memorix 0.9.33 → 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 });
@@ -24563,9 +24563,6 @@ import { promises as fs2 } from "fs";
24563
24563
  import path3 from "path";
24564
24564
  import os from "os";
24565
24565
  async function getProjectDataDir(_projectId, baseDir) {
24566
- if (_projectId === "__invalid__") {
24567
- throw new Error("Cannot create data directory for invalid project");
24568
- }
24569
24566
  const base = baseDir ?? DEFAULT_DATA_DIR;
24570
24567
  await fs2.mkdir(base, { recursive: true });
24571
24568
  return base;
@@ -25252,15 +25249,15 @@ function getDocumentProperties(doc, paths) {
25252
25249
  const properties = {};
25253
25250
  const pathsLength = paths.length;
25254
25251
  for (let i2 = 0; i2 < pathsLength; i2++) {
25255
- const path9 = paths[i2];
25256
- const pathTokens = path9.split(".");
25252
+ const path10 = paths[i2];
25253
+ const pathTokens = path10.split(".");
25257
25254
  let current = doc;
25258
25255
  const pathTokensLength = pathTokens.length;
25259
25256
  for (let j3 = 0; j3 < pathTokensLength; j3++) {
25260
25257
  current = current[pathTokens[j3]];
25261
25258
  if (typeof current === "object") {
25262
25259
  if (current !== null && "lat" in current && "lon" in current && typeof current.lat === "number" && typeof current.lon === "number") {
25263
- current = properties[path9] = current;
25260
+ current = properties[path10] = current;
25264
25261
  break;
25265
25262
  } else if (!Array.isArray(current) && current !== null && j3 === pathTokensLength - 1) {
25266
25263
  current = void 0;
@@ -25272,14 +25269,14 @@ function getDocumentProperties(doc, paths) {
25272
25269
  }
25273
25270
  }
25274
25271
  if (typeof current !== "undefined") {
25275
- properties[path9] = current;
25272
+ properties[path10] = current;
25276
25273
  }
25277
25274
  }
25278
25275
  return properties;
25279
25276
  }
25280
- function getNested(obj, path9) {
25281
- const props = getDocumentProperties(obj, [path9]);
25282
- return props[path9];
25277
+ function getNested(obj, path10) {
25278
+ const props = getDocumentProperties(obj, [path10]);
25279
+ return props[path10];
25283
25280
  }
25284
25281
  function convertDistanceToMeters(distance, unit) {
25285
25282
  const ratio = mapDistanceToMeters[unit];
@@ -25295,10 +25292,10 @@ function removeVectorsFromHits(searchResult, vectorProperties) {
25295
25292
  ...result.document,
25296
25293
  // Remove embeddings from the result
25297
25294
  ...vectorProperties.reduce((acc, prop) => {
25298
- const path9 = prop.split(".");
25299
- const lastKey = path9.pop();
25295
+ const path10 = prop.split(".");
25296
+ const lastKey = path10.pop();
25300
25297
  let obj = acc;
25301
- for (const key of path9) {
25298
+ for (const key of path10) {
25302
25299
  obj[key] = obj[key] ?? {};
25303
25300
  obj = obj[key];
25304
25301
  }
@@ -26030,15 +26027,15 @@ var init_avl = __esm({
26030
26027
  if (node === null) {
26031
26028
  return new AVLNode(key, [value]);
26032
26029
  }
26033
- const path9 = [];
26030
+ const path10 = [];
26034
26031
  let current = node;
26035
26032
  let parent = null;
26036
26033
  while (current !== null) {
26037
- path9.push({ parent, node: current });
26034
+ path10.push({ parent, node: current });
26038
26035
  if (key < current.k) {
26039
26036
  if (current.l === null) {
26040
26037
  current.l = new AVLNode(key, [value]);
26041
- path9.push({ parent: current, node: current.l });
26038
+ path10.push({ parent: current, node: current.l });
26042
26039
  break;
26043
26040
  } else {
26044
26041
  parent = current;
@@ -26047,7 +26044,7 @@ var init_avl = __esm({
26047
26044
  } else if (key > current.k) {
26048
26045
  if (current.r === null) {
26049
26046
  current.r = new AVLNode(key, [value]);
26050
- path9.push({ parent: current, node: current.r });
26047
+ path10.push({ parent: current, node: current.r });
26051
26048
  break;
26052
26049
  } else {
26053
26050
  parent = current;
@@ -26062,8 +26059,8 @@ var init_avl = __esm({
26062
26059
  if (this.insertCount++ % rebalanceThreshold === 0) {
26063
26060
  needRebalance = true;
26064
26061
  }
26065
- for (let i2 = path9.length - 1; i2 >= 0; i2--) {
26066
- 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];
26067
26064
  currentNode.updateHeight();
26068
26065
  if (needRebalance) {
26069
26066
  const rebalancedNode = this.rebalanceNode(currentNode);
@@ -26169,10 +26166,10 @@ var init_avl = __esm({
26169
26166
  removeNode(node, key) {
26170
26167
  if (node === null)
26171
26168
  return null;
26172
- const path9 = [];
26169
+ const path10 = [];
26173
26170
  let current = node;
26174
26171
  while (current !== null && current.k !== key) {
26175
- path9.push(current);
26172
+ path10.push(current);
26176
26173
  if (key < current.k) {
26177
26174
  current = current.l;
26178
26175
  } else {
@@ -26184,10 +26181,10 @@ var init_avl = __esm({
26184
26181
  }
26185
26182
  if (current.l === null || current.r === null) {
26186
26183
  const child = current.l ? current.l : current.r;
26187
- if (path9.length === 0) {
26184
+ if (path10.length === 0) {
26188
26185
  node = child;
26189
26186
  } else {
26190
- const parent = path9[path9.length - 1];
26187
+ const parent = path10[path10.length - 1];
26191
26188
  if (parent.l === current) {
26192
26189
  parent.l = child;
26193
26190
  } else {
@@ -26210,13 +26207,13 @@ var init_avl = __esm({
26210
26207
  }
26211
26208
  current = successorParent;
26212
26209
  }
26213
- path9.push(current);
26214
- for (let i2 = path9.length - 1; i2 >= 0; i2--) {
26215
- const currentNode = path9[i2];
26210
+ path10.push(current);
26211
+ for (let i2 = path10.length - 1; i2 >= 0; i2--) {
26212
+ const currentNode = path10[i2];
26216
26213
  currentNode.updateHeight();
26217
26214
  const rebalancedNode = this.rebalanceNode(currentNode);
26218
26215
  if (i2 > 0) {
26219
- const parent = path9[i2 - 1];
26216
+ const parent = path10[i2 - 1];
26220
26217
  if (parent.l === currentNode) {
26221
26218
  parent.l = rebalancedNode;
26222
26219
  } else if (parent.r === currentNode) {
@@ -27383,15 +27380,15 @@ function create2(orama, sharedInternalDocumentStore, schema, index, prefix = "")
27383
27380
  };
27384
27381
  }
27385
27382
  for (const [prop, type] of Object.entries(schema)) {
27386
- const path9 = `${prefix}${prefix ? "." : ""}${prop}`;
27383
+ const path10 = `${prefix}${prefix ? "." : ""}${prop}`;
27387
27384
  if (typeof type === "object" && !Array.isArray(type)) {
27388
- create2(orama, sharedInternalDocumentStore, type, index, path9);
27385
+ create2(orama, sharedInternalDocumentStore, type, index, path10);
27389
27386
  continue;
27390
27387
  }
27391
27388
  if (isVectorType(type)) {
27392
- index.searchableProperties.push(path9);
27393
- index.searchablePropertiesWithTypes[path9] = type;
27394
- index.vectorIndexes[path9] = {
27389
+ index.searchableProperties.push(path10);
27390
+ index.searchablePropertiesWithTypes[path10] = type;
27391
+ index.vectorIndexes[path10] = {
27395
27392
  type: "Vector",
27396
27393
  node: new VectorIndex(getVectorSize(type)),
27397
27394
  isArray: false
@@ -27401,32 +27398,32 @@ function create2(orama, sharedInternalDocumentStore, schema, index, prefix = "")
27401
27398
  switch (type) {
27402
27399
  case "boolean":
27403
27400
  case "boolean[]":
27404
- index.indexes[path9] = { type: "Bool", node: new BoolNode(), isArray };
27401
+ index.indexes[path10] = { type: "Bool", node: new BoolNode(), isArray };
27405
27402
  break;
27406
27403
  case "number":
27407
27404
  case "number[]":
27408
- index.indexes[path9] = { type: "AVL", node: new AVLTree(0, []), isArray };
27405
+ index.indexes[path10] = { type: "AVL", node: new AVLTree(0, []), isArray };
27409
27406
  break;
27410
27407
  case "string":
27411
27408
  case "string[]":
27412
- index.indexes[path9] = { type: "Radix", node: new RadixTree(), isArray };
27413
- index.avgFieldLength[path9] = 0;
27414
- index.frequencies[path9] = {};
27415
- index.tokenOccurrences[path9] = {};
27416
- 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] = {};
27417
27414
  break;
27418
27415
  case "enum":
27419
27416
  case "enum[]":
27420
- index.indexes[path9] = { type: "Flat", node: new FlatTree(), isArray };
27417
+ index.indexes[path10] = { type: "Flat", node: new FlatTree(), isArray };
27421
27418
  break;
27422
27419
  case "geopoint":
27423
- index.indexes[path9] = { type: "BKD", node: new BKDTree(), isArray };
27420
+ index.indexes[path10] = { type: "BKD", node: new BKDTree(), isArray };
27424
27421
  break;
27425
27422
  default:
27426
- throw createError("INVALID_SCHEMA_TYPE", Array.isArray(type) ? "array" : type, path9);
27423
+ throw createError("INVALID_SCHEMA_TYPE", Array.isArray(type) ? "array" : type, path10);
27427
27424
  }
27428
- index.searchableProperties.push(path9);
27429
- index.searchablePropertiesWithTypes[path9] = type;
27425
+ index.searchableProperties.push(path10);
27426
+ index.searchablePropertiesWithTypes[path10] = type;
27430
27427
  }
27431
27428
  }
27432
27429
  return index;
@@ -27997,12 +27994,12 @@ function innerCreate(orama, sharedInternalDocumentStore, schema, sortableDeniedP
27997
27994
  sorts: {}
27998
27995
  };
27999
27996
  for (const [prop, type] of Object.entries(schema)) {
28000
- const path9 = `${prefix}${prefix ? "." : ""}${prop}`;
28001
- if (sortableDeniedProperties.includes(path9)) {
27997
+ const path10 = `${prefix}${prefix ? "." : ""}${prop}`;
27998
+ if (sortableDeniedProperties.includes(path10)) {
28002
27999
  continue;
28003
28000
  }
28004
28001
  if (typeof type === "object" && !Array.isArray(type)) {
28005
- const ret = innerCreate(orama, sharedInternalDocumentStore, type, sortableDeniedProperties, path9);
28002
+ const ret = innerCreate(orama, sharedInternalDocumentStore, type, sortableDeniedProperties, path10);
28006
28003
  safeArrayPush(sorter.sortableProperties, ret.sortableProperties);
28007
28004
  sorter.sorts = {
28008
28005
  ...sorter.sorts,
@@ -28019,9 +28016,9 @@ function innerCreate(orama, sharedInternalDocumentStore, schema, sortableDeniedP
28019
28016
  case "boolean":
28020
28017
  case "number":
28021
28018
  case "string":
28022
- sorter.sortableProperties.push(path9);
28023
- sorter.sortablePropertiesWithTypes[path9] = type;
28024
- sorter.sorts[path9] = {
28019
+ sorter.sortableProperties.push(path10);
28020
+ sorter.sortablePropertiesWithTypes[path10] = type;
28021
+ sorter.sorts[path10] = {
28025
28022
  docs: /* @__PURE__ */ new Map(),
28026
28023
  orderedDocsToRemove: /* @__PURE__ */ new Map(),
28027
28024
  orderedDocs: [],
@@ -28037,7 +28034,7 @@ function innerCreate(orama, sharedInternalDocumentStore, schema, sortableDeniedP
28037
28034
  case "string[]":
28038
28035
  continue;
28039
28036
  default:
28040
- 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);
28041
28038
  }
28042
28039
  }
28043
28040
  }
@@ -29882,8 +29879,8 @@ function innerFullTextSearch(orama, params, language) {
29882
29879
  function escapeRegex2(str2) {
29883
29880
  return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
29884
29881
  }
29885
- function getPropValue(obj, path9) {
29886
- const keys = path9.split(".");
29882
+ function getPropValue(obj, path10) {
29883
+ const keys = path10.split(".");
29887
29884
  let value = obj;
29888
29885
  for (const key of keys) {
29889
29886
  if (value && typeof value === "object" && key in value) {
@@ -31045,6 +31042,217 @@ var init_provider = __esm({
31045
31042
  }
31046
31043
  });
31047
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
+
31048
31256
  // src/store/orama-store.ts
31049
31257
  var orama_store_exports = {};
31050
31258
  __export(orama_store_exports, {
@@ -31116,17 +31324,27 @@ async function removeObservation(oramaId) {
31116
31324
  }
31117
31325
  async function searchObservations(options2) {
31118
31326
  const database = await getDb();
31119
- const filters = {};
31327
+ let projectIds = null;
31120
31328
  if (options2.projectId) {
31121
- 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];
31122
31339
  }
31123
31340
  if (options2.type) {
31124
31341
  filters["type"] = options2.type;
31125
31342
  }
31126
31343
  const hasQuery = options2.query && options2.query.trim().length > 0;
31344
+ const requestLimit = projectIds && projectIds.length > 1 ? (options2.limit ?? 20) * 3 : options2.limit ?? 20;
31127
31345
  let searchParams = {
31128
31346
  term: options2.query,
31129
- limit: options2.limit ?? 20,
31347
+ limit: requestLimit,
31130
31348
  ...Object.keys(filters).length > 0 ? { where: filters } : {},
31131
31349
  // Search specific fields (not tokens, accessCount, etc.)
31132
31350
  properties: ["title", "entityName", "narrative", "facts", "concepts", "filesModified"],
@@ -31165,7 +31383,11 @@ async function searchObservations(options2) {
31165
31383
  }
31166
31384
  }
31167
31385
  const results = await search2(database, searchParams);
31168
- 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) => {
31169
31391
  const doc = hit.document;
31170
31392
  const obsType = doc.type;
31171
31393
  return {
@@ -31186,50 +31408,30 @@ async function searchObservations(options2) {
31186
31408
  const untilDate = new Date(options2.until).getTime();
31187
31409
  intermediate = intermediate.filter((e3) => new Date(e3.rawTime).getTime() <= untilDate);
31188
31410
  }
31411
+ if (projectIds && projectIds.length > 1) {
31412
+ intermediate = intermediate.slice(0, options2.limit ?? 20);
31413
+ }
31189
31414
  let entries = intermediate.map(({ rawTime: _4, ...rest }) => rest);
31190
31415
  if (hasQuery && options2.query) {
31191
31416
  const queryLower = options2.query.toLowerCase();
31192
31417
  const queryTokens = queryLower.split(/\s+/).filter((t2) => t2.length > 1);
31418
+ const entryMap = new Map(entries.map((e3) => [e3.id, e3]));
31193
31419
  for (const hit of results.hits) {
31194
31420
  const doc = hit.document;
31195
- const entry = entries.find((e3) => e3.id === doc.observationId);
31421
+ const entry = entryMap.get(doc.observationId);
31196
31422
  if (!entry) continue;
31197
31423
  const reasons = [];
31198
- for (const token of queryTokens) {
31199
- if (doc.title.toLowerCase().includes(token)) {
31200
- reasons.push("title");
31201
- break;
31202
- }
31203
- }
31204
- for (const token of queryTokens) {
31205
- if (doc.entityName.toLowerCase().includes(token)) {
31206
- reasons.push("entity");
31207
- break;
31208
- }
31209
- }
31210
- for (const token of queryTokens) {
31211
- if (doc.concepts.toLowerCase().includes(token)) {
31212
- reasons.push("concept");
31213
- break;
31214
- }
31215
- }
31216
- for (const token of queryTokens) {
31217
- if (doc.narrative.toLowerCase().includes(token)) {
31218
- reasons.push("narrative");
31219
- break;
31220
- }
31221
- }
31222
- for (const token of queryTokens) {
31223
- if (doc.facts.toLowerCase().includes(token)) {
31224
- reasons.push("fact");
31225
- break;
31226
- }
31227
- }
31228
- for (const token of queryTokens) {
31229
- if (doc.filesModified.toLowerCase().includes(token)) {
31230
- reasons.push("file");
31231
- break;
31232
- }
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);
31233
31435
  }
31234
31436
  if (reasons.length === 0) reasons.push("fuzzy");
31235
31437
  entry["matchedFields"] = reasons;
@@ -31238,8 +31440,8 @@ async function searchObservations(options2) {
31238
31440
  if (options2.maxTokens && options2.maxTokens > 0) {
31239
31441
  entries = applyTokenBudget(entries, options2.maxTokens);
31240
31442
  }
31241
- const hitIds = results.hits.map((h3) => h3.document.id);
31242
- recordAccessBatch(hitIds).catch(() => {
31443
+ const hitDocs = results.hits.map((h3) => ({ id: h3.id, doc: h3.document }));
31444
+ recordAccessBatch(hitDocs).catch(() => {
31243
31445
  });
31244
31446
  return entries;
31245
31447
  }
@@ -31288,20 +31490,12 @@ async function getTimeline(anchorId, _projectId, depthBefore = 3, depthAfter = 3
31288
31490
  after
31289
31491
  };
31290
31492
  }
31291
- async function recordAccessBatch(oramaIds) {
31493
+ async function recordAccessBatch(hitDocs) {
31292
31494
  const database = await getDb();
31293
31495
  const now = (/* @__PURE__ */ new Date()).toISOString();
31294
- for (const id of oramaIds) {
31496
+ for (const { id, doc } of hitDocs) {
31295
31497
  try {
31296
- const result = await search2(database, {
31297
- term: "",
31298
- where: { id },
31299
- limit: 1
31300
- });
31301
- if (result.hits.length === 0) continue;
31302
- const doc = result.hits[0].document;
31303
- await remove4(database, id);
31304
- await insert3(database, {
31498
+ await update(database, id, {
31305
31499
  ...doc,
31306
31500
  accessCount: (doc.accessCount ?? 0) + 1,
31307
31501
  lastAccessedAt: now
@@ -33355,6 +33549,7 @@ __export(observations_exports, {
33355
33549
  getObservationCount: () => getObservationCount2,
33356
33550
  getProjectObservations: () => getProjectObservations,
33357
33551
  initObservations: () => initObservations,
33552
+ migrateProjectIds: () => migrateProjectIds,
33358
33553
  reindexObservations: () => reindexObservations,
33359
33554
  storeObservation: () => storeObservation,
33360
33555
  suggestTopicKey: () => suggestTopicKey
@@ -33516,8 +33711,29 @@ function getObservation(id) {
33516
33711
  return observations.find((o3) => o3.id === id);
33517
33712
  }
33518
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
+ }
33519
33718
  return observations.filter((o3) => o3.projectId === projectId);
33520
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
+ }
33521
33737
  function getAllObservations() {
33522
33738
  return [...observations];
33523
33739
  }
@@ -33857,31 +34073,34 @@ __export(detector_exports, {
33857
34073
  });
33858
34074
  import { execSync } from "child_process";
33859
34075
  import { existsSync, readFileSync } from "fs";
33860
- import os2 from "os";
33861
- import path4 from "path";
34076
+ import os3 from "os";
34077
+ import path5 from "path";
33862
34078
  function detectProject(cwd) {
33863
34079
  const basePath = cwd ?? process.cwd();
33864
34080
  const rootPath = getGitRoot(basePath) ?? findPackageRoot(basePath) ?? basePath;
33865
34081
  const gitRemote = getGitRemote(rootPath);
33866
34082
  if (gitRemote) {
33867
34083
  const id2 = normalizeGitRemote(gitRemote);
33868
- const name2 = id2.split("/").pop() ?? path4.basename(rootPath);
34084
+ const name2 = id2.split("/").pop() ?? path5.basename(rootPath);
33869
34085
  return { id: id2, name: name2, gitRemote, rootPath };
33870
34086
  }
33871
34087
  if (isDangerousRoot(rootPath)) {
33872
- console.error(`[memorix] Skipped invalid project root: ${rootPath}`);
33873
- return { id: "__invalid__", name: "unknown", rootPath };
34088
+ const name2 = path5.basename(rootPath) || "unknown";
34089
+ const id2 = `placeholder/${name2}`;
34090
+ console.error(`[memorix] WARNING: cwd "${rootPath}" is not a project directory \u2014 using degraded mode (${id2})`);
34091
+ console.error(`[memorix] For best results, set MEMORIX_PROJECT_ROOT or --cwd to your project path.`);
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, {
@@ -40338,31 +40557,37 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir) {
40338
40557
  let effectiveProjectId = projectId;
40339
40558
  let effectiveProjectName = projectName;
40340
40559
  if (requestedProject && requestedProject !== projectId) {
40341
- const sanitized = requestedProject.replace(/\//g, "--").replace(/[<>:"|?*\\]/g, "_");
40342
- const candidateDir = path6.join(baseDir, sanitized);
40343
- try {
40344
- await fs4.access(candidateDir);
40345
- effectiveDataDir = candidateDir;
40346
- effectiveProjectId = requestedProject;
40347
- effectiveProjectName = requestedProject.split("/").pop() || requestedProject;
40348
- } catch {
40349
- }
40560
+ effectiveDataDir = baseDir;
40561
+ effectiveProjectId = requestedProject;
40562
+ effectiveProjectName = requestedProject.split("/").pop() || requestedProject;
40350
40563
  }
40351
40564
  try {
40352
40565
  switch (apiPath) {
40353
40566
  case "/projects": {
40354
40567
  try {
40355
- const entries = await fs4.readdir(baseDir, { withFileTypes: true });
40356
- const projects = entries.filter((e3) => e3.isDirectory() && e3.name.includes("--") && !e3.name.startsWith("local--")).map((e3) => {
40357
- const dirName = e3.name;
40358
- const id = dirName.replace(/--/g, "/");
40359
- return {
40360
- id,
40361
- name: id.split("/").pop() || id,
40362
- dirName,
40363
- isCurrent: id === projectId
40364
- };
40365
- });
40568
+ const allObs = await loadObservationsJson(baseDir);
40569
+ const projectSet = /* @__PURE__ */ new Map();
40570
+ for (const obs of allObs) {
40571
+ if (obs.projectId) {
40572
+ projectSet.set(obs.projectId, (projectSet.get(obs.projectId) || 0) + 1);
40573
+ }
40574
+ }
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]) => ({
40586
+ id,
40587
+ name: id.split("/").pop() || id,
40588
+ count: count3,
40589
+ isCurrent: id === projectId
40590
+ }));
40366
40591
  sendJson(res, projects);
40367
40592
  } catch {
40368
40593
  sendJson(res, []);
@@ -40522,14 +40747,14 @@ async function serveStatic(req, res, staticDir) {
40522
40747
  if (urlPath === "/" || !urlPath.includes(".")) {
40523
40748
  urlPath = "/index.html";
40524
40749
  }
40525
- const filePath = path6.join(staticDir, urlPath);
40750
+ const filePath = path7.join(staticDir, urlPath);
40526
40751
  if (!filePath.startsWith(staticDir)) {
40527
40752
  sendError(res, "Forbidden", 403);
40528
40753
  return;
40529
40754
  }
40530
40755
  try {
40531
- const data = await fs4.readFile(filePath);
40532
- const ext = path6.extname(filePath);
40756
+ const data = await fs5.readFile(filePath);
40757
+ const ext = path7.extname(filePath);
40533
40758
  res.writeHead(200, {
40534
40759
  "Content-Type": MIME_TYPES[ext] || "application/octet-stream",
40535
40760
  "Cache-Control": "no-cache"
@@ -40537,7 +40762,7 @@ async function serveStatic(req, res, staticDir) {
40537
40762
  res.end(data);
40538
40763
  } catch {
40539
40764
  try {
40540
- const indexData = await fs4.readFile(path6.join(staticDir, "index.html"));
40765
+ const indexData = await fs5.readFile(path7.join(staticDir, "index.html"));
40541
40766
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
40542
40767
  res.end(indexData);
40543
40768
  } catch {
@@ -40568,17 +40793,9 @@ async function startDashboard(dataDir, port, staticDir, projectId, projectName,
40568
40793
  try {
40569
40794
  const body = JSON.parse(await readBody(req));
40570
40795
  if (body.projectId) {
40571
- const sanitized = body.projectId.replace(/\//g, "--").replace(/[<>:"|?*\\]/g, "_");
40572
- const candidateDir = path6.join(baseDir, sanitized);
40573
- try {
40574
- await fs4.access(candidateDir);
40575
- } catch {
40576
- sendError(res, `Project data directory not found: ${candidateDir}`, 404);
40577
- return;
40578
- }
40579
40796
  state.projectId = body.projectId;
40580
40797
  state.projectName = body.projectName || body.projectId.split("/").pop() || body.projectId;
40581
- state.dataDir = candidateDir;
40798
+ state.dataDir = baseDir;
40582
40799
  console.error(`[dashboard] Switched current project to: ${state.projectId}`);
40583
40800
  sendJson(res, { ok: true, projectId: state.projectId, projectName: state.projectName });
40584
40801
  } else {
@@ -40647,9 +40864,9 @@ __export(installers_exports, {
40647
40864
  installHooks: () => installHooks,
40648
40865
  uninstallHooks: () => uninstallHooks
40649
40866
  });
40650
- import * as fs5 from "fs/promises";
40651
- import * as path7 from "path";
40652
- import * as os3 from "os";
40867
+ import * as fs6 from "fs/promises";
40868
+ import * as path8 from "path";
40869
+ import * as os4 from "os";
40653
40870
  function resolveHookCommand() {
40654
40871
  if (process.platform === "win32") {
40655
40872
  return "cmd /c memorix";
@@ -40797,81 +41014,81 @@ function generateKiroHookFiles() {
40797
41014
  function getProjectConfigPath(agent, projectRoot) {
40798
41015
  switch (agent) {
40799
41016
  case "claude":
40800
- return path7.join(projectRoot, ".claude", "settings.local.json");
41017
+ return path8.join(projectRoot, ".claude", "settings.local.json");
40801
41018
  case "copilot":
40802
- return path7.join(projectRoot, ".github", "hooks", "memorix.json");
41019
+ return path8.join(projectRoot, ".github", "hooks", "memorix.json");
40803
41020
  case "windsurf":
40804
- return path7.join(projectRoot, ".windsurf", "hooks.json");
41021
+ return path8.join(projectRoot, ".windsurf", "hooks.json");
40805
41022
  case "cursor":
40806
- return path7.join(projectRoot, ".cursor", "hooks.json");
41023
+ return path8.join(projectRoot, ".cursor", "hooks.json");
40807
41024
  case "kiro":
40808
- return path7.join(projectRoot, ".kiro", "hooks", "memorix-agent-stop.kiro.hook");
41025
+ return path8.join(projectRoot, ".kiro", "hooks", "memorix-agent-stop.kiro.hook");
40809
41026
  case "codex":
40810
- return path7.join(projectRoot, "AGENTS.md");
41027
+ return path8.join(projectRoot, "AGENTS.md");
40811
41028
  case "antigravity":
40812
- return path7.join(projectRoot, ".gemini", "settings.json");
41029
+ return path8.join(projectRoot, ".gemini", "settings.json");
40813
41030
  default:
40814
- return path7.join(projectRoot, ".memorix", "hooks.json");
41031
+ return path8.join(projectRoot, ".memorix", "hooks.json");
40815
41032
  }
40816
41033
  }
40817
41034
  function getGlobalConfigPath(agent) {
40818
- const home = os3.homedir();
41035
+ const home = os4.homedir();
40819
41036
  switch (agent) {
40820
41037
  case "claude":
40821
41038
  case "copilot":
40822
- return path7.join(home, ".claude", "settings.json");
41039
+ return path8.join(home, ".claude", "settings.json");
40823
41040
  case "windsurf":
40824
- return path7.join(home, ".codeium", "windsurf", "hooks.json");
41041
+ return path8.join(home, ".codeium", "windsurf", "hooks.json");
40825
41042
  case "cursor":
40826
- return path7.join(home, ".cursor", "hooks.json");
41043
+ return path8.join(home, ".cursor", "hooks.json");
40827
41044
  case "antigravity":
40828
- return path7.join(home, ".gemini", "settings.json");
41045
+ return path8.join(home, ".gemini", "settings.json");
40829
41046
  default:
40830
- return path7.join(home, ".memorix", "hooks.json");
41047
+ return path8.join(home, ".memorix", "hooks.json");
40831
41048
  }
40832
41049
  }
40833
41050
  async function detectInstalledAgents() {
40834
41051
  const agents = [];
40835
- const home = os3.homedir();
40836
- const claudeDir = path7.join(home, ".claude");
41052
+ const home = os4.homedir();
41053
+ const claudeDir = path8.join(home, ".claude");
40837
41054
  try {
40838
- await fs5.access(claudeDir);
41055
+ await fs6.access(claudeDir);
40839
41056
  agents.push("claude");
40840
41057
  } catch {
40841
41058
  }
40842
- const windsurfDir = path7.join(home, ".codeium", "windsurf");
41059
+ const windsurfDir = path8.join(home, ".codeium", "windsurf");
40843
41060
  try {
40844
- await fs5.access(windsurfDir);
41061
+ await fs6.access(windsurfDir);
40845
41062
  agents.push("windsurf");
40846
41063
  } catch {
40847
41064
  }
40848
- const cursorDir = path7.join(home, ".cursor");
41065
+ const cursorDir = path8.join(home, ".cursor");
40849
41066
  try {
40850
- await fs5.access(cursorDir);
41067
+ await fs6.access(cursorDir);
40851
41068
  agents.push("cursor");
40852
41069
  } catch {
40853
41070
  }
40854
- const vscodeDir = path7.join(home, ".vscode");
41071
+ const vscodeDir = path8.join(home, ".vscode");
40855
41072
  try {
40856
- await fs5.access(vscodeDir);
41073
+ await fs6.access(vscodeDir);
40857
41074
  agents.push("copilot");
40858
41075
  } catch {
40859
41076
  }
40860
- const kiroConfig = path7.join(home, ".kiro");
41077
+ const kiroConfig = path8.join(home, ".kiro");
40861
41078
  try {
40862
- await fs5.access(kiroConfig);
41079
+ await fs6.access(kiroConfig);
40863
41080
  agents.push("kiro");
40864
41081
  } catch {
40865
41082
  }
40866
- const codexDir = path7.join(home, ".codex");
41083
+ const codexDir = path8.join(home, ".codex");
40867
41084
  try {
40868
- await fs5.access(codexDir);
41085
+ await fs6.access(codexDir);
40869
41086
  agents.push("codex");
40870
41087
  } catch {
40871
41088
  }
40872
- const geminiDir = path7.join(home, ".gemini");
41089
+ const geminiDir = path8.join(home, ".gemini");
40873
41090
  try {
40874
- await fs5.access(geminiDir);
41091
+ await fs6.access(geminiDir);
40875
41092
  agents.push("antigravity");
40876
41093
  } catch {
40877
41094
  }
@@ -40910,18 +41127,18 @@ async function installHooks(agent, projectRoot, global = false) {
40910
41127
  default:
40911
41128
  generated = generateClaudeConfig();
40912
41129
  }
40913
- await fs5.mkdir(path7.dirname(configPath), { recursive: true });
41130
+ await fs6.mkdir(path8.dirname(configPath), { recursive: true });
40914
41131
  if (agent === "kiro") {
40915
41132
  const hookFiles = generateKiroHookFiles();
40916
- const hooksDir = path7.join(path7.dirname(configPath));
40917
- await fs5.mkdir(hooksDir, { recursive: true });
41133
+ const hooksDir = path8.join(path8.dirname(configPath));
41134
+ await fs6.mkdir(hooksDir, { recursive: true });
40918
41135
  for (const hf of hookFiles) {
40919
- 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");
40920
41137
  }
40921
41138
  } else {
40922
41139
  let existing = {};
40923
41140
  try {
40924
- const content = await fs5.readFile(configPath, "utf-8");
41141
+ const content = await fs6.readFile(configPath, "utf-8");
40925
41142
  existing = JSON.parse(content);
40926
41143
  } catch {
40927
41144
  }
@@ -40948,7 +41165,7 @@ async function installHooks(agent, projectRoot, global = false) {
40948
41165
  const h3 = merged.hooks;
40949
41166
  if (h3) delete h3.preToolUse;
40950
41167
  }
40951
- await fs5.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
41168
+ await fs6.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
40952
41169
  }
40953
41170
  const events = [];
40954
41171
  switch (agent) {
@@ -40984,45 +41201,45 @@ async function installAgentRules(agent, projectRoot) {
40984
41201
  let rulesPath;
40985
41202
  switch (agent) {
40986
41203
  case "windsurf":
40987
- rulesPath = path7.join(projectRoot, ".windsurf", "rules", "memorix.md");
41204
+ rulesPath = path8.join(projectRoot, ".windsurf", "rules", "memorix.md");
40988
41205
  break;
40989
41206
  case "cursor":
40990
- rulesPath = path7.join(projectRoot, ".cursor", "rules", "memorix.mdc");
41207
+ rulesPath = path8.join(projectRoot, ".cursor", "rules", "memorix.mdc");
40991
41208
  break;
40992
41209
  case "claude":
40993
41210
  case "copilot":
40994
- rulesPath = path7.join(projectRoot, ".github", "copilot-instructions.md");
41211
+ rulesPath = path8.join(projectRoot, ".github", "copilot-instructions.md");
40995
41212
  break;
40996
41213
  case "codex":
40997
- rulesPath = path7.join(projectRoot, "AGENTS.md");
41214
+ rulesPath = path8.join(projectRoot, "AGENTS.md");
40998
41215
  break;
40999
41216
  case "kiro":
41000
- rulesPath = path7.join(projectRoot, ".kiro", "steering", "memorix.md");
41217
+ rulesPath = path8.join(projectRoot, ".kiro", "steering", "memorix.md");
41001
41218
  break;
41002
41219
  case "antigravity":
41003
- rulesPath = path7.join(projectRoot, "GEMINI.md");
41220
+ rulesPath = path8.join(projectRoot, "GEMINI.md");
41004
41221
  break;
41005
41222
  default:
41006
- rulesPath = path7.join(projectRoot, ".agent", "rules", "memorix.md");
41223
+ rulesPath = path8.join(projectRoot, ".agent", "rules", "memorix.md");
41007
41224
  break;
41008
41225
  }
41009
41226
  try {
41010
- await fs5.mkdir(path7.dirname(rulesPath), { recursive: true });
41227
+ await fs6.mkdir(path8.dirname(rulesPath), { recursive: true });
41011
41228
  if (agent === "codex" || agent === "antigravity") {
41012
41229
  try {
41013
- const existing = await fs5.readFile(rulesPath, "utf-8");
41230
+ const existing = await fs6.readFile(rulesPath, "utf-8");
41014
41231
  if (existing.includes("Memorix")) {
41015
41232
  return;
41016
41233
  }
41017
- await fs5.writeFile(rulesPath, existing + "\n\n" + rulesContent, "utf-8");
41234
+ await fs6.writeFile(rulesPath, existing + "\n\n" + rulesContent, "utf-8");
41018
41235
  } catch {
41019
- await fs5.writeFile(rulesPath, rulesContent, "utf-8");
41236
+ await fs6.writeFile(rulesPath, rulesContent, "utf-8");
41020
41237
  }
41021
41238
  } else {
41022
41239
  try {
41023
- await fs5.access(rulesPath);
41240
+ await fs6.access(rulesPath);
41024
41241
  } catch {
41025
- await fs5.writeFile(rulesPath, rulesContent, "utf-8");
41242
+ await fs6.writeFile(rulesPath, rulesContent, "utf-8");
41026
41243
  }
41027
41244
  }
41028
41245
  } catch {
@@ -41107,15 +41324,15 @@ async function uninstallHooks(agent, projectRoot, global = false) {
41107
41324
  const configPath = global ? getGlobalConfigPath(agent) : getProjectConfigPath(agent, projectRoot);
41108
41325
  try {
41109
41326
  if (agent === "kiro") {
41110
- await fs5.unlink(configPath);
41327
+ await fs6.unlink(configPath);
41111
41328
  } else {
41112
- const content = await fs5.readFile(configPath, "utf-8");
41329
+ const content = await fs6.readFile(configPath, "utf-8");
41113
41330
  const config2 = JSON.parse(content);
41114
41331
  delete config2.hooks;
41115
41332
  if (Object.keys(config2).length === 0) {
41116
- await fs5.unlink(configPath);
41333
+ await fs6.unlink(configPath);
41117
41334
  } else {
41118
- await fs5.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
41335
+ await fs6.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
41119
41336
  }
41120
41337
  }
41121
41338
  return true;
@@ -41132,11 +41349,11 @@ async function getHookStatus(projectRoot) {
41132
41349
  let installed = false;
41133
41350
  let usedPath = projectPath;
41134
41351
  try {
41135
- await fs5.access(projectPath);
41352
+ await fs6.access(projectPath);
41136
41353
  installed = true;
41137
41354
  } catch {
41138
41355
  try {
41139
- await fs5.access(globalPath);
41356
+ await fs6.access(globalPath);
41140
41357
  installed = true;
41141
41358
  usedPath = globalPath;
41142
41359
  } catch {
@@ -41212,18 +41429,7 @@ function coerceObjectArray(val) {
41212
41429
  return [];
41213
41430
  }
41214
41431
  async function createMemorixServer(cwd, existingServer) {
41215
- const project = detectProject(cwd);
41216
- if (project.id === "__invalid__") {
41217
- const resolvedCwd = cwd ?? process.cwd();
41218
- console.error(`[memorix] ERROR: Could not detect a valid project at: ${resolvedCwd}`);
41219
- console.error(`[memorix] The directory is not a git repository and has no project indicator files.`);
41220
- console.error(`[memorix] Fix: set --cwd to your project directory, or set MEMORIX_PROJECT_ROOT env var.`);
41221
- console.error(`[memorix] Example: memorix serve --cwd /path/to/your/project`);
41222
- console.error(`[memorix] Example: "env": { "MEMORIX_PROJECT_ROOT": "/path/to/your/project" }`);
41223
- throw new Error(
41224
- `Cannot start Memorix: no valid project detected at "${resolvedCwd}". Set --cwd or MEMORIX_PROJECT_ROOT to your project directory.`
41225
- );
41226
- }
41432
+ const rawProject = detectProject(cwd);
41227
41433
  try {
41228
41434
  const { migrateSubdirsToFlat: migrateSubdirsToFlat2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
41229
41435
  const migrated = await migrateSubdirsToFlat2();
@@ -41232,10 +41438,44 @@ async function createMemorixServer(cwd, existingServer) {
41232
41438
  }
41233
41439
  } catch {
41234
41440
  }
41235
- 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
+ }
41236
41448
  const graphManager = new KnowledgeGraphManager(projectDir2);
41237
41449
  await graphManager.init();
41238
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
+ }
41239
41479
  const reindexed = await reindexObservations();
41240
41480
  if (reindexed > 0) {
41241
41481
  console.error(`[memorix] Reindexed ${reindexed} observations for project: ${project.id}`);
@@ -41947,9 +42187,9 @@ ${skill.content}` }]
41947
42187
  lines.push(`- **Description**: ${sk.description}`);
41948
42188
  lines.push(`- **Observations**: ${sk.content.split("\n").length} lines of knowledge`);
41949
42189
  if (write && target) {
41950
- const path9 = engine.writeSkill(sk, target);
41951
- if (path9) {
41952
- lines.push(`- \u2705 **Written**: \`${path9}\``);
42190
+ const path10 = engine.writeSkill(sk, target);
42191
+ if (path10) {
42192
+ lines.push(`- \u2705 **Written**: \`${path10}\``);
41953
42193
  } else {
41954
42194
  lines.push(`- \u274C Failed to write`);
41955
42195
  }
@@ -42398,6 +42638,7 @@ var init_server4 = __esm({
42398
42638
  init_entity_extractor();
42399
42639
  init_engine();
42400
42640
  init_detector();
42641
+ init_aliases();
42401
42642
  init_persistence();
42402
42643
  init_syncer();
42403
42644
  init_engine2();
@@ -42456,7 +42697,8 @@ var init_serve = __esm({
42456
42697
  const looksValid = existsSync5(join13(projectRoot, ".git")) || existsSync5(join13(projectRoot, "package.json")) || existsSync5(join13(projectRoot, "Cargo.toml")) || existsSync5(join13(projectRoot, "go.mod")) || existsSync5(join13(projectRoot, "pyproject.toml"));
42457
42698
  if (!looksValid) {
42458
42699
  const earlyDetect = detectProject2(projectRoot);
42459
- if (earlyDetect.id !== "__invalid__") {
42700
+ const isDegraded = earlyDetect.id.startsWith("placeholder/");
42701
+ if (!isDegraded) {
42460
42702
  console.error(`[memorix] detectProject succeeded without standard indicators`);
42461
42703
  const { server, projectId, deferredInit } = await createMemorixServer2(projectRoot);
42462
42704
  const transport = new StdioServerTransport2();
@@ -42475,7 +42717,6 @@ var init_serve = __esm({
42475
42717
  }));
42476
42718
  const transport = new StdioServerTransport2();
42477
42719
  await mcpServer.connect(transport);
42478
- let rootResolved = false;
42479
42720
  try {
42480
42721
  const rootsResult = await Promise.race([
42481
42722
  mcpServer.server.listRoots(),
@@ -42488,20 +42729,11 @@ var init_serve = __esm({
42488
42729
  const normalizedPath = process.platform === "win32" && urlPath.match(/^\/[A-Za-z]:/) ? urlPath.slice(1) : urlPath;
42489
42730
  console.error(`[memorix] MCP client root: ${normalizedPath}`);
42490
42731
  projectRoot = normalizedPath;
42491
- rootResolved = true;
42492
42732
  }
42493
42733
  }
42494
42734
  } catch {
42495
42735
  console.error(`[memorix] MCP roots not available (client may not support it)`);
42496
42736
  }
42497
- if (!rootResolved) {
42498
- const retryDetect = detectProject2(projectRoot);
42499
- if (retryDetect.id === "__invalid__") {
42500
- console.error(`[memorix] ERROR: Could not detect a valid project.`);
42501
- console.error(`[memorix] Fix: set --cwd or MEMORIX_PROJECT_ROOT, or use an IDE that supports MCP roots.`);
42502
- process.exit(1);
42503
- }
42504
- }
42505
42737
  const { projectId, deferredInit } = await createMemorixServer2(projectRoot, mcpServer);
42506
42738
  console.error(`[memorix] MCP Server running on stdio (project: ${projectId})`);
42507
42739
  console.error(`[memorix] Project root: ${projectRoot}`);
@@ -43367,8 +43599,8 @@ var init_sync = __esm({
43367
43599
  run: async ({ args }) => {
43368
43600
  const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
43369
43601
  const { RulesSyncer: RulesSyncer2 } = await Promise.resolve().then(() => (init_syncer(), syncer_exports));
43370
- const { promises: fs6 } = await import("fs");
43371
- const path9 = await import("path");
43602
+ const { promises: fs7 } = await import("fs");
43603
+ const path10 = await import("path");
43372
43604
  we("memorix sync");
43373
43605
  const project = detectProject2();
43374
43606
  v4.info(`Project: ${project.name} (${project.id})`);
@@ -43436,9 +43668,9 @@ var init_sync = __esm({
43436
43668
  process.exit(0);
43437
43669
  }
43438
43670
  for (const file of files) {
43439
- const fullPath = path9.join(project.rootPath, file.filePath);
43440
- await fs6.mkdir(path9.dirname(fullPath), { recursive: true });
43441
- 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");
43442
43674
  v4.success(`Written: ${file.filePath}`);
43443
43675
  }
43444
43676
  fe2(`Synced ${files.length} rule(s) to ${target} format`);
@@ -44259,12 +44491,12 @@ var init_hooks_install = __esm({
44259
44491
  },
44260
44492
  run: async ({ args }) => {
44261
44493
  const { detectInstalledAgents: detectInstalledAgents2, installHooks: installHooks2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
44262
- const os4 = await import("os");
44494
+ const os5 = await import("os");
44263
44495
  let cwd;
44264
44496
  try {
44265
44497
  cwd = process.cwd();
44266
44498
  } catch {
44267
- cwd = os4.homedir();
44499
+ cwd = os5.homedir();
44268
44500
  console.log(`\u26A0\uFE0F Could not access current directory, using home: ${cwd}`);
44269
44501
  }
44270
44502
  let agents;
@@ -44327,12 +44559,12 @@ var init_hooks_uninstall = __esm({
44327
44559
  },
44328
44560
  run: async ({ args }) => {
44329
44561
  const { detectInstalledAgents: detectInstalledAgents2, uninstallHooks: uninstallHooks2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
44330
- const os4 = await import("os");
44562
+ const os5 = await import("os");
44331
44563
  let cwd;
44332
44564
  try {
44333
44565
  cwd = process.cwd();
44334
44566
  } catch {
44335
- cwd = os4.homedir();
44567
+ cwd = os5.homedir();
44336
44568
  }
44337
44569
  let agents;
44338
44570
  if (args.agent) {
@@ -44375,12 +44607,12 @@ var init_hooks_status = __esm({
44375
44607
  },
44376
44608
  run: async () => {
44377
44609
  const { getHookStatus: getHookStatus2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
44378
- const os4 = await import("os");
44610
+ const os5 = await import("os");
44379
44611
  let cwd;
44380
44612
  try {
44381
44613
  cwd = process.cwd();
44382
44614
  } catch {
44383
- cwd = os4.homedir();
44615
+ cwd = os5.homedir();
44384
44616
  }
44385
44617
  const statuses = await getHookStatus2(cwd);
44386
44618
  console.log("\nMemorix Hooks Status");
@@ -44429,7 +44661,7 @@ __export(dashboard_exports, {
44429
44661
  default: () => dashboard_default
44430
44662
  });
44431
44663
  import { fileURLToPath as fileURLToPath2 } from "url";
44432
- import path8 from "path";
44664
+ import path9 from "path";
44433
44665
  var dashboard_default;
44434
44666
  var init_dashboard = __esm({
44435
44667
  "src/cli/commands/dashboard.ts"() {
@@ -44455,8 +44687,8 @@ var init_dashboard = __esm({
44455
44687
  const project = detectProject2();
44456
44688
  const dataDir = await getProjectDataDir2(project.id);
44457
44689
  const port = parseInt(args.port, 10) || 3210;
44458
- const cliDir = path8.dirname(fileURLToPath2(import.meta.url));
44459
- const staticDir = path8.join(cliDir, "..", "dashboard", "static");
44690
+ const cliDir = path9.dirname(fileURLToPath2(import.meta.url));
44691
+ const staticDir = path9.join(cliDir, "..", "dashboard", "static");
44460
44692
  await startDashboard2(dataDir, port, staticDir, project.id, project.name);
44461
44693
  await new Promise(() => {
44462
44694
  });
@@ -44509,9 +44741,9 @@ var init_cleanup = __esm({
44509
44741
  },
44510
44742
  async run({ args }) {
44511
44743
  const project = detectProject();
44512
- if (project.id === "__invalid__") {
44513
- console.error("\u274C Not in a valid project directory.");
44514
- process.exit(1);
44744
+ if (project.id.startsWith("placeholder/")) {
44745
+ console.error("\u26A0\uFE0F Not in a valid project directory \u2014 using degraded mode.");
44746
+ console.error("Set MEMORIX_PROJECT_ROOT or --cwd for best results.");
44515
44747
  }
44516
44748
  console.log(`
44517
44749
  \u{1F4E6} Project: ${project.name} (${project.id})