memorix 0.6.2 → 0.6.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/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  <a href="https://www.npmjs.com/package/memorix"><img src="https://img.shields.io/npm/dm/memorix.svg?style=flat-square&color=blue" alt="npm downloads"></a>
8
8
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-green.svg?style=flat-square" alt="License"></a>
9
9
  <a href="https://github.com/AVIDS2/memorix"><img src="https://img.shields.io/github/stars/AVIDS2/memorix?style=flat-square&color=yellow" alt="GitHub stars"></a>
10
- <img src="https://img.shields.io/badge/tests-274%20passed-brightgreen?style=flat-square" alt="Tests">
10
+ <img src="https://img.shields.io/badge/tests-405%20passed-brightgreen?style=flat-square" alt="Tests">
11
11
  </p>
12
12
  <p align="center">
13
13
  <a href="#-quick-start">Quick Start</a> •
@@ -20,9 +20,9 @@
20
20
 
21
21
  ---
22
22
 
23
- > **One project, six agents, zero context loss.**
23
+ > **One project, seven agents, zero context loss.**
24
24
  >
25
- > Memorix is a **cross-agent memory bridge** — it lets Cursor, Windsurf, Claude Code, Codex, Copilot, and Antigravity **share the same project knowledge** in real-time. Architecture decisions made in one IDE are instantly available in another. Switch tools, open new windows, start fresh sessions — your context follows you everywhere via [MCP](https://modelcontextprotocol.io/). It also **syncs MCP configs, rules, skills, and workflows** across all your agents automatically.
25
+ > Memorix is a **cross-agent memory bridge** — it lets Cursor, Windsurf, Claude Code, Codex, Copilot, Antigravity, and **Kiro** **share the same project knowledge** in real-time. Architecture decisions made in one IDE are instantly available in another. Switch tools, open new windows, start fresh sessions — your context follows you everywhere via [MCP](https://modelcontextprotocol.io/). It also **syncs MCP configs, rules, skills, and workflows** across all your agents automatically.
26
26
 
27
27
  ---
28
28
 
@@ -46,7 +46,7 @@ Add Memorix to your agent's MCP config — **that's it**. No global install need
46
46
 
47
47
  Restart your agent and Memorix is running! 🎉
48
48
 
49
- > 💡 More agent configs: [Cursor](#cursor) • [Claude Code](#claude-code) • [Codex](#codex) • [VS Code Copilot](#vs-code-copilot) • [Antigravity](#antigravity)
49
+ > 💡 More agent configs: [Cursor](#cursor) • [Claude Code](#claude-code) • [Codex](#codex) • [VS Code Copilot](#vs-code-copilot) • [Antigravity](#antigravity) • [Kiro](#kiro)
50
50
 
51
51
  ### Or Install Globally
52
52
 
@@ -93,7 +93,7 @@ Then use `"command": "memorix"` instead of `"command": "npx"` in your config.
93
93
 
94
94
  ### 🔄 Cross-Agent Workspace Sync
95
95
 
96
- - **6 Agent Adapters** — Windsurf, Cursor, Claude Code, Codex, VS Code Copilot, Antigravity
96
+ - **7 Agent Adapters** — Windsurf, Cursor, Claude Code, Codex, VS Code Copilot, Antigravity, **Kiro**
97
97
  - **MCP Config Migration** — Detect and migrate MCP server configs (merges — never overwrites)
98
98
  - **Rules Sync** — Scan → Deduplicate → Conflict detection → Cross-format generation
99
99
  - **Skills & Workflows** — Copy skill folders and workflow files across agents
@@ -113,11 +113,13 @@ Then use `"command": "memorix"` instead of `"command": "npx"` in your config.
113
113
  - **Project Switcher** — Dropdown to view any project's data without switching IDEs
114
114
  - **Knowledge Graph** — Interactive visualization of entities and relations
115
115
  - **Retention Scores** — Exponential decay scoring with immunity status
116
+ - **Observation Management** — Expand/collapse details, search, delete with confirmation, data export
116
117
  - **Light/Dark Theme** — Premium glassmorphism design, bilingual (EN/中文)
117
118
 
118
119
  ### 🪝 Auto-Memory Hooks
119
120
 
120
121
  - **Implicit Memory** — Auto-captures decisions, errors, gotchas from agent activity
122
+ - **Session Start Injection** — Intelligently loads recent high-value memories (gotchas, decisions, problem-solutions) and injects a concise summary into the agent's system prompt at session start
121
123
  - **Multi-Language Pattern Detection** — English + Chinese keyword matching
122
124
  - **Cooldown & Noise Filtering** — 30s cooldown, skips trivial commands (ls, cat, pwd)
123
125
  - **One-Command Install** — `memorix hooks install` sets up hooks + rules for your agent
@@ -204,6 +206,20 @@ args = ["-y", "memorix@latest", "serve"]
204
206
  }
205
207
  ```
206
208
 
209
+ ### Kiro
210
+
211
+ `.kiro/settings/mcp.json`:
212
+ ```json
213
+ {
214
+ "mcpServers": {
215
+ "memorix": {
216
+ "command": "npx",
217
+ "args": ["-y", "memorix@latest", "serve"]
218
+ }
219
+ }
220
+ }
221
+ ```
222
+
207
223
  ---
208
224
 
209
225
  ## 🛠 Available MCP Tools
@@ -278,33 +294,33 @@ Files: ["src/auth/jwt.ts", "src/config.ts"]
278
294
  ### Architecture
279
295
 
280
296
  ```
281
- ┌──────────────────────────────────────────────────────────────┐
282
- AI Coding Agents
283
- │ Windsurf │ Cursor │ Claude Code │ Codex │ Copilot │ Antigravity
284
- └────────────────────────┬─────────────────────────────────────┘
285
- │ MCP Protocol (stdio)
286
- ┌────────────────────────▼─────────────────────────────────────┐
287
- Memorix MCP Server (17 tools)
288
-
289
- │ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐
290
- │ │ Memory │ │ Compact │ │ Workspace Sync │
291
- │ │ Layer │ │ Engine │ │ (6 adapters) │
292
- │ │ │ │ (3-layer) │ │ │
293
- │ │ • Graph │ │ │ │ • MCP Configs │
294
- │ │ • Retention │ │ │ │ • Rules │
295
- │ │ • Entities │ │ │ │ • Skills │
296
- │ │ • Relations │ │ │ │ • Workflows │
297
- │ └──────┬──────┘ └──────┬───────┘ └──────────────────┘
298
- │ │ │
299
- │ ┌──────▼────────────────▼───────────────────────────────┐
300
- │ │ Orama Store (BM25 + Vector) │ Persistence (JSONL) │
301
- │ └───────────────────────────────────────────────────────┘
302
-
303
- │ ┌───────────────────────────────────────────────────────┐
304
- │ │ Hooks System: Normalizer → Pattern Detector → Store │
305
- │ │ (Auto-captures decisions, bugs, gotchas from agents) │
306
- │ └───────────────────────────────────────────────────────┘
307
- └──────────────────────────────────────────────────────────────┘
297
+ ┌───────────────────────────────────────────────────────────────────┐
298
+ AI Coding Agents
299
+ │ Windsurf │ Cursor │ Claude Code │ Codex │ Copilot │ Antigravity │ Kiro
300
+ └───────────────────────────┬───────────────────────────────────────┘
301
+ │ MCP Protocol (stdio)
302
+ ┌───────────────────────────▼───────────────────────────────────────┐
303
+ Memorix MCP Server (17 tools)
304
+
305
+ │ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐
306
+ │ │ Memory │ │ Compact │ │ Workspace Sync │
307
+ │ │ Layer │ │ Engine │ │ (7 adapters) │
308
+ │ │ │ │ (3-layer) │ │ │
309
+ │ │ • Graph │ │ │ │ • MCP Configs │
310
+ │ │ • Retention │ │ │ │ • Rules │
311
+ │ │ • Entities │ │ │ │ • Skills │
312
+ │ │ • Relations │ │ │ │ • Workflows │
313
+ │ └──────┬──────┘ └──────┬───────┘ └──────────────────┘
314
+ │ │ │
315
+ │ ┌──────▼────────────────▼───────────────────────────────┐
316
+ │ │ Orama Store (BM25 + Vector) │ Persistence (JSONL) │
317
+ │ └───────────────────────────────────────────────────────┘
318
+
319
+ │ ┌───────────────────────────────────────────────────────┐
320
+ │ │ Hooks System: Normalizer → Pattern Detector → Store │
321
+ │ │ (Auto-captures decisions, bugs, gotchas from agents) │
322
+ │ └───────────────────────────────────────────────────────┘
323
+ └───────────────────────────────────────────────────────────────────┘
308
324
  ```
309
325
 
310
326
  ---
@@ -349,7 +365,7 @@ cd memorix
349
365
  npm install
350
366
 
351
367
  npm run dev # tsup watch mode
352
- npm test # vitest (274 tests)
368
+ npm test # vitest (405 tests)
353
369
  npm run lint # TypeScript type check
354
370
  npm run build # Production build
355
371
  ```
@@ -366,7 +382,7 @@ src/
366
382
  ├── embedding/ # Optional fastembed vector provider
367
383
  ├── hooks/ # Auto-memory hooks (normalizer + pattern detector)
368
384
  ├── workspace/ # Cross-agent MCP/workflow/skills sync
369
- ├── rules/ # Cross-agent rules sync (6 adapters)
385
+ ├── rules/ # Cross-agent rules sync (7 adapters)
370
386
  ├── dashboard/ # Visual web dashboard (knowledge graph, stats)
371
387
  ├── project/ # Git-based project detection
372
388
  └── cli/ # CLI commands (serve, hook, sync, dashboard)
package/dist/cli/index.js CHANGED
@@ -52,6 +52,9 @@ function sanitizeProjectId(projectId) {
52
52
  return projectId.replace(/\//g, "--").replace(/[<>:"|?*\\]/g, "_");
53
53
  }
54
54
  async function getProjectDataDir(projectId, baseDir) {
55
+ if (projectId === "__invalid__") {
56
+ throw new Error("Cannot create data directory for invalid project");
57
+ }
55
58
  const base = baseDir ?? DEFAULT_DATA_DIR;
56
59
  const dirName = sanitizeProjectId(projectId);
57
60
  const dataDir = path2.join(base, dirName);
@@ -1223,6 +1226,7 @@ __export(detector_exports, {
1223
1226
  });
1224
1227
  import { execSync } from "child_process";
1225
1228
  import { existsSync } from "fs";
1229
+ import os2 from "os";
1226
1230
  import path3 from "path";
1227
1231
  function detectProject(cwd) {
1228
1232
  const basePath = cwd ?? process.cwd();
@@ -1233,11 +1237,77 @@ function detectProject(cwd) {
1233
1237
  const name2 = id2.split("/").pop() ?? path3.basename(rootPath);
1234
1238
  return { id: id2, name: name2, gitRemote, rootPath };
1235
1239
  }
1240
+ if (!isValidProjectRoot(rootPath)) {
1241
+ console.error(`[memorix] Skipped invalid project root: ${rootPath}`);
1242
+ return { id: "__invalid__", name: "unknown", rootPath };
1243
+ }
1236
1244
  const name = path3.basename(rootPath);
1237
1245
  const id = `local/${name}`;
1238
1246
  console.error(`[memorix] Warning: no git remote found at ${rootPath}, using fallback projectId: ${id}`);
1239
1247
  return { id, name, rootPath };
1240
1248
  }
1249
+ function isValidProjectRoot(dirPath) {
1250
+ const resolved = path3.resolve(dirPath);
1251
+ const home = path3.resolve(os2.homedir());
1252
+ if (resolved === home) return false;
1253
+ if (resolved === path3.parse(resolved).root) return false;
1254
+ const basename2 = path3.basename(resolved).toLowerCase();
1255
+ const knownNonProjectDirs = /* @__PURE__ */ new Set([
1256
+ // IDE / editor config dirs
1257
+ ".vscode",
1258
+ ".cursor",
1259
+ ".windsurf",
1260
+ ".kiro",
1261
+ ".codex",
1262
+ ".gemini",
1263
+ ".claude",
1264
+ ".github",
1265
+ ".git",
1266
+ // OS / system dirs
1267
+ "desktop",
1268
+ "documents",
1269
+ "downloads",
1270
+ "pictures",
1271
+ "videos",
1272
+ "music",
1273
+ "appdata",
1274
+ "application data",
1275
+ "library",
1276
+ // Package manager / tool dirs
1277
+ "node_modules",
1278
+ ".npm",
1279
+ ".yarn",
1280
+ ".pnpm-store",
1281
+ ".config",
1282
+ ".local",
1283
+ ".cache",
1284
+ ".ssh",
1285
+ ".memorix"
1286
+ ]);
1287
+ if (knownNonProjectDirs.has(basename2)) {
1288
+ const parent = path3.resolve(path3.dirname(resolved));
1289
+ if (parent === home || parent === path3.parse(parent).root) {
1290
+ return false;
1291
+ }
1292
+ }
1293
+ const projectIndicators = [
1294
+ "package.json",
1295
+ "Cargo.toml",
1296
+ "go.mod",
1297
+ "pyproject.toml",
1298
+ "setup.py",
1299
+ "pom.xml",
1300
+ "build.gradle",
1301
+ "Makefile",
1302
+ "CMakeLists.txt",
1303
+ "composer.json",
1304
+ "Gemfile",
1305
+ ".git",
1306
+ "README.md",
1307
+ "README"
1308
+ ];
1309
+ return projectIndicators.some((f) => existsSync(path3.join(resolved, f)));
1310
+ }
1241
1311
  function findPackageRoot(cwd) {
1242
1312
  let dir = path3.resolve(cwd);
1243
1313
  const root = path3.parse(dir).root;
@@ -3164,7 +3234,7 @@ __export(installers_exports, {
3164
3234
  });
3165
3235
  import * as fs3 from "fs/promises";
3166
3236
  import * as path5 from "path";
3167
- import * as os2 from "os";
3237
+ import * as os3 from "os";
3168
3238
  import { createRequire } from "module";
3169
3239
  function resolveHookCommand() {
3170
3240
  if (process.platform === "win32") {
@@ -3269,7 +3339,7 @@ function getProjectConfigPath(agent, projectRoot) {
3269
3339
  }
3270
3340
  }
3271
3341
  function getGlobalConfigPath(agent) {
3272
- const home = os2.homedir();
3342
+ const home = os3.homedir();
3273
3343
  switch (agent) {
3274
3344
  case "claude":
3275
3345
  case "copilot":
@@ -3284,7 +3354,7 @@ function getGlobalConfigPath(agent) {
3284
3354
  }
3285
3355
  async function detectInstalledAgents() {
3286
3356
  const agents = [];
3287
- const home = os2.homedir();
3357
+ const home = os3.homedir();
3288
3358
  const claudeDir = path5.join(home, ".claude");
3289
3359
  try {
3290
3360
  await fs3.access(claudeDir);
@@ -3409,7 +3479,7 @@ async function installAgentRules(agent, projectRoot) {
3409
3479
  rulesPath = path5.join(projectRoot, "AGENTS.md");
3410
3480
  break;
3411
3481
  case "kiro":
3412
- rulesPath = path5.join(projectRoot, ".kiro", "rules", "memorix.md");
3482
+ rulesPath = path5.join(projectRoot, ".kiro", "steering", "memorix.md");
3413
3483
  break;
3414
3484
  default:
3415
3485
  rulesPath = path5.join(projectRoot, ".agent", "rules", "memorix.md");
@@ -4820,7 +4890,8 @@ var init_sync = __esm({
4820
4890
  "claude-code": "Claude Code (CLAUDE.md, .claude/rules/*.md)",
4821
4891
  codex: "Codex (SKILL.md, AGENTS.md)",
4822
4892
  windsurf: "Windsurf (.windsurfrules, .windsurf/rules/*.md)",
4823
- antigravity: "Antigravity (.agent/rules/*.md, GEMINI.md)"
4893
+ antigravity: "Antigravity (.agent/rules/*.md, GEMINI.md)",
4894
+ kiro: "Kiro (.kiro/steering/*.md, AGENTS.md)"
4824
4895
  };
4825
4896
  sync_default = defineCommand3({
4826
4897
  meta: {
@@ -4830,7 +4901,7 @@ var init_sync = __esm({
4830
4901
  args: {
4831
4902
  target: {
4832
4903
  type: "string",
4833
- description: "Target agent format (cursor, claude-code, codex, windsurf, antigravity)",
4904
+ description: "Target agent format (cursor, claude-code, codex, windsurf, antigravity, kiro)",
4834
4905
  required: false
4835
4906
  },
4836
4907
  dry: {
@@ -4873,11 +4944,11 @@ var init_sync = __esm({
4873
4944
  }
4874
4945
  let target = args.target;
4875
4946
  if (!target) {
4876
- const available = ["cursor", "claude-code", "codex", "windsurf", "antigravity"].filter(
4947
+ const available = ["cursor", "claude-code", "codex", "windsurf", "antigravity", "kiro"].filter(
4877
4948
  (t) => !sources.includes(t)
4878
4949
  );
4879
4950
  if (available.length === 0) {
4880
- available.push("cursor", "claude-code", "codex", "windsurf", "antigravity");
4951
+ available.push("cursor", "claude-code", "codex", "windsurf", "antigravity", "kiro");
4881
4952
  }
4882
4953
  const selected = await p2.select({
4883
4954
  message: "Generate rules for which agent?",
@@ -5028,9 +5099,10 @@ function normalizeCursor(payload, event) {
5028
5099
  return result;
5029
5100
  }
5030
5101
  function normalizeHookInput(payload) {
5102
+ const directEvent = typeof payload.event === "string" ? EVENT_MAP[payload.event] : void 0;
5031
5103
  const agent = detectAgent(payload);
5032
5104
  const rawEventName = extractEventName(payload, agent);
5033
- const event = EVENT_MAP[rawEventName] ?? "post_tool";
5105
+ const event = directEvent ?? EVENT_MAP[rawEventName] ?? "post_tool";
5034
5106
  const timestamp = payload.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
5035
5107
  let agentSpecific = {};
5036
5108
  switch (agent) {
@@ -5063,6 +5135,16 @@ var init_normalizer = __esm({
5063
5135
  "use strict";
5064
5136
  init_esm_shims();
5065
5137
  EVENT_MAP = {
5138
+ // Identity mappings — already-normalized event names
5139
+ // This allows direct payloads like { event: 'session_start' } to work
5140
+ session_start: "session_start",
5141
+ user_prompt: "user_prompt",
5142
+ post_edit: "post_edit",
5143
+ post_command: "post_command",
5144
+ post_tool: "post_tool",
5145
+ pre_compact: "pre_compact",
5146
+ session_end: "session_end",
5147
+ post_response: "post_response",
5066
5148
  // Claude Code / VS Code Copilot
5067
5149
  SessionStart: "session_start",
5068
5150
  UserPromptSubmit: "user_prompt",
@@ -5306,14 +5388,65 @@ async function handleHookEvent(input) {
5306
5388
  return { observation: null, output: defaultOutput };
5307
5389
  }
5308
5390
  switch (input.event) {
5309
- case "session_start":
5391
+ case "session_start": {
5392
+ let contextSummary = "";
5393
+ try {
5394
+ const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
5395
+ const { getProjectDataDir: getProjectDataDir2, loadObservationsJson: loadObservationsJson2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
5396
+ const project = await detectProject2(input.cwd || process.cwd());
5397
+ const dataDir = await getProjectDataDir2(project.id);
5398
+ const allObs = await loadObservationsJson2(dataDir);
5399
+ if (allObs.length > 0) {
5400
+ const PRIORITY_ORDER = {
5401
+ "gotcha": 6,
5402
+ "decision": 5,
5403
+ "problem-solution": 4,
5404
+ "trade-off": 3,
5405
+ "discovery": 2,
5406
+ "how-it-works": 1
5407
+ };
5408
+ const scored = allObs.map((obs, i) => ({
5409
+ obs,
5410
+ priority: PRIORITY_ORDER[obs.type ?? ""] ?? 0,
5411
+ recency: i
5412
+ // higher index = more recent
5413
+ })).sort((a, b) => {
5414
+ if (b.priority !== a.priority) return b.priority - a.priority;
5415
+ return b.recency - a.recency;
5416
+ });
5417
+ const top = scored.slice(0, 5);
5418
+ const TYPE_EMOJI = {
5419
+ "gotcha": "\u{1F534}",
5420
+ "decision": "\u{1F7E4}",
5421
+ "problem-solution": "\u{1F7E1}",
5422
+ "trade-off": "\u2696\uFE0F",
5423
+ "discovery": "\u{1F7E3}",
5424
+ "how-it-works": "\u{1F535}",
5425
+ "what-changed": "\u{1F7E2}",
5426
+ "why-it-exists": "\u{1F7E0}",
5427
+ "session-request": "\u{1F3AF}"
5428
+ };
5429
+ const lines = top.map(({ obs }) => {
5430
+ const emoji = TYPE_EMOJI[obs.type ?? ""] ?? "\u{1F4CC}";
5431
+ const title = obs.title ?? "(untitled)";
5432
+ const fact = obs.facts?.[0] ? ` \u2014 ${obs.facts[0]}` : "";
5433
+ return `${emoji} ${title}${fact}`;
5434
+ });
5435
+ contextSummary = `
5436
+
5437
+ Recent project memories (${project.name}):
5438
+ ${lines.join("\n")}`;
5439
+ }
5440
+ } catch {
5441
+ }
5310
5442
  return {
5311
5443
  observation: null,
5312
5444
  output: {
5313
5445
  continue: true,
5314
- systemMessage: "Memorix is active. Your memories from previous sessions are available via memorix_search."
5446
+ systemMessage: `Memorix is active. Your memories from previous sessions are available via memorix_search.${contextSummary}`
5315
5447
  }
5316
5448
  };
5449
+ }
5317
5450
  case "pre_compact":
5318
5451
  return {
5319
5452
  observation: buildObservation(input, extractContent(input)),