memorix 0.9.34 → 0.9.35

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
@@ -2615,10 +2615,10 @@ function assignProp(target, prop, value) {
2615
2615
  configurable: true
2616
2616
  });
2617
2617
  }
2618
- function getElementAtPath(obj, path9) {
2619
- if (!path9)
2618
+ function getElementAtPath(obj, path10) {
2619
+ if (!path10)
2620
2620
  return obj;
2621
- return path9.reduce((acc, key) => acc?.[key], obj);
2621
+ return path10.reduce((acc, key) => acc?.[key], obj);
2622
2622
  }
2623
2623
  function promiseAllObject(promisesObj) {
2624
2624
  const keys = Object.keys(promisesObj);
@@ -2867,11 +2867,11 @@ function aborted(x3, startIndex = 0) {
2867
2867
  }
2868
2868
  return false;
2869
2869
  }
2870
- function prefixIssues(path9, issues) {
2870
+ function prefixIssues(path10, issues) {
2871
2871
  return issues.map((iss) => {
2872
2872
  var _a;
2873
2873
  (_a = iss).path ?? (_a.path = []);
2874
- iss.path.unshift(path9);
2874
+ iss.path.unshift(path10);
2875
2875
  return iss;
2876
2876
  });
2877
2877
  }
@@ -9225,8 +9225,8 @@ var init_parseUtil = __esm({
9225
9225
  init_errors3();
9226
9226
  init_en2();
9227
9227
  makeIssue = (params) => {
9228
- const { data, path: path9, errorMaps, issueData } = params;
9229
- const fullPath = [...path9, ...issueData.path || []];
9228
+ const { data, path: path10, errorMaps, issueData } = params;
9229
+ const fullPath = [...path10, ...issueData.path || []];
9230
9230
  const fullIssue = {
9231
9231
  ...issueData,
9232
9232
  path: fullPath
@@ -9540,11 +9540,11 @@ var init_types2 = __esm({
9540
9540
  init_parseUtil();
9541
9541
  init_util2();
9542
9542
  ParseInputLazyPath = class {
9543
- constructor(parent, value, path9, key) {
9543
+ constructor(parent, value, path10, key) {
9544
9544
  this._cachedPath = [];
9545
9545
  this.parent = parent;
9546
9546
  this.data = value;
9547
- this._path = path9;
9547
+ this._path = path10;
9548
9548
  this._key = key;
9549
9549
  }
9550
9550
  get path() {
@@ -19090,8 +19090,8 @@ var require_utils = __commonJS({
19090
19090
  }
19091
19091
  return ind;
19092
19092
  }
19093
- function removeDotSegments(path9) {
19094
- let input = path9;
19093
+ function removeDotSegments(path10) {
19094
+ let input = path10;
19095
19095
  const output = [];
19096
19096
  let nextSlash = -1;
19097
19097
  let len = 0;
@@ -19291,8 +19291,8 @@ var require_schemes = __commonJS({
19291
19291
  wsComponent.secure = void 0;
19292
19292
  }
19293
19293
  if (wsComponent.resourceName) {
19294
- const [path9, query] = wsComponent.resourceName.split("?");
19295
- wsComponent.path = path9 && path9 !== "/" ? path9 : void 0;
19294
+ const [path10, query] = wsComponent.resourceName.split("?");
19295
+ wsComponent.path = path10 && path10 !== "/" ? path10 : void 0;
19296
19296
  wsComponent.query = query;
19297
19297
  wsComponent.resourceName = void 0;
19298
19298
  }
@@ -22691,12 +22691,12 @@ var require_dist = __commonJS({
22691
22691
  throw new Error(`Unknown format "${name}"`);
22692
22692
  return f4;
22693
22693
  };
22694
- function addFormats(ajv, list, fs6, exportName) {
22694
+ function addFormats(ajv, list, fs7, exportName) {
22695
22695
  var _a;
22696
22696
  var _b;
22697
22697
  (_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
22698
22698
  for (const f4 of list)
22699
- ajv.addFormat(f4, fs6[f4]);
22699
+ ajv.addFormat(f4, fs7[f4]);
22700
22700
  }
22701
22701
  module2.exports = exports2 = formatsPlugin;
22702
22702
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -25249,15 +25249,15 @@ function getDocumentProperties(doc, paths) {
25249
25249
  const properties = {};
25250
25250
  const pathsLength = paths.length;
25251
25251
  for (let i2 = 0; i2 < pathsLength; i2++) {
25252
- const path9 = paths[i2];
25253
- const pathTokens = path9.split(".");
25252
+ const path10 = paths[i2];
25253
+ const pathTokens = path10.split(".");
25254
25254
  let current = doc;
25255
25255
  const pathTokensLength = pathTokens.length;
25256
25256
  for (let j3 = 0; j3 < pathTokensLength; j3++) {
25257
25257
  current = current[pathTokens[j3]];
25258
25258
  if (typeof current === "object") {
25259
25259
  if (current !== null && "lat" in current && "lon" in current && typeof current.lat === "number" && typeof current.lon === "number") {
25260
- current = properties[path9] = current;
25260
+ current = properties[path10] = current;
25261
25261
  break;
25262
25262
  } else if (!Array.isArray(current) && current !== null && j3 === pathTokensLength - 1) {
25263
25263
  current = void 0;
@@ -25269,14 +25269,14 @@ function getDocumentProperties(doc, paths) {
25269
25269
  }
25270
25270
  }
25271
25271
  if (typeof current !== "undefined") {
25272
- properties[path9] = current;
25272
+ properties[path10] = current;
25273
25273
  }
25274
25274
  }
25275
25275
  return properties;
25276
25276
  }
25277
- function getNested(obj, path9) {
25278
- const props = getDocumentProperties(obj, [path9]);
25279
- return props[path9];
25277
+ function getNested(obj, path10) {
25278
+ const props = getDocumentProperties(obj, [path10]);
25279
+ return props[path10];
25280
25280
  }
25281
25281
  function convertDistanceToMeters(distance, unit) {
25282
25282
  const ratio = mapDistanceToMeters[unit];
@@ -25292,10 +25292,10 @@ function removeVectorsFromHits(searchResult, vectorProperties) {
25292
25292
  ...result.document,
25293
25293
  // Remove embeddings from the result
25294
25294
  ...vectorProperties.reduce((acc, prop) => {
25295
- const path9 = prop.split(".");
25296
- const lastKey = path9.pop();
25295
+ const path10 = prop.split(".");
25296
+ const lastKey = path10.pop();
25297
25297
  let obj = acc;
25298
- for (const key of path9) {
25298
+ for (const key of path10) {
25299
25299
  obj[key] = obj[key] ?? {};
25300
25300
  obj = obj[key];
25301
25301
  }
@@ -26027,15 +26027,15 @@ var init_avl = __esm({
26027
26027
  if (node === null) {
26028
26028
  return new AVLNode(key, [value]);
26029
26029
  }
26030
- const path9 = [];
26030
+ const path10 = [];
26031
26031
  let current = node;
26032
26032
  let parent = null;
26033
26033
  while (current !== null) {
26034
- path9.push({ parent, node: current });
26034
+ path10.push({ parent, node: current });
26035
26035
  if (key < current.k) {
26036
26036
  if (current.l === null) {
26037
26037
  current.l = new AVLNode(key, [value]);
26038
- path9.push({ parent: current, node: current.l });
26038
+ path10.push({ parent: current, node: current.l });
26039
26039
  break;
26040
26040
  } else {
26041
26041
  parent = current;
@@ -26044,7 +26044,7 @@ var init_avl = __esm({
26044
26044
  } else if (key > current.k) {
26045
26045
  if (current.r === null) {
26046
26046
  current.r = new AVLNode(key, [value]);
26047
- path9.push({ parent: current, node: current.r });
26047
+ path10.push({ parent: current, node: current.r });
26048
26048
  break;
26049
26049
  } else {
26050
26050
  parent = current;
@@ -26059,8 +26059,8 @@ var init_avl = __esm({
26059
26059
  if (this.insertCount++ % rebalanceThreshold === 0) {
26060
26060
  needRebalance = true;
26061
26061
  }
26062
- for (let i2 = path9.length - 1; i2 >= 0; i2--) {
26063
- const { parent: parent2, node: currentNode } = path9[i2];
26062
+ for (let i2 = path10.length - 1; i2 >= 0; i2--) {
26063
+ const { parent: parent2, node: currentNode } = path10[i2];
26064
26064
  currentNode.updateHeight();
26065
26065
  if (needRebalance) {
26066
26066
  const rebalancedNode = this.rebalanceNode(currentNode);
@@ -26166,10 +26166,10 @@ var init_avl = __esm({
26166
26166
  removeNode(node, key) {
26167
26167
  if (node === null)
26168
26168
  return null;
26169
- const path9 = [];
26169
+ const path10 = [];
26170
26170
  let current = node;
26171
26171
  while (current !== null && current.k !== key) {
26172
- path9.push(current);
26172
+ path10.push(current);
26173
26173
  if (key < current.k) {
26174
26174
  current = current.l;
26175
26175
  } else {
@@ -26181,10 +26181,10 @@ var init_avl = __esm({
26181
26181
  }
26182
26182
  if (current.l === null || current.r === null) {
26183
26183
  const child = current.l ? current.l : current.r;
26184
- if (path9.length === 0) {
26184
+ if (path10.length === 0) {
26185
26185
  node = child;
26186
26186
  } else {
26187
- const parent = path9[path9.length - 1];
26187
+ const parent = path10[path10.length - 1];
26188
26188
  if (parent.l === current) {
26189
26189
  parent.l = child;
26190
26190
  } else {
@@ -26207,13 +26207,13 @@ var init_avl = __esm({
26207
26207
  }
26208
26208
  current = successorParent;
26209
26209
  }
26210
- path9.push(current);
26211
- for (let i2 = path9.length - 1; i2 >= 0; i2--) {
26212
- const currentNode = path9[i2];
26210
+ path10.push(current);
26211
+ for (let i2 = path10.length - 1; i2 >= 0; i2--) {
26212
+ const currentNode = path10[i2];
26213
26213
  currentNode.updateHeight();
26214
26214
  const rebalancedNode = this.rebalanceNode(currentNode);
26215
26215
  if (i2 > 0) {
26216
- const parent = path9[i2 - 1];
26216
+ const parent = path10[i2 - 1];
26217
26217
  if (parent.l === currentNode) {
26218
26218
  parent.l = rebalancedNode;
26219
26219
  } else if (parent.r === currentNode) {
@@ -27380,15 +27380,15 @@ function create2(orama, sharedInternalDocumentStore, schema, index, prefix = "")
27380
27380
  };
27381
27381
  }
27382
27382
  for (const [prop, type] of Object.entries(schema)) {
27383
- const path9 = `${prefix}${prefix ? "." : ""}${prop}`;
27383
+ const path10 = `${prefix}${prefix ? "." : ""}${prop}`;
27384
27384
  if (typeof type === "object" && !Array.isArray(type)) {
27385
- create2(orama, sharedInternalDocumentStore, type, index, path9);
27385
+ create2(orama, sharedInternalDocumentStore, type, index, path10);
27386
27386
  continue;
27387
27387
  }
27388
27388
  if (isVectorType(type)) {
27389
- index.searchableProperties.push(path9);
27390
- index.searchablePropertiesWithTypes[path9] = type;
27391
- index.vectorIndexes[path9] = {
27389
+ index.searchableProperties.push(path10);
27390
+ index.searchablePropertiesWithTypes[path10] = type;
27391
+ index.vectorIndexes[path10] = {
27392
27392
  type: "Vector",
27393
27393
  node: new VectorIndex(getVectorSize(type)),
27394
27394
  isArray: false
@@ -27398,32 +27398,32 @@ function create2(orama, sharedInternalDocumentStore, schema, index, prefix = "")
27398
27398
  switch (type) {
27399
27399
  case "boolean":
27400
27400
  case "boolean[]":
27401
- index.indexes[path9] = { type: "Bool", node: new BoolNode(), isArray };
27401
+ index.indexes[path10] = { type: "Bool", node: new BoolNode(), isArray };
27402
27402
  break;
27403
27403
  case "number":
27404
27404
  case "number[]":
27405
- index.indexes[path9] = { type: "AVL", node: new AVLTree(0, []), isArray };
27405
+ index.indexes[path10] = { type: "AVL", node: new AVLTree(0, []), isArray };
27406
27406
  break;
27407
27407
  case "string":
27408
27408
  case "string[]":
27409
- index.indexes[path9] = { type: "Radix", node: new RadixTree(), isArray };
27410
- index.avgFieldLength[path9] = 0;
27411
- index.frequencies[path9] = {};
27412
- index.tokenOccurrences[path9] = {};
27413
- index.fieldLengths[path9] = {};
27409
+ index.indexes[path10] = { type: "Radix", node: new RadixTree(), isArray };
27410
+ index.avgFieldLength[path10] = 0;
27411
+ index.frequencies[path10] = {};
27412
+ index.tokenOccurrences[path10] = {};
27413
+ index.fieldLengths[path10] = {};
27414
27414
  break;
27415
27415
  case "enum":
27416
27416
  case "enum[]":
27417
- index.indexes[path9] = { type: "Flat", node: new FlatTree(), isArray };
27417
+ index.indexes[path10] = { type: "Flat", node: new FlatTree(), isArray };
27418
27418
  break;
27419
27419
  case "geopoint":
27420
- index.indexes[path9] = { type: "BKD", node: new BKDTree(), isArray };
27420
+ index.indexes[path10] = { type: "BKD", node: new BKDTree(), isArray };
27421
27421
  break;
27422
27422
  default:
27423
- throw createError("INVALID_SCHEMA_TYPE", Array.isArray(type) ? "array" : type, path9);
27423
+ throw createError("INVALID_SCHEMA_TYPE", Array.isArray(type) ? "array" : type, path10);
27424
27424
  }
27425
- index.searchableProperties.push(path9);
27426
- index.searchablePropertiesWithTypes[path9] = type;
27425
+ index.searchableProperties.push(path10);
27426
+ index.searchablePropertiesWithTypes[path10] = type;
27427
27427
  }
27428
27428
  }
27429
27429
  return index;
@@ -27994,12 +27994,12 @@ function innerCreate(orama, sharedInternalDocumentStore, schema, sortableDeniedP
27994
27994
  sorts: {}
27995
27995
  };
27996
27996
  for (const [prop, type] of Object.entries(schema)) {
27997
- const path9 = `${prefix}${prefix ? "." : ""}${prop}`;
27998
- if (sortableDeniedProperties.includes(path9)) {
27997
+ const path10 = `${prefix}${prefix ? "." : ""}${prop}`;
27998
+ if (sortableDeniedProperties.includes(path10)) {
27999
27999
  continue;
28000
28000
  }
28001
28001
  if (typeof type === "object" && !Array.isArray(type)) {
28002
- const ret = innerCreate(orama, sharedInternalDocumentStore, type, sortableDeniedProperties, path9);
28002
+ const ret = innerCreate(orama, sharedInternalDocumentStore, type, sortableDeniedProperties, path10);
28003
28003
  safeArrayPush(sorter.sortableProperties, ret.sortableProperties);
28004
28004
  sorter.sorts = {
28005
28005
  ...sorter.sorts,
@@ -28016,9 +28016,9 @@ function innerCreate(orama, sharedInternalDocumentStore, schema, sortableDeniedP
28016
28016
  case "boolean":
28017
28017
  case "number":
28018
28018
  case "string":
28019
- sorter.sortableProperties.push(path9);
28020
- sorter.sortablePropertiesWithTypes[path9] = type;
28021
- sorter.sorts[path9] = {
28019
+ sorter.sortableProperties.push(path10);
28020
+ sorter.sortablePropertiesWithTypes[path10] = type;
28021
+ sorter.sorts[path10] = {
28022
28022
  docs: /* @__PURE__ */ new Map(),
28023
28023
  orderedDocsToRemove: /* @__PURE__ */ new Map(),
28024
28024
  orderedDocs: [],
@@ -28034,7 +28034,7 @@ function innerCreate(orama, sharedInternalDocumentStore, schema, sortableDeniedP
28034
28034
  case "string[]":
28035
28035
  continue;
28036
28036
  default:
28037
- throw createError("INVALID_SORT_SCHEMA_TYPE", Array.isArray(type) ? "array" : type, path9);
28037
+ throw createError("INVALID_SORT_SCHEMA_TYPE", Array.isArray(type) ? "array" : type, path10);
28038
28038
  }
28039
28039
  }
28040
28040
  }
@@ -29879,8 +29879,8 @@ function innerFullTextSearch(orama, params, language) {
29879
29879
  function escapeRegex2(str2) {
29880
29880
  return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
29881
29881
  }
29882
- function getPropValue(obj, path9) {
29883
- const keys = path9.split(".");
29882
+ function getPropValue(obj, path10) {
29883
+ const keys = path10.split(".");
29884
29884
  let value = obj;
29885
29885
  for (const key of keys) {
29886
29886
  if (value && typeof value === "object" && key in value) {
@@ -31042,6 +31042,217 @@ var init_provider = __esm({
31042
31042
  }
31043
31043
  });
31044
31044
 
31045
+ // src/project/aliases.ts
31046
+ var aliases_exports = {};
31047
+ __export(aliases_exports, {
31048
+ autoMergeByBaseName: () => autoMergeByBaseName,
31049
+ getAllAliasGroups: () => getAllAliasGroups,
31050
+ getCanonicalId: () => getCanonicalId,
31051
+ initAliasRegistry: () => initAliasRegistry,
31052
+ registerAlias: () => registerAlias,
31053
+ resetAliasCache: () => resetAliasCache,
31054
+ resolveAliases: () => resolveAliases
31055
+ });
31056
+ import { promises as fs3 } from "fs";
31057
+ import path4 from "path";
31058
+ import os2 from "os";
31059
+ function normalizePath(p2) {
31060
+ let normalized = p2.replace(/\\/g, "/").replace(/\/+$/, "");
31061
+ if (process.platform === "win32") {
31062
+ normalized = normalized.toLowerCase();
31063
+ }
31064
+ return normalized;
31065
+ }
31066
+ function idPriority(id) {
31067
+ if (id.startsWith("placeholder/")) return 0;
31068
+ if (id.startsWith("local/")) return 1;
31069
+ return 2;
31070
+ }
31071
+ function getRegistryPath(baseDir) {
31072
+ return path4.join(baseDir ?? registryDir ?? DEFAULT_DATA_DIR2, ALIAS_FILE);
31073
+ }
31074
+ async function loadRegistry(baseDir) {
31075
+ if (registryCache) return registryCache;
31076
+ try {
31077
+ const data = await fs3.readFile(getRegistryPath(baseDir), "utf-8");
31078
+ const parsed = JSON.parse(data);
31079
+ if (parsed.version === 1 && Array.isArray(parsed.groups)) {
31080
+ registryCache = parsed;
31081
+ return registryCache;
31082
+ }
31083
+ } catch {
31084
+ }
31085
+ registryCache = { version: 1, groups: [] };
31086
+ return registryCache;
31087
+ }
31088
+ async function saveRegistry(baseDir) {
31089
+ if (!registryCache) return;
31090
+ const filePath = getRegistryPath(baseDir);
31091
+ await fs3.mkdir(path4.dirname(filePath), { recursive: true });
31092
+ await fs3.writeFile(filePath, JSON.stringify(registryCache, null, 2), "utf-8");
31093
+ }
31094
+ function findMatchingGroup(registry2, projectInfo) {
31095
+ const normalizedRoot = normalizePath(projectInfo.rootPath);
31096
+ for (const group of registry2.groups) {
31097
+ if (group.aliases.includes(projectInfo.id)) return group;
31098
+ if (group.rootPaths.some((rp) => rp === normalizedRoot)) return group;
31099
+ if (projectInfo.gitRemote && group.gitRemote && group.gitRemote === projectInfo.gitRemote) {
31100
+ return group;
31101
+ }
31102
+ }
31103
+ return null;
31104
+ }
31105
+ function selectCanonical(aliases) {
31106
+ return [...aliases].sort((a3, b3) => idPriority(b3) - idPriority(a3))[0];
31107
+ }
31108
+ async function registerAlias(projectInfo, baseDir) {
31109
+ const registry2 = await loadRegistry(baseDir);
31110
+ const normalizedRoot = normalizePath(projectInfo.rootPath);
31111
+ const existingGroup = findMatchingGroup(registry2, projectInfo);
31112
+ if (existingGroup) {
31113
+ let changed = false;
31114
+ if (!existingGroup.aliases.includes(projectInfo.id)) {
31115
+ existingGroup.aliases.push(projectInfo.id);
31116
+ changed = true;
31117
+ }
31118
+ if (!existingGroup.rootPaths.includes(normalizedRoot)) {
31119
+ existingGroup.rootPaths.push(normalizedRoot);
31120
+ changed = true;
31121
+ }
31122
+ if (projectInfo.gitRemote && !existingGroup.gitRemote) {
31123
+ existingGroup.gitRemote = projectInfo.gitRemote;
31124
+ changed = true;
31125
+ }
31126
+ const newCanonical = selectCanonical(existingGroup.aliases);
31127
+ if (newCanonical !== existingGroup.canonical) {
31128
+ existingGroup.canonical = newCanonical;
31129
+ changed = true;
31130
+ }
31131
+ if (changed) {
31132
+ await saveRegistry(baseDir);
31133
+ }
31134
+ return existingGroup.canonical;
31135
+ }
31136
+ const newGroup = {
31137
+ canonical: projectInfo.id,
31138
+ aliases: [projectInfo.id],
31139
+ rootPaths: [normalizedRoot],
31140
+ ...projectInfo.gitRemote ? { gitRemote: projectInfo.gitRemote } : {}
31141
+ };
31142
+ registry2.groups.push(newGroup);
31143
+ await saveRegistry(baseDir);
31144
+ return newGroup.canonical;
31145
+ }
31146
+ async function resolveAliases(projectId, baseDir) {
31147
+ const registry2 = await loadRegistry(baseDir);
31148
+ for (const group of registry2.groups) {
31149
+ if (group.aliases.includes(projectId) || group.canonical === projectId) {
31150
+ return [...group.aliases];
31151
+ }
31152
+ }
31153
+ return [projectId];
31154
+ }
31155
+ async function getCanonicalId(projectId, baseDir) {
31156
+ const registry2 = await loadRegistry(baseDir);
31157
+ for (const group of registry2.groups) {
31158
+ if (group.aliases.includes(projectId) || group.canonical === projectId) {
31159
+ return group.canonical;
31160
+ }
31161
+ }
31162
+ return projectId;
31163
+ }
31164
+ async function getAllAliasGroups(baseDir) {
31165
+ const registry2 = await loadRegistry(baseDir);
31166
+ return registry2.groups;
31167
+ }
31168
+ async function autoMergeByBaseName(observedIds, baseDir) {
31169
+ if (observedIds.length <= 1) return 0;
31170
+ const registry2 = await loadRegistry(baseDir);
31171
+ const byBaseName = /* @__PURE__ */ new Map();
31172
+ for (const id of observedIds) {
31173
+ const baseName = id.split("/").pop() ?? id;
31174
+ if (!byBaseName.has(baseName)) byBaseName.set(baseName, []);
31175
+ byBaseName.get(baseName).push(id);
31176
+ }
31177
+ let mergeCount = 0;
31178
+ for (const [_baseName, ids] of byBaseName) {
31179
+ if (ids.length <= 1) continue;
31180
+ const existingGroups = /* @__PURE__ */ new Set();
31181
+ const ungroupedIds = [];
31182
+ for (const id of ids) {
31183
+ const groupIdx = registry2.groups.findIndex(
31184
+ (g3) => g3.aliases.includes(id) || g3.canonical === id
31185
+ );
31186
+ if (groupIdx >= 0) {
31187
+ existingGroups.add(groupIdx);
31188
+ } else {
31189
+ ungroupedIds.push(id);
31190
+ }
31191
+ }
31192
+ if (existingGroups.size <= 1 && ungroupedIds.length === 0) continue;
31193
+ const allIdsInGroup = [...ids];
31194
+ const canonical = selectCanonical(allIdsInGroup);
31195
+ if (existingGroups.size > 0) {
31196
+ const primaryIdx = [...existingGroups][0];
31197
+ const primaryGroup = registry2.groups[primaryIdx];
31198
+ for (const id of allIdsInGroup) {
31199
+ if (!primaryGroup.aliases.includes(id)) {
31200
+ primaryGroup.aliases.push(id);
31201
+ }
31202
+ }
31203
+ const otherIdxs = [...existingGroups].slice(1).sort((a3, b3) => b3 - a3);
31204
+ for (const idx of otherIdxs) {
31205
+ const other = registry2.groups[idx];
31206
+ for (const alias of other.aliases) {
31207
+ if (!primaryGroup.aliases.includes(alias)) {
31208
+ primaryGroup.aliases.push(alias);
31209
+ }
31210
+ }
31211
+ for (const rp of other.rootPaths) {
31212
+ if (!primaryGroup.rootPaths.includes(rp)) {
31213
+ primaryGroup.rootPaths.push(rp);
31214
+ }
31215
+ }
31216
+ if (other.gitRemote && !primaryGroup.gitRemote) {
31217
+ primaryGroup.gitRemote = other.gitRemote;
31218
+ }
31219
+ registry2.groups.splice(idx, 1);
31220
+ }
31221
+ primaryGroup.canonical = selectCanonical(primaryGroup.aliases);
31222
+ mergeCount++;
31223
+ } else {
31224
+ registry2.groups.push({
31225
+ canonical,
31226
+ aliases: allIdsInGroup,
31227
+ rootPaths: []
31228
+ });
31229
+ mergeCount++;
31230
+ }
31231
+ }
31232
+ if (mergeCount > 0) {
31233
+ await saveRegistry(baseDir);
31234
+ }
31235
+ return mergeCount;
31236
+ }
31237
+ function initAliasRegistry(dataDir) {
31238
+ registryDir = dataDir;
31239
+ registryCache = null;
31240
+ }
31241
+ function resetAliasCache() {
31242
+ registryCache = null;
31243
+ }
31244
+ var DEFAULT_DATA_DIR2, ALIAS_FILE, registryCache, registryDir;
31245
+ var init_aliases = __esm({
31246
+ "src/project/aliases.ts"() {
31247
+ "use strict";
31248
+ init_esm_shims();
31249
+ DEFAULT_DATA_DIR2 = path4.join(os2.homedir(), ".memorix", "data");
31250
+ ALIAS_FILE = ".project-aliases.json";
31251
+ registryCache = null;
31252
+ registryDir = null;
31253
+ }
31254
+ });
31255
+
31045
31256
  // src/store/orama-store.ts
31046
31257
  var orama_store_exports = {};
31047
31258
  __export(orama_store_exports, {
@@ -31113,17 +31324,27 @@ async function removeObservation(oramaId) {
31113
31324
  }
31114
31325
  async function searchObservations(options2) {
31115
31326
  const database = await getDb();
31116
- const filters = {};
31327
+ let projectIds = null;
31117
31328
  if (options2.projectId) {
31118
- filters["projectId"] = options2.projectId;
31329
+ try {
31330
+ const { resolveAliases: resolveAliases3 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
31331
+ projectIds = await resolveAliases3(options2.projectId);
31332
+ } catch {
31333
+ projectIds = [options2.projectId];
31334
+ }
31335
+ }
31336
+ const filters = {};
31337
+ if (projectIds && projectIds.length === 1) {
31338
+ filters["projectId"] = projectIds[0];
31119
31339
  }
31120
31340
  if (options2.type) {
31121
31341
  filters["type"] = options2.type;
31122
31342
  }
31123
31343
  const hasQuery = options2.query && options2.query.trim().length > 0;
31344
+ const requestLimit = projectIds && projectIds.length > 1 ? (options2.limit ?? 20) * 3 : options2.limit ?? 20;
31124
31345
  let searchParams = {
31125
31346
  term: options2.query,
31126
- limit: options2.limit ?? 20,
31347
+ limit: requestLimit,
31127
31348
  ...Object.keys(filters).length > 0 ? { where: filters } : {},
31128
31349
  // Search specific fields (not tokens, accessCount, etc.)
31129
31350
  properties: ["title", "entityName", "narrative", "facts", "concepts", "filesModified"],
@@ -31162,7 +31383,11 @@ async function searchObservations(options2) {
31162
31383
  }
31163
31384
  }
31164
31385
  const results = await search2(database, searchParams);
31165
- let intermediate = results.hits.map((hit) => {
31386
+ let intermediate = results.hits.filter((hit) => {
31387
+ if (!projectIds || projectIds.length <= 1) return true;
31388
+ const doc = hit.document;
31389
+ return projectIds.includes(doc.projectId);
31390
+ }).map((hit) => {
31166
31391
  const doc = hit.document;
31167
31392
  const obsType = doc.type;
31168
31393
  return {
@@ -31183,50 +31408,30 @@ async function searchObservations(options2) {
31183
31408
  const untilDate = new Date(options2.until).getTime();
31184
31409
  intermediate = intermediate.filter((e3) => new Date(e3.rawTime).getTime() <= untilDate);
31185
31410
  }
31411
+ if (projectIds && projectIds.length > 1) {
31412
+ intermediate = intermediate.slice(0, options2.limit ?? 20);
31413
+ }
31186
31414
  let entries = intermediate.map(({ rawTime: _4, ...rest }) => rest);
31187
31415
  if (hasQuery && options2.query) {
31188
31416
  const queryLower = options2.query.toLowerCase();
31189
31417
  const queryTokens = queryLower.split(/\s+/).filter((t2) => t2.length > 1);
31418
+ const entryMap = new Map(entries.map((e3) => [e3.id, e3]));
31190
31419
  for (const hit of results.hits) {
31191
31420
  const doc = hit.document;
31192
- const entry = entries.find((e3) => e3.id === doc.observationId);
31421
+ const entry = entryMap.get(doc.observationId);
31193
31422
  if (!entry) continue;
31194
31423
  const reasons = [];
31195
- for (const token of queryTokens) {
31196
- if (doc.title.toLowerCase().includes(token)) {
31197
- reasons.push("title");
31198
- break;
31199
- }
31200
- }
31201
- for (const token of queryTokens) {
31202
- if (doc.entityName.toLowerCase().includes(token)) {
31203
- reasons.push("entity");
31204
- break;
31205
- }
31206
- }
31207
- for (const token of queryTokens) {
31208
- if (doc.concepts.toLowerCase().includes(token)) {
31209
- reasons.push("concept");
31210
- break;
31211
- }
31212
- }
31213
- for (const token of queryTokens) {
31214
- if (doc.narrative.toLowerCase().includes(token)) {
31215
- reasons.push("narrative");
31216
- break;
31217
- }
31218
- }
31219
- for (const token of queryTokens) {
31220
- if (doc.facts.toLowerCase().includes(token)) {
31221
- reasons.push("fact");
31222
- break;
31223
- }
31224
- }
31225
- for (const token of queryTokens) {
31226
- if (doc.filesModified.toLowerCase().includes(token)) {
31227
- reasons.push("file");
31228
- break;
31229
- }
31424
+ const fields = [
31425
+ ["title", doc.title],
31426
+ ["entity", doc.entityName],
31427
+ ["concept", doc.concepts],
31428
+ ["narrative", doc.narrative],
31429
+ ["fact", doc.facts],
31430
+ ["file", doc.filesModified]
31431
+ ];
31432
+ for (const [name, value] of fields) {
31433
+ const valueLower = value.toLowerCase();
31434
+ if (queryTokens.some((t2) => valueLower.includes(t2))) reasons.push(name);
31230
31435
  }
31231
31436
  if (reasons.length === 0) reasons.push("fuzzy");
31232
31437
  entry["matchedFields"] = reasons;
@@ -31235,8 +31440,8 @@ async function searchObservations(options2) {
31235
31440
  if (options2.maxTokens && options2.maxTokens > 0) {
31236
31441
  entries = applyTokenBudget(entries, options2.maxTokens);
31237
31442
  }
31238
- const hitIds = results.hits.map((h3) => h3.document.id);
31239
- recordAccessBatch(hitIds).catch(() => {
31443
+ const hitDocs = results.hits.map((h3) => ({ id: h3.id, doc: h3.document }));
31444
+ recordAccessBatch(hitDocs).catch(() => {
31240
31445
  });
31241
31446
  return entries;
31242
31447
  }
@@ -31285,20 +31490,12 @@ async function getTimeline(anchorId, _projectId, depthBefore = 3, depthAfter = 3
31285
31490
  after
31286
31491
  };
31287
31492
  }
31288
- async function recordAccessBatch(oramaIds) {
31493
+ async function recordAccessBatch(hitDocs) {
31289
31494
  const database = await getDb();
31290
31495
  const now = (/* @__PURE__ */ new Date()).toISOString();
31291
- for (const id of oramaIds) {
31496
+ for (const { id, doc } of hitDocs) {
31292
31497
  try {
31293
- const result = await search2(database, {
31294
- term: "",
31295
- where: { id },
31296
- limit: 1
31297
- });
31298
- if (result.hits.length === 0) continue;
31299
- const doc = result.hits[0].document;
31300
- await remove4(database, id);
31301
- await insert3(database, {
31498
+ await update(database, id, {
31302
31499
  ...doc,
31303
31500
  accessCount: (doc.accessCount ?? 0) + 1,
31304
31501
  lastAccessedAt: now
@@ -33352,6 +33549,7 @@ __export(observations_exports, {
33352
33549
  getObservationCount: () => getObservationCount2,
33353
33550
  getProjectObservations: () => getProjectObservations,
33354
33551
  initObservations: () => initObservations,
33552
+ migrateProjectIds: () => migrateProjectIds,
33355
33553
  reindexObservations: () => reindexObservations,
33356
33554
  storeObservation: () => storeObservation,
33357
33555
  suggestTopicKey: () => suggestTopicKey
@@ -33513,8 +33711,29 @@ function getObservation(id) {
33513
33711
  return observations.find((o3) => o3.id === id);
33514
33712
  }
33515
33713
  function getProjectObservations(projectId) {
33714
+ if (Array.isArray(projectId)) {
33715
+ const idSet = new Set(projectId);
33716
+ return observations.filter((o3) => idSet.has(o3.projectId));
33717
+ }
33516
33718
  return observations.filter((o3) => o3.projectId === projectId);
33517
33719
  }
33720
+ async function migrateProjectIds(aliasIds, canonicalId) {
33721
+ const nonCanonical = new Set(aliasIds.filter((id) => id !== canonicalId));
33722
+ if (nonCanonical.size === 0) return 0;
33723
+ let migrated = 0;
33724
+ for (const obs of observations) {
33725
+ if (nonCanonical.has(obs.projectId)) {
33726
+ obs.projectId = canonicalId;
33727
+ migrated++;
33728
+ }
33729
+ }
33730
+ if (migrated > 0 && projectDir) {
33731
+ await withFileLock(projectDir, async () => {
33732
+ await saveObservationsJson(projectDir, observations);
33733
+ });
33734
+ }
33735
+ return migrated;
33736
+ }
33518
33737
  function getAllObservations() {
33519
33738
  return [...observations];
33520
33739
  }
@@ -33854,34 +34073,34 @@ __export(detector_exports, {
33854
34073
  });
33855
34074
  import { execSync } from "child_process";
33856
34075
  import { existsSync, readFileSync } from "fs";
33857
- import os2 from "os";
33858
- import path4 from "path";
34076
+ import os3 from "os";
34077
+ import path5 from "path";
33859
34078
  function detectProject(cwd) {
33860
34079
  const basePath = cwd ?? process.cwd();
33861
34080
  const rootPath = getGitRoot(basePath) ?? findPackageRoot(basePath) ?? basePath;
33862
34081
  const gitRemote = getGitRemote(rootPath);
33863
34082
  if (gitRemote) {
33864
34083
  const id2 = normalizeGitRemote(gitRemote);
33865
- const name2 = id2.split("/").pop() ?? path4.basename(rootPath);
34084
+ const name2 = id2.split("/").pop() ?? path5.basename(rootPath);
33866
34085
  return { id: id2, name: name2, gitRemote, rootPath };
33867
34086
  }
33868
34087
  if (isDangerousRoot(rootPath)) {
33869
- const name2 = path4.basename(rootPath) || "unknown";
34088
+ const name2 = path5.basename(rootPath) || "unknown";
33870
34089
  const id2 = `placeholder/${name2}`;
33871
34090
  console.error(`[memorix] WARNING: cwd "${rootPath}" is not a project directory \u2014 using degraded mode (${id2})`);
33872
34091
  console.error(`[memorix] For best results, set MEMORIX_PROJECT_ROOT or --cwd to your project path.`);
33873
34092
  return { id: id2, name: name2, rootPath };
33874
34093
  }
33875
- const name = path4.basename(rootPath);
34094
+ const name = path5.basename(rootPath);
33876
34095
  const id = `local/${name}`;
33877
34096
  return { id, name, rootPath };
33878
34097
  }
33879
34098
  function isDangerousRoot(dirPath) {
33880
- const resolved = path4.resolve(dirPath);
33881
- const home = path4.resolve(os2.homedir());
34099
+ const resolved = path5.resolve(dirPath);
34100
+ const home = path5.resolve(os3.homedir());
33882
34101
  if (resolved === home) return true;
33883
- if (resolved === path4.parse(resolved).root) return true;
33884
- const basename2 = path4.basename(resolved).toLowerCase();
34102
+ if (resolved === path5.parse(resolved).root) return true;
34103
+ const basename2 = path5.basename(resolved).toLowerCase();
33885
34104
  const knownNonProjectDirs = /* @__PURE__ */ new Set([
33886
34105
  // IDE / editor config dirs
33887
34106
  ".vscode",
@@ -33915,31 +34134,31 @@ function isDangerousRoot(dirPath) {
33915
34134
  ".memorix"
33916
34135
  ]);
33917
34136
  if (knownNonProjectDirs.has(basename2)) {
33918
- const parent = path4.resolve(path4.dirname(resolved));
33919
- if (parent === home || parent === path4.parse(parent).root) {
34137
+ const parent = path5.resolve(path5.dirname(resolved));
34138
+ if (parent === home || parent === path5.parse(parent).root) {
33920
34139
  return true;
33921
34140
  }
33922
34141
  }
33923
34142
  return false;
33924
34143
  }
33925
34144
  function findPackageRoot(cwd) {
33926
- let dir = path4.resolve(cwd);
33927
- const root = path4.parse(dir).root;
34145
+ let dir = path5.resolve(cwd);
34146
+ const root = path5.parse(dir).root;
33928
34147
  while (dir !== root) {
33929
34148
  if (isDangerousRoot(dir)) return null;
33930
- if (existsSync(path4.join(dir, "package.json"))) {
34149
+ if (existsSync(path5.join(dir, "package.json"))) {
33931
34150
  return dir;
33932
34151
  }
33933
- dir = path4.dirname(dir);
34152
+ dir = path5.dirname(dir);
33934
34153
  }
33935
34154
  return null;
33936
34155
  }
33937
34156
  function getGitRoot(cwd) {
33938
- let dir = path4.resolve(cwd);
33939
- const fsRoot = path4.parse(dir).root;
34157
+ let dir = path5.resolve(cwd);
34158
+ const fsRoot = path5.parse(dir).root;
33940
34159
  while (dir !== fsRoot) {
33941
- if (existsSync(path4.join(dir, ".git"))) return dir;
33942
- dir = path4.dirname(dir);
34160
+ if (existsSync(path5.join(dir, ".git"))) return dir;
34161
+ dir = path5.dirname(dir);
33943
34162
  }
33944
34163
  try {
33945
34164
  const root = execSync("git -c safe.directory=* rev-parse --show-toplevel", {
@@ -33970,7 +34189,7 @@ function getGitRemote(cwd) {
33970
34189
  }
33971
34190
  function readGitConfigRemote(cwd) {
33972
34191
  try {
33973
- const configPath = path4.join(cwd, ".git", "config");
34192
+ const configPath = path5.join(cwd, ".git", "config");
33974
34193
  if (!existsSync(configPath)) return null;
33975
34194
  const content = readFileSync(configPath, "utf-8");
33976
34195
  const remoteMatch = content.match(/\[remote\s+"origin"\]([\s\S]*?)(?=\n\[|$)/);
@@ -37419,7 +37638,7 @@ var require_gray_matter = __commonJS({
37419
37638
  "node_modules/gray-matter/index.js"(exports2, module2) {
37420
37639
  "use strict";
37421
37640
  init_esm_shims();
37422
- var fs6 = __require("fs");
37641
+ var fs7 = __require("fs");
37423
37642
  var sections = require_section_matter();
37424
37643
  var defaults = require_defaults2();
37425
37644
  var stringify = require_stringify();
@@ -37503,7 +37722,7 @@ var require_gray_matter = __commonJS({
37503
37722
  return stringify(file, data, options2);
37504
37723
  };
37505
37724
  matter9.read = function(filepath, options2) {
37506
- const str2 = fs6.readFileSync(filepath, "utf8");
37725
+ const str2 = fs7.readFileSync(filepath, "utf8");
37507
37726
  const file = matter9(str2, options2);
37508
37727
  file.path = filepath;
37509
37728
  return file;
@@ -38123,8 +38342,8 @@ var syncer_exports = {};
38123
38342
  __export(syncer_exports, {
38124
38343
  RulesSyncer: () => RulesSyncer
38125
38344
  });
38126
- import { promises as fs3 } from "fs";
38127
- import path5 from "path";
38345
+ import { promises as fs4 } from "fs";
38346
+ import path6 from "path";
38128
38347
  var RulesSyncer;
38129
38348
  var init_syncer = __esm({
38130
38349
  "src/rules/syncer.ts"() {
@@ -38165,8 +38384,8 @@ var init_syncer = __esm({
38165
38384
  const found = await this.findFiles(scanPath);
38166
38385
  for (const filePath of found) {
38167
38386
  try {
38168
- const content = await fs3.readFile(filePath, "utf-8");
38169
- const relativePath = path5.relative(this.projectRoot, filePath).replace(/\\/g, "/");
38387
+ const content = await fs4.readFile(filePath, "utf-8");
38388
+ const relativePath = path6.relative(this.projectRoot, filePath).replace(/\\/g, "/");
38170
38389
  const parsed = entry.adapter.parse(relativePath, content);
38171
38390
  rules.push(...parsed);
38172
38391
  } catch {
@@ -38234,7 +38453,7 @@ var init_syncer = __esm({
38234
38453
  const entries = [];
38235
38454
  for (const adapter of this.adapters.values()) {
38236
38455
  const absolutePaths = adapter.filePatterns.map(
38237
- (p2) => path5.join(this.projectRoot, p2)
38456
+ (p2) => path6.join(this.projectRoot, p2)
38238
38457
  );
38239
38458
  entries.push({ adapter, paths: absolutePaths });
38240
38459
  }
@@ -38242,13 +38461,13 @@ var init_syncer = __esm({
38242
38461
  }
38243
38462
  /** Find files matching a glob-like path (simple implementation) */
38244
38463
  async findFiles(pattern) {
38245
- const dir = path5.dirname(pattern);
38246
- const fileGlob = path5.basename(pattern);
38464
+ const dir = path6.dirname(pattern);
38465
+ const fileGlob = path6.basename(pattern);
38247
38466
  try {
38248
- const stat = await fs3.stat(dir);
38467
+ const stat = await fs4.stat(dir);
38249
38468
  if (!stat.isDirectory()) {
38250
38469
  try {
38251
- await fs3.access(pattern);
38470
+ await fs4.access(pattern);
38252
38471
  return [pattern];
38253
38472
  } catch {
38254
38473
  return [];
@@ -38256,7 +38475,7 @@ var init_syncer = __esm({
38256
38475
  }
38257
38476
  } catch {
38258
38477
  try {
38259
- await fs3.access(pattern);
38478
+ await fs4.access(pattern);
38260
38479
  return [pattern];
38261
38480
  } catch {
38262
38481
  return [];
@@ -38264,16 +38483,16 @@ var init_syncer = __esm({
38264
38483
  }
38265
38484
  if (fileGlob.includes("*")) {
38266
38485
  try {
38267
- const files = await fs3.readdir(dir);
38486
+ const files = await fs4.readdir(dir);
38268
38487
  const ext = fileGlob.replace("*", "");
38269
- return files.filter((f4) => ext ? f4.endsWith(ext) : true).map((f4) => path5.join(dir, f4));
38488
+ return files.filter((f4) => ext ? f4.endsWith(ext) : true).map((f4) => path6.join(dir, f4));
38270
38489
  } catch {
38271
38490
  return [];
38272
38491
  }
38273
38492
  }
38274
38493
  try {
38275
- await fs3.access(path5.join(dir, fileGlob));
38276
- return [path5.join(dir, fileGlob)];
38494
+ await fs4.access(path6.join(dir, fileGlob));
38495
+ return [path6.join(dir, fileGlob)];
38277
38496
  } catch {
38278
38497
  return [];
38279
38498
  }
@@ -39108,10 +39327,10 @@ var init_engine2 = __esm({
39108
39327
  for (const [target, adapter] of this.adapters) {
39109
39328
  const configPath = adapter.getConfigPath(this.projectRoot);
39110
39329
  const globalPath = adapter.getConfigPath();
39111
- for (const path9 of [configPath, globalPath]) {
39112
- if (existsSync3(path9)) {
39330
+ for (const path10 of [configPath, globalPath]) {
39331
+ if (existsSync3(path10)) {
39113
39332
  try {
39114
- const content = readFileSync2(path9, "utf-8");
39333
+ const content = readFileSync2(path10, "utf-8");
39115
39334
  const servers = adapter.parse(content);
39116
39335
  if (servers.length > 0) {
39117
39336
  mcpConfigs[target] = servers;
@@ -40314,8 +40533,8 @@ __export(server_exports, {
40314
40533
  startDashboard: () => startDashboard
40315
40534
  });
40316
40535
  import { createServer } from "http";
40317
- import { promises as fs4 } from "fs";
40318
- import path6 from "path";
40536
+ import { promises as fs5 } from "fs";
40537
+ import path7 from "path";
40319
40538
  import { exec } from "child_process";
40320
40539
  function sendJson(res, data, status = 200) {
40321
40540
  res.writeHead(status, {
@@ -40353,7 +40572,17 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir) {
40353
40572
  projectSet.set(obs.projectId, (projectSet.get(obs.projectId) || 0) + 1);
40354
40573
  }
40355
40574
  }
40356
- const projects = Array.from(projectSet.entries()).sort((a3, b3) => b3[1] - a3[1]).map(([id, count3]) => ({
40575
+ let mergedSet = projectSet;
40576
+ try {
40577
+ const { getCanonicalId: getCanonicalId2 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
40578
+ mergedSet = /* @__PURE__ */ new Map();
40579
+ for (const [id, count3] of projectSet) {
40580
+ const canonical = await getCanonicalId2(id);
40581
+ mergedSet.set(canonical, (mergedSet.get(canonical) || 0) + count3);
40582
+ }
40583
+ } catch {
40584
+ }
40585
+ const projects = Array.from(mergedSet.entries()).sort((a3, b3) => b3[1] - a3[1]).map(([id, count3]) => ({
40357
40586
  id,
40358
40587
  name: id.split("/").pop() || id,
40359
40588
  count: count3,
@@ -40518,14 +40747,14 @@ async function serveStatic(req, res, staticDir) {
40518
40747
  if (urlPath === "/" || !urlPath.includes(".")) {
40519
40748
  urlPath = "/index.html";
40520
40749
  }
40521
- const filePath = path6.join(staticDir, urlPath);
40750
+ const filePath = path7.join(staticDir, urlPath);
40522
40751
  if (!filePath.startsWith(staticDir)) {
40523
40752
  sendError(res, "Forbidden", 403);
40524
40753
  return;
40525
40754
  }
40526
40755
  try {
40527
- const data = await fs4.readFile(filePath);
40528
- const ext = path6.extname(filePath);
40756
+ const data = await fs5.readFile(filePath);
40757
+ const ext = path7.extname(filePath);
40529
40758
  res.writeHead(200, {
40530
40759
  "Content-Type": MIME_TYPES[ext] || "application/octet-stream",
40531
40760
  "Cache-Control": "no-cache"
@@ -40533,7 +40762,7 @@ async function serveStatic(req, res, staticDir) {
40533
40762
  res.end(data);
40534
40763
  } catch {
40535
40764
  try {
40536
- const indexData = await fs4.readFile(path6.join(staticDir, "index.html"));
40765
+ const indexData = await fs5.readFile(path7.join(staticDir, "index.html"));
40537
40766
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
40538
40767
  res.end(indexData);
40539
40768
  } catch {
@@ -40635,9 +40864,9 @@ __export(installers_exports, {
40635
40864
  installHooks: () => installHooks,
40636
40865
  uninstallHooks: () => uninstallHooks
40637
40866
  });
40638
- import * as fs5 from "fs/promises";
40639
- import * as path7 from "path";
40640
- import * as os3 from "os";
40867
+ import * as fs6 from "fs/promises";
40868
+ import * as path8 from "path";
40869
+ import * as os4 from "os";
40641
40870
  function resolveHookCommand() {
40642
40871
  if (process.platform === "win32") {
40643
40872
  return "cmd /c memorix";
@@ -40785,81 +41014,81 @@ function generateKiroHookFiles() {
40785
41014
  function getProjectConfigPath(agent, projectRoot) {
40786
41015
  switch (agent) {
40787
41016
  case "claude":
40788
- return path7.join(projectRoot, ".claude", "settings.local.json");
41017
+ return path8.join(projectRoot, ".claude", "settings.local.json");
40789
41018
  case "copilot":
40790
- return path7.join(projectRoot, ".github", "hooks", "memorix.json");
41019
+ return path8.join(projectRoot, ".github", "hooks", "memorix.json");
40791
41020
  case "windsurf":
40792
- return path7.join(projectRoot, ".windsurf", "hooks.json");
41021
+ return path8.join(projectRoot, ".windsurf", "hooks.json");
40793
41022
  case "cursor":
40794
- return path7.join(projectRoot, ".cursor", "hooks.json");
41023
+ return path8.join(projectRoot, ".cursor", "hooks.json");
40795
41024
  case "kiro":
40796
- return path7.join(projectRoot, ".kiro", "hooks", "memorix-agent-stop.kiro.hook");
41025
+ return path8.join(projectRoot, ".kiro", "hooks", "memorix-agent-stop.kiro.hook");
40797
41026
  case "codex":
40798
- return path7.join(projectRoot, "AGENTS.md");
41027
+ return path8.join(projectRoot, "AGENTS.md");
40799
41028
  case "antigravity":
40800
- return path7.join(projectRoot, ".gemini", "settings.json");
41029
+ return path8.join(projectRoot, ".gemini", "settings.json");
40801
41030
  default:
40802
- return path7.join(projectRoot, ".memorix", "hooks.json");
41031
+ return path8.join(projectRoot, ".memorix", "hooks.json");
40803
41032
  }
40804
41033
  }
40805
41034
  function getGlobalConfigPath(agent) {
40806
- const home = os3.homedir();
41035
+ const home = os4.homedir();
40807
41036
  switch (agent) {
40808
41037
  case "claude":
40809
41038
  case "copilot":
40810
- return path7.join(home, ".claude", "settings.json");
41039
+ return path8.join(home, ".claude", "settings.json");
40811
41040
  case "windsurf":
40812
- return path7.join(home, ".codeium", "windsurf", "hooks.json");
41041
+ return path8.join(home, ".codeium", "windsurf", "hooks.json");
40813
41042
  case "cursor":
40814
- return path7.join(home, ".cursor", "hooks.json");
41043
+ return path8.join(home, ".cursor", "hooks.json");
40815
41044
  case "antigravity":
40816
- return path7.join(home, ".gemini", "settings.json");
41045
+ return path8.join(home, ".gemini", "settings.json");
40817
41046
  default:
40818
- return path7.join(home, ".memorix", "hooks.json");
41047
+ return path8.join(home, ".memorix", "hooks.json");
40819
41048
  }
40820
41049
  }
40821
41050
  async function detectInstalledAgents() {
40822
41051
  const agents = [];
40823
- const home = os3.homedir();
40824
- const claudeDir = path7.join(home, ".claude");
41052
+ const home = os4.homedir();
41053
+ const claudeDir = path8.join(home, ".claude");
40825
41054
  try {
40826
- await fs5.access(claudeDir);
41055
+ await fs6.access(claudeDir);
40827
41056
  agents.push("claude");
40828
41057
  } catch {
40829
41058
  }
40830
- const windsurfDir = path7.join(home, ".codeium", "windsurf");
41059
+ const windsurfDir = path8.join(home, ".codeium", "windsurf");
40831
41060
  try {
40832
- await fs5.access(windsurfDir);
41061
+ await fs6.access(windsurfDir);
40833
41062
  agents.push("windsurf");
40834
41063
  } catch {
40835
41064
  }
40836
- const cursorDir = path7.join(home, ".cursor");
41065
+ const cursorDir = path8.join(home, ".cursor");
40837
41066
  try {
40838
- await fs5.access(cursorDir);
41067
+ await fs6.access(cursorDir);
40839
41068
  agents.push("cursor");
40840
41069
  } catch {
40841
41070
  }
40842
- const vscodeDir = path7.join(home, ".vscode");
41071
+ const vscodeDir = path8.join(home, ".vscode");
40843
41072
  try {
40844
- await fs5.access(vscodeDir);
41073
+ await fs6.access(vscodeDir);
40845
41074
  agents.push("copilot");
40846
41075
  } catch {
40847
41076
  }
40848
- const kiroConfig = path7.join(home, ".kiro");
41077
+ const kiroConfig = path8.join(home, ".kiro");
40849
41078
  try {
40850
- await fs5.access(kiroConfig);
41079
+ await fs6.access(kiroConfig);
40851
41080
  agents.push("kiro");
40852
41081
  } catch {
40853
41082
  }
40854
- const codexDir = path7.join(home, ".codex");
41083
+ const codexDir = path8.join(home, ".codex");
40855
41084
  try {
40856
- await fs5.access(codexDir);
41085
+ await fs6.access(codexDir);
40857
41086
  agents.push("codex");
40858
41087
  } catch {
40859
41088
  }
40860
- const geminiDir = path7.join(home, ".gemini");
41089
+ const geminiDir = path8.join(home, ".gemini");
40861
41090
  try {
40862
- await fs5.access(geminiDir);
41091
+ await fs6.access(geminiDir);
40863
41092
  agents.push("antigravity");
40864
41093
  } catch {
40865
41094
  }
@@ -40898,18 +41127,18 @@ async function installHooks(agent, projectRoot, global = false) {
40898
41127
  default:
40899
41128
  generated = generateClaudeConfig();
40900
41129
  }
40901
- await fs5.mkdir(path7.dirname(configPath), { recursive: true });
41130
+ await fs6.mkdir(path8.dirname(configPath), { recursive: true });
40902
41131
  if (agent === "kiro") {
40903
41132
  const hookFiles = generateKiroHookFiles();
40904
- const hooksDir = path7.join(path7.dirname(configPath));
40905
- await fs5.mkdir(hooksDir, { recursive: true });
41133
+ const hooksDir = path8.join(path8.dirname(configPath));
41134
+ await fs6.mkdir(hooksDir, { recursive: true });
40906
41135
  for (const hf of hookFiles) {
40907
- await fs5.writeFile(path7.join(hooksDir, hf.filename), hf.content, "utf-8");
41136
+ await fs6.writeFile(path8.join(hooksDir, hf.filename), hf.content, "utf-8");
40908
41137
  }
40909
41138
  } else {
40910
41139
  let existing = {};
40911
41140
  try {
40912
- const content = await fs5.readFile(configPath, "utf-8");
41141
+ const content = await fs6.readFile(configPath, "utf-8");
40913
41142
  existing = JSON.parse(content);
40914
41143
  } catch {
40915
41144
  }
@@ -40936,7 +41165,7 @@ async function installHooks(agent, projectRoot, global = false) {
40936
41165
  const h3 = merged.hooks;
40937
41166
  if (h3) delete h3.preToolUse;
40938
41167
  }
40939
- await fs5.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
41168
+ await fs6.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
40940
41169
  }
40941
41170
  const events = [];
40942
41171
  switch (agent) {
@@ -40972,45 +41201,45 @@ async function installAgentRules(agent, projectRoot) {
40972
41201
  let rulesPath;
40973
41202
  switch (agent) {
40974
41203
  case "windsurf":
40975
- rulesPath = path7.join(projectRoot, ".windsurf", "rules", "memorix.md");
41204
+ rulesPath = path8.join(projectRoot, ".windsurf", "rules", "memorix.md");
40976
41205
  break;
40977
41206
  case "cursor":
40978
- rulesPath = path7.join(projectRoot, ".cursor", "rules", "memorix.mdc");
41207
+ rulesPath = path8.join(projectRoot, ".cursor", "rules", "memorix.mdc");
40979
41208
  break;
40980
41209
  case "claude":
40981
41210
  case "copilot":
40982
- rulesPath = path7.join(projectRoot, ".github", "copilot-instructions.md");
41211
+ rulesPath = path8.join(projectRoot, ".github", "copilot-instructions.md");
40983
41212
  break;
40984
41213
  case "codex":
40985
- rulesPath = path7.join(projectRoot, "AGENTS.md");
41214
+ rulesPath = path8.join(projectRoot, "AGENTS.md");
40986
41215
  break;
40987
41216
  case "kiro":
40988
- rulesPath = path7.join(projectRoot, ".kiro", "steering", "memorix.md");
41217
+ rulesPath = path8.join(projectRoot, ".kiro", "steering", "memorix.md");
40989
41218
  break;
40990
41219
  case "antigravity":
40991
- rulesPath = path7.join(projectRoot, "GEMINI.md");
41220
+ rulesPath = path8.join(projectRoot, "GEMINI.md");
40992
41221
  break;
40993
41222
  default:
40994
- rulesPath = path7.join(projectRoot, ".agent", "rules", "memorix.md");
41223
+ rulesPath = path8.join(projectRoot, ".agent", "rules", "memorix.md");
40995
41224
  break;
40996
41225
  }
40997
41226
  try {
40998
- await fs5.mkdir(path7.dirname(rulesPath), { recursive: true });
41227
+ await fs6.mkdir(path8.dirname(rulesPath), { recursive: true });
40999
41228
  if (agent === "codex" || agent === "antigravity") {
41000
41229
  try {
41001
- const existing = await fs5.readFile(rulesPath, "utf-8");
41230
+ const existing = await fs6.readFile(rulesPath, "utf-8");
41002
41231
  if (existing.includes("Memorix")) {
41003
41232
  return;
41004
41233
  }
41005
- await fs5.writeFile(rulesPath, existing + "\n\n" + rulesContent, "utf-8");
41234
+ await fs6.writeFile(rulesPath, existing + "\n\n" + rulesContent, "utf-8");
41006
41235
  } catch {
41007
- await fs5.writeFile(rulesPath, rulesContent, "utf-8");
41236
+ await fs6.writeFile(rulesPath, rulesContent, "utf-8");
41008
41237
  }
41009
41238
  } else {
41010
41239
  try {
41011
- await fs5.access(rulesPath);
41240
+ await fs6.access(rulesPath);
41012
41241
  } catch {
41013
- await fs5.writeFile(rulesPath, rulesContent, "utf-8");
41242
+ await fs6.writeFile(rulesPath, rulesContent, "utf-8");
41014
41243
  }
41015
41244
  }
41016
41245
  } catch {
@@ -41095,15 +41324,15 @@ async function uninstallHooks(agent, projectRoot, global = false) {
41095
41324
  const configPath = global ? getGlobalConfigPath(agent) : getProjectConfigPath(agent, projectRoot);
41096
41325
  try {
41097
41326
  if (agent === "kiro") {
41098
- await fs5.unlink(configPath);
41327
+ await fs6.unlink(configPath);
41099
41328
  } else {
41100
- const content = await fs5.readFile(configPath, "utf-8");
41329
+ const content = await fs6.readFile(configPath, "utf-8");
41101
41330
  const config2 = JSON.parse(content);
41102
41331
  delete config2.hooks;
41103
41332
  if (Object.keys(config2).length === 0) {
41104
- await fs5.unlink(configPath);
41333
+ await fs6.unlink(configPath);
41105
41334
  } else {
41106
- await fs5.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
41335
+ await fs6.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
41107
41336
  }
41108
41337
  }
41109
41338
  return true;
@@ -41120,11 +41349,11 @@ async function getHookStatus(projectRoot) {
41120
41349
  let installed = false;
41121
41350
  let usedPath = projectPath;
41122
41351
  try {
41123
- await fs5.access(projectPath);
41352
+ await fs6.access(projectPath);
41124
41353
  installed = true;
41125
41354
  } catch {
41126
41355
  try {
41127
- await fs5.access(globalPath);
41356
+ await fs6.access(globalPath);
41128
41357
  installed = true;
41129
41358
  usedPath = globalPath;
41130
41359
  } catch {
@@ -41200,7 +41429,7 @@ function coerceObjectArray(val) {
41200
41429
  return [];
41201
41430
  }
41202
41431
  async function createMemorixServer(cwd, existingServer) {
41203
- const project = detectProject(cwd);
41432
+ const rawProject = detectProject(cwd);
41204
41433
  try {
41205
41434
  const { migrateSubdirsToFlat: migrateSubdirsToFlat2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
41206
41435
  const migrated = await migrateSubdirsToFlat2();
@@ -41209,10 +41438,44 @@ async function createMemorixServer(cwd, existingServer) {
41209
41438
  }
41210
41439
  } catch {
41211
41440
  }
41212
- const projectDir2 = await getProjectDataDir(project.id);
41441
+ const projectDir2 = await getProjectDataDir(rawProject.id);
41442
+ initAliasRegistry(projectDir2);
41443
+ const canonicalId = await registerAlias(rawProject);
41444
+ const project = { ...rawProject, id: canonicalId };
41445
+ if (canonicalId !== rawProject.id) {
41446
+ console.error(`[memorix] Alias resolved: ${rawProject.id} \u2192 ${canonicalId}`);
41447
+ }
41213
41448
  const graphManager = new KnowledgeGraphManager(projectDir2);
41214
41449
  await graphManager.init();
41215
41450
  await initObservations(projectDir2);
41451
+ try {
41452
+ const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
41453
+ const allObs = getAllObservations2();
41454
+ const observedIds = [...new Set(allObs.map((o3) => o3.projectId))];
41455
+ const merged = await autoMergeByBaseName(observedIds);
41456
+ if (merged > 0) {
41457
+ console.error(`[memorix] Auto-merged ${merged} alias group(s) by base name`);
41458
+ }
41459
+ } catch {
41460
+ }
41461
+ try {
41462
+ const { getAllAliasGroups: getAllAliasGroups2 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
41463
+ const groups = await getAllAliasGroups2();
41464
+ let totalMigrated = 0;
41465
+ for (const group of groups) {
41466
+ if (group.aliases.length > 1) {
41467
+ const migrated = await migrateProjectIds(group.aliases, group.canonical);
41468
+ if (migrated > 0) {
41469
+ console.error(`[memorix] Migrated ${migrated} observations \u2192 ${group.canonical}`);
41470
+ totalMigrated += migrated;
41471
+ }
41472
+ }
41473
+ }
41474
+ if (totalMigrated > 0) {
41475
+ console.error(`[memorix] Total migrated: ${totalMigrated} observations across ${groups.filter((g3) => g3.aliases.length > 1).length} project(s)`);
41476
+ }
41477
+ } catch {
41478
+ }
41216
41479
  const reindexed = await reindexObservations();
41217
41480
  if (reindexed > 0) {
41218
41481
  console.error(`[memorix] Reindexed ${reindexed} observations for project: ${project.id}`);
@@ -41924,9 +42187,9 @@ ${skill.content}` }]
41924
42187
  lines.push(`- **Description**: ${sk.description}`);
41925
42188
  lines.push(`- **Observations**: ${sk.content.split("\n").length} lines of knowledge`);
41926
42189
  if (write && target) {
41927
- const path9 = engine.writeSkill(sk, target);
41928
- if (path9) {
41929
- lines.push(`- \u2705 **Written**: \`${path9}\``);
42190
+ const path10 = engine.writeSkill(sk, target);
42191
+ if (path10) {
42192
+ lines.push(`- \u2705 **Written**: \`${path10}\``);
41930
42193
  } else {
41931
42194
  lines.push(`- \u274C Failed to write`);
41932
42195
  }
@@ -42375,6 +42638,7 @@ var init_server4 = __esm({
42375
42638
  init_entity_extractor();
42376
42639
  init_engine();
42377
42640
  init_detector();
42641
+ init_aliases();
42378
42642
  init_persistence();
42379
42643
  init_syncer();
42380
42644
  init_engine2();
@@ -43335,8 +43599,8 @@ var init_sync = __esm({
43335
43599
  run: async ({ args }) => {
43336
43600
  const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
43337
43601
  const { RulesSyncer: RulesSyncer2 } = await Promise.resolve().then(() => (init_syncer(), syncer_exports));
43338
- const { promises: fs6 } = await import("fs");
43339
- const path9 = await import("path");
43602
+ const { promises: fs7 } = await import("fs");
43603
+ const path10 = await import("path");
43340
43604
  we("memorix sync");
43341
43605
  const project = detectProject2();
43342
43606
  v4.info(`Project: ${project.name} (${project.id})`);
@@ -43404,9 +43668,9 @@ var init_sync = __esm({
43404
43668
  process.exit(0);
43405
43669
  }
43406
43670
  for (const file of files) {
43407
- const fullPath = path9.join(project.rootPath, file.filePath);
43408
- await fs6.mkdir(path9.dirname(fullPath), { recursive: true });
43409
- await fs6.writeFile(fullPath, file.content, "utf-8");
43671
+ const fullPath = path10.join(project.rootPath, file.filePath);
43672
+ await fs7.mkdir(path10.dirname(fullPath), { recursive: true });
43673
+ await fs7.writeFile(fullPath, file.content, "utf-8");
43410
43674
  v4.success(`Written: ${file.filePath}`);
43411
43675
  }
43412
43676
  fe2(`Synced ${files.length} rule(s) to ${target} format`);
@@ -44227,12 +44491,12 @@ var init_hooks_install = __esm({
44227
44491
  },
44228
44492
  run: async ({ args }) => {
44229
44493
  const { detectInstalledAgents: detectInstalledAgents2, installHooks: installHooks2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
44230
- const os4 = await import("os");
44494
+ const os5 = await import("os");
44231
44495
  let cwd;
44232
44496
  try {
44233
44497
  cwd = process.cwd();
44234
44498
  } catch {
44235
- cwd = os4.homedir();
44499
+ cwd = os5.homedir();
44236
44500
  console.log(`\u26A0\uFE0F Could not access current directory, using home: ${cwd}`);
44237
44501
  }
44238
44502
  let agents;
@@ -44295,12 +44559,12 @@ var init_hooks_uninstall = __esm({
44295
44559
  },
44296
44560
  run: async ({ args }) => {
44297
44561
  const { detectInstalledAgents: detectInstalledAgents2, uninstallHooks: uninstallHooks2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
44298
- const os4 = await import("os");
44562
+ const os5 = await import("os");
44299
44563
  let cwd;
44300
44564
  try {
44301
44565
  cwd = process.cwd();
44302
44566
  } catch {
44303
- cwd = os4.homedir();
44567
+ cwd = os5.homedir();
44304
44568
  }
44305
44569
  let agents;
44306
44570
  if (args.agent) {
@@ -44343,12 +44607,12 @@ var init_hooks_status = __esm({
44343
44607
  },
44344
44608
  run: async () => {
44345
44609
  const { getHookStatus: getHookStatus2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
44346
- const os4 = await import("os");
44610
+ const os5 = await import("os");
44347
44611
  let cwd;
44348
44612
  try {
44349
44613
  cwd = process.cwd();
44350
44614
  } catch {
44351
- cwd = os4.homedir();
44615
+ cwd = os5.homedir();
44352
44616
  }
44353
44617
  const statuses = await getHookStatus2(cwd);
44354
44618
  console.log("\nMemorix Hooks Status");
@@ -44397,7 +44661,7 @@ __export(dashboard_exports, {
44397
44661
  default: () => dashboard_default
44398
44662
  });
44399
44663
  import { fileURLToPath as fileURLToPath2 } from "url";
44400
- import path8 from "path";
44664
+ import path9 from "path";
44401
44665
  var dashboard_default;
44402
44666
  var init_dashboard = __esm({
44403
44667
  "src/cli/commands/dashboard.ts"() {
@@ -44423,8 +44687,8 @@ var init_dashboard = __esm({
44423
44687
  const project = detectProject2();
44424
44688
  const dataDir = await getProjectDataDir2(project.id);
44425
44689
  const port = parseInt(args.port, 10) || 3210;
44426
- const cliDir = path8.dirname(fileURLToPath2(import.meta.url));
44427
- const staticDir = path8.join(cliDir, "..", "dashboard", "static");
44690
+ const cliDir = path9.dirname(fileURLToPath2(import.meta.url));
44691
+ const staticDir = path9.join(cliDir, "..", "dashboard", "static");
44428
44692
  await startDashboard2(dataDir, port, staticDir, project.id, project.name);
44429
44693
  await new Promise(() => {
44430
44694
  });