memorix 0.9.2 → 0.9.4

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/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.9.4] — 2026-02-25
6
+
7
+ ### Fixed
8
+ - **Codex/all-IDE `tools/list -> Method not found`** — Critical bug where `local/<dirname>` projects (any directory without a git remote) wrongly entered the MCP roots resolution flow. This flow connects the server *before* registering tools, so the MCP `initialize` handshake declared no `tools` capability, causing all subsequent `tools/list` calls to fail with "Method not found". Now only truly invalid projects (home dir, system dirs) enter the roots flow; `local/` projects go through the normal path (register tools first, then connect).
9
+
10
+ ## [0.9.3] — 2026-02-25
11
+
12
+ ### Fixed
13
+ - **`memorix_timeline` "not found" bug** — Timeline was using unreliable Orama empty-term search. Now uses in-memory observations (same fix pattern as `memorix_detail`).
14
+ - **`memorix_retention` "no observations found" bug** — Same root cause as timeline. Now uses in-memory observations for reliable document retrieval.
15
+ - **`memorix_search` cross-IDE projectId mismatch** — Removed redundant projectId filter from search. Data isolation is already handled at the directory level. Different IDEs resolving different projectIds for the same directory no longer causes empty search results.
16
+ - **Claude Code hooks format** — Updated `generateClaudeConfig` to use the new `{matcher: {}, hooks: [...]}` structure required by Claude Code 2025+. Fixes "Expected array, but received undefined" error on `memorix hooks install --agent claude --global`.
17
+ - **EPERM `process.cwd()` crash** — All CLI commands (`serve`, `hooks install/uninstall/status`) now safely handle `process.cwd()` failures (e.g., deleted CWD on macOS) with fallback to home directory.
18
+
5
19
  ## [0.9.2] — 2026-02-25
6
20
 
7
21
  ### Fixed
package/dist/cli/index.js CHANGED
@@ -959,37 +959,30 @@ async function getObservationsByIds(ids, projectId) {
959
959
  }
960
960
  return results;
961
961
  }
962
- async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3) {
963
- const database = await getDb();
964
- const searchParams = {
965
- term: "",
966
- limit: 1e3
967
- };
968
- if (projectId) {
969
- searchParams.where = { projectId };
970
- }
971
- const allResults = await search(database, searchParams);
972
- const docs = allResults.hits.map((h) => h.document).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
973
- const anchorIndex = docs.findIndex((d) => d.observationId === anchorId);
962
+ async function getTimeline(anchorId, _projectId, depthBefore = 3, depthAfter = 3) {
963
+ const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
964
+ const allObs = getAllObservations2();
965
+ const sorted = allObs.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
966
+ const anchorIndex = sorted.findIndex((o) => o.id === anchorId);
974
967
  if (anchorIndex === -1) {
975
968
  return { before: [], anchor: null, after: [] };
976
969
  }
977
- const toIndexEntry = (doc) => {
978
- const obsType = doc.type;
970
+ const toIndexEntry = (obs) => {
971
+ const obsType = obs.type;
979
972
  return {
980
- id: doc.observationId,
981
- time: formatTime(doc.createdAt),
973
+ id: obs.id,
974
+ time: formatTime(obs.createdAt),
982
975
  type: obsType,
983
976
  icon: OBSERVATION_ICONS[obsType] ?? "\u2753",
984
- title: doc.title,
985
- tokens: doc.tokens
977
+ title: obs.title,
978
+ tokens: obs.tokens
986
979
  };
987
980
  };
988
- const before = docs.slice(Math.max(0, anchorIndex - depthBefore), anchorIndex).map(toIndexEntry);
989
- const after = docs.slice(anchorIndex + 1, anchorIndex + 1 + depthAfter).map(toIndexEntry);
981
+ const before = sorted.slice(Math.max(0, anchorIndex - depthBefore), anchorIndex).map(toIndexEntry);
982
+ const after = sorted.slice(anchorIndex + 1, anchorIndex + 1 + depthAfter).map(toIndexEntry);
990
983
  return {
991
984
  before,
992
- anchor: toIndexEntry(docs[anchorIndex]),
985
+ anchor: toIndexEntry(sorted[anchorIndex]),
993
986
  after
994
987
  };
995
988
  }
@@ -1169,6 +1162,7 @@ var init_entity_extractor = __esm({
1169
1162
  // src/memory/observations.ts
1170
1163
  var observations_exports = {};
1171
1164
  __export(observations_exports, {
1165
+ getAllObservations: () => getAllObservations,
1172
1166
  getObservation: () => getObservation,
1173
1167
  getObservationCount: () => getObservationCount2,
1174
1168
  getProjectObservations: () => getProjectObservations,
@@ -1336,6 +1330,9 @@ function getObservation(id) {
1336
1330
  function getProjectObservations(projectId) {
1337
1331
  return observations.filter((o) => o.projectId === projectId);
1338
1332
  }
1333
+ function getAllObservations() {
1334
+ return [...observations];
1335
+ }
1339
1336
  function getObservationCount2() {
1340
1337
  return observations.length;
1341
1338
  }
@@ -3703,18 +3700,23 @@ function resolveHookCommand() {
3703
3700
  }
3704
3701
  function generateClaudeConfig() {
3705
3702
  const cmd = `${resolveHookCommand()} hook`;
3706
- const hookEntry = {
3707
- type: "command",
3708
- command: cmd,
3709
- timeout: 10
3703
+ const hookGroup = {
3704
+ matcher: {},
3705
+ hooks: [
3706
+ {
3707
+ type: "command",
3708
+ command: cmd,
3709
+ timeout: 10
3710
+ }
3711
+ ]
3710
3712
  };
3711
3713
  return {
3712
3714
  hooks: {
3713
- SessionStart: [hookEntry],
3714
- PostToolUse: [hookEntry],
3715
- UserPromptSubmit: [hookEntry],
3716
- PreCompact: [hookEntry],
3717
- Stop: [hookEntry]
3715
+ SessionStart: [hookGroup],
3716
+ PostToolUse: [hookGroup],
3717
+ UserPromptSubmit: [hookGroup],
3718
+ PreCompact: [hookGroup],
3719
+ Stop: [hookGroup]
3718
3720
  }
3719
3721
  };
3720
3722
  }
@@ -5613,8 +5615,10 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
5613
5615
  maxTokens: safeMaxTokens,
5614
5616
  since,
5615
5617
  until,
5616
- // Default to current project scope; 'global' removes the project filter
5617
- projectId: scope === "global" ? void 0 : project.id
5618
+ // Data isolation is handled at the directory level (each project has its own data dir).
5619
+ // No projectId filter needed avoids cross-IDE search failures when different IDEs
5620
+ // resolve different projectIds for the same directory.
5621
+ projectId: void 0
5618
5622
  });
5619
5623
  let text = result.formatted;
5620
5624
  if (!syncAdvisoryShown && syncAdvisory) {
@@ -5711,13 +5715,24 @@ ${result.remaining} active observations remaining.
5711
5715
  Archived memories can be restored manually if needed.` }]
5712
5716
  };
5713
5717
  }
5714
- const database = await (await Promise.resolve().then(() => (init_orama_store(), orama_store_exports))).getDb();
5715
- const allResults = await search2(database, {
5716
- term: "",
5717
- where: {},
5718
- limit: 1e4
5719
- });
5720
- const docs = allResults.hits.map((h) => h.document);
5718
+ const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
5719
+ const allObs = getAllObservations2();
5720
+ const docs = allObs.map((obs) => ({
5721
+ id: `obs-${obs.id}`,
5722
+ observationId: obs.id,
5723
+ entityName: obs.entityName,
5724
+ type: obs.type,
5725
+ title: obs.title,
5726
+ narrative: obs.narrative,
5727
+ facts: obs.facts.join("\n"),
5728
+ filesModified: obs.filesModified.join("\n"),
5729
+ concepts: obs.concepts.join(", "),
5730
+ tokens: obs.tokens,
5731
+ createdAt: obs.createdAt,
5732
+ projectId: obs.projectId,
5733
+ accessCount: 0,
5734
+ lastAccessedAt: ""
5735
+ }));
5721
5736
  if (docs.length === 0) {
5722
5737
  return {
5723
5738
  content: [{ type: "text", text: "No observations found for this project." }]
@@ -6587,10 +6602,16 @@ var init_serve = __esm({
6587
6602
  const { McpServer: McpServer2 } = await import("@modelcontextprotocol/sdk/server/mcp.js");
6588
6603
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server2(), server_exports2));
6589
6604
  const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
6590
- let projectRoot = args.cwd || process.env.MEMORIX_PROJECT_ROOT || process.env.INIT_CWD || process.cwd();
6605
+ let safeCwd;
6606
+ try {
6607
+ safeCwd = process.cwd();
6608
+ } catch {
6609
+ safeCwd = (await import("os")).homedir();
6610
+ }
6611
+ let projectRoot = args.cwd || process.env.MEMORIX_PROJECT_ROOT || process.env.INIT_CWD || safeCwd;
6591
6612
  console.error(`[memorix] Starting with cwd: ${projectRoot}`);
6592
6613
  const earlyDetect = detectProject2(projectRoot);
6593
- const needsRoots = earlyDetect.id === "__invalid__" || earlyDetect.id.startsWith("local/");
6614
+ const needsRoots = earlyDetect.id === "__invalid__";
6594
6615
  if (needsRoots) {
6595
6616
  console.error(`[memorix] cwd is not a valid project, trying MCP roots protocol...`);
6596
6617
  const mcpServer = new McpServer2({ name: "memorix", version: "0.1.0" });
@@ -7486,7 +7507,14 @@ var init_hooks_install = __esm({
7486
7507
  },
7487
7508
  run: async ({ args }) => {
7488
7509
  const { detectInstalledAgents: detectInstalledAgents2, installHooks: installHooks2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
7489
- const cwd = process.cwd();
7510
+ const os4 = await import("os");
7511
+ let cwd;
7512
+ try {
7513
+ cwd = process.cwd();
7514
+ } catch {
7515
+ cwd = os4.homedir();
7516
+ console.log(`\u26A0\uFE0F Could not access current directory, using home: ${cwd}`);
7517
+ }
7490
7518
  let agents;
7491
7519
  if (args.agent) {
7492
7520
  agents = [args.agent];
@@ -7547,7 +7575,13 @@ var init_hooks_uninstall = __esm({
7547
7575
  },
7548
7576
  run: async ({ args }) => {
7549
7577
  const { detectInstalledAgents: detectInstalledAgents2, uninstallHooks: uninstallHooks2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
7550
- const cwd = process.cwd();
7578
+ const os4 = await import("os");
7579
+ let cwd;
7580
+ try {
7581
+ cwd = process.cwd();
7582
+ } catch {
7583
+ cwd = os4.homedir();
7584
+ }
7551
7585
  let agents;
7552
7586
  if (args.agent) {
7553
7587
  agents = [args.agent];
@@ -7589,7 +7623,13 @@ var init_hooks_status = __esm({
7589
7623
  },
7590
7624
  run: async () => {
7591
7625
  const { getHookStatus: getHookStatus2 } = await Promise.resolve().then(() => (init_installers(), installers_exports));
7592
- const cwd = process.cwd();
7626
+ const os4 = await import("os");
7627
+ let cwd;
7628
+ try {
7629
+ cwd = process.cwd();
7630
+ } catch {
7631
+ cwd = os4.homedir();
7632
+ }
7593
7633
  const statuses = await getHookStatus2(cwd);
7594
7634
  console.log("\nMemorix Hooks Status");
7595
7635
  console.log("\u2550".repeat(50));