local-agent-memory 0.0.0-stage → 1.0.1
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/LICENSE +15 -0
- package/README.md +108 -2
- package/dist/cli/commands/hook.d.ts +1 -0
- package/dist/cli/commands/hook.js +66 -0
- package/dist/cli/commands/init.d.ts +5 -0
- package/dist/cli/commands/init.js +158 -0
- package/dist/cli/commands/record.d.ts +3 -0
- package/dist/cli/commands/record.js +58 -0
- package/dist/cli/commands/reindex.d.ts +1 -0
- package/dist/cli/commands/reindex.js +41 -0
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.js +88 -0
- package/dist/cli/commands/sync.d.ts +8 -0
- package/dist/cli/commands/sync.js +134 -0
- package/dist/cli/commands/template.d.ts +1 -0
- package/dist/cli/commands/template.js +116 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +140 -0
- package/dist/cli/mcp.d.ts +2 -0
- package/dist/cli/mcp.js +13 -0
- package/dist/core/embeddings.d.ts +14 -0
- package/dist/core/embeddings.js +127 -0
- package/dist/core/parser.d.ts +25 -0
- package/dist/core/parser.js +151 -0
- package/dist/core/search.d.ts +19 -0
- package/dist/core/search.js +100 -0
- package/dist/core/sync.d.ts +22 -0
- package/dist/core/sync.js +159 -0
- package/dist/db/project.d.ts +12 -0
- package/dist/db/project.js +113 -0
- package/dist/db/registry.d.ts +9 -0
- package/dist/db/registry.js +92 -0
- package/dist/mcp/middleware.d.ts +1 -0
- package/dist/mcp/middleware.js +46 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +102 -0
- package/dist/mcp/tools.d.ts +2 -0
- package/dist/mcp/tools.js +244 -0
- package/dist/mcp/utils.d.ts +5 -0
- package/dist/mcp/utils.js +82 -0
- package/dist/setup/initGlobal.d.ts +5 -0
- package/dist/setup/initGlobal.js +167 -0
- package/package.json +72 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,109 @@
|
|
|
1
|
-
#
|
|
1
|
+
# local-agent-memory
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**local-agent-memory** es una herramienta de indexación de memoria para agentes de IA que opera localmente en tus repositorios. Permite sincronizar documentos Markdown, llevar un registro de sesiones de codificación y exponer toda esta información a asistentes virtuales e IDEs a través del protocolo **Model Context Protocol (MCP)**. Además, aprovecha bases de datos SQLite con FTS5 (búsqueda de texto completo) y modelos vectoriales ONNX para potentes búsquedas semánticas.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Instalación
|
|
8
|
+
|
|
9
|
+
Puedes instalar este paquete de forma global usando npm, lo cual te dará acceso a los binarios `memory` y `memory-mcp`.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g local-agent-memory
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Funcionalidades y Comandos Principales (`memory`)
|
|
18
|
+
|
|
19
|
+
Una vez instalado, tienes a tu disposición el CLI `memory` con los siguientes comandos para gestionar la memoria de tus proyectos:
|
|
20
|
+
|
|
21
|
+
### 1. `init`
|
|
22
|
+
Inicializa el entorno de memoria en el repositorio actual. Esto crea las bases de datos SQLite locales y configura las estructuras necesarias en una carpeta oculta (`.memory`).
|
|
23
|
+
|
|
24
|
+
**Opciones:**
|
|
25
|
+
- `-f, --force`: Fuerza la inicialización incluso si el proyecto ya fue inicializado.
|
|
26
|
+
- `-r, --rules <rules>`: Inyecta reglas específicas para tu IDE (opciones: `cursor`, `claude`, `copilot`, `antigravity-ide`, `antigravity-cli`, `all`).
|
|
27
|
+
- `-d, --docs <path>`: Ruta opcional a un directorio de documentación existente para mapearlo/indexarlo automáticamente.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
memory init --rules all --docs ./docs
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. `sync`
|
|
34
|
+
Sincroniza los archivos Markdown del proyecto en el índice de memoria local. Escanea los documentos y actualiza la base de datos para que los agentes cuenten con el contexto más reciente.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
memory sync
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 3. `status`
|
|
41
|
+
Comprueba el estado de sincronización del proyecto de memoria actual, mostrando detalles sobre archivos indexados y pendienetes.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
memory status
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 4. `reindex` (o `prune`)
|
|
48
|
+
Reconstruye los índices FTS (texto completo) y vectoriales desde cero. Útil si sospechas que hay corrupción o desincronización en la base de datos local.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
memory reindex
|
|
52
|
+
# o
|
|
53
|
+
memory prune
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 5. `record-session`
|
|
57
|
+
Registra un resumen de tu sesión de trabajo actual en el "ledger" (libro mayor) de memoria, documentando las tareas completadas y el contexto para retomarlo más adelante.
|
|
58
|
+
|
|
59
|
+
**Opciones:**
|
|
60
|
+
- `-s, --summary <summary>`: Define el texto del resumen del trabajo realizado.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
memory record-session -s "Implementación del comando sync y corrección de bugs en SQLite"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 6. `install-hook`
|
|
67
|
+
Instala ganchos de Git (Git hooks) en tu repositorio local para recordar automáticamente a los desarrolladores registrar sesiones al hacer commits, o para automatizar el registro.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
memory install-hook
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 7. `template <action>`
|
|
74
|
+
Gestiona las plantillas de memoria (permite listar, crear o exportar plantillas desde el proyecto).
|
|
75
|
+
|
|
76
|
+
**Opciones:**
|
|
77
|
+
- `-n, --name <name>`: El nombre de la plantilla a gestionar.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
memory template create --name mi-plantilla
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Integración de Agentes y Model Context Protocol (MCP)
|
|
86
|
+
|
|
87
|
+
Para que tus agentes de IA (como Claude Desktop, Cursor o Antigravity) puedan acceder a esta memoria enriquecida, el paquete provee un servidor MCP incorporado.
|
|
88
|
+
|
|
89
|
+
### Configuración Automática (`memory mcp-setup`)
|
|
90
|
+
Esta utilidad configura tu entorno global instalando los modelos ONNX y binarios SQLite necesarios y, de manera interactiva, modifica el archivo de configuración del cliente MCP seleccionado (Claude, Cursor, Antigravity) para apuntar automáticamente a `memory-mcp`.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
memory mcp-setup
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Ejecución Manual del Servidor (`memory-mcp`)
|
|
97
|
+
Levanta el servidor compatible con el Model Context Protocol a través de stdio. Normalmente no necesitas ejecutar este comando directamente; los clientes como Claude Desktop o Cursor lo lanzan de forma transparente utilizando su archivo de configuración interno.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
memory-mcp
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
*(Si configuras MCP manualmente, deberás establecer que tu cliente ejecute `memory-mcp` sin argumentos extra)*
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Licencia
|
|
108
|
+
|
|
109
|
+
Este proyecto está bajo la licencia **ISC**.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function installGitHook(): Promise<void>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.installGitHook = installGitHook;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
async function installGitHook() {
|
|
11
|
+
const cwd = process.cwd();
|
|
12
|
+
const gitDir = node_path_1.default.join(cwd, ".git");
|
|
13
|
+
const hooksDir = node_path_1.default.join(gitDir, "hooks");
|
|
14
|
+
const spinner = (0, ora_1.default)("Installing Git hook...").start();
|
|
15
|
+
try {
|
|
16
|
+
if (!node_fs_1.default.existsSync(gitDir)) {
|
|
17
|
+
spinner.fail("No .git directory found. Initialize a git repository first.");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
if (!node_fs_1.default.existsSync(hooksDir)) {
|
|
21
|
+
await node_fs_1.default.promises.mkdir(hooksDir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
// A prepare-commit-msg hook that reminds the developer
|
|
24
|
+
const hookName = "prepare-commit-msg";
|
|
25
|
+
const hookPath = node_path_1.default.join(hooksDir, hookName);
|
|
26
|
+
const hookContent = `#!/bin/sh
|
|
27
|
+
# local-agent-memory prepare-commit-msg hook
|
|
28
|
+
|
|
29
|
+
# Add a reminder to the commit message template
|
|
30
|
+
echo "" >> "$1"
|
|
31
|
+
echo "# 🧠 Remember to update the local-agent-memory ledger if you made architectural changes." >> "$1"
|
|
32
|
+
echo "# You can run: memory record-session --summary \\"..."\\"" >> "$1"
|
|
33
|
+
`;
|
|
34
|
+
// Write file
|
|
35
|
+
await node_fs_1.default.promises.writeFile(hookPath, hookContent, { mode: 0o755 });
|
|
36
|
+
node_fs_1.default.chmodSync(hookPath, 0o755);
|
|
37
|
+
// Also a post-commit hook that optionally captures the commit msg as a session if wanted.
|
|
38
|
+
// For now, the prompt requested a prepare-commit-msg or pre-commit hook that reminds or updates the ledger.
|
|
39
|
+
// Given git hooks are usually headless, appending a reminder to prepare-commit-msg is the standard approach,
|
|
40
|
+
// or using a post-commit hook to execute the memory update automatically.
|
|
41
|
+
// Let's create a post-commit hook too, as a fallback passive memory strategy
|
|
42
|
+
const postHookName = "post-commit";
|
|
43
|
+
const postHookPath = node_path_1.default.join(hooksDir, postHookName);
|
|
44
|
+
const postHookContent = `#!/bin/sh
|
|
45
|
+
# local-agent-memory post-commit hook
|
|
46
|
+
# Automatically records a memory session for this commit.
|
|
47
|
+
|
|
48
|
+
COMMIT_MSG=$(git log -1 --pretty=%B)
|
|
49
|
+
|
|
50
|
+
# If memory CLI exists, run it
|
|
51
|
+
if command -v memory >/dev/null 2>&1; then
|
|
52
|
+
memory record-session --summary "Commit: $COMMIT_MSG" > /dev/null 2>&1 &
|
|
53
|
+
elif npx --no-install memory --version >/dev/null 2>&1; then
|
|
54
|
+
npx --no-install memory record-session --summary "Commit: $COMMIT_MSG" > /dev/null 2>&1 &
|
|
55
|
+
fi
|
|
56
|
+
`;
|
|
57
|
+
await node_fs_1.default.promises.writeFile(postHookPath, postHookContent, { mode: 0o755 });
|
|
58
|
+
node_fs_1.default.chmodSync(postHookPath, 0o755);
|
|
59
|
+
spinner.succeed(`Successfully installed git hooks (prepare-commit-msg, post-commit) to ensure memory consistency.`);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
spinner.fail("Failed to install Git hook.");
|
|
63
|
+
console.error(err.message);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.initProject = initProject;
|
|
7
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
|
+
const ora_1 = __importDefault(require("ora"));
|
|
12
|
+
const project_1 = require("../../db/project");
|
|
13
|
+
const registry_1 = require("../../db/registry");
|
|
14
|
+
async function initProject(options = {}) {
|
|
15
|
+
const cwd = process.cwd();
|
|
16
|
+
const memoryDir = node_path_1.default.join(cwd, ".memory");
|
|
17
|
+
const gitDir = node_path_1.default.join(cwd, ".git");
|
|
18
|
+
if (!node_fs_1.default.existsSync(gitDir)) {
|
|
19
|
+
console.warn("⚠️ No .git directory found. Automatic Git hooks cannot be installed yet.");
|
|
20
|
+
console.warn(" Run `git init` and then `memory install-hook` later.\n");
|
|
21
|
+
}
|
|
22
|
+
const spinner = (0, ora_1.default)("Initializing project...").start();
|
|
23
|
+
try {
|
|
24
|
+
if (node_fs_1.default.existsSync(memoryDir)) {
|
|
25
|
+
if (!options.force) {
|
|
26
|
+
spinner.fail("Project already initialized. Use --force to overwrite.");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// If force, we just continue (or we might want to clear it, but prompt says "a menos que se use --force", meaning we can overwrite or just proceed)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
await node_fs_1.default.promises.mkdir(memoryDir, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
// Subdirectories
|
|
37
|
+
await node_fs_1.default.promises.mkdir(node_path_1.default.join(memoryDir, "decisions"), {
|
|
38
|
+
recursive: true,
|
|
39
|
+
});
|
|
40
|
+
await node_fs_1.default.promises.mkdir(node_path_1.default.join(memoryDir, "context"), {
|
|
41
|
+
recursive: true,
|
|
42
|
+
});
|
|
43
|
+
// UUID
|
|
44
|
+
const projectId = node_crypto_1.default.randomUUID();
|
|
45
|
+
// Config file
|
|
46
|
+
const configData = {
|
|
47
|
+
project_id: projectId,
|
|
48
|
+
created_at: new Date().toISOString(),
|
|
49
|
+
include_paths: [".memory"],
|
|
50
|
+
};
|
|
51
|
+
if (options.docs) {
|
|
52
|
+
configData.include_paths.push(options.docs);
|
|
53
|
+
}
|
|
54
|
+
await node_fs_1.default.promises.writeFile(node_path_1.default.join(memoryDir, "config.json"), JSON.stringify(configData, null, 2));
|
|
55
|
+
// Core markdown
|
|
56
|
+
const coreMd = `# Core Rules\n\nThis file contains the core behavior and rules for this project.\n`;
|
|
57
|
+
await node_fs_1.default.promises.writeFile(node_path_1.default.join(memoryDir, "core.md"), coreMd);
|
|
58
|
+
// Initialize local DB
|
|
59
|
+
const localDbPath = (0, project_1.getLocalDbPath)(cwd);
|
|
60
|
+
const localDb = (0, project_1.initLocalDb)(localDbPath);
|
|
61
|
+
await new Promise((resolve, reject) => {
|
|
62
|
+
localDb.close((err) => {
|
|
63
|
+
if (err)
|
|
64
|
+
reject(err);
|
|
65
|
+
else
|
|
66
|
+
resolve();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
// Global Registry
|
|
70
|
+
// Make sure global registry db is initialized
|
|
71
|
+
const registryDb = (0, registry_1.initRegistryDb)();
|
|
72
|
+
await new Promise((resolve, reject) => {
|
|
73
|
+
registryDb.close((err) => {
|
|
74
|
+
if (err)
|
|
75
|
+
reject(err);
|
|
76
|
+
else
|
|
77
|
+
resolve();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
await (0, registry_1.registerProject)(projectId, cwd);
|
|
81
|
+
// Inject agent instructions
|
|
82
|
+
const memoryInstructions = `
|
|
83
|
+
<!-- local-agent-memory rules -->
|
|
84
|
+
- Consulta la herramienta MCP \`search_memory\` antes de proponer cambios de arquitectura, interfaces o decisiones de diseño.
|
|
85
|
+
- Utiliza \`log_decision\` cada vez que se adopte una convención de código, biblioteca o patrón técnico nuevo.
|
|
86
|
+
- Utiliza la herramienta de sesión para resumir acuerdos antes de dar por terminada una tarea.
|
|
87
|
+
`;
|
|
88
|
+
spinner.stop();
|
|
89
|
+
let tools = [];
|
|
90
|
+
const availableRules = {
|
|
91
|
+
cursor: ".cursorrules",
|
|
92
|
+
windsurf: ".windsurfrules",
|
|
93
|
+
copilot: ".github/copilot-instructions.md",
|
|
94
|
+
claude: "CLAUDE.md",
|
|
95
|
+
"antigravity-ide": ".agents/rules/memory.md",
|
|
96
|
+
"antigravity-cli": ".agents/rules/memory.md",
|
|
97
|
+
};
|
|
98
|
+
if (options.rules) {
|
|
99
|
+
if (options.rules.toLowerCase() === "all") {
|
|
100
|
+
tools = Array.from(new Set(Object.values(availableRules)));
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const rulesList = options.rules
|
|
104
|
+
.split(",")
|
|
105
|
+
.map((r) => r.trim().toLowerCase());
|
|
106
|
+
tools = Array.from(new Set(rulesList
|
|
107
|
+
.map((r) => availableRules[r])
|
|
108
|
+
.filter((r) => Boolean(r))));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
const answers = await inquirer_1.default.prompt([
|
|
113
|
+
{
|
|
114
|
+
type: "checkbox",
|
|
115
|
+
name: "tools",
|
|
116
|
+
message: "Select the IDEs/CLIs to generate or inject memory rules into:",
|
|
117
|
+
choices: [
|
|
118
|
+
{ name: "Cursor (.cursorrules)", value: ".cursorrules" },
|
|
119
|
+
{ name: "Windsurf (.windsurfrules)", value: ".windsurfrules" },
|
|
120
|
+
{
|
|
121
|
+
name: "GitHub Copilot (.github/copilot-instructions.md)",
|
|
122
|
+
value: ".github/copilot-instructions.md",
|
|
123
|
+
},
|
|
124
|
+
{ name: "Claude Code (CLAUDE.md)", value: "CLAUDE.md" },
|
|
125
|
+
{
|
|
126
|
+
name: "Antigravity IDE / CLI (.agents/rules/memory.md)",
|
|
127
|
+
value: ".agents/rules/memory.md",
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
]);
|
|
132
|
+
tools = answers.tools;
|
|
133
|
+
}
|
|
134
|
+
spinner.start("Finishing initialization...");
|
|
135
|
+
for (const file of tools) {
|
|
136
|
+
const filePath = node_path_1.default.join(cwd, file);
|
|
137
|
+
const dir = node_path_1.default.dirname(filePath);
|
|
138
|
+
if (!node_fs_1.default.existsSync(dir)) {
|
|
139
|
+
await node_fs_1.default.promises.mkdir(dir, { recursive: true });
|
|
140
|
+
}
|
|
141
|
+
if (node_fs_1.default.existsSync(filePath)) {
|
|
142
|
+
const content = await node_fs_1.default.promises.readFile(filePath, "utf8");
|
|
143
|
+
if (!content.includes("<!-- local-agent-memory rules -->")) {
|
|
144
|
+
await node_fs_1.default.promises.appendFile(filePath, `\n${memoryInstructions}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
await node_fs_1.default.promises.writeFile(filePath, `${memoryInstructions.trim()}\n`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
spinner.succeed(`Project initialized successfully with ID: ${projectId}`);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
spinner.fail("Failed to initialize project.");
|
|
155
|
+
console.error(err.message);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.recordSession = recordSession;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
|
+
const ora_1 = __importDefault(require("ora"));
|
|
11
|
+
const middleware_1 = require("../../mcp/middleware");
|
|
12
|
+
async function recordSession(options = {}) {
|
|
13
|
+
const cwd = process.cwd();
|
|
14
|
+
const memoryDir = node_path_1.default.join(cwd, ".memory");
|
|
15
|
+
const ledgerDir = node_path_1.default.join(memoryDir, "ledger");
|
|
16
|
+
const dateObj = new Date();
|
|
17
|
+
const year = dateObj.getFullYear();
|
|
18
|
+
const month = String(dateObj.getMonth() + 1).padStart(2, "0");
|
|
19
|
+
const day = String(dateObj.getDate()).padStart(2, "0");
|
|
20
|
+
const hours = String(dateObj.getHours()).padStart(2, "0");
|
|
21
|
+
const minutes = String(dateObj.getMinutes()).padStart(2, "0");
|
|
22
|
+
const seconds = String(dateObj.getSeconds()).padStart(2, "0");
|
|
23
|
+
const filename = `${year}-${month}-${day}-${hours}-${minutes}-${seconds}-session.md`;
|
|
24
|
+
const summariesPath = node_path_1.default.join(ledgerDir, filename);
|
|
25
|
+
if (!node_fs_1.default.existsSync(memoryDir)) {
|
|
26
|
+
console.error("No .memory directory found. Run `memory init` first.");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
let summary = options.summary;
|
|
30
|
+
if (!summary) {
|
|
31
|
+
const answers = await inquirer_1.default.prompt([
|
|
32
|
+
{
|
|
33
|
+
type: "input",
|
|
34
|
+
name: "summary",
|
|
35
|
+
message: "Enter a summary of the work done in this session:",
|
|
36
|
+
validate: (input) => input.trim().length > 0 || "Summary cannot be empty",
|
|
37
|
+
},
|
|
38
|
+
]);
|
|
39
|
+
summary = answers.summary;
|
|
40
|
+
}
|
|
41
|
+
const spinner = (0, ora_1.default)("Recording session summary...").start();
|
|
42
|
+
try {
|
|
43
|
+
if (!node_fs_1.default.existsSync(ledgerDir)) {
|
|
44
|
+
await node_fs_1.default.promises.mkdir(ledgerDir, { recursive: true });
|
|
45
|
+
}
|
|
46
|
+
const date = dateObj.toISOString();
|
|
47
|
+
const content = `\n## Session ended at ${date}\n\n${summary}\n\n---\n`;
|
|
48
|
+
await node_fs_1.default.promises.writeFile(summariesPath, content);
|
|
49
|
+
// Trigger sync
|
|
50
|
+
await (0, middleware_1.syncMiddleware)();
|
|
51
|
+
spinner.succeed(`Session summary recorded successfully in .memory/ledger/${filename}`);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
spinner.fail("Failed to record session summary.");
|
|
55
|
+
console.error(err.message);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function reindexProject(): Promise<void>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.reindexProject = reindexProject;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
const project_1 = require("../../db/project");
|
|
11
|
+
const sync_1 = require("./sync");
|
|
12
|
+
async function reindexProject() {
|
|
13
|
+
const cwd = process.cwd();
|
|
14
|
+
const memoryDir = node_path_1.default.join(cwd, ".memory");
|
|
15
|
+
if (!node_fs_1.default.existsSync(memoryDir)) {
|
|
16
|
+
console.error("Project not initialized. Run memory init first.");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const spinner = (0, ora_1.default)("Reindexing project memory (this may take a while)...").start();
|
|
20
|
+
try {
|
|
21
|
+
const localDbPath = (0, project_1.getLocalDbPath)(cwd);
|
|
22
|
+
// Check if db exists, then delete to force complete reindex
|
|
23
|
+
if (node_fs_1.default.existsSync(localDbPath)) {
|
|
24
|
+
spinner.text = "Deleting corrupted or existing local index...";
|
|
25
|
+
node_fs_1.default.unlinkSync(localDbPath);
|
|
26
|
+
// Also delete the WAL and SHM files if they exist
|
|
27
|
+
if (node_fs_1.default.existsSync(`${localDbPath}-wal`))
|
|
28
|
+
node_fs_1.default.unlinkSync(`${localDbPath}-wal`);
|
|
29
|
+
if (node_fs_1.default.existsSync(`${localDbPath}-shm`))
|
|
30
|
+
node_fs_1.default.unlinkSync(`${localDbPath}-shm`);
|
|
31
|
+
}
|
|
32
|
+
spinner.text = "Rebuilding index from scratch...";
|
|
33
|
+
const { newCount } = await (0, sync_1.doSyncProject)(cwd);
|
|
34
|
+
spinner.succeed(`Reindex complete: ${newCount} files processed.`);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
spinner.fail("Reindex failed.");
|
|
38
|
+
console.error(err.message);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function projectStatus(): Promise<void>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.projectStatus = projectStatus;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
const project_1 = require("../../db/project");
|
|
11
|
+
const registry_1 = require("../../db/registry");
|
|
12
|
+
const sync_1 = require("./sync");
|
|
13
|
+
async function projectStatus() {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
const memoryDir = node_path_1.default.join(cwd, ".memory");
|
|
16
|
+
if (!node_fs_1.default.existsSync(memoryDir)) {
|
|
17
|
+
console.error("Project not initialized. Run memory init first.");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
const spinner = (0, ora_1.default)("Checking project status...").start();
|
|
21
|
+
try {
|
|
22
|
+
const localDbPath = (0, project_1.getLocalDbPath)(cwd);
|
|
23
|
+
// Check global registry
|
|
24
|
+
const projectId = await (0, registry_1.getProjectByPath)(cwd);
|
|
25
|
+
// Check db manifests
|
|
26
|
+
let manifests = [];
|
|
27
|
+
if (node_fs_1.default.existsSync(localDbPath)) {
|
|
28
|
+
manifests = await (0, project_1.getAllManifests)(localDbPath);
|
|
29
|
+
}
|
|
30
|
+
const manifestMap = new Map(manifests.map((m) => [m.file_path, m]));
|
|
31
|
+
const currentFiles = await getAllMarkdownFiles(memoryDir);
|
|
32
|
+
let newCount = 0;
|
|
33
|
+
let modCount = 0;
|
|
34
|
+
let delCount = 0;
|
|
35
|
+
let syncedCount = 0;
|
|
36
|
+
for (const file of currentFiles) {
|
|
37
|
+
const stats = await node_fs_1.default.promises.stat(file);
|
|
38
|
+
const mtime = stats.mtimeMs;
|
|
39
|
+
const relativePath = node_path_1.default.relative(cwd, file);
|
|
40
|
+
const existing = manifestMap.get(relativePath);
|
|
41
|
+
if (!existing) {
|
|
42
|
+
newCount++;
|
|
43
|
+
}
|
|
44
|
+
else if (existing.mtime !== mtime) {
|
|
45
|
+
// Hash check for modified
|
|
46
|
+
const sha256 = await (0, sync_1.computeSha256)(file);
|
|
47
|
+
if (sha256 !== existing.sha256) {
|
|
48
|
+
modCount++;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
syncedCount++;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
syncedCount++;
|
|
56
|
+
}
|
|
57
|
+
manifestMap.delete(relativePath);
|
|
58
|
+
}
|
|
59
|
+
delCount = manifestMap.size;
|
|
60
|
+
spinner.stop();
|
|
61
|
+
console.log(`\nProject ID: ${projectId || "Unknown (Not in global registry)"}`);
|
|
62
|
+
console.log(`Total MD files tracking: ${currentFiles.length}`);
|
|
63
|
+
console.log(`---------------------------------`);
|
|
64
|
+
console.log(`In sync: ${syncedCount}`);
|
|
65
|
+
console.log(`New (untracked): ${newCount}`);
|
|
66
|
+
console.log(`Modified (unsynced): ${modCount}`);
|
|
67
|
+
console.log(`Deleted (orphaned in db): ${delCount}`);
|
|
68
|
+
console.log(`\nRun \`memory sync\` to update index.`);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
spinner.fail("Status check failed.");
|
|
72
|
+
console.error(err.message);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Utility identical to sync, duplication for simplicity of module boundaries, or could be extracted
|
|
77
|
+
async function getAllMarkdownFiles(dirPath, arrayOfFiles = []) {
|
|
78
|
+
const files = await node_fs_1.default.promises.readdir(dirPath, { withFileTypes: true });
|
|
79
|
+
for (const file of files) {
|
|
80
|
+
if (file.isDirectory()) {
|
|
81
|
+
arrayOfFiles = await getAllMarkdownFiles(node_path_1.default.join(dirPath, file.name), arrayOfFiles);
|
|
82
|
+
}
|
|
83
|
+
else if (file.name.endsWith(".md")) {
|
|
84
|
+
arrayOfFiles.push(node_path_1.default.join(dirPath, file.name));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return arrayOfFiles;
|
|
88
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface SyncResult {
|
|
2
|
+
newCount: number;
|
|
3
|
+
modCount: number;
|
|
4
|
+
delCount: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function doSyncProject(cwd: string): Promise<SyncResult>;
|
|
7
|
+
export declare function syncProject(): Promise<void>;
|
|
8
|
+
export declare function computeSha256(filePath: string): Promise<string>;
|