memorix 0.9.32 → 0.9.34

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
@@ -8790,12 +8790,12 @@ var init_stdio2 = __esm({
8790
8790
  this.onclose?.();
8791
8791
  }
8792
8792
  send(message) {
8793
- return new Promise((resolve2) => {
8793
+ return new Promise((resolve) => {
8794
8794
  const json = serializeMessage(message);
8795
8795
  if (this._stdout.write(json)) {
8796
- resolve2();
8796
+ resolve();
8797
8797
  } else {
8798
- this._stdout.once("drain", resolve2);
8798
+ this._stdout.once("drain", resolve);
8799
8799
  }
8800
8800
  });
8801
8801
  }
@@ -15438,7 +15438,7 @@ var init_protocol = __esm({
15438
15438
  return;
15439
15439
  }
15440
15440
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
15441
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
15441
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
15442
15442
  options2?.signal?.throwIfAborted();
15443
15443
  }
15444
15444
  } catch (error2) {
@@ -15455,7 +15455,7 @@ var init_protocol = __esm({
15455
15455
  */
15456
15456
  request(request, resultSchema, options2) {
15457
15457
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options2 ?? {};
15458
- return new Promise((resolve2, reject) => {
15458
+ return new Promise((resolve, reject) => {
15459
15459
  const earlyReject = (error2) => {
15460
15460
  reject(error2);
15461
15461
  };
@@ -15533,7 +15533,7 @@ var init_protocol = __esm({
15533
15533
  if (!parseResult.success) {
15534
15534
  reject(parseResult.error);
15535
15535
  } else {
15536
- resolve2(parseResult.data);
15536
+ resolve(parseResult.data);
15537
15537
  }
15538
15538
  } catch (error2) {
15539
15539
  reject(error2);
@@ -15794,12 +15794,12 @@ var init_protocol = __esm({
15794
15794
  }
15795
15795
  } catch {
15796
15796
  }
15797
- return new Promise((resolve2, reject) => {
15797
+ return new Promise((resolve, reject) => {
15798
15798
  if (signal.aborted) {
15799
15799
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
15800
15800
  return;
15801
15801
  }
15802
- const timeoutId = setTimeout(resolve2, interval);
15802
+ const timeoutId = setTimeout(resolve, interval);
15803
15803
  signal.addEventListener("abort", () => {
15804
15804
  clearTimeout(timeoutId);
15805
15805
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -18847,7 +18847,7 @@ var require_compile = __commonJS({
18847
18847
  const schOrFunc = root.refs[ref];
18848
18848
  if (schOrFunc)
18849
18849
  return schOrFunc;
18850
- let _sch = resolve2.call(this, root, ref);
18850
+ let _sch = resolve.call(this, root, ref);
18851
18851
  if (_sch === void 0) {
18852
18852
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
18853
18853
  const { schemaId } = this.opts;
@@ -18874,7 +18874,7 @@ var require_compile = __commonJS({
18874
18874
  function sameSchemaEnv(s1, s2) {
18875
18875
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
18876
18876
  }
18877
- function resolve2(root, ref) {
18877
+ function resolve(root, ref) {
18878
18878
  let sch;
18879
18879
  while (typeof (sch = this.refs[ref]) == "string")
18880
18880
  ref = sch;
@@ -19452,7 +19452,7 @@ var require_fast_uri = __commonJS({
19452
19452
  }
19453
19453
  return uri;
19454
19454
  }
19455
- function resolve2(baseURI, relativeURI, options2) {
19455
+ function resolve(baseURI, relativeURI, options2) {
19456
19456
  const schemelessOptions = options2 ? Object.assign({ scheme: "null" }, options2) : { scheme: "null" };
19457
19457
  const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
19458
19458
  schemelessOptions.skipEscape = true;
@@ -19679,7 +19679,7 @@ var require_fast_uri = __commonJS({
19679
19679
  var fastUri = {
19680
19680
  SCHEMES,
19681
19681
  normalize,
19682
- resolve: resolve2,
19682
+ resolve,
19683
19683
  resolveComponent,
19684
19684
  equal,
19685
19685
  serialize,
@@ -23943,7 +23943,7 @@ var init_mcp = __esm({
23943
23943
  let task = createTaskResult.task;
23944
23944
  const pollInterval = task.pollInterval ?? 5e3;
23945
23945
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
23946
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
23946
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
23947
23947
  const updatedTask = await extra.taskStore.getTask(taskId);
23948
23948
  if (!updatedTask) {
23949
23949
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -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;
@@ -29397,12 +29394,12 @@ async function removeMultipleAsync(orama, ids, batchSize, language, skipHooks) {
29397
29394
  if (!skipHooks) {
29398
29395
  await runMultipleHook(orama.beforeRemoveMultiple, orama, docIdsForHooks);
29399
29396
  }
29400
- await new Promise((resolve2, reject) => {
29397
+ await new Promise((resolve, reject) => {
29401
29398
  let i2 = 0;
29402
29399
  async function _removeMultiple() {
29403
29400
  const batch = ids.slice(i2 * batchSize, ++i2 * batchSize);
29404
29401
  if (!batch.length) {
29405
- return resolve2();
29402
+ return resolve();
29406
29403
  }
29407
29404
  for (const doc of batch) {
29408
29405
  try {
@@ -30801,13 +30798,42 @@ var fastembed_provider_exports = {};
30801
30798
  __export(fastembed_provider_exports, {
30802
30799
  FastEmbedProvider: () => FastEmbedProvider
30803
30800
  });
30804
- var cache, MAX_CACHE_SIZE, FastEmbedProvider;
30801
+ import { createHash } from "crypto";
30802
+ import { readFile, writeFile, mkdir } from "fs/promises";
30803
+ import { join } from "path";
30804
+ import { homedir } from "os";
30805
+ function textHash(text) {
30806
+ return createHash("sha256").update(text).digest("hex").slice(0, 16);
30807
+ }
30808
+ async function loadDiskCache() {
30809
+ try {
30810
+ const raw = await readFile(CACHE_FILE, "utf-8");
30811
+ const entries = JSON.parse(raw);
30812
+ for (const [k5, v5] of entries) cache.set(k5, v5);
30813
+ console.error(`[memorix] Loaded ${entries.length} cached embeddings from disk`);
30814
+ } catch {
30815
+ }
30816
+ }
30817
+ async function saveDiskCache() {
30818
+ if (!diskCacheDirty) return;
30819
+ try {
30820
+ await mkdir(CACHE_DIR, { recursive: true });
30821
+ const entries = Array.from(cache.entries());
30822
+ await writeFile(CACHE_FILE, JSON.stringify(entries));
30823
+ diskCacheDirty = false;
30824
+ } catch {
30825
+ }
30826
+ }
30827
+ var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, diskCacheDirty, FastEmbedProvider;
30805
30828
  var init_fastembed_provider = __esm({
30806
30829
  "src/embedding/fastembed-provider.ts"() {
30807
30830
  "use strict";
30808
30831
  init_esm_shims();
30832
+ CACHE_DIR = join(homedir(), ".memorix", "data");
30833
+ CACHE_FILE = join(CACHE_DIR, ".embedding-cache.json");
30809
30834
  cache = /* @__PURE__ */ new Map();
30810
30835
  MAX_CACHE_SIZE = 5e3;
30836
+ diskCacheDirty = false;
30811
30837
  FastEmbedProvider = class _FastEmbedProvider {
30812
30838
  name = "fastembed-bge-small";
30813
30839
  dimensions = 384;
@@ -30818,23 +30844,26 @@ var init_fastembed_provider = __esm({
30818
30844
  /**
30819
30845
  * Initialize the FastEmbed provider.
30820
30846
  * Downloads model on first use (~30MB), cached locally after.
30847
+ * Loads persistent embedding cache from disk.
30821
30848
  */
30822
30849
  static async create() {
30823
30850
  const { EmbeddingModel, FlagEmbedding } = await import("fastembed");
30824
30851
  const model = await FlagEmbedding.init({
30825
30852
  model: EmbeddingModel.BGESmallENV15
30826
30853
  });
30854
+ await loadDiskCache();
30827
30855
  return new _FastEmbedProvider(model);
30828
30856
  }
30829
30857
  async embed(text) {
30830
- const cached2 = cache.get(text);
30858
+ const hash = textHash(text);
30859
+ const cached2 = cache.get(hash);
30831
30860
  if (cached2) return cached2;
30832
30861
  const raw = await this.model.queryEmbed(text);
30833
30862
  const result = Array.from(raw);
30834
30863
  if (result.length !== this.dimensions) {
30835
30864
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
30836
30865
  }
30837
- this.cacheSet(text, result);
30866
+ this.cacheSet(hash, result);
30838
30867
  return result;
30839
30868
  }
30840
30869
  async embedBatch(texts) {
@@ -30842,7 +30871,8 @@ var init_fastembed_provider = __esm({
30842
30871
  const uncachedIndices = [];
30843
30872
  const uncachedTexts = [];
30844
30873
  for (let i2 = 0; i2 < texts.length; i2++) {
30845
- const cached2 = cache.get(texts[i2]);
30874
+ const hash = textHash(texts[i2]);
30875
+ const cached2 = cache.get(hash);
30846
30876
  if (cached2) {
30847
30877
  results[i2] = cached2;
30848
30878
  } else {
@@ -30851,25 +30881,28 @@ var init_fastembed_provider = __esm({
30851
30881
  }
30852
30882
  }
30853
30883
  if (uncachedTexts.length > 0) {
30884
+ console.error(`[memorix] Embedding ${uncachedTexts.length}/${texts.length} uncached texts (${texts.length - uncachedTexts.length} from cache)`);
30854
30885
  let batchIdx = 0;
30855
30886
  for await (const batch of this.model.embed(uncachedTexts, 64)) {
30856
30887
  for (const vec of batch) {
30857
30888
  const originalIdx = uncachedIndices[batchIdx];
30858
30889
  const plain = Array.from(vec);
30859
30890
  results[originalIdx] = plain;
30860
- this.cacheSet(uncachedTexts[batchIdx], plain);
30891
+ this.cacheSet(textHash(uncachedTexts[batchIdx]), plain);
30861
30892
  batchIdx++;
30862
30893
  }
30863
30894
  }
30895
+ await saveDiskCache();
30864
30896
  }
30865
30897
  return results;
30866
30898
  }
30867
- cacheSet(key, value) {
30899
+ cacheSet(hash, value) {
30868
30900
  if (cache.size >= MAX_CACHE_SIZE) {
30869
30901
  const firstKey = cache.keys().next().value;
30870
30902
  if (firstKey !== void 0) cache.delete(firstKey);
30871
30903
  }
30872
- cache.set(key, value);
30904
+ cache.set(hash, value);
30905
+ diskCacheDirty = true;
30873
30906
  }
30874
30907
  };
30875
30908
  }
@@ -31012,6 +31045,7 @@ var init_provider = __esm({
31012
31045
  // src/store/orama-store.ts
31013
31046
  var orama_store_exports = {};
31014
31047
  __export(orama_store_exports, {
31048
+ batchGenerateEmbeddings: () => batchGenerateEmbeddings,
31015
31049
  generateEmbedding: () => generateEmbedding,
31016
31050
  getDb: () => getDb,
31017
31051
  getObservationCount: () => getObservationCount,
@@ -31059,6 +31093,16 @@ async function generateEmbedding(text) {
31059
31093
  if (!provider2) return null;
31060
31094
  return provider2.embed(text);
31061
31095
  }
31096
+ async function batchGenerateEmbeddings(texts) {
31097
+ const provider2 = await getEmbeddingProvider();
31098
+ if (!provider2 || texts.length === 0) return texts.map(() => null);
31099
+ try {
31100
+ const results = await provider2.embedBatch(texts);
31101
+ return results;
31102
+ } catch {
31103
+ return texts.map(() => null);
31104
+ }
31105
+ }
31062
31106
  async function insertObservation(doc) {
31063
31107
  const database = await getDb();
31064
31108
  await insert3(database, doc);
@@ -33491,17 +33535,22 @@ function suggestTopicKey(type, title) {
33491
33535
  return `${family}/${slug}`;
33492
33536
  }
33493
33537
  async function reindexObservations() {
33538
+ if (observations.length === 0) return 0;
33539
+ let embeddings = [];
33540
+ if (isEmbeddingEnabled()) {
33541
+ try {
33542
+ const texts = observations.map(
33543
+ (obs) => [obs.title, obs.narrative, ...obs.facts].join(" ")
33544
+ );
33545
+ embeddings = await batchGenerateEmbeddings(texts);
33546
+ } catch {
33547
+ }
33548
+ }
33494
33549
  let count3 = 0;
33495
- for (const obs of observations) {
33550
+ for (let i2 = 0; i2 < observations.length; i2++) {
33551
+ const obs = observations[i2];
33496
33552
  try {
33497
- let embedding = null;
33498
- if (isEmbeddingEnabled()) {
33499
- try {
33500
- const searchableText = [obs.title, obs.narrative, ...obs.facts].join(" ");
33501
- embedding = await generateEmbedding(searchableText);
33502
- } catch {
33503
- }
33504
- }
33553
+ const embedding = embeddings[i2] ?? null;
33505
33554
  const doc = {
33506
33555
  id: `obs-${obs.id}`,
33507
33556
  observationId: obs.id,
@@ -33817,8 +33866,11 @@ function detectProject(cwd) {
33817
33866
  return { id: id2, name: name2, gitRemote, rootPath };
33818
33867
  }
33819
33868
  if (isDangerousRoot(rootPath)) {
33820
- console.error(`[memorix] Skipped invalid project root: ${rootPath}`);
33821
- return { id: "__invalid__", name: "unknown", rootPath };
33869
+ const name2 = path4.basename(rootPath) || "unknown";
33870
+ const id2 = `placeholder/${name2}`;
33871
+ console.error(`[memorix] WARNING: cwd "${rootPath}" is not a project directory \u2014 using degraded mode (${id2})`);
33872
+ console.error(`[memorix] For best results, set MEMORIX_PROJECT_ROOT or --cwd to your project path.`);
33873
+ return { id: id2, name: name2, rootPath };
33822
33874
  }
33823
33875
  const name = path4.basename(rootPath);
33824
33876
  const id = `local/${name}`;
@@ -37480,9 +37532,9 @@ var require_gray_matter = __commonJS({
37480
37532
  });
37481
37533
 
37482
37534
  // src/rules/utils.ts
37483
- import { createHash } from "crypto";
37535
+ import { createHash as createHash2 } from "crypto";
37484
37536
  function hashContent(content) {
37485
- return createHash("sha256").update(content.trim()).digest("hex").substring(0, 16);
37537
+ return createHash2("sha256").update(content.trim()).digest("hex").substring(0, 16);
37486
37538
  }
37487
37539
  function generateRuleId(source, filePath) {
37488
37540
  const sanitized = filePath.replace(/[\/\\]/g, "-").replace(/^\./, "");
@@ -38240,8 +38292,8 @@ var init_syncer = __esm({
38240
38292
  });
38241
38293
 
38242
38294
  // src/workspace/mcp-adapters/windsurf.ts
38243
- import { homedir } from "os";
38244
- import { join } from "path";
38295
+ import { homedir as homedir2 } from "os";
38296
+ import { join as join2 } from "path";
38245
38297
  var WindsurfMCPAdapter;
38246
38298
  var init_windsurf2 = __esm({
38247
38299
  "src/workspace/mcp-adapters/windsurf.ts"() {
@@ -38303,15 +38355,15 @@ var init_windsurf2 = __esm({
38303
38355
  return JSON.stringify({ mcpServers }, null, 2);
38304
38356
  }
38305
38357
  getConfigPath(_projectRoot) {
38306
- return join(homedir(), ".codeium", "windsurf", "mcp_config.json");
38358
+ return join2(homedir2(), ".codeium", "windsurf", "mcp_config.json");
38307
38359
  }
38308
38360
  };
38309
38361
  }
38310
38362
  });
38311
38363
 
38312
38364
  // src/workspace/mcp-adapters/cursor.ts
38313
- import { homedir as homedir2 } from "os";
38314
- import { join as join2 } from "path";
38365
+ import { homedir as homedir3 } from "os";
38366
+ import { join as join3 } from "path";
38315
38367
  var CursorMCPAdapter;
38316
38368
  var init_cursor2 = __esm({
38317
38369
  "src/workspace/mcp-adapters/cursor.ts"() {
@@ -38353,17 +38405,17 @@ var init_cursor2 = __esm({
38353
38405
  }
38354
38406
  getConfigPath(projectRoot) {
38355
38407
  if (projectRoot) {
38356
- return join2(projectRoot, ".cursor", "mcp.json");
38408
+ return join3(projectRoot, ".cursor", "mcp.json");
38357
38409
  }
38358
- return join2(homedir2(), ".cursor", "mcp.json");
38410
+ return join3(homedir3(), ".cursor", "mcp.json");
38359
38411
  }
38360
38412
  };
38361
38413
  }
38362
38414
  });
38363
38415
 
38364
38416
  // src/workspace/mcp-adapters/codex.ts
38365
- import { homedir as homedir3 } from "os";
38366
- import { join as join3 } from "path";
38417
+ import { homedir as homedir4 } from "os";
38418
+ import { join as join4 } from "path";
38367
38419
  var CodexMCPAdapter;
38368
38420
  var init_codex2 = __esm({
38369
38421
  "src/workspace/mcp-adapters/codex.ts"() {
@@ -38458,9 +38510,9 @@ var init_codex2 = __esm({
38458
38510
  }
38459
38511
  getConfigPath(projectRoot) {
38460
38512
  if (projectRoot) {
38461
- return join3(projectRoot, ".codex", "config.toml");
38513
+ return join4(projectRoot, ".codex", "config.toml");
38462
38514
  }
38463
- return join3(homedir3(), ".codex", "config.toml");
38515
+ return join4(homedir4(), ".codex", "config.toml");
38464
38516
  }
38465
38517
  // ---- TOML helpers ----
38466
38518
  parseTomlString(raw) {
@@ -38508,8 +38560,8 @@ var init_codex2 = __esm({
38508
38560
  });
38509
38561
 
38510
38562
  // src/workspace/mcp-adapters/claude-code.ts
38511
- import { homedir as homedir4 } from "os";
38512
- import { join as join4 } from "path";
38563
+ import { homedir as homedir5 } from "os";
38564
+ import { join as join5 } from "path";
38513
38565
  var ClaudeCodeMCPAdapter;
38514
38566
  var init_claude_code2 = __esm({
38515
38567
  "src/workspace/mcp-adapters/claude-code.ts"() {
@@ -38551,17 +38603,17 @@ var init_claude_code2 = __esm({
38551
38603
  }
38552
38604
  getConfigPath(projectRoot) {
38553
38605
  if (projectRoot) {
38554
- return join4(projectRoot, ".claude", "settings.json");
38606
+ return join5(projectRoot, ".claude", "settings.json");
38555
38607
  }
38556
- return join4(homedir4(), ".claude.json");
38608
+ return join5(homedir5(), ".claude.json");
38557
38609
  }
38558
38610
  };
38559
38611
  }
38560
38612
  });
38561
38613
 
38562
38614
  // src/workspace/mcp-adapters/copilot.ts
38563
- import { homedir as homedir5 } from "os";
38564
- import { join as join5 } from "path";
38615
+ import { homedir as homedir6 } from "os";
38616
+ import { join as join6 } from "path";
38565
38617
  var CopilotMCPAdapter;
38566
38618
  var init_copilot2 = __esm({
38567
38619
  "src/workspace/mcp-adapters/copilot.ts"() {
@@ -38621,15 +38673,15 @@ var init_copilot2 = __esm({
38621
38673
  }
38622
38674
  getConfigPath(projectRoot) {
38623
38675
  if (projectRoot) {
38624
- return join5(projectRoot, ".vscode", "mcp.json");
38676
+ return join6(projectRoot, ".vscode", "mcp.json");
38625
38677
  }
38626
- const home = homedir5();
38678
+ const home = homedir6();
38627
38679
  if (process.platform === "win32") {
38628
- return join5(home, "AppData", "Roaming", "Code", "User", "settings.json");
38680
+ return join6(home, "AppData", "Roaming", "Code", "User", "settings.json");
38629
38681
  } else if (process.platform === "darwin") {
38630
- return join5(home, "Library", "Application Support", "Code", "User", "settings.json");
38682
+ return join6(home, "Library", "Application Support", "Code", "User", "settings.json");
38631
38683
  } else {
38632
- return join5(home, ".config", "Code", "User", "settings.json");
38684
+ return join6(home, ".config", "Code", "User", "settings.json");
38633
38685
  }
38634
38686
  }
38635
38687
  };
@@ -38637,8 +38689,8 @@ var init_copilot2 = __esm({
38637
38689
  });
38638
38690
 
38639
38691
  // src/workspace/mcp-adapters/antigravity.ts
38640
- import { homedir as homedir6 } from "os";
38641
- import { join as join6 } from "path";
38692
+ import { homedir as homedir7 } from "os";
38693
+ import { join as join7 } from "path";
38642
38694
  var AntigravityMCPAdapter;
38643
38695
  var init_antigravity2 = __esm({
38644
38696
  "src/workspace/mcp-adapters/antigravity.ts"() {
@@ -38701,17 +38753,17 @@ var init_antigravity2 = __esm({
38701
38753
  }
38702
38754
  getConfigPath(projectRoot) {
38703
38755
  if (projectRoot) {
38704
- return join6(projectRoot, ".gemini", "settings.json");
38756
+ return join7(projectRoot, ".gemini", "settings.json");
38705
38757
  }
38706
- return join6(homedir6(), ".gemini", "settings.json");
38758
+ return join7(homedir7(), ".gemini", "settings.json");
38707
38759
  }
38708
38760
  };
38709
38761
  }
38710
38762
  });
38711
38763
 
38712
38764
  // src/workspace/mcp-adapters/kiro.ts
38713
- import { homedir as homedir7 } from "os";
38714
- import { join as join7 } from "path";
38765
+ import { homedir as homedir8 } from "os";
38766
+ import { join as join8 } from "path";
38715
38767
  var KiroMCPAdapter;
38716
38768
  var init_kiro2 = __esm({
38717
38769
  "src/workspace/mcp-adapters/kiro.ts"() {
@@ -38753,9 +38805,9 @@ var init_kiro2 = __esm({
38753
38805
  }
38754
38806
  getConfigPath(projectRoot) {
38755
38807
  if (projectRoot) {
38756
- return join7(projectRoot, ".kiro", "settings", "mcp.json");
38808
+ return join8(projectRoot, ".kiro", "settings", "mcp.json");
38757
38809
  }
38758
- return join7(homedir7(), ".kiro", "settings", "mcp.json");
38810
+ return join8(homedir8(), ".kiro", "settings", "mcp.json");
38759
38811
  }
38760
38812
  };
38761
38813
  }
@@ -39004,8 +39056,8 @@ var init_applier = __esm({
39004
39056
 
39005
39057
  // src/workspace/engine.ts
39006
39058
  import { readFileSync as readFileSync2, readdirSync, existsSync as existsSync3, cpSync, mkdirSync as mkdirSync2 } from "fs";
39007
- import { join as join9 } from "path";
39008
- import { homedir as homedir8 } from "os";
39059
+ import { join as join10 } from "path";
39060
+ import { homedir as homedir9 } from "os";
39009
39061
  var WorkspaceSyncEngine;
39010
39062
  var init_engine2 = __esm({
39011
39063
  "src/workspace/engine.ts"() {
@@ -39162,7 +39214,7 @@ var init_engine2 = __esm({
39162
39214
  getTargetSkillsDir(target) {
39163
39215
  const dirs = _WorkspaceSyncEngine.SKILLS_DIRS[target];
39164
39216
  if (!dirs || dirs.length === 0) return null;
39165
- return join9(this.projectRoot, dirs[0]);
39217
+ return join10(this.projectRoot, dirs[0]);
39166
39218
  }
39167
39219
  /**
39168
39220
  * Scan all agent skills directories and collect unique skills.
@@ -39171,12 +39223,12 @@ var init_engine2 = __esm({
39171
39223
  const skills = [];
39172
39224
  const conflicts = [];
39173
39225
  const seen = /* @__PURE__ */ new Map();
39174
- const home = homedir8();
39226
+ const home = homedir9();
39175
39227
  for (const [agent, dirs] of Object.entries(_WorkspaceSyncEngine.SKILLS_DIRS)) {
39176
39228
  for (const dir of dirs) {
39177
39229
  const paths = [
39178
- join9(this.projectRoot, dir),
39179
- join9(home, dir)
39230
+ join10(this.projectRoot, dir),
39231
+ join10(home, dir)
39180
39232
  ];
39181
39233
  for (const skillsRoot of paths) {
39182
39234
  if (!existsSync3(skillsRoot)) continue;
@@ -39184,7 +39236,7 @@ var init_engine2 = __esm({
39184
39236
  const entries = readdirSync(skillsRoot, { withFileTypes: true });
39185
39237
  for (const entry of entries) {
39186
39238
  if (!entry.isDirectory()) continue;
39187
- const skillMd = join9(skillsRoot, entry.name, "SKILL.md");
39239
+ const skillMd = join10(skillsRoot, entry.name, "SKILL.md");
39188
39240
  if (!existsSync3(skillMd)) continue;
39189
39241
  let description = "";
39190
39242
  try {
@@ -39196,7 +39248,7 @@ var init_engine2 = __esm({
39196
39248
  const newEntry = {
39197
39249
  name: entry.name,
39198
39250
  description,
39199
- sourcePath: join9(skillsRoot, entry.name),
39251
+ sourcePath: join10(skillsRoot, entry.name),
39200
39252
  sourceAgent: agent
39201
39253
  };
39202
39254
  const existing = seen.get(entry.name);
@@ -39233,7 +39285,7 @@ var init_engine2 = __esm({
39233
39285
  }
39234
39286
  for (const skill of skills) {
39235
39287
  if (skill.sourceAgent === target) continue;
39236
- const dest = join9(targetDir, skill.name);
39288
+ const dest = join10(targetDir, skill.name);
39237
39289
  if (existsSync3(dest)) {
39238
39290
  skipped.push(`${skill.name} (already exists in ${target})`);
39239
39291
  continue;
@@ -39249,13 +39301,13 @@ var init_engine2 = __esm({
39249
39301
  }
39250
39302
  scanWorkflows() {
39251
39303
  const workflows = [];
39252
- const wfDir = join9(this.projectRoot, ".windsurf", "workflows");
39304
+ const wfDir = join10(this.projectRoot, ".windsurf", "workflows");
39253
39305
  if (!existsSync3(wfDir)) return workflows;
39254
39306
  try {
39255
39307
  const files = readdirSync(wfDir).filter((f4) => f4.endsWith(".md"));
39256
39308
  for (const file of files) {
39257
39309
  try {
39258
- const content = readFileSync2(join9(wfDir, file), "utf-8");
39310
+ const content = readFileSync2(join10(wfDir, file), "utf-8");
39259
39311
  workflows.push(this.workflowSyncer.parseWindsurfWorkflow(file, content));
39260
39312
  } catch {
39261
39313
  }
@@ -39646,8 +39698,8 @@ __export(engine_exports, {
39646
39698
  SkillsEngine: () => SkillsEngine
39647
39699
  });
39648
39700
  import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync2 } from "fs";
39649
- import { join as join10 } from "path";
39650
- import { homedir as homedir9 } from "os";
39701
+ import { join as join11 } from "path";
39702
+ import { homedir as homedir10 } from "os";
39651
39703
  var SKILLS_DIRS, SKILL_WORTHY_TYPES, MIN_OBS_FOR_SKILL, MIN_SCORE_FOR_SKILL, SkillsEngine;
39652
39704
  var init_engine3 = __esm({
39653
39705
  "src/skills/engine.ts"() {
@@ -39686,12 +39738,12 @@ var init_engine3 = __esm({
39686
39738
  listSkills() {
39687
39739
  const skills = [];
39688
39740
  const seen = /* @__PURE__ */ new Set();
39689
- const home = homedir9();
39741
+ const home = homedir10();
39690
39742
  for (const [agent, dirs] of Object.entries(SKILLS_DIRS)) {
39691
39743
  for (const dir of dirs) {
39692
- const paths = [join10(this.projectRoot, dir)];
39744
+ const paths = [join11(this.projectRoot, dir)];
39693
39745
  if (!this.skipGlobal) {
39694
- paths.push(join10(home, dir));
39746
+ paths.push(join11(home, dir));
39695
39747
  }
39696
39748
  for (const skillsRoot of paths) {
39697
39749
  if (!existsSync4(skillsRoot)) continue;
@@ -39701,7 +39753,7 @@ var init_engine3 = __esm({
39701
39753
  if (!entry.isDirectory()) continue;
39702
39754
  const name = entry.name;
39703
39755
  if (seen.has(name)) continue;
39704
- const skillMd = join10(skillsRoot, name, "SKILL.md");
39756
+ const skillMd = join11(skillsRoot, name, "SKILL.md");
39705
39757
  if (!existsSync4(skillMd)) continue;
39706
39758
  try {
39707
39759
  const content = readFileSync3(skillMd, "utf-8");
@@ -39709,7 +39761,7 @@ var init_engine3 = __esm({
39709
39761
  skills.push({
39710
39762
  name,
39711
39763
  description,
39712
- sourcePath: join10(skillsRoot, name),
39764
+ sourcePath: join11(skillsRoot, name),
39713
39765
  sourceAgent: agent,
39714
39766
  content,
39715
39767
  generated: false
@@ -39751,11 +39803,11 @@ var init_engine3 = __esm({
39751
39803
  writeSkill(skill, target) {
39752
39804
  const dirs = SKILLS_DIRS[target];
39753
39805
  if (!dirs || dirs.length === 0) return null;
39754
- const targetDir = join10(this.projectRoot, dirs[0], skill.name);
39806
+ const targetDir = join11(this.projectRoot, dirs[0], skill.name);
39755
39807
  try {
39756
39808
  mkdirSync3(targetDir, { recursive: true });
39757
- writeFileSync2(join10(targetDir, "SKILL.md"), skill.content, "utf-8");
39758
- return join10(dirs[0], skill.name, "SKILL.md");
39809
+ writeFileSync2(join11(targetDir, "SKILL.md"), skill.content, "utf-8");
39810
+ return join11(dirs[0], skill.name, "SKILL.md");
39759
39811
  } catch {
39760
39812
  return null;
39761
39813
  }
@@ -40286,31 +40338,27 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir) {
40286
40338
  let effectiveProjectId = projectId;
40287
40339
  let effectiveProjectName = projectName;
40288
40340
  if (requestedProject && requestedProject !== projectId) {
40289
- const sanitized = requestedProject.replace(/\//g, "--").replace(/[<>:"|?*\\]/g, "_");
40290
- const candidateDir = path6.join(baseDir, sanitized);
40291
- try {
40292
- await fs4.access(candidateDir);
40293
- effectiveDataDir = candidateDir;
40294
- effectiveProjectId = requestedProject;
40295
- effectiveProjectName = requestedProject.split("/").pop() || requestedProject;
40296
- } catch {
40297
- }
40341
+ effectiveDataDir = baseDir;
40342
+ effectiveProjectId = requestedProject;
40343
+ effectiveProjectName = requestedProject.split("/").pop() || requestedProject;
40298
40344
  }
40299
40345
  try {
40300
40346
  switch (apiPath) {
40301
40347
  case "/projects": {
40302
40348
  try {
40303
- const entries = await fs4.readdir(baseDir, { withFileTypes: true });
40304
- const projects = entries.filter((e3) => e3.isDirectory() && e3.name.includes("--") && !e3.name.startsWith("local--")).map((e3) => {
40305
- const dirName = e3.name;
40306
- const id = dirName.replace(/--/g, "/");
40307
- return {
40308
- id,
40309
- name: id.split("/").pop() || id,
40310
- dirName,
40311
- isCurrent: id === projectId
40312
- };
40313
- });
40349
+ const allObs = await loadObservationsJson(baseDir);
40350
+ const projectSet = /* @__PURE__ */ new Map();
40351
+ for (const obs of allObs) {
40352
+ if (obs.projectId) {
40353
+ projectSet.set(obs.projectId, (projectSet.get(obs.projectId) || 0) + 1);
40354
+ }
40355
+ }
40356
+ const projects = Array.from(projectSet.entries()).sort((a3, b3) => b3[1] - a3[1]).map(([id, count3]) => ({
40357
+ id,
40358
+ name: id.split("/").pop() || id,
40359
+ count: count3,
40360
+ isCurrent: id === projectId
40361
+ }));
40314
40362
  sendJson(res, projects);
40315
40363
  } catch {
40316
40364
  sendJson(res, []);
@@ -40499,10 +40547,10 @@ function openBrowser(url) {
40499
40547
  });
40500
40548
  }
40501
40549
  function readBody(req) {
40502
- return new Promise((resolve2, reject) => {
40550
+ return new Promise((resolve, reject) => {
40503
40551
  const chunks = [];
40504
40552
  req.on("data", (c5) => chunks.push(c5));
40505
- req.on("end", () => resolve2(Buffer.concat(chunks).toString("utf-8")));
40553
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
40506
40554
  req.on("error", reject);
40507
40555
  });
40508
40556
  }
@@ -40516,17 +40564,9 @@ async function startDashboard(dataDir, port, staticDir, projectId, projectName,
40516
40564
  try {
40517
40565
  const body = JSON.parse(await readBody(req));
40518
40566
  if (body.projectId) {
40519
- const sanitized = body.projectId.replace(/\//g, "--").replace(/[<>:"|?*\\]/g, "_");
40520
- const candidateDir = path6.join(baseDir, sanitized);
40521
- try {
40522
- await fs4.access(candidateDir);
40523
- } catch {
40524
- sendError(res, `Project data directory not found: ${candidateDir}`, 404);
40525
- return;
40526
- }
40527
40567
  state.projectId = body.projectId;
40528
40568
  state.projectName = body.projectName || body.projectId.split("/").pop() || body.projectId;
40529
- state.dataDir = candidateDir;
40569
+ state.dataDir = baseDir;
40530
40570
  console.error(`[dashboard] Switched current project to: ${state.projectId}`);
40531
40571
  sendJson(res, { ok: true, projectId: state.projectId, projectName: state.projectName });
40532
40572
  } else {
@@ -40543,7 +40583,7 @@ async function startDashboard(dataDir, port, staticDir, projectId, projectName,
40543
40583
  await serveStatic(req, res, resolvedStaticDir);
40544
40584
  }
40545
40585
  });
40546
- return new Promise((resolve2, reject) => {
40586
+ return new Promise((resolve, reject) => {
40547
40587
  server.on("error", (err) => {
40548
40588
  if (err.code === "EADDRINUSE") {
40549
40589
  console.error(`Port ${port} is already in use. Try: memorix dashboard --port ${port + 1}`);
@@ -40564,7 +40604,7 @@ async function startDashboard(dataDir, port, staticDir, projectId, projectName,
40564
40604
  Press Ctrl+C to stop
40565
40605
  `);
40566
40606
  if (autoOpen) openBrowser(url);
40567
- resolve2();
40607
+ resolve();
40568
40608
  });
40569
40609
  });
40570
40610
  }
@@ -40598,25 +40638,9 @@ __export(installers_exports, {
40598
40638
  import * as fs5 from "fs/promises";
40599
40639
  import * as path7 from "path";
40600
40640
  import * as os3 from "os";
40601
- import { createRequire } from "module";
40602
40641
  function resolveHookCommand() {
40603
40642
  if (process.platform === "win32") {
40604
- try {
40605
- const devPath = path7.resolve(import.meta.dirname ?? __dirname, "../../cli/index.js");
40606
- try {
40607
- const fsStat = __require("fs");
40608
- if (fsStat.existsSync(devPath)) {
40609
- return `node ${devPath.replace(/\\/g, "/")}`;
40610
- }
40611
- } catch {
40612
- }
40613
- const require_ = createRequire(import.meta.url);
40614
- const pkgPath = require_.resolve("memorix/package.json");
40615
- const cliPath = path7.join(path7.dirname(pkgPath), "dist", "cli", "index.js");
40616
- return `node ${cliPath.replace(/\\/g, "/")}`;
40617
- } catch {
40618
- return "memorix";
40619
- }
40643
+ return "cmd /c memorix";
40620
40644
  }
40621
40645
  return "memorix";
40622
40646
  }
@@ -40651,7 +40675,9 @@ function generateCopilotConfig() {
40651
40675
  sessionStart: [hookEntry],
40652
40676
  sessionEnd: [hookEntry],
40653
40677
  userPromptSubmitted: [hookEntry],
40654
- preToolUse: [hookEntry],
40678
+ // NOTE: preToolUse intentionally omitted — VS Code Copilot requires
40679
+ // hookSpecificOutput.permissionDecision in the response; memorix is
40680
+ // an observer, not a gatekeeper, so we only use postToolUse.
40655
40681
  postToolUse: [hookEntry],
40656
40682
  errorOccurred: [hookEntry]
40657
40683
  }
@@ -40906,6 +40932,10 @@ async function installHooks(agent, projectRoot, global = false) {
40906
40932
  if (Object.keys(t2).length === 0) delete merged.tools;
40907
40933
  }
40908
40934
  }
40935
+ if (agent === "copilot") {
40936
+ const h3 = merged.hooks;
40937
+ if (h3) delete h3.preToolUse;
40938
+ }
40909
40939
  await fs5.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
40910
40940
  }
40911
40941
  const events = [];
@@ -41171,17 +41201,6 @@ function coerceObjectArray(val) {
41171
41201
  }
41172
41202
  async function createMemorixServer(cwd, existingServer) {
41173
41203
  const project = detectProject(cwd);
41174
- if (project.id === "__invalid__") {
41175
- const resolvedCwd = cwd ?? process.cwd();
41176
- console.error(`[memorix] ERROR: Could not detect a valid project at: ${resolvedCwd}`);
41177
- console.error(`[memorix] The directory is not a git repository and has no project indicator files.`);
41178
- console.error(`[memorix] Fix: set --cwd to your project directory, or set MEMORIX_PROJECT_ROOT env var.`);
41179
- console.error(`[memorix] Example: memorix serve --cwd /path/to/your/project`);
41180
- console.error(`[memorix] Example: "env": { "MEMORIX_PROJECT_ROOT": "/path/to/your/project" }`);
41181
- throw new Error(
41182
- `Cannot start Memorix: no valid project detected at "${resolvedCwd}". Set --cwd or MEMORIX_PROJECT_ROOT to your project directory.`
41183
- );
41184
- }
41185
41204
  try {
41186
41205
  const { migrateSubdirsToFlat: migrateSubdirsToFlat2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
41187
41206
  const migrated = await migrateSubdirsToFlat2();
@@ -42143,33 +42162,33 @@ ${json}
42143
42162
  const url = `http://localhost:${portNum}`;
42144
42163
  if (dashboardRunning) {
42145
42164
  const { createConnection } = await import("net");
42146
- const isAlive = await new Promise((resolve2) => {
42165
+ const isAlive = await new Promise((resolve) => {
42147
42166
  const sock = createConnection(portNum, "127.0.0.1");
42148
42167
  sock.once("connect", () => {
42149
42168
  sock.destroy();
42150
- resolve2(true);
42169
+ resolve(true);
42151
42170
  });
42152
42171
  sock.once("error", () => {
42153
42172
  sock.destroy();
42154
- resolve2(false);
42173
+ resolve(false);
42155
42174
  });
42156
42175
  setTimeout(() => {
42157
42176
  sock.destroy();
42158
- resolve2(false);
42177
+ resolve(false);
42159
42178
  }, 1e3);
42160
42179
  });
42161
42180
  if (isAlive) {
42162
42181
  const http = await import("http");
42163
42182
  const postData = JSON.stringify({ projectId: project.id, projectName: project.name });
42164
- await new Promise((resolve2) => {
42183
+ await new Promise((resolve) => {
42165
42184
  const req = http.request({
42166
42185
  hostname: "127.0.0.1",
42167
42186
  port: portNum,
42168
42187
  path: "/api/set-current-project",
42169
42188
  method: "POST",
42170
42189
  headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(postData) }
42171
- }, () => resolve2());
42172
- req.on("error", () => resolve2());
42190
+ }, () => resolve());
42191
+ req.on("error", () => resolve());
42173
42192
  req.write(postData);
42174
42193
  req.end();
42175
42194
  });
@@ -42215,18 +42234,18 @@ ${json}
42215
42234
  dashboardRunning = false;
42216
42235
  });
42217
42236
  const { createConnection } = await import("net");
42218
- await new Promise((resolve2) => {
42237
+ await new Promise((resolve) => {
42219
42238
  const deadline = Date.now() + 5e3;
42220
42239
  const tryConnect = () => {
42221
42240
  const sock = createConnection(portNum, "127.0.0.1");
42222
42241
  sock.once("connect", () => {
42223
42242
  sock.destroy();
42224
- resolve2();
42243
+ resolve();
42225
42244
  });
42226
42245
  sock.once("error", () => {
42227
42246
  sock.destroy();
42228
42247
  if (Date.now() < deadline) setTimeout(tryConnect, 100);
42229
- else resolve2();
42248
+ else resolve();
42230
42249
  });
42231
42250
  };
42232
42251
  tryConnect();
@@ -42402,7 +42421,7 @@ var init_serve = __esm({
42402
42421
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
42403
42422
  const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
42404
42423
  const { existsSync: existsSync5 } = await import("fs");
42405
- const { join: join12 } = await import("path");
42424
+ const { join: join13 } = await import("path");
42406
42425
  let safeCwd;
42407
42426
  try {
42408
42427
  safeCwd = process.cwd();
@@ -42411,43 +42430,51 @@ var init_serve = __esm({
42411
42430
  }
42412
42431
  let projectRoot = args.cwd || process.env.MEMORIX_PROJECT_ROOT || process.env.INIT_CWD || safeCwd;
42413
42432
  console.error(`[memorix] Starting with cwd: ${projectRoot}`);
42414
- const looksValid = existsSync5(join12(projectRoot, ".git")) || existsSync5(join12(projectRoot, "package.json")) || existsSync5(join12(projectRoot, "Cargo.toml")) || existsSync5(join12(projectRoot, "go.mod")) || existsSync5(join12(projectRoot, "pyproject.toml"));
42433
+ 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"));
42415
42434
  if (!looksValid) {
42416
- console.error(`[memorix] cwd may not be a valid project, trying MCP roots protocol...`);
42417
- const mcpServer = new McpServer2({ name: "memorix", version: "0.1.0" });
42418
- const transport = new StdioServerTransport2();
42419
- await mcpServer.connect(transport);
42420
- let rootResolved = false;
42421
- try {
42422
- const rootsResult = await Promise.race([
42423
- mcpServer.server.listRoots(),
42424
- new Promise((_4, reject) => setTimeout(() => reject(new Error("timeout")), 5e3))
42425
- ]);
42426
- if (rootsResult && "roots" in rootsResult && Array.isArray(rootsResult.roots) && rootsResult.roots.length > 0) {
42427
- const rootUri = rootsResult.roots[0].uri;
42428
- if (rootUri.startsWith("file://")) {
42429
- const urlPath = decodeURIComponent(new URL(rootUri).pathname);
42430
- const normalizedPath = process.platform === "win32" && urlPath.match(/^\/[A-Za-z]:/) ? urlPath.slice(1) : urlPath;
42431
- console.error(`[memorix] MCP client root: ${normalizedPath}`);
42432
- projectRoot = normalizedPath;
42433
- rootResolved = true;
42435
+ const earlyDetect = detectProject2(projectRoot);
42436
+ const isDegraded = earlyDetect.id.startsWith("placeholder/");
42437
+ if (!isDegraded) {
42438
+ console.error(`[memorix] detectProject succeeded without standard indicators`);
42439
+ const { server, projectId, deferredInit } = await createMemorixServer2(projectRoot);
42440
+ const transport = new StdioServerTransport2();
42441
+ await server.connect(transport);
42442
+ console.error(`[memorix] MCP Server running on stdio (project: ${projectId})`);
42443
+ console.error(`[memorix] Project root: ${projectRoot}`);
42444
+ deferredInit().catch((e3) => console.error(`[memorix] Deferred init error:`, e3));
42445
+ } else {
42446
+ console.error(`[memorix] cwd may not be a valid project, trying MCP roots protocol...`);
42447
+ const mcpServer = new McpServer2({ name: "memorix", version: "0.1.0" });
42448
+ mcpServer.registerTool("_memorix_loading", {
42449
+ description: "Memorix is initializing, detecting project root...",
42450
+ inputSchema: {}
42451
+ }, async () => ({
42452
+ content: [{ type: "text", text: "Memorix is still loading. Please retry shortly." }]
42453
+ }));
42454
+ const transport = new StdioServerTransport2();
42455
+ await mcpServer.connect(transport);
42456
+ try {
42457
+ const rootsResult = await Promise.race([
42458
+ mcpServer.server.listRoots(),
42459
+ new Promise((_4, reject) => setTimeout(() => reject(new Error("timeout")), 5e3))
42460
+ ]);
42461
+ if (rootsResult && "roots" in rootsResult && Array.isArray(rootsResult.roots) && rootsResult.roots.length > 0) {
42462
+ const rootUri = rootsResult.roots[0].uri;
42463
+ if (rootUri.startsWith("file://")) {
42464
+ const urlPath = decodeURIComponent(new URL(rootUri).pathname);
42465
+ const normalizedPath = process.platform === "win32" && urlPath.match(/^\/[A-Za-z]:/) ? urlPath.slice(1) : urlPath;
42466
+ console.error(`[memorix] MCP client root: ${normalizedPath}`);
42467
+ projectRoot = normalizedPath;
42468
+ }
42434
42469
  }
42470
+ } catch {
42471
+ console.error(`[memorix] MCP roots not available (client may not support it)`);
42435
42472
  }
42436
- } catch {
42437
- console.error(`[memorix] MCP roots not available (client may not support it)`);
42438
- }
42439
- if (!rootResolved) {
42440
- const earlyDetect = detectProject2(projectRoot);
42441
- if (earlyDetect.id === "__invalid__") {
42442
- console.error(`[memorix] ERROR: Could not detect a valid project.`);
42443
- console.error(`[memorix] Fix: set --cwd or MEMORIX_PROJECT_ROOT, or use an IDE that supports MCP roots.`);
42444
- process.exit(1);
42445
- }
42473
+ const { projectId, deferredInit } = await createMemorixServer2(projectRoot, mcpServer);
42474
+ console.error(`[memorix] MCP Server running on stdio (project: ${projectId})`);
42475
+ console.error(`[memorix] Project root: ${projectRoot}`);
42476
+ deferredInit().catch((e3) => console.error(`[memorix] Deferred init error:`, e3));
42446
42477
  }
42447
- const { projectId, deferredInit } = await createMemorixServer2(projectRoot, mcpServer);
42448
- console.error(`[memorix] MCP Server running on stdio (project: ${projectId})`);
42449
- console.error(`[memorix] Project root: ${projectRoot}`);
42450
- deferredInit().catch((e3) => console.error(`[memorix] Deferred init error:`, e3));
42451
42478
  } else {
42452
42479
  const { server, projectId, deferredInit } = await createMemorixServer2(projectRoot);
42453
42480
  const transport = new StdioServerTransport2();
@@ -43200,13 +43227,13 @@ var init_status = __esm({
43200
43227
  const { getProjectDataDir: getProjectDataDir2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
43201
43228
  const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
43202
43229
  const { existsSync: existsSync5, readFileSync: readFileSync4 } = await import("fs");
43203
- const { join: join12 } = await import("path");
43230
+ const { join: join13 } = await import("path");
43204
43231
  we("memorix status");
43205
43232
  const project = detectProject2();
43206
43233
  const dataDir = await getProjectDataDir2(project.id);
43207
43234
  let obsCount = 0;
43208
43235
  try {
43209
- const obsFile = join12(dataDir, "observations.json");
43236
+ const obsFile = join13(dataDir, "observations.json");
43210
43237
  if (existsSync5(obsFile)) {
43211
43238
  const data = JSON.parse(readFileSync4(obsFile, "utf-8"));
43212
43239
  obsCount = Array.isArray(data) ? data.length : 0;
@@ -44052,20 +44079,35 @@ async function handleHookEvent(input) {
44052
44079
  };
44053
44080
  }
44054
44081
  async function runHook() {
44055
- const chunks = [];
44056
- for await (const chunk of process.stdin) {
44057
- chunks.push(chunk);
44058
- }
44059
- const rawInput = Buffer.concat(chunks).toString("utf-8").trim();
44082
+ const rawInput = await new Promise((resolve) => {
44083
+ const chunks = [];
44084
+ const finish = () => resolve(Buffer.concat(chunks).toString("utf-8").trim());
44085
+ const timer = setTimeout(() => {
44086
+ process.stdin.removeAllListeners("data");
44087
+ process.stdin.removeAllListeners("end");
44088
+ finish();
44089
+ }, 3e3);
44090
+ process.stdin.on("data", (chunk) => {
44091
+ chunks.push(chunk);
44092
+ });
44093
+ process.stdin.on("end", () => {
44094
+ clearTimeout(timer);
44095
+ finish();
44096
+ });
44097
+ process.stdin.on("error", () => {
44098
+ clearTimeout(timer);
44099
+ finish();
44100
+ });
44101
+ });
44060
44102
  if (!rawInput) {
44061
- process.stdout.write(JSON.stringify({ continue: true }));
44103
+ process.stdout.write(JSON.stringify({ continue: true, hookSpecificOutput: {} }));
44062
44104
  return;
44063
44105
  }
44064
44106
  let payload;
44065
44107
  try {
44066
44108
  payload = JSON.parse(rawInput);
44067
44109
  } catch {
44068
- process.stdout.write(JSON.stringify({ continue: true }));
44110
+ process.stdout.write(JSON.stringify({ continue: true, hookSpecificOutput: {} }));
44069
44111
  return;
44070
44112
  }
44071
44113
  const input = normalizeHookInput(payload);
@@ -44085,7 +44127,13 @@ ${emoji2} Memorix saved: ${observation.title} [${observation.type}]`;
44085
44127
  } catch {
44086
44128
  }
44087
44129
  }
44088
- process.stdout.write(JSON.stringify(output));
44130
+ const hookEventName = payload.hookEventName ?? "";
44131
+ const finalOutput = { ...output };
44132
+ finalOutput.hookSpecificOutput = {
44133
+ ...hookEventName ? { hookEventName } : {},
44134
+ ...output.systemMessage ? { additionalContext: output.systemMessage } : {}
44135
+ };
44136
+ process.stdout.write(JSON.stringify(finalOutput));
44089
44137
  }
44090
44138
  var TYPE_EMOJI, cooldowns, COOLDOWN_MS, MIN_PROMPT_LENGTH, MAX_CONTENT_LENGTH, NOISE_COMMANDS, STORAGE_POLICY;
44091
44139
  var init_handler = __esm({
@@ -44429,9 +44477,9 @@ var init_cleanup = __esm({
44429
44477
  },
44430
44478
  async run({ args }) {
44431
44479
  const project = detectProject();
44432
- if (project.id === "__invalid__") {
44433
- console.error("\u274C Not in a valid project directory.");
44434
- process.exit(1);
44480
+ if (project.id.startsWith("placeholder/")) {
44481
+ console.error("\u26A0\uFE0F Not in a valid project directory \u2014 using degraded mode.");
44482
+ console.error("Set MEMORIX_PROJECT_ROOT or --cwd for best results.");
44435
44483
  }
44436
44484
  console.log(`
44437
44485
  \u{1F4E6} Project: ${project.name} (${project.id})
@@ -44484,8 +44532,8 @@ var init_cleanup = __esm({
44484
44532
  if (!args.force) {
44485
44533
  const readline = await import("readline");
44486
44534
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
44487
- const answer = await new Promise((resolve2) => {
44488
- rl.question(`\u26A0\uFE0F Delete ${toRemove.length} observations? (y/N) `, resolve2);
44535
+ const answer = await new Promise((resolve) => {
44536
+ rl.question(`\u26A0\uFE0F Delete ${toRemove.length} observations? (y/N) `, resolve);
44489
44537
  });
44490
44538
  rl.close();
44491
44539
  if (answer.trim().toLowerCase() !== "y") {
@@ -44505,8 +44553,8 @@ var init_cleanup = __esm({
44505
44553
  // src/cli/index.ts
44506
44554
  init_esm_shims();
44507
44555
  init_dist2();
44508
- import { createRequire as createRequire2 } from "module";
44509
- var require2 = createRequire2(import.meta.url);
44556
+ import { createRequire } from "module";
44557
+ var require2 = createRequire(import.meta.url);
44510
44558
  var pkg = require2("../../package.json");
44511
44559
  var main = defineCommand({
44512
44560
  meta: {