local-agent-memory 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/init.js +2 -1
- package/dist/cli/commands/list.js +20 -4
- package/dist/core/parser.js +10 -8
- package/dist/core/sync.js +2 -0
- package/dist/db/registry.d.ts +1 -0
- package/dist/db/registry.js +21 -1
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +42 -6
- package/dist/setup/initGlobal.js +1 -1
- package/package.json +1 -1
|
@@ -21,7 +21,8 @@ async function initProject(options = {}) {
|
|
|
21
21
|
}
|
|
22
22
|
const spinner = (0, ora_1.default)("Initializing project...").start();
|
|
23
23
|
try {
|
|
24
|
-
|
|
24
|
+
const configFile = node_path_1.default.join(memoryDir, "config.json");
|
|
25
|
+
if (node_fs_1.default.existsSync(memoryDir) && node_fs_1.default.existsSync(configFile)) {
|
|
25
26
|
if (!options.force) {
|
|
26
27
|
spinner.fail("Project already initialized. Use --force to overwrite.");
|
|
27
28
|
process.exit(1);
|
|
@@ -13,8 +13,20 @@ const registry_1 = require("../../db/registry");
|
|
|
13
13
|
async function listProjects() {
|
|
14
14
|
const spinner = (0, ora_1.default)("Cargando proyectos registrados...").start();
|
|
15
15
|
try {
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
const rawProjects = await (0, registry_1.getAllProjectsFromRegistry)();
|
|
17
|
+
// Auto-prune projects whose directories no longer exist on disk
|
|
18
|
+
const projects = [];
|
|
19
|
+
if (rawProjects && rawProjects.length > 0) {
|
|
20
|
+
for (const p of rawProjects) {
|
|
21
|
+
if (node_fs_1.default.existsSync(p.file_path)) {
|
|
22
|
+
projects.push(p);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
await (0, registry_1.unregisterProject)(p.project_id);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (projects.length === 0) {
|
|
18
30
|
spinner.info("No hay proyectos registrados en la memoria global.");
|
|
19
31
|
return;
|
|
20
32
|
}
|
|
@@ -57,7 +69,9 @@ async function listProjects() {
|
|
|
57
69
|
.filter((f) => f.endsWith(".md"));
|
|
58
70
|
if (decisions.length > 0) {
|
|
59
71
|
console.log(`\nDecisiones Arquitectónicas (ADRs):`);
|
|
60
|
-
|
|
72
|
+
for (const d of decisions) {
|
|
73
|
+
console.log(` - decisions/${d}`);
|
|
74
|
+
}
|
|
61
75
|
}
|
|
62
76
|
}
|
|
63
77
|
const ledgerDir = node_path_1.default.join(memoryDir, "ledger");
|
|
@@ -67,7 +81,9 @@ async function listProjects() {
|
|
|
67
81
|
.filter((f) => f.endsWith(".md"));
|
|
68
82
|
if (ledgers.length > 0) {
|
|
69
83
|
console.log(`\nRegistros de Sesión (Ledger):`);
|
|
70
|
-
|
|
84
|
+
for (const l of ledgers) {
|
|
85
|
+
console.log(` - ledger/${l}`);
|
|
86
|
+
}
|
|
71
87
|
}
|
|
72
88
|
}
|
|
73
89
|
console.log("\n==============================");
|
package/dist/core/parser.js
CHANGED
|
@@ -76,13 +76,15 @@ async function parseMarkdown(content, path, maxTokens = 450, overlapTokens = 50)
|
|
|
76
76
|
if (currentBlocks.length > 0) {
|
|
77
77
|
const first = currentBlocks[0];
|
|
78
78
|
const last = currentBlocks[currentBlocks.length - 1];
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
if (first && last) {
|
|
80
|
+
chunks.push({
|
|
81
|
+
path,
|
|
82
|
+
text: currentBlocks.map((b) => b.text).join("\n\n"),
|
|
83
|
+
breadcrumbs: first.breadcrumb,
|
|
84
|
+
startLine: first.startLine,
|
|
85
|
+
endLine: last.endLine,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
if (ast?.children) {
|
|
@@ -131,7 +133,7 @@ async function parseMarkdown(content, path, maxTokens = 450, overlapTokens = 50)
|
|
|
131
133
|
let overlapTokensCount = 0;
|
|
132
134
|
for (let i = currentBlocks.length - 1; i >= 0; i--) {
|
|
133
135
|
const b = currentBlocks[i];
|
|
134
|
-
if (overlapTokensCount + b.tokens <= overlapTokens) {
|
|
136
|
+
if (b && overlapTokensCount + b.tokens <= overlapTokens) {
|
|
135
137
|
overlapBlocks.unshift(b);
|
|
136
138
|
overlapTokensCount += b.tokens;
|
|
137
139
|
}
|
package/dist/core/sync.js
CHANGED
|
@@ -113,6 +113,8 @@ async function syncFile(path, content, db) {
|
|
|
113
113
|
for (let j = 0; j < batch.length; j++) {
|
|
114
114
|
const chunk = batch[j];
|
|
115
115
|
const embedding = embeddings[j];
|
|
116
|
+
if (!chunk || !embedding)
|
|
117
|
+
continue;
|
|
116
118
|
// Store in main table
|
|
117
119
|
await asyncDb.run("INSERT INTO chunks (path, text, breadcrumbs, startLine, endLine) VALUES (?, ?, ?, ?, ?)", [
|
|
118
120
|
chunk.path,
|
package/dist/db/registry.d.ts
CHANGED
package/dist/db/registry.js
CHANGED
|
@@ -9,12 +9,20 @@ exports.registerProject = registerProject;
|
|
|
9
9
|
exports.getProjectByPath = getProjectByPath;
|
|
10
10
|
exports.getProjectRootFromRegistry = getProjectRootFromRegistry;
|
|
11
11
|
exports.getAllProjectsFromRegistry = getAllProjectsFromRegistry;
|
|
12
|
+
exports.unregisterProject = unregisterProject;
|
|
12
13
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
13
14
|
const node_path_1 = __importDefault(require("node:path"));
|
|
14
15
|
const sqlite3_1 = __importDefault(require("sqlite3"));
|
|
15
16
|
const initGlobal_1 = require("../setup/initGlobal");
|
|
16
17
|
function getRegistryDbPath() {
|
|
17
|
-
|
|
18
|
+
if (process.env.MEMORY_REGISTRY_PATH) {
|
|
19
|
+
const dir = node_path_1.default.dirname(process.env.MEMORY_REGISTRY_PATH);
|
|
20
|
+
if (!node_fs_1.default.existsSync(dir)) {
|
|
21
|
+
node_fs_1.default.mkdirSync(dir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
return process.env.MEMORY_REGISTRY_PATH;
|
|
24
|
+
}
|
|
25
|
+
const memDir = process.env.MEMORY_GLOBAL_DIR || (0, initGlobal_1.getLocalMemoryDir)();
|
|
18
26
|
if (!node_fs_1.default.existsSync(memDir)) {
|
|
19
27
|
node_fs_1.default.mkdirSync(memDir, { recursive: true });
|
|
20
28
|
}
|
|
@@ -103,3 +111,15 @@ async function getAllProjectsFromRegistry(dbPath = getRegistryDbPath()) {
|
|
|
103
111
|
});
|
|
104
112
|
});
|
|
105
113
|
}
|
|
114
|
+
async function unregisterProject(projectId, dbPath = getRegistryDbPath()) {
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
const db = initRegistryDb(dbPath);
|
|
117
|
+
db.run("DELETE FROM projects WHERE project_id = ?", [projectId], (err) => {
|
|
118
|
+
db.close();
|
|
119
|
+
if (err)
|
|
120
|
+
reject(err);
|
|
121
|
+
else
|
|
122
|
+
resolve();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
3
|
export declare function setupMCPServer(): Promise<McpServer>;
|
|
4
|
+
export declare function setupProcessLifecycle(transport: StdioServerTransport, log?: (msg: string) => void): void;
|
package/dist/mcp/server.js
CHANGED
|
@@ -34,22 +34,28 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.setupMCPServer = setupMCPServer;
|
|
37
|
+
exports.setupProcessLifecycle = setupProcessLifecycle;
|
|
37
38
|
const fs = __importStar(require("node:fs"));
|
|
38
39
|
const path = __importStar(require("node:path"));
|
|
39
40
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
40
41
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
42
|
+
const initGlobal_1 = require("../setup/initGlobal");
|
|
41
43
|
const logger_1 = require("./logger");
|
|
42
44
|
const tools_1 = require("./tools");
|
|
43
45
|
const utils_1 = require("./utils");
|
|
44
46
|
async function setupMCPServer() {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
fs.mkdirSync(memoryDir, { recursive: true });
|
|
47
|
+
const globalMemDir = (0, initGlobal_1.getLocalMemoryDir)();
|
|
48
|
+
if (!fs.existsSync(globalMemDir)) {
|
|
49
|
+
fs.mkdirSync(globalMemDir, { recursive: true });
|
|
49
50
|
}
|
|
50
|
-
const logFile = path.join(
|
|
51
|
+
const logFile = path.join(globalMemDir, "mcp-debug.log");
|
|
51
52
|
const log = (msg) => {
|
|
52
|
-
|
|
53
|
+
try {
|
|
54
|
+
fs.appendFileSync(logFile, `[${new Date().toISOString()}] ${msg}\n`);
|
|
55
|
+
}
|
|
56
|
+
catch (_err) {
|
|
57
|
+
// Ignore write errors to prevent server crash
|
|
58
|
+
}
|
|
53
59
|
};
|
|
54
60
|
log("Starting MCP Server");
|
|
55
61
|
const server = new mcp_js_1.McpServer({
|
|
@@ -99,9 +105,39 @@ async function setupMCPServer() {
|
|
|
99
105
|
transport.onerror = (err) => {
|
|
100
106
|
log(`Transport error: ${err}`);
|
|
101
107
|
};
|
|
108
|
+
setupProcessLifecycle(transport, log);
|
|
102
109
|
await server.connect(transport);
|
|
103
110
|
log("MCP Server connected via stdio");
|
|
104
111
|
// Opcionalmente podemos exponer log para ser usado por tools si se lo pasamos,
|
|
105
112
|
// pero el servidor ya está funcionando y registrando arranque y errores.
|
|
106
113
|
return server;
|
|
107
114
|
}
|
|
115
|
+
function setupProcessLifecycle(transport, log = () => { }) {
|
|
116
|
+
let isExiting = false;
|
|
117
|
+
const exitHandler = (reason) => {
|
|
118
|
+
if (isExiting)
|
|
119
|
+
return;
|
|
120
|
+
isExiting = true;
|
|
121
|
+
log(`Shutting down MCP server: ${reason}`);
|
|
122
|
+
process.exit(0);
|
|
123
|
+
};
|
|
124
|
+
process.stdin.on("close", () => exitHandler("stdin closed"));
|
|
125
|
+
process.stdin.on("end", () => exitHandler("stdin ended"));
|
|
126
|
+
transport.onclose = () => exitHandler("transport closed");
|
|
127
|
+
process.on("SIGINT", () => exitHandler("SIGINT received"));
|
|
128
|
+
process.on("SIGTERM", () => exitHandler("SIGTERM received"));
|
|
129
|
+
// Only watch ppid on POSIX platforms where signal 0 check is reliable
|
|
130
|
+
// and process.ppid is not a short-lived batch shell wrapper like cmd.exe on Windows.
|
|
131
|
+
if (process.platform !== "win32" && process.ppid && process.ppid !== 1) {
|
|
132
|
+
const parentCheckInterval = setInterval(() => {
|
|
133
|
+
try {
|
|
134
|
+
process.kill(process.ppid, 0);
|
|
135
|
+
}
|
|
136
|
+
catch (_e) {
|
|
137
|
+
clearInterval(parentCheckInterval);
|
|
138
|
+
exitHandler(`parent process ${process.ppid} exited`);
|
|
139
|
+
}
|
|
140
|
+
}, 2000);
|
|
141
|
+
parentCheckInterval.unref();
|
|
142
|
+
}
|
|
143
|
+
}
|
package/dist/setup/initGlobal.js
CHANGED
|
@@ -46,7 +46,7 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
46
46
|
const node_path_1 = __importDefault(require("node:path"));
|
|
47
47
|
const transformers_1 = require("@xenova/transformers");
|
|
48
48
|
const tar = __importStar(require("tar"));
|
|
49
|
-
const getLocalMemoryDir = () => node_path_1.default.join(node_os_1.default.homedir(), ".local_memory");
|
|
49
|
+
const getLocalMemoryDir = () => process.env.MEMORY_GLOBAL_DIR || node_path_1.default.join(node_os_1.default.homedir(), ".local_memory");
|
|
50
50
|
exports.getLocalMemoryDir = getLocalMemoryDir;
|
|
51
51
|
const getModelsDir = () => node_path_1.default.join((0, exports.getLocalMemoryDir)(), "models");
|
|
52
52
|
exports.getModelsDir = getModelsDir;
|