@datafrog-io/n2n-memory 1.2.0 → 1.2.2

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
@@ -6,25 +6,54 @@
6
6
 
7
7
  All notable changes to this project will be documented in this file.
8
8
 
9
- ## [1.2.0] - 2025-01-12
9
+ ## [Unreleased]
10
+
11
+ ### Fixed
12
+ - Normal reads now refresh cached graph/context snapshots when the underlying `.mcp` files change.
13
+ - Existing but unreadable `memory.json` or `context.json` files now raise data integrity errors instead of being treated as empty state.
14
+ - `n2n_create_relations` now rejects relations that reference missing entities.
15
+ - `n2n_export_markdown` now rejects absolute output paths and relative paths that escape the project root.
16
+ - `projectPath` validation now rejects relative paths before resolving them.
17
+ - Production and development dependency audits now pass with zero vulnerabilities.
18
+
19
+ ### Changed
20
+ - Clarified that `projectPath` must point to the project root or workspace top-level directory.
21
+ - Refactored JSON storage writes through a shared atomic-write path and normalized graph copies before persisting.
22
+ - Switched the test runner from Mocha to Vitest to keep the development dependency tree clean.
23
+ - Tool discovery now exposes concrete JSON input schemas generated from the Zod schemas.
24
+ - README-only directories are treated as weak roots and are rejected during project recognition.
25
+ - Server logs hide full local paths by default unless `N2N_LOG_LEVEL=debug` is set.
26
+ - Updated English and Chinese documentation to reflect the current tool set, storage contract, path containment rules, and graph integrity behavior.
27
+
28
+ ### Added
29
+ - GitHub CI workflow for pull requests and pushes to `main`.
30
+ - Hardened publish workflow that runs the full check suite before `npm publish`.
31
+ - npm package `files` whitelist and richer package metadata.
32
+ - Open-source governance files: contributing guide, security policy, code of conduct, issue templates, and PR template.
33
+
34
+ ## [1.2.1] - 2026-01-12
10
35
 
11
36
  ### Added
37
+ - **Structured JSON Tool Responses**:
38
+ - Upgraded all tool responses from plain text to structured JSON.
39
+ - Added `status`, `message`, and `_protocol` metadata fields for better machine readability.
40
+ - **Dynamic N2N-SYNC Protocol Reminders**:
41
+ - Implemented automatic injection of `[N2N-SYNC]` protocol reminders into `nextSteps`.
42
+ - Embedded protocol hints in tool metadata to guide AI assistants toward better synchronization habits.
12
43
  - **Similarity-Based Observation Deduplication**:
13
44
  - New `similarity.ts` utility module with Jaccard similarity, Levenshtein distance, and containment detection algorithms.
14
45
  - `addEntities()` and `addObservations()` now use intelligent deduplication instead of exact-match `Set` deduplication.
15
46
  - Automatically keeps the longer (more detailed) observation when duplicates are detected.
16
47
  - Prevents redundant entries like "version 2.4.1" and "version 2.4.1 is the current release" from coexisting.
17
-
18
48
  - **Fuzzy Search with Relevance Ranking**:
19
49
  - `n2n_search` tool now supports fuzzy matching by default.
20
50
  - New optional parameters: `fuzzy` (boolean) and `minScore` (0-1 threshold).
21
51
  - Search results are ranked by relevance score (highest first).
22
52
  - Handles typos via Levenshtein similarity.
23
53
  - Supports word-level partial matching and semantic similarity via Jaccard index.
24
-
25
54
  - **New Unit Tests**:
26
55
  - Comprehensive test suite for all similarity functions (`similarity.test.ts`).
27
- - 99 tests passing.
56
+ - Expanded regression coverage for memory and search behavior.
28
57
 
29
58
  ## [1.1.0] - 2024-12-19
30
59
 
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # n2n-memory
2
2
 
3
+ Project-local memory MCP server for AI coding agents.
4
+
3
5
  [![npm version](https://img.shields.io/npm/v/@datafrog-io/n2n-memory)](https://www.npmjs.com/package/@datafrog-io/n2n-memory)
4
6
  [![npm total downloads](https://img.shields.io/npm/dt/@datafrog-io/n2n-memory)](https://www.npmjs.com/package/@datafrog-io/n2n-memory)
5
7
  [![license](https://img.shields.io/github/license/n2ns/n2n-memory)](https://github.com/n2ns/n2n-memory/blob/main/LICENSE)
@@ -14,16 +16,56 @@
14
16
 
15
17
  > **Context as code. Memory as asset.**
16
18
 
17
- A specialized MCP server designed to solve "memory pollution" during AI-assisted cross-project development. It persists AI's cognitive fragments directly within each project's own directory.
19
+ n2n-memory is an open-source, local-first Model Context Protocol (MCP) memory server for AI coding assistants. It prevents cross-project memory pollution by storing durable project knowledge in `.mcp/memory.json` and active task context in `.mcp/context.json` inside each repository.
20
+
21
+ ### Search-friendly positioning
22
+
23
+ If you are searching for:
24
+ - AI coding memory
25
+ - project-local MCP memory
26
+ - local-first context memory for coding agents
27
+ - repository-isolated memory graph for Claude/Cursor/VS Code workflows
28
+
29
+ This server is designed for these goals: deterministic JSON output, project isolation, and Git-friendly reviewability.
30
+
31
+ ## What Is n2n-memory?
32
+
33
+ n2n-memory gives AI coding tools a project-local knowledge graph they can read and update through MCP. It is designed for developers and teams who use AI assistants across multiple repositories and want memory that is local, auditable, Git-friendly, and not mixed with unrelated projects.
34
+
35
+ **TL;DR**
36
+ - **Install**: `npx -y @datafrog-io/n2n-memory`
37
+ - **Protocol**: Model Context Protocol (MCP), stdio transport
38
+ - **Storage**: `.mcp/memory.json` for durable memory, `.mcp/context.json` for active task state
39
+ - **Best for**: AI coding assistant memory, project architecture notes, shared team context
40
+ - **Not for**: cloud sync, global personal memory, source code indexing, or vector database replacement
18
41
 
19
42
  ### 🌟 Key Highlights
20
- - **Project-Level Physical Isolation**: Memory files are stored at `[Project Root]/.mcp/memory.json`.
43
+ - **Project-Level Physical Isolation**: Memory and active context are stored under `[Project Root]/.mcp/`.
21
44
  - **Git-Friendly**: JSON data is automatically sorted by key to generate clean and readable `git diff`.
45
+ - **Dual-Buffer State**: Durable graph knowledge lives in `memory.json`; high-frequency task state lives in `context.json`.
22
46
  - **Tool Agnostic**: Uses the `.mcp` naming convention, not tied to any specific AI brand or IDE plugin.
23
47
  - **Assets for Your Code**: Memory stays with your code; team members can share AI's understanding of the architecture by simply pulling the repository.
24
- - **Universal Compatibility**: Works with all MCP-enabled models including **Claude 4.5**, **Gemini 3 Pro/Flash**, **GPT-5/5.2**, and **DeepSeek V3.2**.
48
+ - **Universal Compatibility**: Works with MCP-enabled clients and AI coding tools that support stdio MCP servers.
25
49
  - **Privacy-First**: Built with security by design, keeping your data local and isolated.
26
50
 
51
+ ## Use Cases
52
+
53
+ - Restore project context at the start of an AI coding session.
54
+ - Preserve architecture decisions, implementation notes, known pitfalls, and active task state.
55
+ - Share durable project memory with teammates through Git.
56
+ - Keep memory isolated per repository when working across many client, product, or open-source projects.
57
+ - Query a local project knowledge graph without adding a database or cloud service.
58
+
59
+ ## How It Compares
60
+
61
+ | Approach | Best for | Tradeoff |
62
+ | --- | --- | --- |
63
+ | n2n-memory | Project-local MCP memory for AI coding agents | Requires assistants to call MCP tools intentionally |
64
+ | Global MCP memory | Personal memory across many chats or projects | Can mix unrelated project context |
65
+ | Markdown memory bank | Human-readable project notes | Less structured for graph queries and tool updates |
66
+ | Vector database memory | Semantic retrieval over large corpora | Heavier infrastructure and less deterministic diffs |
67
+ | IDE-specific rules | Steering assistant behavior in one IDE | Less portable across MCP clients |
68
+
27
69
  ### 🚀 Quick Start
28
70
 
29
71
  #### 1. Installation & Config (IDE / Claude Desktop)
@@ -50,29 +92,91 @@ Add in the MCP settings panel:
50
92
  - **Type**: `command`
51
93
  - **Command**: `npx -y @datafrog-io/n2n-memory`
52
94
 
95
+ ##### Other MCP clients
96
+ - The server uses stdio MCP and can be wired to any MCP client that supports local command execution.
97
+ - If your client supports it, set `N2N_LOG_LEVEL=debug` only when troubleshooting local path resolution.
98
+
53
99
  #### 2. Usage Guide
54
100
 
55
101
  This service is path-driven. AI assistants should pay attention to:
56
102
 
57
- 1. **Absolute Paths**: When calling any `n2n_*` tool, the absolute path of the current project root (`projectPath`) must be provided.
58
- 2. **Auto Storage**: Memory is automatically saved to `[ProjectPath]/.mcp/memory.json`.
59
- 3. **Collaboration**: It is recommended to commit `.mcp/memory.json` to your Git repository to share the knowledge graph with your team.
103
+ 1. **Absolute Project Root**: When calling any `n2n_*` tool, provide the absolute path of the current project root or workspace top-level directory (`projectPath`).
104
+ 2. **Initialization Handshake**: If `.mcp` does not exist yet, call the tool again with `confirmNewProjectRoot` set to the detected root returned by the server.
105
+ 3. **Auto Storage**: Durable memory is saved to `[ProjectPath]/.mcp/memory.json`; active task context is saved to `[ProjectPath]/.mcp/context.json`.
106
+ 4. **Collaboration**: Commit `.mcp/memory.json` when the knowledge graph should be shared with the team. Commit `context.json` only if your workflow wants active task state shared.
107
+
108
+ Recommended `.gitignore` policy for teams that want to share durable memory:
109
+
110
+ ```gitignore
111
+ .mcp/context.json
112
+ !.mcp/
113
+ !.mcp/memory.json
114
+ ```
115
+
116
+ If your project memory may contain private implementation details, keep the whole `.mcp/` directory ignored.
60
117
 
61
118
  ##### Available Tools:
62
119
  - `n2n_add_entities`: Create new entities.
63
120
  - `n2n_add_observations`: Append observations or facts.
64
- - `n2n_create_relations`: Establish connections between entities.
121
+ - `n2n_create_relations`: Establish connections between existing entities.
65
122
  - `n2n_read_graph`: Read project memory and active context (Supports `summaryMode` and `pagination`).
66
123
  - `n2n_get_graph_summary`: Quickly fetch a lightweight index of all entities (Supports `pagination`).
67
124
  - `n2n_update_context`: Update current task status and next steps.
68
- - `n2n_search`: Search the graph via keywords (Supports `pagination`).
125
+ - `n2n_search`: Search the graph via keyword or fuzzy matching (Supports `pagination`).
126
+ - `n2n_delete_entities`: Remove entities and their attached relations.
127
+ - `n2n_delete_observations`: Remove specific observations from entities.
128
+ - `n2n_delete_relations`: Remove specific relations.
69
129
  - `n2n_open_nodes`: Retrieve specific entities by name.
130
+ - `n2n_export_markdown`: Export the graph to a Markdown file inside the project root.
131
+
132
+ ##### Safety Notes:
133
+ - Existing but unreadable JSON files are treated as data integrity errors, not as empty memory.
134
+ - Export paths must be relative and must stay inside the project root.
135
+ - Relations that point to missing entities are rejected.
136
+ - Generic README-only folders are not treated as project roots; use a real project marker such as `.git`, `package.json`, or language-specific build files.
137
+ - Full local paths are hidden from server logs by default. Set `N2N_LOG_LEVEL=debug` when diagnosing path issues.
70
138
 
71
139
  ### 🗺️ Future Roadmap
72
140
  - **Semantic Search**: Integration of minimalist Vector Embeddings for fuzzy memory retrieval.
73
141
  - **Ontology Enforcement**: Optional schema for relation type consistency.
74
142
  - **Time Travel**: Versioned snapshots for memory rollback.
75
143
 
144
+ ## Security and governance notes
145
+
146
+ - `n2n_delete_entities`, `n2n_delete_observations`, and `n2n_delete_relations` are destructive and should be governed by review workflows.
147
+ - Keep `context.json` uncommitted by default, and commit `.mcp/memory.json` only when team sharing is intentional.
148
+ - See [SECURITY.md](./SECURITY.md) for vulnerability reporting.
149
+
150
+ ### Search disambiguation note
151
+
152
+ `n2n-memory` is not a network VPN or mesh overlay project. It is an MCP memory server for AI coding agents.
153
+
154
+ ## FAQ
155
+
156
+ ### Is n2n-memory a vector database?
157
+
158
+ No. n2n-memory stores a deterministic JSON knowledge graph. Semantic search may be added later, but the core design is structured, Git-friendly project memory.
159
+
160
+ ### Should I commit `.mcp/memory.json`?
161
+
162
+ Commit `.mcp/memory.json` when the knowledge graph is useful to your team. Keep it ignored if it may contain private implementation details. `context.json` is active task state and is often better left uncommitted.
163
+
164
+ ### Does n2n-memory send data to the cloud?
165
+
166
+ No. n2n-memory is a local MCP server. It reads and writes files under your project directory.
167
+
168
+ ### How is this different from global memory?
169
+
170
+ Global memory follows the assistant or user across contexts. n2n-memory follows the repository, so unrelated projects do not contaminate each other's memory.
171
+
172
+ ### Does it work with Claude Desktop, Cursor, and VS Code?
173
+
174
+ Yes, when the client supports stdio MCP servers. The README includes Claude Desktop and Cursor / VS Code MCP configuration examples.
175
+
176
+ ### Is this the same as a global AI memory store?
177
+
178
+ No. This is a repository-scoped memory server. Each project gets its own `.mcp` workspace.
179
+
76
180
  ---
77
181
 
78
182
  ## 📖 Related Docs
@@ -81,10 +185,11 @@ This service is path-driven. AI assistants should pay attention to:
81
185
  - **[API Reference](./docs/API_REFERENCE.md)**: Tool descriptions and schema.
82
186
  - **[Development](./docs/DEVELOPMENT.md)**: How to build, test and extend.
83
187
  - **[Changelog](./CHANGELOG.md)**: Version history and incident recovery.
188
+ - **[llms.txt](./llms.txt)**: Short AI-readable project summary for coding agents and answer engines.
84
189
 
85
190
  ## 📄 License
86
191
  This project is licensed under the [MIT License](./LICENSE).
87
192
 
88
193
  ---
89
194
 
90
- **N2N Studio** The AI Innovation Lab of [DataFrog.io](https://datafrog.io).
195
+ Built by N2NS Lab, the open-source lab from Datafrog, focused on practical AI developer tools.
@@ -6,16 +6,16 @@ export const CONTEXT_FILE_PATH = ".mcp/context.json";
6
6
  export class MemoryManager {
7
7
  static async readGraph(projectPath) {
8
8
  const filePath = path.resolve(projectPath, MEMORY_FILE_PATH);
9
+ if (!await fs.pathExists(filePath)) {
10
+ return { entities: [], relations: [] };
11
+ }
9
12
  try {
10
- if (await fs.pathExists(filePath)) {
11
- const data = await fs.readJson(filePath);
12
- return KnowledgeGraphSchema.parse(data);
13
- }
13
+ const data = await fs.readJson(filePath);
14
+ return KnowledgeGraphSchema.parse(data);
14
15
  }
15
16
  catch (error) {
16
- console.error(`[MemoryManager] Read error at ${filePath}:`, error);
17
+ throw new Error(`Failed to read memory file at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
17
18
  }
18
- return { entities: [], relations: [] };
19
19
  }
20
20
  /**
21
21
  * Writes the graph to disk atomically using a temporary file.
@@ -23,69 +23,34 @@ export class MemoryManager {
23
23
  */
24
24
  static async writeGraph(projectPath, graph) {
25
25
  const filePath = path.resolve(projectPath, MEMORY_FILE_PATH);
26
- const tempPath = `${filePath}.${Date.now()}.tmp`;
27
- const dirPath = path.dirname(filePath);
26
+ const normalizedGraph = this.normalizeGraphForStorage(graph);
28
27
  try {
29
- // Git-friendly sorting
30
- graph.entities.sort((a, b) => a.name.localeCompare(b.name));
31
- graph.entities.forEach(entity => {
32
- if (entity.observations) {
33
- entity.observations.sort();
34
- }
35
- else {
36
- entity.observations = [];
37
- }
38
- });
39
- graph.relations.sort((a, b) => {
40
- const fromComp = a.from.localeCompare(b.from);
41
- if (fromComp !== 0)
42
- return fromComp;
43
- const toComp = a.to.localeCompare(b.to);
44
- if (toComp !== 0)
45
- return toComp;
46
- return a.relationType.localeCompare(b.relationType);
47
- });
48
- await fs.ensureDir(dirPath);
49
- // 1. Write to temporary file
50
- await fs.writeJson(tempPath, graph, { spaces: 2 });
51
- // 2. Atomic rename
52
- await fs.move(tempPath, filePath, { overwrite: true });
28
+ await this.atomicWriteJson(filePath, normalizedGraph);
53
29
  }
54
30
  catch (error) {
55
- // Cleanup temp file if it exists and write failed
56
- if (await fs.pathExists(tempPath)) {
57
- await fs.remove(tempPath);
58
- }
59
31
  throw new Error(`Failed to write memory file at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
60
32
  }
61
33
  }
62
34
  static async readContext(projectPath) {
63
35
  const filePath = path.resolve(projectPath, CONTEXT_FILE_PATH);
36
+ if (!await fs.pathExists(filePath)) {
37
+ return { status: "PLANNING", nextSteps: [] };
38
+ }
64
39
  try {
65
- if (await fs.pathExists(filePath)) {
66
- const data = await fs.readJson(filePath);
67
- return ProjectContextSchema.parse(data);
68
- }
40
+ const data = await fs.readJson(filePath);
41
+ return ProjectContextSchema.parse(data);
69
42
  }
70
43
  catch (error) {
71
- console.error(`[MemoryManager] Context read error at ${filePath}:`, error);
44
+ throw new Error(`Failed to read context file at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
72
45
  }
73
- return { status: "PLANNING", nextSteps: [] };
74
46
  }
75
47
  static async writeContext(projectPath, context) {
76
48
  const filePath = path.resolve(projectPath, CONTEXT_FILE_PATH);
77
- const tempPath = `${filePath}.${Date.now()}.tmp`;
78
- const dirPath = path.dirname(filePath);
79
49
  try {
80
50
  context.updatedAt = new Date().toISOString();
81
- await fs.ensureDir(dirPath);
82
- await fs.writeJson(tempPath, context, { spaces: 2 });
83
- await fs.move(tempPath, filePath, { overwrite: true });
51
+ await this.atomicWriteJson(filePath, context);
84
52
  }
85
53
  catch (error) {
86
- if (await fs.pathExists(tempPath)) {
87
- await fs.remove(tempPath);
88
- }
89
54
  throw new Error(`Failed to write context file at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
90
55
  }
91
56
  }
@@ -102,7 +67,7 @@ export class MemoryManager {
102
67
  }
103
68
  static async exportToMarkdown(projectPath, outputPath) {
104
69
  const graph = await this.readGraph(projectPath);
105
- const filePath = path.resolve(projectPath, outputPath || "KNOWLEDGE_GRAPH.md");
70
+ const filePath = this.resolveProjectOutputPath(projectPath, outputPath || "KNOWLEDGE_GRAPH.md");
106
71
  let md = "# Knowledge Graph\n\n";
107
72
  md += `> Generated from \`${MEMORY_FILE_PATH}\`\n\n`;
108
73
  md += "## Entities\n\n";
@@ -137,5 +102,55 @@ export class MemoryManager {
137
102
  await fs.writeFile(filePath, md, "utf-8");
138
103
  return filePath;
139
104
  }
105
+ static normalizeGraphForStorage(graph) {
106
+ return {
107
+ entities: graph.entities
108
+ .map(entity => ({
109
+ ...entity,
110
+ observations: [...(entity.observations || [])].sort()
111
+ }))
112
+ .sort((a, b) => a.name.localeCompare(b.name)),
113
+ relations: [...graph.relations].sort((a, b) => {
114
+ const fromComp = a.from.localeCompare(b.from);
115
+ if (fromComp !== 0)
116
+ return fromComp;
117
+ const toComp = a.to.localeCompare(b.to);
118
+ if (toComp !== 0)
119
+ return toComp;
120
+ return a.relationType.localeCompare(b.relationType);
121
+ })
122
+ };
123
+ }
124
+ static async atomicWriteJson(filePath, data) {
125
+ const dirPath = path.dirname(filePath);
126
+ const tempPath = this.createTempPath(filePath);
127
+ try {
128
+ await fs.ensureDir(dirPath);
129
+ await fs.writeJson(tempPath, data, { spaces: 2 });
130
+ await fs.move(tempPath, filePath, { overwrite: true });
131
+ }
132
+ catch (error) {
133
+ if (await fs.pathExists(tempPath)) {
134
+ await fs.remove(tempPath);
135
+ }
136
+ throw error;
137
+ }
138
+ }
139
+ static createTempPath(filePath) {
140
+ const suffix = `${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
141
+ return `${filePath}.${suffix}.tmp`;
142
+ }
143
+ static resolveProjectOutputPath(projectPath, outputPath) {
144
+ if (path.isAbsolute(outputPath)) {
145
+ throw new Error("Export outputPath must be relative to the project root.");
146
+ }
147
+ const projectRoot = path.resolve(projectPath);
148
+ const filePath = path.resolve(projectRoot, outputPath);
149
+ const relative = path.relative(projectRoot, filePath);
150
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
151
+ throw new Error("Export outputPath must stay inside the project root.");
152
+ }
153
+ return filePath;
154
+ }
140
155
  }
141
156
  //# sourceMappingURL=memory-manager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-manager.js","sourceRoot":"","sources":["../../src/core/memory-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory-manager.js","sourceRoot":"","sources":["../../src/core/memory-manager.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ import fs from "fs-extra";
4
4
  import path from "path";
5
5
  import { deduplicateObservations, fuzzyMatchScore } from "../utils/similarity.js";
6
6
  import { MemoryManager, MEMORY_FILE_PATH, CONTEXT_FILE_PATH } from "./memory-manager.js";
7
+ import { formatProjectPath, logError, logInfo } from "../utils/logging.js";
7
8
  /**
8
9
  * MemoryService - High reliability dual-buffer memory manager.
9
10
  * - Snapshot Buffer: Instant in-memory reads for both Graph and Context.
@@ -38,7 +39,7 @@ export class MemoryService {
38
39
  * Gracefully shuts down the service, releasing all active file locks.
39
40
  */
40
41
  async shutdown() {
41
- console.error(`[MemoryService] Shutting down, releasing ${this.activeLocks.size} locks...`);
42
+ logInfo(`[MemoryService] Shutting down, releasing ${this.activeLocks.size} locks...`);
42
43
  const lockReleases = Array.from(this.activeLocks.values());
43
44
  await Promise.all(lockReleases.map(release => release().catch(() => { })));
44
45
  this.activeLocks.clear();
@@ -77,61 +78,47 @@ export class MemoryService {
77
78
  async loadSnapshot(projectPath) {
78
79
  this.touchProject(projectPath);
79
80
  const filePath = path.resolve(projectPath, MEMORY_FILE_PATH);
80
- try {
81
- if (await fs.pathExists(filePath)) {
82
- const stats = await fs.stat(filePath);
83
- const currentMtime = stats.mtimeMs;
84
- if (this.snapshots.has(projectPath) && this.lastMtimes.get(projectPath) === currentMtime) {
85
- return this.snapshots.get(projectPath);
86
- }
87
- const graph = await MemoryManager.readGraph(projectPath);
88
- this.snapshots.set(projectPath, graph);
89
- this.lastMtimes.set(projectPath, currentMtime);
90
- return graph;
81
+ if (await fs.pathExists(filePath)) {
82
+ const stats = await fs.stat(filePath);
83
+ const currentMtime = stats.mtimeMs;
84
+ if (this.snapshots.has(projectPath) && this.lastMtimes.get(projectPath) === currentMtime) {
85
+ return this.snapshots.get(projectPath);
91
86
  }
92
- }
93
- catch (error) {
94
- console.error(`[MemoryService] Failed to load graph snapshot: ${error}`);
87
+ const graph = await MemoryManager.readGraph(projectPath);
88
+ this.snapshots.set(projectPath, graph);
89
+ this.lastMtimes.set(projectPath, currentMtime);
90
+ return graph;
95
91
  }
96
92
  const emptyGraph = { entities: [], relations: [] };
97
93
  this.snapshots.set(projectPath, emptyGraph);
94
+ this.lastMtimes.delete(projectPath);
98
95
  return emptyGraph;
99
96
  }
100
97
  async getGraph(projectPath) {
101
- if (!this.snapshots.has(projectPath)) {
102
- return await this.loadSnapshot(projectPath);
103
- }
104
- return this.snapshots.get(projectPath);
98
+ return await this.loadSnapshot(projectPath);
105
99
  }
106
100
  // --- Context Operations ---
107
101
  async loadContextSnapshot(projectPath) {
108
102
  this.touchProject(projectPath);
109
103
  const filePath = path.resolve(projectPath, CONTEXT_FILE_PATH);
110
- try {
111
- if (await fs.pathExists(filePath)) {
112
- const stats = await fs.stat(filePath);
113
- const currentMtime = stats.mtimeMs;
114
- if (this.contextSnapshots.has(projectPath) && this.contextMtimes.get(projectPath) === currentMtime) {
115
- return this.contextSnapshots.get(projectPath);
116
- }
117
- const context = await MemoryManager.readContext(projectPath);
118
- this.contextSnapshots.set(projectPath, context);
119
- this.contextMtimes.set(projectPath, currentMtime);
120
- return context;
104
+ if (await fs.pathExists(filePath)) {
105
+ const stats = await fs.stat(filePath);
106
+ const currentMtime = stats.mtimeMs;
107
+ if (this.contextSnapshots.has(projectPath) && this.contextMtimes.get(projectPath) === currentMtime) {
108
+ return this.contextSnapshots.get(projectPath);
121
109
  }
122
- }
123
- catch (error) {
124
- console.error(`[MemoryService] Failed to load context snapshot: ${error}`);
110
+ const context = await MemoryManager.readContext(projectPath);
111
+ this.contextSnapshots.set(projectPath, context);
112
+ this.contextMtimes.set(projectPath, currentMtime);
113
+ return context;
125
114
  }
126
115
  const defaultContext = { status: "PLANNING", nextSteps: [] };
127
116
  this.contextSnapshots.set(projectPath, defaultContext);
117
+ this.contextMtimes.delete(projectPath);
128
118
  return defaultContext;
129
119
  }
130
120
  async getContext(projectPath) {
131
- if (!this.contextSnapshots.has(projectPath)) {
132
- return await this.loadContextSnapshot(projectPath);
133
- }
134
- return this.contextSnapshots.get(projectPath);
121
+ return await this.loadContextSnapshot(projectPath);
135
122
  }
136
123
  /**
137
124
  * Updates project context with independent locking.
@@ -169,11 +156,33 @@ export class MemoryService {
169
156
  });
170
157
  }
171
158
  // --- Combined Operations ---
159
+ /**
160
+ * Protocol reminder prefix for nextSteps.
161
+ * Used to inject and identify protocol reminders in context.
162
+ */
163
+ static PROTOCOL_PREFIX = "[N2N-SYNC]";
164
+ static PROTOCOL_REMINDER = `${MemoryService.PROTOCOL_PREFIX} Before 'git commit': call n2n_update_context to sync your progress.`;
165
+ /**
166
+ * Injects protocol reminder into nextSteps if not already present.
167
+ * Preserves user-defined nextSteps while ensuring protocol visibility.
168
+ */
169
+ injectProtocolReminder(context) {
170
+ const hasProtocolReminder = context.nextSteps.some(step => step.startsWith(MemoryService.PROTOCOL_PREFIX));
171
+ if (!hasProtocolReminder) {
172
+ return {
173
+ ...context,
174
+ nextSteps: [MemoryService.PROTOCOL_REMINDER, ...context.nextSteps]
175
+ };
176
+ }
177
+ return context;
178
+ }
172
179
  async getCompleteState(projectPath, options = {}) {
173
- const [graph, context] = await Promise.all([
180
+ const [graph, rawContext] = await Promise.all([
174
181
  this.getGraph(projectPath),
175
182
  this.getContext(projectPath)
176
183
  ]);
184
+ // Inject protocol reminder into context.nextSteps
185
+ const context = this.injectProtocolReminder(rawContext);
177
186
  const totalEntityCount = graph.entities.length;
178
187
  const offset = options.offset || 0;
179
188
  const limit = options.limit || totalEntityCount;
@@ -232,7 +241,7 @@ export class MemoryService {
232
241
  this.snapshots.set(projectPath, currentGraph);
233
242
  }
234
243
  catch (error) {
235
- console.error(`[MemoryService] Write operation failed for ${projectPath}:`, error);
244
+ logError(`[MemoryService] Write operation failed for ${formatProjectPath(projectPath)}.`, error);
236
245
  throw error;
237
246
  }
238
247
  finally {
@@ -282,6 +291,17 @@ export class MemoryService {
282
291
  }
283
292
  async createRelations(projectPath, relations) {
284
293
  await this.executeWrite(projectPath, (graph) => {
294
+ const entityNames = new Set(graph.entities.map(entity => entity.name));
295
+ const missingNames = new Set();
296
+ for (const relation of relations) {
297
+ if (!entityNames.has(relation.from))
298
+ missingNames.add(relation.from);
299
+ if (!entityNames.has(relation.to))
300
+ missingNames.add(relation.to);
301
+ }
302
+ if (missingNames.size > 0) {
303
+ throw new Error(`Cannot create relations with missing entities: ${Array.from(missingNames).sort().join(", ")}`);
304
+ }
285
305
  relations.forEach(newRel => {
286
306
  const exists = graph.relations.some(r => r.from === newRel.from && r.to === newRel.to && r.relationType === newRel.relationType);
287
307
  if (!exists) {