local-agent-memory 1.0.4 → 1.0.6

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
@@ -25,6 +25,7 @@ Inicializa el entorno de memoria en el repositorio actual. Esto crea las bases d
25
25
  - `-f, --force`: Fuerza la inicialización incluso si el proyecto ya fue inicializado.
26
26
  - `-r, --rules <rules>`: Inyecta reglas específicas para tu IDE (opciones: `cursor`, `claude`, `copilot`, `antigravity-ide`, `antigravity-cli`, `all`).
27
27
  - `-d, --docs <path>`: Ruta opcional a un directorio de documentación existente para mapearlo/indexarlo automáticamente.
28
+ - `--no-log`: Desactiva el registro automático de llamadas del agente en `.memory/usage.log` (activado por defecto vía `"log_usage": true` en `.memory/config.json`).
28
29
 
29
30
  ```bash
30
31
  memory init --rules all --docs ./docs
@@ -2,4 +2,5 @@ export declare function initProject(options?: {
2
2
  force?: boolean;
3
3
  rules?: string;
4
4
  docs?: string;
5
+ log?: boolean;
5
6
  }): Promise<void>;
@@ -47,6 +47,7 @@ async function initProject(options = {}) {
47
47
  project_id: projectId,
48
48
  created_at: new Date().toISOString(),
49
49
  include_paths: [".memory"],
50
+ log_usage: options.log !== false,
50
51
  };
51
52
  if (options.docs) {
52
53
  configData.include_paths.push(options.docs);
@@ -52,18 +52,26 @@ async function listProjects() {
52
52
  // You could also list recent decisions or ledgers specifically if needed
53
53
  const decisionsDir = node_path_1.default.join(memoryDir, "decisions");
54
54
  if (node_fs_1.default.existsSync(decisionsDir)) {
55
- const decisions = node_fs_1.default.readdirSync(decisionsDir).filter(f => f.endsWith(".md"));
55
+ const decisions = node_fs_1.default
56
+ .readdirSync(decisionsDir)
57
+ .filter((f) => f.endsWith(".md"));
56
58
  if (decisions.length > 0) {
57
59
  console.log(`\nDecisiones Arquitectónicas (ADRs):`);
58
- decisions.forEach(d => console.log(` - decisions/${d}`));
60
+ for (const d of decisions) {
61
+ console.log(` - decisions/${d}`);
62
+ }
59
63
  }
60
64
  }
61
65
  const ledgerDir = node_path_1.default.join(memoryDir, "ledger");
62
66
  if (node_fs_1.default.existsSync(ledgerDir)) {
63
- const ledgers = node_fs_1.default.readdirSync(ledgerDir).filter(f => f.endsWith(".md"));
67
+ const ledgers = node_fs_1.default
68
+ .readdirSync(ledgerDir)
69
+ .filter((f) => f.endsWith(".md"));
64
70
  if (ledgers.length > 0) {
65
71
  console.log(`\nRegistros de Sesión (Ledger):`);
66
- ledgers.forEach(l => console.log(` - ledger/${l}`));
72
+ for (const l of ledgers) {
73
+ console.log(` - ledger/${l}`);
74
+ }
67
75
  }
68
76
  }
69
77
  console.log("\n==============================");
@@ -78,6 +78,9 @@ async function getAllMarkdownFiles(dirPath, arrayOfFiles = []) {
78
78
  const files = await node_fs_1.default.promises.readdir(dirPath, { withFileTypes: true });
79
79
  for (const file of files) {
80
80
  if (file.isDirectory()) {
81
+ if (file.name === "node_modules" || file.name === ".git") {
82
+ continue;
83
+ }
81
84
  arrayOfFiles = await getAllMarkdownFiles(node_path_1.default.join(dirPath, file.name), arrayOfFiles);
82
85
  }
83
86
  else if (file.name.endsWith(".md")) {
@@ -115,6 +115,9 @@ async function getAllMarkdownFiles(dirPath, arrayOfFiles = []) {
115
115
  const files = await node_fs_1.default.promises.readdir(dirPath, { withFileTypes: true });
116
116
  for (const file of files) {
117
117
  if (file.isDirectory()) {
118
+ if (file.name === "node_modules" || file.name === ".git") {
119
+ continue;
120
+ }
118
121
  arrayOfFiles = await getAllMarkdownFiles(node_path_1.default.join(dirPath, file.name), arrayOfFiles);
119
122
  }
120
123
  else if (file.name.endsWith(".md")) {
package/dist/cli/index.js CHANGED
@@ -35,6 +35,7 @@ program
35
35
  .option("-f, --force", "Force initialization even if already exists")
36
36
  .option("-r, --rules <rules>", "Comma-separated IDE rules to inject (cursor,claude,copilot,antigravity-ide,antigravity-cli,all)")
37
37
  .option("-d, --docs <path>", "Optional path to an existing docs directory to map/index automatically")
38
+ .option("--no-log", "Disable automatic agent memory usage logging in .memory/usage.log")
38
39
  .action(init_1.initProject);
39
40
  program
40
41
  .command("template <action>")
@@ -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
- chunks.push({
80
- path,
81
- text: currentBlocks.map((b) => b.text).join("\n\n"),
82
- breadcrumbs: first.breadcrumb,
83
- startLine: first.startLine,
84
- endLine: last.endLine,
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,
@@ -0,0 +1,10 @@
1
+ export interface MemoryConfig {
2
+ project_id?: string;
3
+ created_at?: string;
4
+ include_paths?: string[];
5
+ log_usage?: boolean;
6
+ usage_log_file?: string;
7
+ [key: string]: unknown;
8
+ }
9
+ export declare function getMemoryConfig(cwd: string): MemoryConfig | null;
10
+ export declare function logUsage(cwd: string, action: string, details?: Record<string, unknown>): void;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getMemoryConfig = getMemoryConfig;
37
+ exports.logUsage = logUsage;
38
+ const fs = __importStar(require("node:fs"));
39
+ const path = __importStar(require("node:path"));
40
+ function getMemoryConfig(cwd) {
41
+ const configPath = path.join(cwd, ".memory", "config.json");
42
+ if (!fs.existsSync(configPath)) {
43
+ return null;
44
+ }
45
+ try {
46
+ const raw = fs.readFileSync(configPath, "utf8");
47
+ return JSON.parse(raw);
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ function logUsage(cwd, action, details) {
54
+ try {
55
+ const config = getMemoryConfig(cwd);
56
+ if (!config?.log_usage) {
57
+ return;
58
+ }
59
+ const logRelativePath = config.usage_log_file || path.join(".memory", "usage.log");
60
+ const logFilePath = path.isAbsolute(logRelativePath)
61
+ ? logRelativePath
62
+ : path.join(cwd, logRelativePath);
63
+ const logDir = path.dirname(logFilePath);
64
+ if (!fs.existsSync(logDir)) {
65
+ fs.mkdirSync(logDir, { recursive: true });
66
+ }
67
+ const timestamp = new Date().toISOString();
68
+ let detailsStr = "";
69
+ if (details && Object.keys(details).length > 0) {
70
+ detailsStr = ` | ${JSON.stringify(details)}`;
71
+ }
72
+ const entry = `[${timestamp}] ${action}${detailsStr}\n`;
73
+ fs.appendFileSync(logFilePath, entry, "utf8");
74
+ }
75
+ catch {
76
+ // Fail silently to avoid breaking any MCP operation
77
+ }
78
+ }
@@ -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;
@@ -34,10 +34,12 @@ 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 logger_1 = require("./logger");
41
43
  const tools_1 = require("./tools");
42
44
  const utils_1 = require("./utils");
43
45
  async function setupMCPServer() {
@@ -57,6 +59,9 @@ async function setupMCPServer() {
57
59
  });
58
60
  server.resource("agent-guidelines", "memory://agent-guidelines", { description: "Agent guidelines and memory core rules" }, async (uri) => {
59
61
  const { cwd: resolvedCwd } = await (0, utils_1.resolveCwd)();
62
+ (0, logger_1.logUsage)(resolvedCwd, "RESOURCE_READ: agent-guidelines", {
63
+ uri: uri.href,
64
+ });
60
65
  const corePath = path.join(resolvedCwd, ".memory", "core.md");
61
66
  const text = fs.existsSync(corePath)
62
67
  ? fs.readFileSync(corePath, "utf8")
@@ -73,6 +78,7 @@ async function setupMCPServer() {
73
78
  });
74
79
  server.prompt("agent-guidelines", "Get the agent guidelines and memory core rules", async () => {
75
80
  const { cwd: resolvedCwd } = await (0, utils_1.resolveCwd)();
81
+ (0, logger_1.logUsage)(resolvedCwd, "PROMPT_GET: agent-guidelines");
76
82
  const corePath = path.join(resolvedCwd, ".memory", "core.md");
77
83
  const text = fs.existsSync(corePath)
78
84
  ? fs.readFileSync(corePath, "utf8")
@@ -94,9 +100,37 @@ async function setupMCPServer() {
94
100
  transport.onerror = (err) => {
95
101
  log(`Transport error: ${err}`);
96
102
  };
103
+ setupProcessLifecycle(transport, log);
97
104
  await server.connect(transport);
98
105
  log("MCP Server connected via stdio");
99
106
  // Opcionalmente podemos exponer log para ser usado por tools si se lo pasamos,
100
107
  // pero el servidor ya está funcionando y registrando arranque y errores.
101
108
  return server;
102
109
  }
110
+ function setupProcessLifecycle(transport, log = () => { }) {
111
+ let isExiting = false;
112
+ const exitHandler = (reason) => {
113
+ if (isExiting)
114
+ return;
115
+ isExiting = true;
116
+ log(`Shutting down MCP server: ${reason}`);
117
+ process.exit(0);
118
+ };
119
+ process.stdin.on("close", () => exitHandler("stdin closed"));
120
+ process.stdin.on("end", () => exitHandler("stdin ended"));
121
+ transport.onclose = () => exitHandler("transport closed");
122
+ process.on("SIGINT", () => exitHandler("SIGINT received"));
123
+ process.on("SIGTERM", () => exitHandler("SIGTERM received"));
124
+ if (process.ppid && process.ppid !== 1) {
125
+ const parentCheckInterval = setInterval(() => {
126
+ try {
127
+ process.kill(process.ppid, 0);
128
+ }
129
+ catch (_e) {
130
+ clearInterval(parentCheckInterval);
131
+ exitHandler(`parent process ${process.ppid} exited`);
132
+ }
133
+ }, 2000);
134
+ parentCheckInterval.unref();
135
+ }
136
+ }
package/dist/mcp/tools.js CHANGED
@@ -39,6 +39,7 @@ const path = __importStar(require("node:path"));
39
39
  const zod_1 = require("zod");
40
40
  const search_1 = require("../core/search");
41
41
  const project_1 = require("../db/project");
42
+ const logger_1 = require("./logger");
42
43
  const middleware_1 = require("./middleware");
43
44
  const utils_1 = require("./utils");
44
45
  function registerTools(server) {
@@ -50,6 +51,9 @@ function registerTools(server) {
50
51
  .describe("Ruta absoluta del proyecto (usar si MCP es global)"),
51
52
  }, async (args) => {
52
53
  const { cwd, fallbackUsed } = await (0, utils_1.resolveCwd)(args.projectRoot, server);
54
+ (0, logger_1.logUsage)(cwd, "TOOL_CALL: get_project_context", {
55
+ projectRoot: args.projectRoot,
56
+ });
53
57
  await (0, middleware_1.syncMiddleware)(cwd);
54
58
  const memoryDir = path.join(cwd, ".memory");
55
59
  const corePath = path.join(memoryDir, "core.md");
@@ -88,6 +92,11 @@ function registerTools(server) {
88
92
  .describe("Ruta absoluta del proyecto (usar si MCP es global)"),
89
93
  }, async (args) => {
90
94
  const { cwd, fallbackUsed } = await (0, utils_1.resolveCwd)(args.projectRoot, server);
95
+ (0, logger_1.logUsage)(cwd, "TOOL_CALL: search_memory", {
96
+ query: args.query,
97
+ filter: args.filter,
98
+ limit: args.limit,
99
+ });
91
100
  await (0, middleware_1.syncMiddleware)(cwd);
92
101
  const dbPath = (0, project_1.getLocalDbPath)(cwd);
93
102
  const db = (0, project_1.initLocalDb)(dbPath);
@@ -138,6 +147,10 @@ function registerTools(server) {
138
147
  .replace(/(^-|-$)/g, "");
139
148
  const filename = `${date}-${slug}.md`;
140
149
  const { cwd, fallbackUsed } = await (0, utils_1.resolveCwd)(args.projectRoot, server);
150
+ (0, logger_1.logUsage)(cwd, "TOOL_CALL: log_decision", {
151
+ title: args.title,
152
+ status: args.status,
153
+ });
141
154
  const memoryDir = path.join(cwd, ".memory");
142
155
  const decisionsDir = path.join(memoryDir, "decisions");
143
156
  if (!fs.existsSync(decisionsDir)) {
@@ -186,6 +199,9 @@ ${args.consequences}
186
199
  .describe("Ruta absoluta del proyecto (usar si MCP es global)"),
187
200
  }, async (args) => {
188
201
  const { cwd, fallbackUsed } = await (0, utils_1.resolveCwd)(args.projectRoot, server);
202
+ (0, logger_1.logUsage)(cwd, "TOOL_CALL: update_core_rules", {
203
+ charsAdded: args.changes_to_append.length,
204
+ });
189
205
  const memoryDir = path.join(cwd, ".memory");
190
206
  const corePath = path.join(memoryDir, "core.md");
191
207
  if (!fs.existsSync(memoryDir)) {
@@ -213,6 +229,9 @@ ${args.consequences}
213
229
  .describe("Ruta absoluta del proyecto (usar si MCP es global)"),
214
230
  }, async (args) => {
215
231
  const { cwd, fallbackUsed } = await (0, utils_1.resolveCwd)(args.projectRoot, server);
232
+ (0, logger_1.logUsage)(cwd, "TOOL_CALL: end_session", {
233
+ summaryLength: args.summary.length,
234
+ });
216
235
  const memoryDir = path.join(cwd, ".memory");
217
236
  const ledgerDir = path.join(memoryDir, "ledger");
218
237
  const dateObj = new Date();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "local-agent-memory",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",