@lotargo/memory_plugin 1.6.2 → 1.6.3
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 +30 -3
- package/README.md +57 -20
- package/mcp-server/cli/direct_commands.js +10 -1
- package/mcp-server/cli.js +2 -1
- package/mcp-server/codex_config.js +171 -0
- package/mcp-server/codex_diagnostics.js +263 -0
- package/mcp-server/index.js +4 -2
- package/mcp-server/prompt_manager.js +19 -17
- package/mcp-server/setup.js +37 -21
- package/package.json +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,9 +3,36 @@
|
|
|
3
3
|
All notable changes to `@lotargo/memory_plugin` are documented in this file.
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.6.3] - 2026-08-13
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `memory_plugin doctor --codex` validates the configured executable and Node.js
|
|
15
|
+
version, performs direct MCP `initialize` / `tools/list`, and calls both
|
|
16
|
+
`memory_info` and `recall(scope="all")`.
|
|
17
|
+
- Codex regression coverage for Windows paths with spaces, legacy config migration,
|
|
18
|
+
duplicate removal, standalone MCP protocol smoke testing, two-repository project
|
|
19
|
+
isolation, global-only recall outside Git, and prompt idempotency.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Codex setup no longer launches the memory MCP through `npx` or a Windows `.cmd`
|
|
24
|
+
shim. It writes absolute paths for the active Node executable and packaged
|
|
25
|
+
`mcp-server/boot.js` entry point after validating Node.js >= 22.5.0.
|
|
26
|
+
- Existing plugin-owned `[mcp_servers.memory-agent]` sections, including the legacy
|
|
27
|
+
`opencode-memory-plugin` entry, are migrated in place without modifying unrelated
|
|
28
|
+
TOML sections or creating duplicates.
|
|
29
|
+
- Repeated prompt setup now collapses duplicate plugin-owned blocks while preserving
|
|
30
|
+
user-authored AGENTS.md / CLAUDE.md content.
|
|
31
|
+
- README tool counts now match the implementations: 15 MCP tools, 16 native
|
|
32
|
+
OpenCode tools, and 17 unique tool names across both surfaces. The OpenCode tool
|
|
33
|
+
list and the MCP-only status of `batch_query_knowledge_base` are documented explicitly.
|
|
34
|
+
|
|
35
|
+
## [1.6.2] - 2026-08-12
|
|
9
36
|
|
|
10
37
|
### Added
|
|
11
38
|
|
package/README.md
CHANGED
|
@@ -77,9 +77,25 @@ memory_plugin setup --opencode
|
|
|
77
77
|
# Claude Code
|
|
78
78
|
memory_plugin setup --claude
|
|
79
79
|
|
|
80
|
-
# Codex
|
|
81
|
-
memory_plugin setup --codex
|
|
82
|
-
```
|
|
80
|
+
# Codex
|
|
81
|
+
memory_plugin setup --codex
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Codex is configured with a direct executable chain (`node` → `mcp-server/boot.js`),
|
|
85
|
+
not `npx`. This avoids Windows stdio handshake failures caused by `.cmd` launchers.
|
|
86
|
+
Running setup again safely migrates legacy `npx` entries and preserves unrelated
|
|
87
|
+
sections in `~/.codex/config.toml`.
|
|
88
|
+
|
|
89
|
+
To verify registration, the Node runtime, MCP initialization, tool discovery, and
|
|
90
|
+
real `memory_info` / `recall(scope="all")` calls:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
memory_plugin doctor --codex
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The doctor checks the server process itself. If it passes but a fresh Codex Desktop
|
|
97
|
+
task still does not expose the tools, the remaining fault is in Desktop tool
|
|
98
|
+
exposure rather than the memory MCP launcher.
|
|
83
99
|
|
|
84
100
|
`setup` also accepts `--gemini` (alias for Antigravity) and `--local` (registers the MCP server in the project-local `.agents/` directory). Without a specific flag, all detected environments are configured.
|
|
85
101
|
|
|
@@ -107,7 +123,7 @@ memory_plugin setup --mode only-cloud
|
|
|
107
123
|
- **Project Identity**: Project stores are bound to a **Git-based project identity** — the normalized remote URL (`git:github.com/owner/repo`) or `git:local:<repo basename>` — never to a directory path. Memories follow the repository across machines, OSes, and subdirectories. Legacy path/basename stores can be linked and merged via `link_project_memory`.
|
|
108
124
|
|
|
109
125
|
2. **Layer 2: RAG Knowledge Base (Technical Documents & Codebases)**
|
|
110
|
-
- **Tools**: `ingest_document`, `query_knowledge_base`, `manage_knowledge_base`, `reindex_knowledge_base`
|
|
126
|
+
- **Tools**: `ingest_document`, `query_knowledge_base`, `batch_query_knowledge_base`, `manage_knowledge_base`, `reindex_knowledge_base`
|
|
111
127
|
- **Capabilities**: Ingests raw text files, Markdown, HTML, Web URLs, office documents (PDF, DOCX, XLSX, CSV), and codebases.
|
|
112
128
|
- **Engine Components**: 3-tier hierarchy chunking (Big / Medium / Small), SQLite FTS5 BM25 search, ONNX dense vector embeddings (`multilingual-e5-small`), Reciprocal Rank Fusion (RRF / RSF), cross-encoder reranking (optional), and GraphRAG Lite code symbol extraction.
|
|
113
129
|
|
|
@@ -147,11 +163,22 @@ Cloud authentication tokens are never written to `config.json`. They are stored
|
|
|
147
163
|
|
|
148
164
|
---
|
|
149
165
|
|
|
150
|
-
## Available
|
|
151
|
-
|
|
152
|
-
The
|
|
153
|
-
|
|
154
|
-
|
|
166
|
+
## Available Tools
|
|
167
|
+
|
|
168
|
+
The package currently exposes **17 unique tool names** across its two integration surfaces:
|
|
169
|
+
|
|
170
|
+
| Integration surface | Tool count | Composition |
|
|
171
|
+
| :------------------ | ---------: | :---------- |
|
|
172
|
+
| **MCP server** (Codex, Claude Code, Antigravity / Gemini CLI and other MCP clients) | **15** | 6 Notebook + 4 identity/graph + 5 RAG tools |
|
|
173
|
+
| **Native OpenCode plugin** | **16** | 14 shared memory/identity/RAG tools + 2 OpenCode-only helpers |
|
|
174
|
+
|
|
175
|
+
The two surfaces are intentionally counted separately. The OpenCode plugin adds
|
|
176
|
+
`list-mcp-tools` and `mcp-reminder`, but currently does not register the MCP-only
|
|
177
|
+
`batch_query_knowledge_base` tool.
|
|
178
|
+
|
|
179
|
+
### MCP Server Tools (15)
|
|
180
|
+
|
|
181
|
+
#### 1. Memory Notebook Tools (Layer 1)
|
|
155
182
|
|
|
156
183
|
| Tool | Scope / Target | Key Parameters | Description |
|
|
157
184
|
| :--- | :------------- | :------------- | :---------- |
|
|
@@ -162,7 +189,7 @@ The MCP server registers **14 MCP tools** accessible across all connected AI env
|
|
|
162
189
|
| `forget` | `project` / `global` | `id` / `range` / `query`, `scope`, `force` | Remove a fact by index number, ID, range (e.g. `"3-30"`), or query. Requires `force: true` for protected (`[KEEP]`) facts. |
|
|
163
190
|
| `memory_info` | - | - | Show storage paths, fact counts, RAG statistics, git identity bindings, and package version. |
|
|
164
191
|
|
|
165
|
-
|
|
192
|
+
#### 2. Project Identity Tools
|
|
166
193
|
|
|
167
194
|
| Tool | Key Parameters | Description |
|
|
168
195
|
| :--- | :------------- | :---------- |
|
|
@@ -170,7 +197,7 @@ The MCP server registers **14 MCP tools** accessible across all connected AI env
|
|
|
170
197
|
| `unlink_project_memory` | `directory`, `purge` | Remove a path alias binding for a directory. Optionally purge the project identity entry if `purge: true`. |
|
|
171
198
|
| `relink_project_memory` | `directory`, `remote` | Switch a project's primary identity to a new remote URL and merge all stored facts into the target store with fact-text deduplication. |
|
|
172
199
|
|
|
173
|
-
|
|
200
|
+
#### 3. RAG Knowledge Base & Graph Tools (Layers 2 & 3)
|
|
174
201
|
|
|
175
202
|
| Tool | Key Parameters | Description |
|
|
176
203
|
| :--- | :------------- | :---------- |
|
|
@@ -181,12 +208,21 @@ The MCP server registers **14 MCP tools** accessible across all connected AI env
|
|
|
181
208
|
| `reindex_knowledge_base` | `model`, `dimension` | Re-embed all stored vectors with the active (or specified) embedding model and vector dimension. Use after switching the embedding model or vector dimension so previously indexed documents remain retrievable. Preserves documents, FTS index, graph edges, and fact links. |
|
|
182
209
|
| `link_knowledge` | `action`, `factText`, `docId`, `scope`, `startLine`, `endLine`, `relationType` | Create, list, or retrieve semantic graph links connecting Notebook facts to Knowledge Base documents, sections, or line ranges. Actions: `link`, `list_links`, `get_doc_links`. |
|
|
183
210
|
|
|
184
|
-
###
|
|
185
|
-
|
|
186
|
-
|
|
|
187
|
-
|
|
|
188
|
-
|
|
|
189
|
-
|
|
|
211
|
+
### Native OpenCode Plugin Tools (16)
|
|
212
|
+
|
|
213
|
+
| Group | Count | Tools |
|
|
214
|
+
| :---- | ----: | :---- |
|
|
215
|
+
| **Memory Notebook** | 6 | `remember`, `recall`, `get_fact`, `forget`, `update_fact`, `memory_info` |
|
|
216
|
+
| **Project Identity & Knowledge Graph** | 4 | `link_knowledge`, `link_project_memory`, `unlink_project_memory`, `relink_project_memory` |
|
|
217
|
+
| **RAG Knowledge Base** | 4 | `ingest_document`, `query_knowledge_base`, `manage_knowledge_base`, `reindex_knowledge_base` |
|
|
218
|
+
| **OpenCode-only helpers** | 2 | `list-mcp-tools`, `mcp-reminder` |
|
|
219
|
+
|
|
220
|
+
The OpenCode-only helpers have the following purpose:
|
|
221
|
+
|
|
222
|
+
| Tool | Key Parameters | Description |
|
|
223
|
+
| :--- | :------------- | :---------- |
|
|
224
|
+
| `list-mcp-tools` | - | Discover connected MCP servers and their intended use cases. |
|
|
225
|
+
| `mcp-reminder` | `task` | Recommend the appropriate MCP tool or server for a developer task. |
|
|
190
226
|
|
|
191
227
|
---
|
|
192
228
|
|
|
@@ -216,7 +252,8 @@ Both binaries accept the same commands. `memory_plugin` with **no** command star
|
|
|
216
252
|
| **`identity`** | `--dir <path>` | Inspects Git project identity key, primary remote, name, and toplevel path for a directory. |
|
|
217
253
|
| **`migrate_titles`** | `--key <key>` | Auto-generates `**Title**` prefixes for legacy facts without titles. |
|
|
218
254
|
| **`enable-prompt`** | - | Injects memory agent instructions into client agent files (`AGENTS.md`, `CLAUDE.md`). |
|
|
219
|
-
| **`disable-prompt`** | - | Removes memory agent instructions from client agent files. |
|
|
255
|
+
| **`disable-prompt`** | - | Removes memory agent instructions from client agent files. |
|
|
256
|
+
| **`doctor`** | `--codex` | Validates Codex config, direct Node runtime, MCP initialize/tools/list, and live `memory_info` + `recall` calls. |
|
|
220
257
|
| **`login`** | `--api-token`, `--from-env`, `--db-url <URL> --db-token`, `$TURSO_API_TOKEN`, `$TURSO_DB_TOKEN` | Authenticates with Turso Cloud via API token, direct DB token, or environment variables. Token values are read from the environment or a hidden stdin prompt. |
|
|
221
258
|
| **`logout`** | `--api-key` | Signs out of Turso Cloud or removes stored API key while retaining DB session. |
|
|
222
259
|
| **`auth-status`** | - | Displays authentication source, endpoint URL, username, organization, database, and sync mode. |
|
|
@@ -295,7 +332,7 @@ During `ingest_document`, code symbols are extracted from code blocks using fast
|
|
|
295
332
|
| Platform | Status | Configuration Mechanism |
|
|
296
333
|
| :--- | :--- | :--- |
|
|
297
334
|
| **Antigravity / Gemini CLI** | Supported | MCP Server (`~/.gemini/config/mcp_config.json` & `.agents/mcp_config.json`) |
|
|
298
|
-
| **OpenCode** | Native | Native plugin
|
|
335
|
+
| **OpenCode** | Native | Native plugin with 16 tools (`~/.config/opencode/opencode.json`) |
|
|
299
336
|
| **Claude Code** | Supported | MCP Server (`~/.claude.json`) |
|
|
300
337
|
| **Codex** | Supported | MCP Server (`~/.codex/config.toml`) |
|
|
301
338
|
| **Google Jules** | Supported | MCP Server via global install + setup (`memory_plugin setup`) |
|
|
@@ -339,7 +376,7 @@ The engine is configured through `<memory-dir>/config.json` (created with defaul
|
|
|
339
376
|
To run the automated test suite and benchmarks locally, from the repository root:
|
|
340
377
|
|
|
341
378
|
```bash
|
|
342
|
-
# Unit + integration + cloud suites (
|
|
379
|
+
# Unit + integration + cloud suites (16 files) — fast and fully offline
|
|
343
380
|
npm test
|
|
344
381
|
|
|
345
382
|
# End-to-end smoke test with REAL ONNX embeddings — run before a release
|
|
@@ -11,7 +11,16 @@ import {
|
|
|
11
11
|
} from "../memory.js";
|
|
12
12
|
import { factBody } from "../fact_format.js";
|
|
13
13
|
|
|
14
|
-
export async function handleDirectCommands(cliArgs) {
|
|
14
|
+
export async function handleDirectCommands(cliArgs) {
|
|
15
|
+
if (cliArgs[0] === "doctor" && cliArgs.includes("--codex")) {
|
|
16
|
+
const { runCodexDoctor } = await import("../codex_diagnostics.js");
|
|
17
|
+
console.log("\nCodex memory-agent diagnostics\n");
|
|
18
|
+
const result = await runCodexDoctor();
|
|
19
|
+
console.log("");
|
|
20
|
+
if (!result.ok) process.exitCode = 1;
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
if (cliArgs[0] === "link") {
|
|
16
25
|
const dirIdx = cliArgs.indexOf("--dir");
|
|
17
26
|
const dir = dirIdx >= 0 && cliArgs[dirIdx + 1] ? cliArgs[dirIdx + 1] : process.cwd();
|
package/mcp-server/cli.js
CHANGED
|
@@ -23,7 +23,8 @@ Usage:
|
|
|
23
23
|
memory-cli auth-status
|
|
24
24
|
memory-cli link|unlink|relink|identity [--dir <path>] [--remote <url>]
|
|
25
25
|
memory-cli migrate_titles [--key <key>]
|
|
26
|
-
memory-cli enable-prompt | disable-prompt
|
|
26
|
+
memory-cli enable-prompt | disable-prompt
|
|
27
|
+
memory-cli doctor --codex
|
|
27
28
|
|
|
28
29
|
Options:
|
|
29
30
|
-h, --help Show this help text`);
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
export const MIN_NODE_VERSION = Object.freeze({ major: 22, minor: 5, patch: 0 });
|
|
4
|
+
export const DEFAULT_CODEX_STARTUP_TIMEOUT_SEC = 60;
|
|
5
|
+
|
|
6
|
+
function parseVersion(version) {
|
|
7
|
+
const match = String(version || "").trim().replace(/^v/i, "").match(/^(\d+)\.(\d+)(?:\.(\d+))?/);
|
|
8
|
+
if (!match) return null;
|
|
9
|
+
return {
|
|
10
|
+
major: Number(match[1]),
|
|
11
|
+
minor: Number(match[2]),
|
|
12
|
+
patch: Number(match[3] || 0),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isSupportedNodeVersion(version) {
|
|
17
|
+
const parsed = parseVersion(version);
|
|
18
|
+
if (!parsed) return false;
|
|
19
|
+
if (parsed.major !== MIN_NODE_VERSION.major) return parsed.major > MIN_NODE_VERSION.major;
|
|
20
|
+
if (parsed.minor !== MIN_NODE_VERSION.minor) return parsed.minor > MIN_NODE_VERSION.minor;
|
|
21
|
+
return parsed.patch >= MIN_NODE_VERSION.patch;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function validateCodexRuntime({
|
|
25
|
+
nodePath,
|
|
26
|
+
nodeVersion = process.versions.node,
|
|
27
|
+
bootPath,
|
|
28
|
+
pathExists = existsSync,
|
|
29
|
+
} = {}) {
|
|
30
|
+
const errors = [];
|
|
31
|
+
if (!nodePath || !pathExists(nodePath)) {
|
|
32
|
+
errors.push(`Node executable not found: ${nodePath || "<empty>"}`);
|
|
33
|
+
}
|
|
34
|
+
if (!isSupportedNodeVersion(nodeVersion)) {
|
|
35
|
+
errors.push(
|
|
36
|
+
`@lotargo/memory_plugin requires Node.js >= ${MIN_NODE_VERSION.major}.${MIN_NODE_VERSION.minor}.0; detected ${nodeVersion || "unknown"}`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
if (!bootPath || !pathExists(bootPath)) {
|
|
40
|
+
errors.push(`MCP boot entry point not found: ${bootPath || "<empty>"}`);
|
|
41
|
+
}
|
|
42
|
+
return { ok: errors.length === 0, errors, nodePath, nodeVersion, bootPath };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function escapeTomlBasicString(value) {
|
|
46
|
+
return String(value)
|
|
47
|
+
.replace(/\\/g, "\\\\")
|
|
48
|
+
.replace(/"/g, '\\"')
|
|
49
|
+
.replace(/\u0008/g, "\\b")
|
|
50
|
+
.replace(/\t/g, "\\t")
|
|
51
|
+
.replace(/\n/g, "\\n")
|
|
52
|
+
.replace(/\f/g, "\\f")
|
|
53
|
+
.replace(/\r/g, "\\r");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function buildCodexMemoryAgentSection({
|
|
57
|
+
nodePath,
|
|
58
|
+
bootPath,
|
|
59
|
+
startupTimeoutSec = DEFAULT_CODEX_STARTUP_TIMEOUT_SEC,
|
|
60
|
+
} = {}) {
|
|
61
|
+
if (!nodePath || !bootPath) throw new Error("nodePath and bootPath are required");
|
|
62
|
+
if (!Number.isInteger(startupTimeoutSec) || startupTimeoutSec <= 0) {
|
|
63
|
+
throw new Error("startupTimeoutSec must be a positive integer");
|
|
64
|
+
}
|
|
65
|
+
return [
|
|
66
|
+
"[mcp_servers.memory-agent]",
|
|
67
|
+
`command = "${escapeTomlBasicString(nodePath)}"`,
|
|
68
|
+
`args = ["${escapeTomlBasicString(bootPath)}"]`,
|
|
69
|
+
`startup_timeout_sec = ${startupTimeoutSec}`,
|
|
70
|
+
].join("\n");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function tableHeaderName(line) {
|
|
74
|
+
const match = String(line).match(/^\s*\[\s*([^\[\]]+?)\s*\]\s*(?:#.*)?$/);
|
|
75
|
+
return match ? match[1] : null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isMemoryAgentHeader(name, { exact = false } = {}) {
|
|
79
|
+
if (!name) return false;
|
|
80
|
+
const suffix = exact ? "$" : "(?:\\s*\\.|$)";
|
|
81
|
+
return new RegExp(
|
|
82
|
+
`^mcp_servers\\s*\\.\\s*(?:memory-agent|"memory-agent"|'memory-agent')${suffix}`,
|
|
83
|
+
"i"
|
|
84
|
+
).test(name);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function isMemoryPluginOwnedSection(sectionText) {
|
|
88
|
+
return /@lotargo[\\/]memory_plugin|opencode-memory-plugin|(?:^|[\\/])memory_plugin(?:[\\/]|\b)|mcp-server[\\/]+boot\.js/i.test(
|
|
89
|
+
String(sectionText || "")
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function sectionRanges(lines) {
|
|
94
|
+
const headers = [];
|
|
95
|
+
for (let i = 0; i < lines.length; i++) {
|
|
96
|
+
const name = tableHeaderName(lines[i]);
|
|
97
|
+
if (name) headers.push({ start: i, name });
|
|
98
|
+
}
|
|
99
|
+
return headers.map((header, index) => ({
|
|
100
|
+
...header,
|
|
101
|
+
end: index + 1 < headers.length ? headers[index + 1].start : lines.length,
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getCodexMemoryAgentSections(content) {
|
|
106
|
+
const lines = String(content || "").split(/\r?\n/);
|
|
107
|
+
return sectionRanges(lines)
|
|
108
|
+
.filter((range) => isMemoryAgentHeader(range.name))
|
|
109
|
+
.map((range) => ({
|
|
110
|
+
...range,
|
|
111
|
+
exact: isMemoryAgentHeader(range.name, { exact: true }),
|
|
112
|
+
text: lines.slice(range.start, range.end).join("\n").trimEnd(),
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function updateCodexMemoryAgentConfig(content, options) {
|
|
117
|
+
const source = String(content || "");
|
|
118
|
+
const eol = source.includes("\r\n") ? "\r\n" : "\n";
|
|
119
|
+
const lines = source.split(/\r?\n/);
|
|
120
|
+
const desired = buildCodexMemoryAgentSection(options).split("\n");
|
|
121
|
+
const ranges = sectionRanges(lines);
|
|
122
|
+
const targets = ranges.filter((range) => isMemoryAgentHeader(range.name));
|
|
123
|
+
const exactTargets = targets.filter((range) => isMemoryAgentHeader(range.name, { exact: true }));
|
|
124
|
+
|
|
125
|
+
const unowned = exactTargets.filter((range) => {
|
|
126
|
+
const text = lines.slice(range.start, range.end).join("\n");
|
|
127
|
+
return !isMemoryPluginOwnedSection(text);
|
|
128
|
+
});
|
|
129
|
+
if (unowned.length > 0) {
|
|
130
|
+
return {
|
|
131
|
+
content: source,
|
|
132
|
+
changed: false,
|
|
133
|
+
status: "conflict",
|
|
134
|
+
reason: "Existing [mcp_servers.memory-agent] section is not recognized as owned by @lotargo/memory_plugin",
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (targets.length === 0) {
|
|
139
|
+
const prefix = source.length === 0 ? "" : source.replace(/[\r\n]+$/, "") + eol + eol;
|
|
140
|
+
return { content: prefix + desired.join(eol) + eol, changed: true, status: "added" };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const targetStarts = new Map(targets.map((target) => [target.start, target]));
|
|
144
|
+
const targetLineIndexes = new Set();
|
|
145
|
+
for (const target of targets) {
|
|
146
|
+
for (let i = target.start; i < target.end; i++) targetLineIndexes.add(i);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const firstStart = Math.min(...targets.map((target) => target.start));
|
|
150
|
+
const result = [];
|
|
151
|
+
for (let i = 0; i < lines.length; i++) {
|
|
152
|
+
if (i === firstStart) {
|
|
153
|
+
while (result.length > 0 && result[result.length - 1] === "") result.pop();
|
|
154
|
+
if (result.length > 0) result.push("");
|
|
155
|
+
result.push(...desired, "");
|
|
156
|
+
}
|
|
157
|
+
if (targetLineIndexes.has(i)) continue;
|
|
158
|
+
if (targetStarts.has(i)) continue;
|
|
159
|
+
result.push(lines[i]);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
while (result.length > 1 && result[result.length - 1] === "" && result[result.length - 2] === "") {
|
|
163
|
+
result.pop();
|
|
164
|
+
}
|
|
165
|
+
const updated = result.join(eol);
|
|
166
|
+
return {
|
|
167
|
+
content: updated,
|
|
168
|
+
changed: updated !== source,
|
|
169
|
+
status: updated === source ? "unchanged" : "migrated",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { getCodexMemoryAgentSections, isSupportedNodeVersion } from "./codex_config.js";
|
|
6
|
+
|
|
7
|
+
function decodeTomlString(raw) {
|
|
8
|
+
const value = String(raw || "").trim();
|
|
9
|
+
if (value.startsWith('"')) {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(value);
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1).replace(/''/g, "'");
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function parseCodexMemoryAgentConfig(content) {
|
|
21
|
+
const exact = getCodexMemoryAgentSections(content).filter((section) => section.exact);
|
|
22
|
+
if (exact.length !== 1) {
|
|
23
|
+
return {
|
|
24
|
+
ok: false,
|
|
25
|
+
error: exact.length === 0
|
|
26
|
+
? "[mcp_servers.memory-agent] section not found"
|
|
27
|
+
: `Found ${exact.length} memory-agent sections`,
|
|
28
|
+
sectionCount: exact.length,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const text = exact[0].text;
|
|
33
|
+
const commandMatch = text.match(/^\s*command\s*=\s*(.+?)\s*$/m);
|
|
34
|
+
const argsMatch = text.match(/^\s*args\s*=\s*\[([\s\S]*?)\]\s*$/m);
|
|
35
|
+
const timeoutMatch = text.match(/^\s*startup_timeout_sec\s*=\s*(\d+)\s*$/m);
|
|
36
|
+
const command = decodeTomlString(commandMatch?.[1]);
|
|
37
|
+
const args = [];
|
|
38
|
+
if (argsMatch) {
|
|
39
|
+
const stringPattern = /"(?:\\.|[^"\\])*"|'(?:''|[^'])*'/g;
|
|
40
|
+
for (const match of argsMatch[1].matchAll(stringPattern)) {
|
|
41
|
+
const decoded = decodeTomlString(match[0]);
|
|
42
|
+
if (decoded === null) return { ok: false, error: "Unable to parse memory-agent args", sectionCount: 1 };
|
|
43
|
+
args.push(decoded);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!command) return { ok: false, error: "Unable to parse memory-agent command", sectionCount: 1 };
|
|
48
|
+
if (!argsMatch) return { ok: false, error: "Unable to parse memory-agent args", sectionCount: 1 };
|
|
49
|
+
return {
|
|
50
|
+
ok: true,
|
|
51
|
+
command,
|
|
52
|
+
args,
|
|
53
|
+
startupTimeoutSec: timeoutMatch ? Number(timeoutMatch[1]) : null,
|
|
54
|
+
sectionCount: 1,
|
|
55
|
+
text,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function toolText(result) {
|
|
60
|
+
return (result?.content || [])
|
|
61
|
+
.filter((item) => item.type === "text")
|
|
62
|
+
.map((item) => item.text)
|
|
63
|
+
.join("\n");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function runDirectMcpSmoke({
|
|
67
|
+
command,
|
|
68
|
+
args = [],
|
|
69
|
+
cwd = process.cwd(),
|
|
70
|
+
env = process.env,
|
|
71
|
+
timeoutMs = 30_000,
|
|
72
|
+
} = {}) {
|
|
73
|
+
if (!command) throw new Error("MCP command is required");
|
|
74
|
+
const child = spawn(command, args, { cwd, env: { ...env }, stdio: ["pipe", "pipe", "pipe"] });
|
|
75
|
+
child.stdout.setEncoding("utf8");
|
|
76
|
+
child.stderr.setEncoding("utf8");
|
|
77
|
+
|
|
78
|
+
let nextId = 0;
|
|
79
|
+
let stdoutBuffer = "";
|
|
80
|
+
let stderr = "";
|
|
81
|
+
let settled = false;
|
|
82
|
+
const pending = new Map();
|
|
83
|
+
|
|
84
|
+
const rejectAll = (error) => {
|
|
85
|
+
for (const { reject } of pending.values()) reject(error);
|
|
86
|
+
pending.clear();
|
|
87
|
+
};
|
|
88
|
+
child.stderr.on("data", (chunk) => { stderr += chunk; });
|
|
89
|
+
child.stdout.on("data", (chunk) => {
|
|
90
|
+
stdoutBuffer += chunk;
|
|
91
|
+
let newline;
|
|
92
|
+
while ((newline = stdoutBuffer.indexOf("\n")) >= 0) {
|
|
93
|
+
const line = stdoutBuffer.slice(0, newline).trim();
|
|
94
|
+
stdoutBuffer = stdoutBuffer.slice(newline + 1);
|
|
95
|
+
if (!line) continue;
|
|
96
|
+
let message;
|
|
97
|
+
try {
|
|
98
|
+
message = JSON.parse(line);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
rejectAll(new Error(`Invalid JSON-RPC output: ${error.message}; line=${line.slice(0, 200)}`));
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const waiter = pending.get(message.id);
|
|
104
|
+
if (!waiter) continue;
|
|
105
|
+
pending.delete(message.id);
|
|
106
|
+
if (message.error) waiter.reject(new Error(message.error.message || JSON.stringify(message.error)));
|
|
107
|
+
else waiter.resolve(message.result);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
child.on("error", (error) => rejectAll(error));
|
|
111
|
+
child.on("exit", (code, signal) => {
|
|
112
|
+
if (!settled && pending.size > 0) {
|
|
113
|
+
rejectAll(new Error(`MCP server exited before completing diagnostics (code=${code}, signal=${signal}): ${stderr.trim()}`));
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const request = (method, params = {}) => {
|
|
118
|
+
const id = ++nextId;
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
pending.set(id, { resolve, reject });
|
|
121
|
+
child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n");
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
const withTimeout = (promise, label) => new Promise((resolve, reject) => {
|
|
125
|
+
const timer = setTimeout(() => reject(new Error(`Timed out waiting for ${label} after ${timeoutMs}ms`)), timeoutMs);
|
|
126
|
+
promise.then(
|
|
127
|
+
(value) => { clearTimeout(timer); resolve(value); },
|
|
128
|
+
(error) => { clearTimeout(timer); reject(error); }
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
await withTimeout(request("initialize", {
|
|
134
|
+
protocolVersion: "2024-11-05",
|
|
135
|
+
capabilities: {},
|
|
136
|
+
clientInfo: { name: "memory-plugin-codex-doctor", version: "1.0.0" },
|
|
137
|
+
}), "initialize");
|
|
138
|
+
child.stdin.write(JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized", params: {} }) + "\n");
|
|
139
|
+
|
|
140
|
+
const toolsResult = await withTimeout(request("tools/list"), "tools/list");
|
|
141
|
+
const toolNames = (toolsResult?.tools || []).map((tool) => tool.name);
|
|
142
|
+
for (const required of ["memory_info", "recall"]) {
|
|
143
|
+
if (!toolNames.includes(required)) throw new Error(`Required MCP tool is missing: ${required}`);
|
|
144
|
+
}
|
|
145
|
+
const infoResult = await withTimeout(
|
|
146
|
+
request("tools/call", { name: "memory_info", arguments: {} }),
|
|
147
|
+
"memory_info"
|
|
148
|
+
);
|
|
149
|
+
const recallResult = await withTimeout(
|
|
150
|
+
request("tools/call", { name: "recall", arguments: { scope: "all" } }),
|
|
151
|
+
"recall(scope=all)"
|
|
152
|
+
);
|
|
153
|
+
settled = true;
|
|
154
|
+
return {
|
|
155
|
+
ok: true,
|
|
156
|
+
toolNames,
|
|
157
|
+
memoryInfo: toolText(infoResult),
|
|
158
|
+
recall: toolText(recallResult),
|
|
159
|
+
stderr: stderr.trim(),
|
|
160
|
+
};
|
|
161
|
+
} finally {
|
|
162
|
+
settled = true;
|
|
163
|
+
rejectAll(new Error("MCP diagnostics finished"));
|
|
164
|
+
if (child.exitCode === null) child.kill();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function countOccurrences(text, needle) {
|
|
169
|
+
let count = 0;
|
|
170
|
+
let offset = 0;
|
|
171
|
+
while ((offset = text.indexOf(needle, offset)) !== -1) {
|
|
172
|
+
count++;
|
|
173
|
+
offset += needle.length;
|
|
174
|
+
}
|
|
175
|
+
return count;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function runCodexDoctor({
|
|
179
|
+
home = homedir(),
|
|
180
|
+
cwd = process.cwd(),
|
|
181
|
+
env = process.env,
|
|
182
|
+
output = console,
|
|
183
|
+
} = {}) {
|
|
184
|
+
const checks = [];
|
|
185
|
+
const record = (level, label, detail = "") => {
|
|
186
|
+
checks.push({ level, label, detail });
|
|
187
|
+
output.log(`[${level}] ${label}${detail ? `: ${detail}` : ""}`);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const configPath = join(home, ".codex", "config.toml");
|
|
191
|
+
if (!existsSync(configPath)) {
|
|
192
|
+
record("FAIL", "Codex config found", configPath);
|
|
193
|
+
return { ok: false, checks, configPath };
|
|
194
|
+
}
|
|
195
|
+
record("OK", "Codex config found", configPath);
|
|
196
|
+
const config = readFileSync(configPath, "utf8");
|
|
197
|
+
const parsed = parseCodexMemoryAgentConfig(config);
|
|
198
|
+
if (!parsed.ok) {
|
|
199
|
+
record("FAIL", "memory-agent configuration", parsed.error);
|
|
200
|
+
return { ok: false, checks, configPath };
|
|
201
|
+
}
|
|
202
|
+
record("OK", "Single memory-agent section found");
|
|
203
|
+
record(parsed.command.toLowerCase().includes("npx") ? "FAIL" : "OK", "Direct executable launcher", parsed.command);
|
|
204
|
+
record(existsSync(parsed.command) ? "OK" : "FAIL", "Command executable exists", parsed.command);
|
|
205
|
+
|
|
206
|
+
const bootPath = parsed.args[0];
|
|
207
|
+
record(bootPath && existsSync(bootPath) ? "OK" : "FAIL", "boot.js exists", bootPath || "missing first arg");
|
|
208
|
+
record(parsed.args.length === 1 ? "OK" : "FAIL", "boot.js is the only launcher argument", JSON.stringify(parsed.args));
|
|
209
|
+
|
|
210
|
+
let detectedVersion = null;
|
|
211
|
+
try {
|
|
212
|
+
detectedVersion = await new Promise((resolve, reject) => {
|
|
213
|
+
const child = spawn(parsed.command, ["--version"], { cwd, env: { ...env }, stdio: ["ignore", "pipe", "pipe"] });
|
|
214
|
+
let stdout = "";
|
|
215
|
+
let stderr = "";
|
|
216
|
+
child.stdout.setEncoding("utf8");
|
|
217
|
+
child.stderr.setEncoding("utf8");
|
|
218
|
+
child.stdout.on("data", (chunk) => { stdout += chunk; });
|
|
219
|
+
child.stderr.on("data", (chunk) => { stderr += chunk; });
|
|
220
|
+
child.on("error", reject);
|
|
221
|
+
child.on("exit", (code) => code === 0 ? resolve(stdout.trim()) : reject(new Error(stderr.trim() || `exit ${code}`)));
|
|
222
|
+
});
|
|
223
|
+
record(isSupportedNodeVersion(detectedVersion) ? "OK" : "FAIL", "Node.js version >= 22.5.0", detectedVersion);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
record("FAIL", "Node.js version check", error.message);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
const smoke = await runDirectMcpSmoke({ command: parsed.command, args: parsed.args, cwd, env });
|
|
230
|
+
record("OK", "MCP initialize");
|
|
231
|
+
record("OK", "MCP tools/list", `${smoke.toolNames.length} tools`);
|
|
232
|
+
record("OK", "memory_info tool call");
|
|
233
|
+
record("OK", "recall(scope=all) tool call");
|
|
234
|
+
} catch (error) {
|
|
235
|
+
record("FAIL", "Direct MCP protocol smoke test", error.message);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
const { resolveProjectIdentity } = await import("./identity.js");
|
|
240
|
+
const identity = await resolveProjectIdentity(cwd);
|
|
241
|
+
record("INFO", "Current project identity", identity?.key || "none (global memory only)");
|
|
242
|
+
} catch (error) {
|
|
243
|
+
record("WARN", "Current project identity", error.message);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const agentsPath = join(home, ".codex", "AGENTS.md");
|
|
247
|
+
if (existsSync(agentsPath)) {
|
|
248
|
+
const agents = readFileSync(agentsPath, "utf8");
|
|
249
|
+
const starts = countOccurrences(agents, "<!-- START MEMORY AGENT PROMPT -->");
|
|
250
|
+
const ends = countOccurrences(agents, "<!-- END MEMORY AGENT PROMPT -->");
|
|
251
|
+
record(starts === 1 && ends === 1 ? "OK" : "WARN", "Codex memory prompt block count", `start=${starts}, end=${ends}`);
|
|
252
|
+
} else {
|
|
253
|
+
record("WARN", "Codex memory prompt", `${agentsPath} not found`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
record(
|
|
257
|
+
"INFO",
|
|
258
|
+
"Codex Desktop exposure",
|
|
259
|
+
"Cannot be proven by the MCP server; start a new task after setup and verify that memory-agent tools are listed"
|
|
260
|
+
);
|
|
261
|
+
const ok = !checks.some((check) => check.level === "FAIL");
|
|
262
|
+
return { ok, checks, configPath, parsed, detectedVersion };
|
|
263
|
+
}
|
package/mcp-server/index.js
CHANGED
|
@@ -36,7 +36,8 @@ const CLI_COMMANDS = new Set([
|
|
|
36
36
|
"identity",
|
|
37
37
|
"migrate_titles",
|
|
38
38
|
"enable-prompt",
|
|
39
|
-
"disable-prompt",
|
|
39
|
+
"disable-prompt",
|
|
40
|
+
"doctor",
|
|
40
41
|
]);
|
|
41
42
|
|
|
42
43
|
function printUsage() {
|
|
@@ -51,7 +52,8 @@ Usage:
|
|
|
51
52
|
memory_plugin auth-status
|
|
52
53
|
memory_plugin link|unlink|relink|identity [--dir <path>] [--remote <url>]
|
|
53
54
|
memory_plugin migrate_titles [--key <key>]
|
|
54
|
-
memory_plugin enable-prompt | disable-prompt
|
|
55
|
+
memory_plugin enable-prompt | disable-prompt
|
|
56
|
+
memory_plugin doctor --codex
|
|
55
57
|
|
|
56
58
|
Options:
|
|
57
59
|
-h, --help Show this help text
|
|
@@ -99,17 +99,21 @@ function buildIncludeBlock(promptFile) {
|
|
|
99
99
|
${END_MARKER}`;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
function stripPromptBlock(content) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
102
|
+
export function stripPromptBlock(content) {
|
|
103
|
+
let clean = String(content || "");
|
|
104
|
+
while (true) {
|
|
105
|
+
const startIndex = clean.indexOf(START_MARKER);
|
|
106
|
+
const endIndex = clean.indexOf(END_MARKER, startIndex + START_MARKER.length);
|
|
107
|
+
if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) break;
|
|
108
|
+
clean = clean.substring(0, startIndex) + clean.substring(endIndex + END_MARKER.length);
|
|
109
|
+
}
|
|
110
|
+
return clean.replace(/\r?\n(?:[ \t]*\r?\n){2,}/g, "\n\n").trim();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function upsertPromptBlock(content, block = PROMPT_BLOCK) {
|
|
114
|
+
const clean = stripPromptBlock(content);
|
|
115
|
+
return clean ? `${clean}\n\n${block}\n` : `${block}\n`;
|
|
116
|
+
}
|
|
113
117
|
|
|
114
118
|
export async function enableGlobalPrompt() {
|
|
115
119
|
const promptFile = await syncPromptFile();
|
|
@@ -126,12 +130,10 @@ export async function enableGlobalPrompt() {
|
|
|
126
130
|
|
|
127
131
|
const existed = existsSync(target.filePath);
|
|
128
132
|
const existing = existed ? await readFile(target.filePath, "utf-8") : "";
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
: PROMPT_BLOCK;
|
|
134
|
-
const updated = clean ? `${clean}\n\n${block}\n` : `${block}\n`;
|
|
133
|
+
const block = target.includeSupported
|
|
134
|
+
? buildIncludeBlock(promptFile)
|
|
135
|
+
: PROMPT_BLOCK;
|
|
136
|
+
const updated = upsertPromptBlock(existing, block);
|
|
135
137
|
|
|
136
138
|
const key = target.filePath;
|
|
137
139
|
const prev = state[key];
|
package/mcp-server/setup.js
CHANGED
|
@@ -187,27 +187,43 @@ export async function runSetup() {
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
// 4. Codex (~/.codex/config.toml)
|
|
191
|
-
if (doCodex) {
|
|
192
|
-
try {
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
190
|
+
// 4. Codex (~/.codex/config.toml)
|
|
191
|
+
if (doCodex) {
|
|
192
|
+
try {
|
|
193
|
+
const {
|
|
194
|
+
updateCodexMemoryAgentConfig,
|
|
195
|
+
validateCodexRuntime,
|
|
196
|
+
} = await import("./codex_config.js");
|
|
197
|
+
const codexDir = join(home, ".codex");
|
|
198
|
+
const codexConfig = join(codexDir, "config.toml");
|
|
199
|
+
const nodePath = process.execPath;
|
|
200
|
+
const bootPath = fileURLToPath(new URL("./boot.js", import.meta.url));
|
|
201
|
+
const runtime = validateCodexRuntime({ nodePath, nodeVersion: process.versions.node, bootPath });
|
|
202
|
+
if (!runtime.ok) {
|
|
203
|
+
throw new Error(`Codex direct launcher validation failed: ${runtime.errors.join("; ")}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
await mkdir(codexDir, { recursive: true });
|
|
207
|
+
const content = existsSync(codexConfig) ? await readFile(codexConfig, "utf-8") : "";
|
|
208
|
+
const update = updateCodexMemoryAgentConfig(content, { nodePath, bootPath });
|
|
209
|
+
if (update.status === "conflict") {
|
|
210
|
+
throw new Error(update.reason);
|
|
211
|
+
}
|
|
212
|
+
if (update.changed) {
|
|
213
|
+
await writeFile(codexConfig, update.content, "utf-8");
|
|
214
|
+
console.log(
|
|
215
|
+
update.status === "added"
|
|
216
|
+
? " [OK] Codex: added direct Node.js memory-agent launcher to ~/.codex/config.toml"
|
|
217
|
+
: " [OK] Codex: migrated memory-agent to a direct Node.js launcher in ~/.codex/config.toml"
|
|
218
|
+
);
|
|
219
|
+
configuredCount++;
|
|
220
|
+
} else {
|
|
221
|
+
console.log(" [INFO] Codex: direct Node.js memory-agent launcher already configured");
|
|
222
|
+
}
|
|
223
|
+
} catch (err) {
|
|
224
|
+
console.log(" [FAIL] Codex setup failed:", err.message);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
211
227
|
|
|
212
228
|
// 5. Global Prompt Instructions (Antigravity, Codex, Claude Code)
|
|
213
229
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotargo/memory_plugin",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"description": "100% local hybrid RAG memory for AI coding agents (OpenCode, Claude Code, Codex, Antigravity). MCP server + plugin: persistent user facts, document ingestion, vector + SQLite FTS5 retrieval across sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "opencode-plugin/index.js",
|
|
@@ -33,8 +33,10 @@
|
|
|
33
33
|
"mcp-server/tools",
|
|
34
34
|
"mcp-server/boot.js",
|
|
35
35
|
"mcp-server/cli_boot.js",
|
|
36
|
-
"mcp-server/cli.js",
|
|
37
|
-
"mcp-server/
|
|
36
|
+
"mcp-server/cli.js",
|
|
37
|
+
"mcp-server/codex_config.js",
|
|
38
|
+
"mcp-server/codex_diagnostics.js",
|
|
39
|
+
"mcp-server/index.js",
|
|
38
40
|
"mcp-server/fact_format.js",
|
|
39
41
|
"mcp-server/identity.js",
|
|
40
42
|
"mcp-server/logger.js",
|