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/cli/index.js CHANGED
@@ -3212,7 +3212,7 @@ ${import_picocolors.default.gray(m3)} ${s2}
3212
3212
 
3213
3213
  // src/cli/version.ts
3214
3214
  function getCliVersion() {
3215
- return true ? "1.1.8" : pkg.version;
3215
+ return true ? "1.1.10" : pkg.version;
3216
3216
  }
3217
3217
  var init_version = __esm({
3218
3218
  "src/cli/version.ts"() {
@@ -12429,6 +12429,7 @@ function loadYamlConfig(projectRoot) {
12429
12429
  agent: { ...userConfig.agent, ...projectConfig.agent },
12430
12430
  embedding: { ...userConfig.embedding, ...projectConfig.embedding },
12431
12431
  git: { ...userConfig.git, ...projectConfig.git },
12432
+ codegraph: { ...userConfig.codegraph, ...projectConfig.codegraph },
12432
12433
  behavior: { ...userConfig.behavior, ...projectConfig.behavior },
12433
12434
  server: { ...userConfig.server, ...projectConfig.server },
12434
12435
  team: { ...userConfig.team, ...projectConfig.team }
@@ -12531,6 +12532,7 @@ function mergeTomlConfig(base, override) {
12531
12532
  embedding: { ...base.embedding, ...override.embedding },
12532
12533
  hooks: { ...base.hooks, ...override.hooks },
12533
12534
  git: { ...base.git, ...override.git },
12535
+ codegraph: { ...base.codegraph, ...override.codegraph },
12534
12536
  server: { ...base.server, ...override.server }
12535
12537
  };
12536
12538
  }
@@ -13154,6 +13156,9 @@ function getResolvedConfig(options2 = {}) {
13154
13156
  excludePatterns: firstArray(toml.git?.exclude_patterns, yaml4.git?.excludePatterns),
13155
13157
  noiseKeywords: firstArray(toml.git?.noise_keywords, yaml4.git?.noiseKeywords)
13156
13158
  },
13159
+ codegraph: {
13160
+ excludePatterns: firstArray(toml.codegraph?.exclude_patterns, yaml4.codegraph?.excludePatterns)
13161
+ },
13157
13162
  server: {
13158
13163
  transport: first(toml.server?.transport, yaml4.server?.transport),
13159
13164
  dashboard: firstBool(toml.server?.dashboard, yaml4.server?.dashboard),
@@ -15701,8 +15706,12 @@ function makeEntryKey(projectId, observationId) {
15701
15706
  return `${projectId ?? ""}::${observationId}`;
15702
15707
  }
15703
15708
  function rememberObservationDoc(doc) {
15704
- if (!doc.projectId || typeof doc.observationId !== "number") return;
15705
- docByObservationKey.set(makeEntryKey(doc.projectId, doc.observationId), doc);
15709
+ const publicDoc = { ...doc };
15710
+ delete publicDoc.embedding;
15711
+ if (doc.projectId && typeof doc.observationId === "number") {
15712
+ docByObservationKey.set(makeEntryKey(doc.projectId, doc.observationId), publicDoc);
15713
+ }
15714
+ return publicDoc;
15706
15715
  }
15707
15716
  function isCommandLikeQuery(query) {
15708
15717
  return COMMAND_LIKE_QUERY2.test(query);
@@ -15804,14 +15813,14 @@ async function batchGenerateEmbeddings(texts) {
15804
15813
  }
15805
15814
  async function hydrateIndex(observations2) {
15806
15815
  const database = await getDb();
15807
- const currentCount = await count2(database);
15808
- if (currentCount > 0) return 0;
15809
15816
  let inserted = 0;
15810
15817
  for (const obs of observations2) {
15811
15818
  if (!obs || !obs.id || !obs.projectId) continue;
15812
15819
  try {
15820
+ const id = makeOramaObservationId(obs.projectId, obs.id);
15821
+ if (getByID(database, id)) continue;
15813
15822
  const doc = {
15814
- id: makeOramaObservationId(obs.projectId, obs.id),
15823
+ id,
15815
15824
  observationId: obs.id,
15816
15825
  entityName: obs.entityName || "",
15817
15826
  type: obs.type || "discovery",
@@ -15905,6 +15914,7 @@ async function searchObservations(options2) {
15905
15914
  let searchParams = {
15906
15915
  term: originalQuery,
15907
15916
  limit: requestLimit,
15917
+ includeVectors: true,
15908
15918
  ...Object.keys(filters).length > 0 ? { where: filters } : {},
15909
15919
  // Search specific fields (not tokens, accessCount, etc.)
15910
15920
  properties: ["title", "entityName", "narrative", "facts", "concepts", "filesModified"],
@@ -15976,6 +15986,7 @@ async function searchObservations(options2) {
15976
15986
  const vectorOnlyParams = {
15977
15987
  term: "",
15978
15988
  limit: requestLimit,
15989
+ includeVectors: true,
15979
15990
  ...Object.keys(filters).length > 0 ? { where: filters } : {},
15980
15991
  mode: "vector",
15981
15992
  vector: {
@@ -16274,6 +16285,7 @@ async function getObservationsByIds(ids, projectId) {
16274
16285
  }
16275
16286
  const searchResult = await search2(database, {
16276
16287
  term: "",
16288
+ includeVectors: true,
16277
16289
  where: {
16278
16290
  observationId: { eq: id },
16279
16291
  ...projectId ? { projectId } : {}
@@ -16281,7 +16293,7 @@ async function getObservationsByIds(ids, projectId) {
16281
16293
  limit: 1
16282
16294
  });
16283
16295
  if (searchResult.hits.length > 0) {
16284
- results.push(searchResult.hits[0].document);
16296
+ results.push(rememberObservationDoc(searchResult.hits[0].document));
16285
16297
  }
16286
16298
  }
16287
16299
  return results;
@@ -17250,6 +17262,44 @@ var init_store = __esm({
17250
17262
  ORDER BY path, startLine
17251
17263
  LIMIT ?
17252
17264
  `).all(projectId, like, like, like, limit).map(rowToSymbol);
17265
+ }
17266
+ listSymbols(projectId) {
17267
+ return this.db.prepare(`
17268
+ SELECT * FROM code_symbols
17269
+ WHERE projectId = ? AND stale = 0
17270
+ ORDER BY path, startLine
17271
+ `).all(projectId).map(rowToSymbol);
17272
+ }
17273
+ findSymbolsByNames(projectId, names, fileIds = []) {
17274
+ const candidates = [...new Set(names.map((name) => name.trim()).filter(Boolean))];
17275
+ if (candidates.length === 0) return [];
17276
+ const candidateJson = JSON.stringify(candidates);
17277
+ const hintedFiles = [...new Set(fileIds.map((fileId) => fileId.trim()).filter(Boolean))];
17278
+ if (hintedFiles.length > 0) {
17279
+ return this.db.prepare(`
17280
+ SELECT * FROM code_symbols
17281
+ WHERE projectId = ?
17282
+ AND stale = 0
17283
+ AND name IN (SELECT value FROM json_each(?))
17284
+ AND fileId IN (SELECT value FROM json_each(?))
17285
+ ORDER BY path, startLine
17286
+ `).all(projectId, candidateJson, JSON.stringify(hintedFiles)).map(rowToSymbol);
17287
+ }
17288
+ return this.db.prepare(`
17289
+ SELECT symbols.*
17290
+ FROM code_symbols AS symbols
17291
+ INNER JOIN (
17292
+ SELECT name
17293
+ FROM code_symbols
17294
+ WHERE projectId = ?
17295
+ AND stale = 0
17296
+ AND name IN (SELECT value FROM json_each(?))
17297
+ GROUP BY name
17298
+ HAVING COUNT(*) = 1
17299
+ ) AS unambiguous ON unambiguous.name = symbols.name
17300
+ WHERE symbols.projectId = ? AND symbols.stale = 0
17301
+ ORDER BY symbols.path, symbols.startLine
17302
+ `).all(projectId, candidateJson, projectId).map(rowToSymbol);
17253
17303
  }
17254
17304
  listSymbolsForFile(fileId) {
17255
17305
  return this.db.prepare(`SELECT * FROM code_symbols WHERE fileId = ? AND stale = 0 ORDER BY startLine`).all(fileId).map(rowToSymbol);
@@ -17263,6 +17313,22 @@ var init_store = __esm({
17263
17313
  WHERE projectId = ? AND observationId = ?
17264
17314
  ORDER BY status, id
17265
17315
  `).all(projectId, observationId).map(rowToRef);
17316
+ }
17317
+ listProjectObservationRefs(projectId) {
17318
+ return this.db.prepare(`
17319
+ SELECT * FROM observation_code_refs
17320
+ WHERE projectId = ?
17321
+ ORDER BY observationId, status, id
17322
+ `).all(projectId).map(rowToRef);
17323
+ }
17324
+ listReferencedSymbols(projectId) {
17325
+ return this.db.prepare(`
17326
+ SELECT DISTINCT symbols.*
17327
+ FROM code_symbols AS symbols
17328
+ INNER JOIN observation_code_refs AS refs ON refs.symbolId = symbols.id
17329
+ WHERE refs.projectId = ? AND symbols.projectId = ? AND symbols.stale = 0
17330
+ ORDER BY symbols.path, symbols.startLine
17331
+ `).all(projectId, projectId).map(rowToSymbol);
17266
17332
  }
17267
17333
  status(projectId) {
17268
17334
  const files = this.db.prepare(`SELECT COUNT(*) AS count FROM code_files WHERE projectId = ?`).get(projectId).count;
@@ -17289,6 +17355,15 @@ function mentionsSymbol(text, symbol) {
17289
17355
  const name = symbol.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17290
17356
  return new RegExp(`(^|[^\\w$])${name}([^\\w$]|$)`).test(text);
17291
17357
  }
17358
+ function identifierCandidates(text) {
17359
+ return [...new Set(text.match(/[A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?/g) ?? [])];
17360
+ }
17361
+ function codeIdentifierCandidates(text) {
17362
+ const explicit = /* @__PURE__ */ new Set();
17363
+ for (const match of text.matchAll(/`([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)`/g)) explicit.add(match[1]);
17364
+ for (const match of text.matchAll(/\b([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)\s*\(/g)) explicit.add(match[1]);
17365
+ 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));
17366
+ }
17292
17367
  function fileRef(projectId, obs, file) {
17293
17368
  return {
17294
17369
  id: makeObservationCodeRefId(projectId, obs.id, file.id),
@@ -17315,40 +17390,101 @@ function symbolRef(projectId, obs, file, symbol) {
17315
17390
  createdAt: obs.createdAt
17316
17391
  };
17317
17392
  }
17318
- async function bindObservationToCode(store2, obs) {
17393
+ function resolveObservationCodeRefs(obs, lookup) {
17319
17394
  const refs = /* @__PURE__ */ new Map();
17320
17395
  const text = observationText(obs);
17321
17396
  const candidateFiles = /* @__PURE__ */ new Map();
17322
17397
  for (const rawPath of obs.filesModified ?? []) {
17323
- const file = store2.getFile(obs.projectId, normalizeCodePath(rawPath));
17398
+ const file = lookup.findFile(normalizeCodePath(rawPath));
17324
17399
  if (!file) continue;
17325
17400
  candidateFiles.set(file.id, file);
17326
17401
  const ref = fileRef(obs.projectId, obs, file);
17327
17402
  refs.set(ref.id, ref);
17328
17403
  }
17329
- const symbols = store2.findSymbols(obs.projectId, "", 500);
17404
+ const hintedFileIds = new Set(candidateFiles.keys());
17405
+ const symbolNames = hintedFileIds.size > 0 ? identifierCandidates(text) : codeIdentifierCandidates(text);
17406
+ const symbols = lookup.findSymbols(symbolNames, [...hintedFileIds]);
17407
+ const symbolsByName = /* @__PURE__ */ new Map();
17330
17408
  for (const symbol of symbols) {
17331
- if (!mentionsSymbol(text, symbol)) continue;
17332
- const file = candidateFiles.get(symbol.fileId) ?? store2.getFile(obs.projectId, symbol.path);
17333
- if (!file) continue;
17334
- candidateFiles.set(file.id, file);
17335
- const ref = symbolRef(obs.projectId, obs, file, symbol);
17336
- refs.set(ref.id, ref);
17409
+ const group = symbolsByName.get(symbol.name) ?? [];
17410
+ group.push(symbol);
17411
+ symbolsByName.set(symbol.name, group);
17337
17412
  }
17338
- const result = [...refs.values()];
17339
- store2.replaceObservationRefs(obs.projectId, obs.id, result);
17340
- return result;
17413
+ for (const group of symbolsByName.values()) {
17414
+ const candidates = group.length === 1 ? group : [];
17415
+ for (const symbol of candidates) {
17416
+ if (!mentionsSymbol(text, symbol)) continue;
17417
+ const file = candidateFiles.get(symbol.fileId) ?? lookup.findFile(symbol.path);
17418
+ if (!file) continue;
17419
+ candidateFiles.set(file.id, file);
17420
+ const ref = symbolRef(obs.projectId, obs, file, symbol);
17421
+ refs.set(ref.id, ref);
17422
+ }
17423
+ }
17424
+ return [...refs.values()];
17425
+ }
17426
+ function createStoreLookup(store2, projectId) {
17427
+ return {
17428
+ findFile: (path29) => store2.getFile(projectId, path29) ?? void 0,
17429
+ findSymbols: (names, hintedFileIds) => store2.findSymbolsByNames(projectId, names, hintedFileIds)
17430
+ };
17431
+ }
17432
+ function createSnapshotLookup(files, symbols) {
17433
+ const filesByPath = new Map(files.map((file) => [normalizeCodePath(file.path), file]));
17434
+ const symbolsByName = /* @__PURE__ */ new Map();
17435
+ for (const symbol of symbols) {
17436
+ const group = symbolsByName.get(symbol.name) ?? [];
17437
+ group.push(symbol);
17438
+ symbolsByName.set(symbol.name, group);
17439
+ }
17440
+ return {
17441
+ findFile: (path29) => filesByPath.get(normalizeCodePath(path29)),
17442
+ findSymbols: (names, hintedFileIds) => {
17443
+ const hinted = new Set(hintedFileIds);
17444
+ const found = [];
17445
+ for (const name of names) {
17446
+ const candidates = symbolsByName.get(name) ?? [];
17447
+ found.push(...hinted.size > 0 ? candidates.filter((symbol) => hinted.has(symbol.fileId)) : candidates);
17448
+ }
17449
+ return found;
17450
+ }
17451
+ };
17452
+ }
17453
+ async function bindObservationToCode(store2, obs) {
17454
+ const refs = resolveObservationCodeRefs(obs, createStoreLookup(store2, obs.projectId));
17455
+ store2.replaceObservationRefs(obs.projectId, obs.id, refs);
17456
+ return refs;
17341
17457
  }
17342
17458
  async function backfillMissingObservationCodeRefs(store2, observations2) {
17343
17459
  let observationsBackfilled = 0;
17344
17460
  let refsBackfilled = 0;
17461
+ const boundObservationIdsByProject = /* @__PURE__ */ new Map();
17462
+ const lookupByProject = /* @__PURE__ */ new Map();
17463
+ for (const projectId of new Set(observations2.map((observation) => observation.projectId))) {
17464
+ boundObservationIdsByProject.set(
17465
+ projectId,
17466
+ new Set(store2.listProjectObservationRefs(projectId).map((ref) => ref.observationId))
17467
+ );
17468
+ lookupByProject.set(
17469
+ projectId,
17470
+ createSnapshotLookup(store2.listFiles(projectId), store2.listSymbols(projectId))
17471
+ );
17472
+ }
17473
+ const refsToInsert = [];
17345
17474
  for (const observation of observations2) {
17346
- if (store2.listObservationRefs(observation.projectId, observation.id).length > 0) continue;
17347
- const refs = await bindObservationToCode(store2, observation);
17475
+ if (boundObservationIdsByProject.get(observation.projectId)?.has(observation.id)) continue;
17476
+ if ((observation.filesModified?.length ?? 0) === 0 && codeIdentifierCandidates(observationText(observation)).length === 0) {
17477
+ continue;
17478
+ }
17479
+ const lookup = lookupByProject.get(observation.projectId);
17480
+ if (!lookup) continue;
17481
+ const refs = resolveObservationCodeRefs(observation, lookup);
17348
17482
  if (refs.length === 0) continue;
17349
17483
  observationsBackfilled += 1;
17350
17484
  refsBackfilled += refs.length;
17485
+ refsToInsert.push(...refs);
17351
17486
  }
17487
+ store2.upsertObservationRefs(refsToInsert);
17352
17488
  return {
17353
17489
  observationsScanned: observations2.length,
17354
17490
  observationsBackfilled,
@@ -23258,11 +23394,17 @@ __export(config_get_exports, {
23258
23394
  function readDotted(source, key) {
23259
23395
  let cursor = source;
23260
23396
  for (const part of key.split(".")) {
23261
- if (!cursor || typeof cursor !== "object" || !(part in cursor)) return void 0;
23262
- cursor = cursor[part];
23397
+ if (!cursor || typeof cursor !== "object") return void 0;
23398
+ const record2 = cursor;
23399
+ const normalizedPart = part in record2 ? part : snakeToCamel(part);
23400
+ if (!(normalizedPart in record2)) return void 0;
23401
+ cursor = record2[normalizedPart];
23263
23402
  }
23264
23403
  return cursor;
23265
23404
  }
23405
+ function snakeToCamel(value) {
23406
+ return value.replace(/_([a-z])/g, (_4, char) => char.toUpperCase());
23407
+ }
23266
23408
  function formatValue(value, key) {
23267
23409
  if (typeof value === "string") return shouldRedactKey(key) ? "<redacted>" : value;
23268
23410
  if (typeof value === "number" || typeof value === "boolean") return String(value);
@@ -26289,6 +26431,58 @@ var init_current_facts = __esm({
26289
26431
  }
26290
26432
  });
26291
26433
 
26434
+ // src/codegraph/exclude.ts
26435
+ function normalizeCodeGraphExcludePatterns(exclude) {
26436
+ return [.../* @__PURE__ */ new Set([
26437
+ ...DEFAULT_CODEGRAPH_EXCLUDES,
26438
+ ...(exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean)
26439
+ ])];
26440
+ }
26441
+ function isCodeGraphExcludedPath(path29, exclude) {
26442
+ const normalized = normalizeCodePath2(path29);
26443
+ return normalizeCodeGraphExcludePatterns(exclude).some((pattern) => matchesPattern(normalized, normalizeCodePath2(pattern)));
26444
+ }
26445
+ function normalizeCodePath2(path29) {
26446
+ return path29.replace(/\\/g, "/").replace(/^\.\/+/, "");
26447
+ }
26448
+ function matchesPattern(path29, pattern) {
26449
+ if (pattern.endsWith("/**")) {
26450
+ const base = pattern.slice(0, -3);
26451
+ if (base.startsWith("**/")) {
26452
+ const suffix = base.slice(3);
26453
+ return path29 === suffix || path29.endsWith(`/${suffix}`) || path29.includes(`/${suffix}/`);
26454
+ }
26455
+ if (!base.includes("/")) {
26456
+ return path29 === base || path29.startsWith(`${base}/`) || path29.includes(`/${base}/`);
26457
+ }
26458
+ return path29 === base || path29.startsWith(`${base}/`);
26459
+ }
26460
+ if (pattern.startsWith("**/")) {
26461
+ const suffix = pattern.slice(3);
26462
+ return path29 === suffix || path29.endsWith(`/${suffix}`);
26463
+ }
26464
+ return path29 === pattern || path29.startsWith(`${pattern}/`);
26465
+ }
26466
+ var DEFAULT_CODEGRAPH_EXCLUDES;
26467
+ var init_exclude = __esm({
26468
+ "src/codegraph/exclude.ts"() {
26469
+ "use strict";
26470
+ init_esm_shims();
26471
+ DEFAULT_CODEGRAPH_EXCLUDES = [
26472
+ "node_modules/**",
26473
+ "dist/**",
26474
+ "build/**",
26475
+ "coverage/**",
26476
+ ".next/**",
26477
+ ".turbo/**",
26478
+ ".git/**",
26479
+ ".tmp/**",
26480
+ ".worktrees/**",
26481
+ ".claude/worktrees/**"
26482
+ ];
26483
+ }
26484
+ });
26485
+
26292
26486
  // src/codegraph/lite-provider.ts
26293
26487
  import { createHash as createHash4 } from "crypto";
26294
26488
  import { readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync2 } from "fs";
@@ -26304,18 +26498,6 @@ function languageForPath(path29) {
26304
26498
  const ext = extension(path29);
26305
26499
  return LANGUAGE_BY_EXTENSION.get(ext) ?? "unknown";
26306
26500
  }
26307
- function isExcluded(path29, exclude) {
26308
- const normalized = normalizeCodePath(path29);
26309
- return exclude.some((pattern) => {
26310
- const p2 = normalizeCodePath(pattern);
26311
- if (p2.endsWith("/**")) {
26312
- const base = p2.slice(0, -3);
26313
- return normalized === base || normalized.startsWith(`${base}/`);
26314
- }
26315
- if (p2.startsWith("**/")) return normalized.endsWith(p2.slice(3));
26316
- return normalized === p2 || normalized.startsWith(`${p2}/`);
26317
- });
26318
- }
26319
26501
  function walk(root, exclude, maxFiles) {
26320
26502
  const out = [];
26321
26503
  const visit = (dir) => {
@@ -26329,7 +26511,7 @@ function walk(root, exclude, maxFiles) {
26329
26511
  for (const entry of entries) {
26330
26512
  const abs = join20(dir, entry.name);
26331
26513
  const rel = normalizeCodePath(relative(root, abs));
26332
- if (isExcluded(rel, exclude)) continue;
26514
+ if (isCodeGraphExcludedPath(rel, exclude)) continue;
26333
26515
  if (entry.isDirectory()) {
26334
26516
  if (entry.name === ".git" || entry.name === ".worktrees" || entry.name === ".tmp" || entry.name === "node_modules") continue;
26335
26517
  if (rel === ".claude/worktrees" || rel.startsWith(".claude/worktrees/")) continue;
@@ -26417,18 +26599,7 @@ function extractImportEdges(projectId, file, text, indexedAt) {
26417
26599
  return edges;
26418
26600
  }
26419
26601
  async function indexProjectLite(options2) {
26420
- const exclude = options2.exclude ?? [
26421
- "node_modules/**",
26422
- "dist/**",
26423
- "build/**",
26424
- "coverage/**",
26425
- ".next/**",
26426
- ".turbo/**",
26427
- ".git/**",
26428
- ".tmp/**",
26429
- ".worktrees/**",
26430
- ".claude/worktrees/**"
26431
- ];
26602
+ const exclude = normalizeCodeGraphExcludePatterns(options2.exclude);
26432
26603
  const maxFiles = options2.maxFiles ?? 5e3;
26433
26604
  const indexedAt = (/* @__PURE__ */ new Date()).toISOString();
26434
26605
  const paths = walk(options2.projectRoot, exclude, maxFiles);
@@ -26467,6 +26638,7 @@ var init_lite_provider = __esm({
26467
26638
  "use strict";
26468
26639
  init_esm_shims();
26469
26640
  init_ids();
26641
+ init_exclude();
26470
26642
  LANGUAGE_BY_EXTENSION = /* @__PURE__ */ new Map([
26471
26643
  [".ts", "typescript"],
26472
26644
  [".tsx", "typescript"],
@@ -26578,8 +26750,8 @@ var init_lite_provider = __esm({
26578
26750
  },
26579
26751
  ruby: {
26580
26752
  symbols: [
26581
- { kind: "class", re: /^class\s+([A-Za-z_][\w:]*?)\b/gm },
26582
- { kind: "function", re: /^def\s+([A-Za-z_][\w!?=]*)\b/gm }
26753
+ { kind: "class", re: /^class\s+([A-Za-z_][\w]*(?:::[A-Za-z_][\w]*)*)/gm },
26754
+ { kind: "function", re: /^def\s+([A-Za-z_][\w]*[!?=]?)/gm }
26583
26755
  ],
26584
26756
  imports: [/require\s+['"]([^'"]+)['"]/g]
26585
26757
  },
@@ -26643,30 +26815,24 @@ function countLanguages(files) {
26643
26815
  }
26644
26816
  return [...counts.entries()].map(([language, files2]) => ({ language, files: files2 })).sort((a3, b3) => a3.language.localeCompare(b3.language));
26645
26817
  }
26646
- function normalizePath2(path29) {
26647
- return path29.replace(/\\/g, "/");
26648
- }
26649
- function isGeneratedPath(path29) {
26650
- const normalized = normalizePath2(path29);
26651
- return /(^|\/)(dist|build|coverage|\.next|\.turbo|node_modules|\.git|\.tmp|\.worktrees)(\/|$)/i.test(normalized) || /(^|\/)\.claude\/worktrees(\/|$)/i.test(normalized);
26652
- }
26653
26818
  function suggestedReadRank(path29) {
26654
- const normalized = normalizePath2(path29);
26819
+ const normalized = path29.replace(/\\/g, "/");
26655
26820
  if (normalized.startsWith("src/")) return 0;
26656
26821
  if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 0;
26657
26822
  if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
26658
26823
  return 3;
26659
26824
  }
26660
- function compactSuggestedReads(paths, limit = 8) {
26661
- return uniq(paths).filter((path29) => !isGeneratedPath(path29)).sort((a3, b3) => suggestedReadRank(a3) - suggestedReadRank(b3)).slice(0, limit);
26825
+ function compactSuggestedReads(paths, limit = 8, exclude) {
26826
+ return uniq(paths).filter((path29) => !isCodeGraphExcludedPath(path29, exclude)).sort((a3, b3) => suggestedReadRank(a3) - suggestedReadRank(b3)).slice(0, limit);
26662
26827
  }
26663
- function collectGraph(store2, projectId, observations2) {
26828
+ function collectGraph(store2, projectId, observations2, exclude) {
26664
26829
  const files = store2.listFiles(projectId);
26665
- const symbols = files.flatMap((file) => store2.listSymbolsForFile(file.id));
26830
+ const symbols = store2.listReferencedSymbols(projectId);
26666
26831
  const filesById = new Map(files.map((file) => [file.id, file]));
26667
26832
  const symbolsById = new Map(symbols.map((symbol) => [symbol.id, symbol]));
26668
26833
  const observationsById = new Map(observations2.map((obs) => [obs.id, obs]));
26669
- const refs = observations2.flatMap((obs) => store2.listObservationRefs(projectId, obs.id));
26834
+ const activeObservationIds = new Set(observations2.map((obs) => obs.id));
26835
+ const refs = store2.listProjectObservationRefs(projectId).filter((ref) => activeObservationIds.has(ref.observationId));
26670
26836
  const freshness = {
26671
26837
  current: 0,
26672
26838
  suspect: 0,
@@ -26682,7 +26848,9 @@ function collectGraph(store2, projectId, observations2) {
26682
26848
  freshness[result.status] += 1;
26683
26849
  const observation = observationsById.get(ref.observationId);
26684
26850
  if (!observation) continue;
26685
- if (result.status === "current" && file) suggestedReads.push(file.path);
26851
+ const excluded = file ? isCodeGraphExcludedPath(file.path, exclude) : false;
26852
+ if (result.status === "current" && file && !excluded) suggestedReads.push(file.path);
26853
+ if (excluded) continue;
26686
26854
  sources.push({
26687
26855
  observationId: observation.id,
26688
26856
  title: observation.title,
@@ -26699,12 +26867,10 @@ function collectGraph(store2, projectId, observations2) {
26699
26867
  refs,
26700
26868
  freshness,
26701
26869
  sources,
26702
- suggestedReads: compactSuggestedReads(suggestedReads)
26870
+ suggestedReads: compactSuggestedReads(suggestedReads, 8, exclude)
26703
26871
  };
26704
26872
  }
26705
- function buildProjectContextOverview(input) {
26706
- const active = activeObservations(input.observations, input.project.id);
26707
- const graph = collectGraph(input.store, input.project.id, active);
26873
+ function overviewFromGraph(input) {
26708
26874
  const status = input.store.status(input.project.id);
26709
26875
  return {
26710
26876
  project: input.project,
@@ -26715,20 +26881,37 @@ function buildProjectContextOverview(input) {
26715
26881
  edges: status.edges,
26716
26882
  refs: status.refs,
26717
26883
  ...status.indexedAt ? { indexedAt: status.indexedAt } : {},
26718
- languages: countLanguages(graph.files)
26884
+ languages: countLanguages(input.graph.files)
26719
26885
  },
26720
26886
  memory: {
26721
26887
  total: input.observations.filter((obs) => obs.projectId === input.project.id).length,
26722
- active: active.length
26888
+ active: input.active.length
26723
26889
  },
26724
- freshness: graph.freshness,
26725
- suggestedReads: graph.suggestedReads
26890
+ freshness: input.graph.freshness,
26891
+ suggestedReads: input.graph.suggestedReads
26726
26892
  };
26727
26893
  }
26894
+ function buildProjectContextOverview(input) {
26895
+ const active = activeObservations(input.observations, input.project.id);
26896
+ const graph = collectGraph(input.store, input.project.id, active, input.exclude);
26897
+ return overviewFromGraph({
26898
+ project: input.project,
26899
+ store: input.store,
26900
+ observations: input.observations,
26901
+ active,
26902
+ graph
26903
+ });
26904
+ }
26728
26905
  function buildProjectContextExplain(input) {
26729
- const overview = buildProjectContextOverview(input);
26730
26906
  const active = activeObservations(input.observations, input.project.id);
26731
- const graph = collectGraph(input.store, input.project.id, active);
26907
+ const graph = collectGraph(input.store, input.project.id, active, input.exclude);
26908
+ const overview = overviewFromGraph({
26909
+ project: input.project,
26910
+ store: input.store,
26911
+ observations: input.observations,
26912
+ active,
26913
+ graph
26914
+ });
26732
26915
  return {
26733
26916
  project: input.project,
26734
26917
  sources: graph.sources.sort((a3, b3) => a3.observationId - b3.observationId || (a3.path ?? "").localeCompare(b3.path ?? "")),
@@ -26782,11 +26965,12 @@ var init_project_context = __esm({
26782
26965
  "use strict";
26783
26966
  init_esm_shims();
26784
26967
  init_freshness2();
26968
+ init_exclude();
26785
26969
  }
26786
26970
  });
26787
26971
 
26788
26972
  // src/codegraph/task-lens.ts
26789
- function normalizePath3(path29) {
26973
+ function normalizePath2(path29) {
26790
26974
  return path29.replace(/\\/g, "/");
26791
26975
  }
26792
26976
  function tokenize3(text) {
@@ -26816,7 +27000,7 @@ function resolveTaskLens(task) {
26816
27000
  return best.score > 0 ? LENSES[best.id] : LENSES.general;
26817
27001
  }
26818
27002
  function pathKindScore(path29, lens) {
26819
- const normalized = normalizePath3(path29).toLowerCase();
27003
+ const normalized = normalizePath2(path29).toLowerCase();
26820
27004
  const name = normalized.split("/").pop() ?? normalized;
26821
27005
  const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
26822
27006
  const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
@@ -26865,7 +27049,7 @@ function sourceTaskMatchScore(source, task) {
26865
27049
  return 0;
26866
27050
  }
26867
27051
  function fallbackPathRank(path29) {
26868
- const normalized = normalizePath3(path29);
27052
+ const normalized = normalizePath2(path29);
26869
27053
  if (normalized.startsWith("src/")) return 0;
26870
27054
  if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
26871
27055
  if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
@@ -26874,7 +27058,7 @@ function fallbackPathRank(path29) {
26874
27058
  }
26875
27059
  function rankLensPaths(paths, lens, task) {
26876
27060
  const tokens = tokenize3(task ?? "");
26877
- return [...new Set(paths.map(normalizePath3))].map((path29, index) => ({
27061
+ return [...new Set(paths.map(normalizePath2))].map((path29, index) => ({
26878
27062
  path: path29,
26879
27063
  index,
26880
27064
  score: pathKindScore(path29, lens) + tokenScore(path29, tokens),
@@ -27128,6 +27312,7 @@ async function buildAutoProjectContext(input) {
27128
27312
  const now = input.now ?? /* @__PURE__ */ new Date();
27129
27313
  const task = input.task?.trim();
27130
27314
  const lens = resolveTaskLens(task);
27315
+ const exclude = input.exclude ?? getResolvedConfig({ projectRoot: input.project.rootPath }).codegraph.excludePatterns;
27131
27316
  const store2 = new CodeGraphStore();
27132
27317
  await store2.init(input.dataDir);
27133
27318
  const initialStatus = store2.status(input.project.id);
@@ -27145,7 +27330,8 @@ async function buildAutoProjectContext(input) {
27145
27330
  try {
27146
27331
  const indexed = await indexProjectLite({
27147
27332
  projectId: input.project.id,
27148
- projectRoot: input.project.rootPath
27333
+ projectRoot: input.project.rootPath,
27334
+ exclude
27149
27335
  });
27150
27336
  store2.replaceProjectIndex(input.project.id, indexed);
27151
27337
  const backfill = await backfillMissingObservationCodeRefs(
@@ -27162,16 +27348,13 @@ async function buildAutoProjectContext(input) {
27162
27348
  };
27163
27349
  }
27164
27350
  }
27165
- const overview = buildProjectContextOverview({
27166
- project: input.project,
27167
- store: store2,
27168
- observations: input.observations
27169
- });
27170
27351
  const explain = buildProjectContextExplain({
27171
27352
  project: input.project,
27172
27353
  store: store2,
27173
- observations: input.observations
27354
+ observations: input.observations,
27355
+ exclude
27174
27356
  });
27357
+ const overview = explain.overview;
27175
27358
  return {
27176
27359
  project: input.project,
27177
27360
  ...task ? { task } : {},
@@ -27380,6 +27563,7 @@ var init_auto_context = __esm({
27380
27563
  "src/codegraph/auto-context.ts"() {
27381
27564
  "use strict";
27382
27565
  init_esm_shims();
27566
+ init_resolved_config();
27383
27567
  init_binder();
27384
27568
  init_current_facts();
27385
27569
  init_lite_provider();
@@ -27516,10 +27700,6 @@ function tokenize4(text) {
27516
27700
  function timestampOf(observation) {
27517
27701
  return Date.parse(observation.updatedAt ?? observation.createdAt ?? "") || 0;
27518
27702
  }
27519
- function isGeneratedPath2(path29) {
27520
- const normalized = path29.replace(/\\/g, "/");
27521
- return /(^|\/)(dist|build|coverage|\.next|\.turbo|node_modules|\.git|\.tmp|\.worktrees)(\/|$)/i.test(normalized) || /(^|\/)\.claude\/worktrees(\/|$)/i.test(normalized);
27522
- }
27523
27703
  function relevanceScore(observation, taskTokens) {
27524
27704
  if (taskTokens.length === 0) return 0;
27525
27705
  const title = observation.title.toLowerCase();
@@ -27570,7 +27750,8 @@ function assembleContextPackForTask(input) {
27570
27750
  refs,
27571
27751
  files,
27572
27752
  symbols,
27573
- suggestedVerification: input.suggestedVerification
27753
+ suggestedVerification: input.suggestedVerification,
27754
+ exclude: input.exclude
27574
27755
  });
27575
27756
  }
27576
27757
  function assembleContextPack(input) {
@@ -27589,6 +27770,7 @@ function assembleContextPack(input) {
27589
27770
  observationIdsWithRefs.add(ref.observationId);
27590
27771
  const file = ref.fileId ? files.get(ref.fileId) : void 0;
27591
27772
  const symbol = ref.symbolId ? symbols.get(ref.symbolId) : void 0;
27773
+ if (file && isCodeGraphExcludedPath(file.path, input.exclude)) continue;
27592
27774
  const freshness = evaluateCodeRefFreshness(ref, file, symbol);
27593
27775
  if (freshness.status === "current") {
27594
27776
  const memoryKey = `${observation.id}:${freshness.status}`;
@@ -27654,8 +27836,8 @@ function buildContextPackPrompt(pack) {
27654
27836
  const reliableMemories = pack.memories.filter((memory) => memory.status === "current");
27655
27837
  const unboundMemories = pack.memories.filter((memory) => memory.status === "unbound");
27656
27838
  const lines = ["## Task", pack.task, "", "## Reliable Memories"];
27657
- const visibleCodeFacts = pack.codeFacts.filter((fact) => !isGeneratedPath2(fact.path)).slice(0, 5);
27658
- const visibleSuggestedReads = pack.suggestedReads.filter((path29) => !isGeneratedPath2(path29)).slice(0, 5);
27839
+ const visibleCodeFacts = pack.codeFacts.filter((fact) => !isCodeGraphExcludedPath(fact.path)).slice(0, 5);
27840
+ const visibleSuggestedReads = pack.suggestedReads.filter((path29) => !isCodeGraphExcludedPath(path29)).slice(0, 5);
27659
27841
  if (reliableMemories.length === 0) lines.push("- none");
27660
27842
  for (const memory of reliableMemories) {
27661
27843
  lines.push(`- #${memory.id} ${memory.status}: [${memory.type}] ${memory.title} (${memory.reason})`);
@@ -27690,6 +27872,7 @@ var init_context_pack = __esm({
27690
27872
  "use strict";
27691
27873
  init_esm_shims();
27692
27874
  init_freshness2();
27875
+ init_exclude();
27693
27876
  }
27694
27877
  });
27695
27878
 
@@ -27728,6 +27911,7 @@ var init_codegraph = __esm({
27728
27911
  init_lite_provider();
27729
27912
  init_context_pack();
27730
27913
  init_binder();
27914
+ init_resolved_config();
27731
27915
  init_observations();
27732
27916
  init_operator_shared();
27733
27917
  codegraph_default = defineCommand({
@@ -27750,6 +27934,7 @@ var init_codegraph = __esm({
27750
27934
  const store2 = new CodeGraphStore();
27751
27935
  await store2.init(dataDir);
27752
27936
  const explicitAction = Boolean(positional[0] || args.action);
27937
+ const exclude = getResolvedConfig({ projectRoot: project.rootPath }).codegraph.excludePatterns;
27753
27938
  switch (action) {
27754
27939
  case "status": {
27755
27940
  const status = store2.status(project.id);
@@ -27762,7 +27947,8 @@ ${formatUsageHint()}`;
27762
27947
  case "refresh": {
27763
27948
  const indexed = await indexProjectLite({
27764
27949
  projectId: project.id,
27765
- projectRoot: project.rootPath
27950
+ projectRoot: project.rootPath,
27951
+ exclude
27766
27952
  });
27767
27953
  store2.replaceProjectIndex(project.id, indexed);
27768
27954
  const activeObservations2 = getAllObservations().filter((obs) => obs.projectId === project.id && (obs.status ?? "active") === "active");
@@ -27793,7 +27979,8 @@ ${formatUsageHint()}`;
27793
27979
  projectId: project.id,
27794
27980
  task,
27795
27981
  observations: observations2,
27796
- limit
27982
+ limit,
27983
+ exclude
27797
27984
  });
27798
27985
  emitResult({ project, pack }, buildContextPackPrompt(pack), asJson);
27799
27986
  return;
@@ -56515,6 +56702,7 @@ var require_loader = __commonJS({
56515
56702
  this.legacy = options2["legacy"] || false;
56516
56703
  this.json = options2["json"] || false;
56517
56704
  this.listener = options2["listener"] || null;
56705
+ this.maxTotalMergeKeys = typeof options2["maxTotalMergeKeys"] === "number" ? options2["maxTotalMergeKeys"] : 1e4;
56518
56706
  this.implicitTypes = this.schema.compiledImplicit;
56519
56707
  this.typeMap = this.schema.compiledTypeMap;
56520
56708
  this.length = input.length;
@@ -56522,6 +56710,7 @@ var require_loader = __commonJS({
56522
56710
  this.line = 0;
56523
56711
  this.lineStart = 0;
56524
56712
  this.lineIndent = 0;
56713
+ this.totalMergeKeys = 0;
56525
56714
  this.documents = [];
56526
56715
  }
56527
56716
  function generateError(state, message) {
@@ -56606,6 +56795,9 @@ var require_loader = __commonJS({
56606
56795
  sourceKeys = Object.keys(source);
56607
56796
  for (index = 0, quantity = sourceKeys.length; index < quantity; index += 1) {
56608
56797
  key = sourceKeys[index];
56798
+ if (state.maxTotalMergeKeys !== -1 && ++state.totalMergeKeys > state.maxTotalMergeKeys) {
56799
+ throwError(state, "merge keys exceeded maxTotalMergeKeys (" + state.maxTotalMergeKeys + ")");
56800
+ }
56609
56801
  if (!_hasOwnProperty.call(destination, key)) {
56610
56802
  setProperty(destination, key, source[key]);
56611
56803
  overridableKeys[key] = true;
@@ -61964,7 +62156,7 @@ The path should point to a directory containing a .git folder.`
61964
62156
  };
61965
62157
  const server = existingServer ?? new McpServer({
61966
62158
  name: "memorix",
61967
- version: true ? "1.1.8" : "1.0.1"
62159
+ version: true ? "1.1.10" : "1.0.1"
61968
62160
  });
61969
62161
  const originalRegisterTool = server.registerTool.bind(server);
61970
62162
  server.registerTool = ((name, ...args) => {
@@ -62666,15 +62858,18 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
62666
62858
  return withFreshIndex(async () => {
62667
62859
  const { CodeGraphStore: CodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
62668
62860
  const { assembleContextPackForTask: assembleContextPackForTask2, buildContextPackPrompt: buildContextPackPrompt2 } = await Promise.resolve().then(() => (init_context_pack(), context_pack_exports));
62861
+ const { getResolvedConfig: getResolvedConfig2 } = await Promise.resolve().then(() => (init_resolved_config(), resolved_config_exports));
62669
62862
  const store2 = new CodeGraphStore2();
62670
62863
  await store2.init(projectDir2);
62864
+ const exclude = getResolvedConfig2({ projectRoot: project.rootPath }).codegraph.excludePatterns;
62671
62865
  const observations2 = getAllObservations().filter((obs) => obs.projectId === project.id && (obs.status ?? "active") === "active").reverse();
62672
62866
  const pack = assembleContextPackForTask2({
62673
62867
  store: store2,
62674
62868
  projectId: project.id,
62675
62869
  task,
62676
62870
  observations: observations2,
62677
- limit: typeof limit === "number" ? limit : 20
62871
+ limit: typeof limit === "number" ? limit : 20,
62872
+ exclude
62678
62873
  });
62679
62874
  const text = buildContextPackPrompt2(pack);
62680
62875
  return {
@@ -71470,7 +71665,7 @@ function getMemorixDir() {
71470
71665
  const home = process.env.HOME || process.env.USERPROFILE || "";
71471
71666
  return home.replace(/\\/g, "/") + "/.memorix";
71472
71667
  }
71473
- function normalizePath4(p2) {
71668
+ function normalizePath3(p2) {
71474
71669
  return process.platform === "win32" ? p2.replace(/\//g, "\\") : p2;
71475
71670
  }
71476
71671
  function getStateFilePath() {
@@ -71636,7 +71831,7 @@ async function doStart(port) {
71636
71831
  ` Port: ${port}`,
71637
71832
  ` Dashboard: http://127.0.0.1:${port}/`,
71638
71833
  ` MCP: http://127.0.0.1:${port}/mcp`,
71639
- ` Logs: ${normalizePath4(logFile)}`,
71834
+ ` Logs: ${normalizePath3(logFile)}`,
71640
71835
  ""
71641
71836
  ].join("\n");
71642
71837
  process.stderr.write(startMsg + "\n");
@@ -71650,7 +71845,7 @@ async function doStart(port) {
71650
71845
  }
71651
71846
  if (!isProcessRunning(pid)) {
71652
71847
  console.error("[ERROR] Background process exited unexpectedly.");
71653
- console.error(` Check logs: ${normalizePath4(logFile)}`);
71848
+ console.error(` Check logs: ${normalizePath3(logFile)}`);
71654
71849
  clearState();
71655
71850
  process.exitCode = 1;
71656
71851
  return;
@@ -71788,7 +71983,7 @@ async function doStatus() {
71788
71983
  if (state.instanceToken) console.log(` Instance: ${state.instanceToken.slice(0, 8)}\u2026`);
71789
71984
  console.log(` Dashboard: http://127.0.0.1:${state.port}/`);
71790
71985
  console.log(` MCP: http://127.0.0.1:${state.port}/mcp`);
71791
- console.log(` Logs: ${normalizePath4(state.logFile)}`);
71986
+ console.log(` Logs: ${normalizePath3(state.logFile)}`);
71792
71987
  if (health.ok && health.data) {
71793
71988
  const d3 = health.data;
71794
71989
  console.log("");
@@ -72759,8 +72954,10 @@ var init_doctor = __esm({
72759
72954
  try {
72760
72955
  const { CodeGraphStore: CodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
72761
72956
  const { buildProjectContextOverview: buildProjectContextOverview2 } = await Promise.resolve().then(() => (init_project_context(), project_context_exports));
72957
+ const { getResolvedConfig: getResolvedConfig2 } = await Promise.resolve().then(() => (init_resolved_config(), resolved_config_exports));
72762
72958
  const codeStore = new CodeGraphStore2();
72763
72959
  await codeStore.init(dataDir);
72960
+ const exclude = getResolvedConfig2({ projectRoot: projectRoot || process.cwd() }).codegraph.excludePatterns;
72764
72961
  const overview = buildProjectContextOverview2({
72765
72962
  project: {
72766
72963
  id: projectId,
@@ -72768,7 +72965,8 @@ var init_doctor = __esm({
72768
72965
  rootPath: projectRoot || process.cwd()
72769
72966
  },
72770
72967
  store: codeStore,
72771
- observations: projectObservations
72968
+ observations: projectObservations,
72969
+ exclude
72772
72970
  });
72773
72971
  const languageText = overview.code.languages.length > 0 ? overview.code.languages.map((item) => `${item.language} ${item.files}`).join(", ") : "none indexed yet";
72774
72972
  if (overview.code.files > 0) {
@@ -74826,8 +75024,8 @@ function buildIdleReasons(available, dispatchedNames, config2, excludeAgents) {
74826
75024
  const excluded = excludeAgents ?? /* @__PURE__ */ new Set();
74827
75025
  for (const adapter of available) {
74828
75026
  if (dispatchedNames.has(adapter.name)) continue;
74829
- const isExcluded2 = excluded.has(adapter.name);
74830
- if (isExcluded2) {
75027
+ const isExcluded = excluded.has(adapter.name);
75028
+ if (isExcluded) {
74831
75029
  result.push({ name: adapter.name, reason: "excluded due to prior failure" });
74832
75030
  } else {
74833
75031
  const inAnyPref = Object.values(DEFAULT_ROLE_PREFERENCES).some((prefs) => prefs.includes(adapter.name));