@jossuealcala/madre 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  Todas las versiones publicadas de `@jossuealcala/madre`. Fechas en ISO.
4
4
 
5
+ Una versión se cierra cuando está en npm: hasta entonces su sección se llama **Sin publicar** y puede crecer. Cada versión publicada tiene exactamente una etiqueta `vX.Y.Z`, una release en GitHub y una sección aquí; el parche puede llegar a dos dígitos (`0.2.10`) antes de subir el menor. Ver `docs/ROADMAP.md` para el criterio de qué sube cada número.
6
+
7
+ ## 0.3.0 · 2026-09-18
8
+
9
+ ### Ollama, la inteligencia local (roadmap 2a)
10
+ - Nuevo módulo `OLLAMA`: si Ollama corre en la máquina, los embeddings de la memoria se calculan localmente y la destilación la hace primero un modelo local, gratis y sin que nada salga. MODULES muestra servidor, modelos y roles, descarga los recomendados con `PULL` (progreso en la sala) y permite apagar cada rol o el módulo. Sin Ollama, todo sigue igual.
11
+ - El archivista local pide JSON estructurado y no cuenta contra ningún presupuesto de proveedor; el reporte `memory.distilled` dice `local`, modelo y tokens.
12
+ - Variables: `PULSE_EMBED_PROVIDER`, `PULSE_OLLAMA_HOST`, `PULSE_OLLAMA_MODEL`, `PULSE_OLLAMA_EMBED_MODEL`.
13
+
14
+ ### CONTROL: prevención antes que restauración
15
+ - Mientras un agente tiene CONTROL, los `.env`, `.pulse/`, `.madre/` y `.claude/settings.local.json` quedan en solo lectura a nivel de sistema de archivos y recuperan sus permisos al terminar; el aviso lista qué se bloqueó. `.git/` sigue restaurándose desde el checkpoint después del turno.
16
+
17
+ ### Núcleo
18
+ - `src/room.mjs` pasa de 1204 a 908 líneas: prompt, contexto, CONTROL, escalación, archivista, vectores, presupuesto, GHOST y adjuntos viven ahora en `src/room/`, cada uno con una responsabilidad. Mismo comportamiento, misma suite.
19
+
20
+ ### Dataset y modelo del proyecto (roadmap 2c)
21
+ - `EXPORT DATASET` en MEMORY y `madre dataset` en terminal escriben `train.jsonl` / `valid.jsonl` junto al ledger: los turnos reales de la sala como pares de chat redactados, más las notas destiladas como pares de recuerdo. `docs/training/` trae la receta LoRA con mlx-lm, el `Modelfile` y `train.sh`. Un modelo registrado en Ollama como `madre-<proyecto>` lo toma `@madre` automáticamente.
22
+
23
+ ### @madre, el quinto agente (roadmap 2b)
24
+ - Con Ollama y un modelo de chat, `@madre` entra a la sala: responde desde todo el archivo con citas `[#n]`, dice cuando algo nunca se discutió, nunca escribe ni delega, y sus tokens locales no cuentan. Los orquestadores pueden delegarle pasos de verificación. Entra y sale con Ollama (`agents.updated`); interruptor en MODULES → OLLAMA.
25
+ - El asistente de terminal y `madre doctor` muestran Ollama junto a las CLIs (servidor, modelo de chat, embeddings) y cuentan a `@madre` como agente en línea: con Ollama corriendo la sala abre aunque ninguna CLI tenga sesión, con el aviso de conectar una para trabajar en archivos.
26
+ - `@madre` ya no se confunde de identidad ni promete lo que no hace: la identidad se repite al final del briefing, cada llamada a Ollama pide una ventana de 8k tokens (la de 4k por defecto recortaba el prompt de sistema), las órdenes de acción (convocar, delegar, ejecutar, escribir) se contestan sin llamar al modelo señalando a los agentes CLI, y el chip `TO @madre` dice "memory · answers, does not act". Los modelos de chat generales (`qwen2.5`, `llama3.1`, `gemma3`) van antes que los `-coder`.
27
+ - README: seis módulos con OLLAMA y el SDK enlazado a CONTRIBUTING, enlaces absolutos para que npm los resuelva, estado real del adaptador de Claude (MCP de memoria), y bloque "Primeros cinco minutos" con el recorrido completo desde `npx` hasta conectar las IAs desde MU/TH/UR sin volver a la terminal.
28
+
29
+ ### SDK de módulos
30
+ - `src/modules/sdk.mjs` con `defineModule`: un módulo es un archivo con sus ajustes en `config.json`, su descripción para MODULES, su interruptor, sus rutas y sus hooks. Los seis módulos (AHP+, Image Studio, Git Pulse, AshCode, RIPLEY, Ollama) viven en `src/modules/`; `extensions.mjs` queda como capa de compatibilidad y el servidor monta las rutas de los módulos de forma genérica.
31
+
32
+ ### Memoria configurable desde MU/TH/UR
33
+ - Sección `MEMORY` en CONNECTIONS: archivista preferido, quiénes pueden destilar, cada cuántos intercambios o minutos de reposo, proveedor de embeddings y porcentaje de recall. Se guarda en `config.json` y se aplica en vivo.
34
+
35
+ ## 0.2.3 · 2026-09-18
36
+
37
+ ### RIPLEY navega
38
+ - El visor renderiza HTML como un navegador del proyecto: la página se sirve en `/preview/project/<ruta>`, sus scripts corren y sus rutas relativas a CSS, JS, imágenes y fuentes funcionan. El marco sigue sellado: sin origen propio, sin red, sin formularios, sin acceso a MADRE, y solo carga recursos del proyecto a través de MADRE. Antes los scripts estaban bloqueados y una página construida con JavaScript se veía vacía.
39
+ - Barra mínima en el visor: atrás y recargar, con la ruta y el título de la página en pantalla. Sin URL editable: RIPLEY es un visor del proyecto, no un navegador general.
40
+ - Recarga sola cuando un agente cambia la página abierta o algo de su carpeta, en CONTROL o en un lease.
41
+ - Los errores de la página se ven: un puente de una línea dentro del marco reenvía `window.onerror`, promesas rechazadas y recursos que no cargan; el visor los muestra en una franja con `ASK THE ROOM`, que deja el error y el archivo en el compositor.
42
+
5
43
  ## 0.2.2 · 2026-09-18
6
44
 
7
45
  ### Corrección crítica
package/CONTRIBUTING.md CHANGED
@@ -19,12 +19,36 @@ node ./bin/madre.mjs doctor --catalog # what MU/TH/UR already knows
19
19
  - Agents never write outside their lease. If you widen what an agent may do, the permission modes and MU/TH/UR's catalog must say so.
20
20
  - MU/TH/UR speaks in uppercase and in short sentences; the room speaks like a person. Keep both voices.
21
21
 
22
+ ## Writing a module
23
+
24
+ A module is one file in `src/modules/`, registered in `src/modules/index.mjs`:
25
+
26
+ ```js
27
+ import { defineModule } from './sdk.mjs';
28
+ export default defineModule({
29
+ id: 'night-vision', name: 'Night Vision', vendor: 'MADRE', summary: '…',
30
+ settings: { enabled: false, gain: 2 }, // lives in ~/.pulse/config.json → modules.nightVision
31
+ confirm: 'Send { "confirm": true }…', // optional: the switch asks first
32
+ async status(ctx) { return { detail: '…' }; },// optional: what MODULES shows
33
+ async onToggle(ctx, enabled) {}, // optional: apply live
34
+ routes: [{ method: 'GET', path: '/api/night-vision', handler: async (ctx, { payload }) => ({ status: 200, body: {} }) }],
35
+ });
36
+ ```
37
+
38
+ `ctx` carries `projectRoot`, `config`, `settings`, `agents`, `room`, `readConfig()`, `updateConfig(patch)`, `record(type, payload)` and `services` (what the server offers: `imageKey`, `setImageModule`, `ollama`). A builtin gets a default switch that flips `enabled`, persists and records `extension.toggled`. Add a card branch in `public/app.js` only if the generic switch is not enough, and a condition in `public/troubleshooting.js` so MU/TH/UR knows it.
39
+
22
40
  ## Where things live
23
41
 
24
42
  ```
25
43
  bin/madre.mjs the CLI · start, doctor, setup
26
44
  src/server.mjs HTTP + SSE, settings, modules, sentinel routes
27
- src/room.mjs turns, permission modes, plans, CONTROL, handoff, memory hooks
45
+ src/room.mjs the turn engine: send, dispatch, turns, plans, handoff, scopes
46
+ src/room/ its pieces: prompt (what an agent reads), context (transcript + recall),
47
+ control (checkpoint, diff, undo), escalation (waiting for the human),
48
+ archivist (distillation), vectors (embeddings), budget (token window),
49
+ ghost (off the record), attachments
50
+ src/modules/ one file per module on the SDK (sdk.mjs): ahp, image-studio, git-pulse, ashcode,
51
+ ripley, ollama; index.mjs is the registry, extensions.mjs the compatibility layer
28
52
  src/adapters/ one file per CLI: Codex, Claude Code, Gemini CLI, OpenCode
29
53
  src/memory.mjs SQLite index, distilled notes, vectors, recall
30
54
  src/distiller.mjs the archivist's prompt and parsing
@@ -35,4 +59,11 @@ public/ the room UI; troubleshooting.js is MU/TH/UR's knowledge bas
35
59
  docs/report-collector/ the Worker that turns sentinel reports into issues
36
60
  ```
37
61
 
62
+ ## Versions
63
+
64
+ - A version is **closed only when it is on npm**. Until then its changelog section reads *Sin publicar* and keeps growing; no new number is opened while the previous one is unpublished.
65
+ - One published version = one `vX.Y.Z` tag = one GitHub release = one changelog section. Tags and releases are created at publish time, never before.
66
+ - Patch (`0.2.x`) for fixes and additions inside existing modules; the patch number may reach two digits. Minor (`0.x`) for a new mode, a new module, a new agent, or a change to what leaves the machine. Major when the room's ledger or memory format stops being readable by the previous version.
67
+ - `scripts/release.mjs` does the closing in one go: checks the tree is clean and CI-green, runs the suite and pack:check, turns *Sin publicar* into the dated section, tags, pushes, creates the release and prints the publish command.
68
+
38
69
  Open questions go to issues with the `question` label. Ideas go through `✎ FEEDBACK` in MU/TH/UR or a plain issue. Be kind to the crew.
package/README.md CHANGED
@@ -27,6 +27,15 @@ Una sola sala para conversar con los agentes de IA que ya están instalados en t
27
27
  npx @jossuealcala/madre start
28
28
  ```
29
29
 
30
+ **Primeros cinco minutos.** Node 22.5 o superior y al menos una de estas CLIs con sesión: Codex, Claude Code, Gemini CLI, OpenCode; u Ollama corriendo con un modelo de chat. Entra a la carpeta del proyecto y corre el comando de arriba.
31
+
32
+ 1. MADRE detecta qué CLIs tienes y quién tiene sesión. Si nadie está en línea, MU/TH/UR abre un asistente en la terminal: elige un número para correr el sign-in de esa CLI, `s` para abrir la sala.
33
+ 2. La sala abre en `http://127.0.0.1:4317`. Escribe a un agente con `TO @agente`; el modo del mensaje va en el chip de al lado (`#1 EXCHANGE` por defecto, solo lectura).
34
+ 3. Para conectar o reconectar una IA sin terminal: MU/TH/UR → `⚙ CONNECTIONS`. Codex y Claude tienen botón `SIGN IN`; Gemini y OpenCode muestran el comando exacto para su propio prompt. Ninguna credencial pasa por MADRE.
35
+ 4. Si tienes Ollama, MODULES → OLLAMA ya está encendido: la memoria se embebe localmente y `@madre` aparece en la fila.
36
+ 5. `⚙ CONNECTIONS → MEMORY` ajusta quién destila y cada cuánto; `◉ NOSTROMO` muestra lo que la sala recuerda.
37
+
38
+
30
39
  Tres nombres, tres capas. **MADRE** es el producto: lo que instalas, abres en el navegador y ves junto al logo del latido. **PULSE** es el canal sobre el que corre una sala: el registro de eventos, el bloque de delegación entre agentes, los homes aislados de cada CLI y la carpeta `.pulse/` donde caen los artefactos; por eso esos identificadores conservan su nombre. **MU/TH/UR** es la voz operativa dentro de MADRE: diagnóstico, conexiones y ajustes. El comando `pulse` sigue funcionando como alias de `madre`.
31
40
 
32
41
  ## Uso
@@ -208,22 +217,41 @@ Solo Codex genera imágenes de forma nativa. El módulo **Image Studio** (en `MO
208
217
 
209
218
  ## Módulos
210
219
 
211
- La sala ofrece módulos integrados de MADRE y una integración externa opcional. El botón `MODULES` de la barra lista los disponibles y su estado. Hoy hay cuatro:
220
+ La sala ofrece módulos integrados de MADRE y una integración externa opcional. El botón `MODULES` de la barra lista los disponibles y su estado. Hoy hay seis:
212
221
 
213
222
  - **Git Pulse** (integrado, sin instalación): `/git status`, `/git log [n]`, `/git diff` y `/git branches` traen a la sala la rama, los cambios sin confirmar, los últimos commits o el resumen del diff, en solo lectura y sin gastar un turno de agente. La tarjeta queda en el registro como `command.output` y entra en el contexto que reciben los agentes, así todos razonan sobre los mismos hechos del repositorio. Requiere que el proyecto sea un repositorio git.
214
223
  - **Image Studio** (integrado): ver la sección anterior.
215
- - **RIPLEY** (integrado): actívalo en `MODULES` y el visor de archivos renderiza HTML, SVG y Markdown en lugar de mostrar su código, con un botón `PREVIEW`/`SOURCE` en la cabecera. HTML y SVG se sirven por `/api/preview` dentro de un marco sellado (`sandbox` sin permisos y una CSP que prohíbe scripts, red, formularios y almacenamiento); Markdown se renderiza en el propio visor. Apagado, esos archivos se muestran como texto con una nota. Sirve para ver lo que los agentes crean en `.pulse/out/` sin salir de la sala.
224
+ - **RIPLEY** (integrado): actívalo en `MODULES` y el visor de archivos renderiza HTML, SVG y Markdown en lugar de mostrar su código, con un botón `PREVIEW`/`SOURCE` en la cabecera. HTML y SVG se sirven por `/preview/project/<ruta>` dentro de un marco sellado: los scripts de la página sí corren, pero el marco no tiene origen propio (`sandbox allow-scripts`, sin `same-origin`), no puede hacer peticiones de red, ni enviar formularios, ni navegar la ventana de MADRE, y solo carga CSS, JS, imágenes y fuentes del propio proyecto a través de MADRE. Las rutas relativas de la página funcionan, el visor tiene atrás y recargar, se recarga solo cuando un agente cambia la página abierta, y los errores de la página aparecen en una franja con `ASK THE ROOM`. Markdown se renderiza en el propio visor. Apagado, esos archivos se muestran como texto con una nota. Sirve para ver lo que los agentes crean en `.pulse/out/` sin salir de la sala.
225
+ - **OLLAMA** (integrado): encendido solo si Ollama corre en la máquina. Muestra servidor, modelos y roles (embeddings, archivista, `@madre`), descarga los recomendados con `PULL` y deja apagar cada rol. Ver [Ollama: la inteligencia local](#ollama-la-inteligencia-local).
216
226
  - **AshCode** (integrado, beta): actívalo en `MODULES` para mostrar el botón `$ ash_code` en el campo de texto. Cuando está iluminado, MADRE intenta abreviar localmente mensajes en español o inglés antes de enviarlos a un agente, y pide respuestas concisas. La burbuja muestra el texto enviado y permite desplegar el original; las respuestas abreviadas también conservan el original. Se omite la transformación si detecta código, rutas, enlaces, negaciones, cifras, estructura compleja, idioma incierto o ninguna reducción segura. **Puede cambiar el significado o producir errores: revisa siempre el original y la respuesta.** Menos caracteres no demuestra menos tokens facturados; consulta el uso real del proveedor. Al encenderlo, el campo se ensancha como con CREATE, la etiqueta pasa a `MU/TH/UR · SPECIAL ORDER 937 ›` y la caja hace un guiño a MADRE: un barrido CRT con líneas de fósforo en verde Homebrew (el verde del perfil Homebrew de Terminal.app), que también viste el botón como un prompt de bash con cursor parpadeante. Se apaga desde `ORDER 937` para el siguiente mensaje o se deshabilita en `MODULES`. No requiere npm ni modifica el proyecto: su estado se guarda en la configuración local de MADRE.
217
227
  - **AHP+** (`@jossuealcala/ahp-plus`): estado verificado del proyecto, checkpoints y handoffs entre sesiones de IA, guardado en `.ahp/`. MADRE lo detecta por `.ahp/manifest.json` y lo instala con `npx --yes @jossuealcala/ahp-plus@1.4.1 setup . --platforms <agentes detectados>`, pidiendo adaptadores solo para los agentes presentes en la máquina que AHP+ soporta (Codex, Claude, OpenCode). Una vez instalado, `/ahp status`, `/ahp check` y `/ahp context` consultan su estado desde el campo de texto.
218
228
 
229
+ Cada módulo es un archivo en `src/modules/` declarado con `defineModule`: sus ajustes en `config.json`, su ficha para `MODULES`, su interruptor, sus rutas y sus hooks. Cómo escribir uno, en [CONTRIBUTING.md](https://github.com/jossuealcacao-exe/madre/blob/main/CONTRIBUTING.md#writing-a-module).
230
+
219
231
  Instalar el módulo externo AHP+ es la única acción de módulos con la que MADRE escribe en el proyecto. Por eso el botón muestra primero el comando exacto y exige confirmación; la ejecución se transmite en vivo a la sala y queda registrada en el log como `extension.install.started`, `extension.install.output` y `extension.install.finished`. La consulta a los agentes sigue siendo de solo lectura.
220
232
 
221
233
  ## Modelo de amenazas, en corto
222
234
 
223
235
  - **Qué sale de la máquina.** MADRE no tiene nube ni cuenta: no almacena credenciales, no tiene backend y no envía nada por sí sola. Pero cada agente es una CLI que llama a su proveedor: lo que un agente lee del proyecto puede viajar a OpenAI, Anthropic o Google según su configuración. El único envío propio de MADRE es el del sentinel, y solo si lo activas.
224
- - **Escritura.** En #1 nadie escribe. En #2 la escritura queda confinada a `.pulse/out/<lease>/` por las reglas de cada CLI. En #3 CONTROL el proyecto entero es escribible salvo `.git/`, `.pulse/` y los `.env`: Claude, Gemini y OpenCode reciben esa prohibición como regla previa; Codex entra con su sandbox `workspace-write`, que no admite excluir rutas dentro del proyecto, así que en su caso la zona prohibida se hace cumplir **después del turno**: MADRE compara con el checkpoint y revierte lo que tocó ahí. Eso protege lo que persiste, no impide que un efecto intermedio ocurra durante el turno. Es deuda conocida: prevención antes que restauración. Mientras tanto, si un `.env` es crítico, no des CONTROL a Codex o baja su MAX MODE.
236
+ - **Escritura.** En #1 nadie escribe. En #2 la escritura queda confinada a `.pulse/out/<lease>/` por las reglas de cada CLI. En #3 CONTROL el proyecto entero es escribible salvo `.git/`, `.pulse/` y los `.env`: Claude, Gemini y OpenCode reciben esa prohibición como regla previa; Codex entra con su sandbox `workspace-write`, que no admite excluir rutas dentro del proyecto, así que MADRE añade prevención propia para todos los agentes: mientras dura el turno, los `.env`, `.pulse/`, `.madre/` y `.claude/settings.local.json` quedan en solo lectura a nivel de sistema de archivos y recuperan sus permisos al terminar; el aviso de CONTROL lista qué quedó bloqueado. `.git/` sigue escribible porque las CLIs lo necesitan, y por eso sigue restaurándose desde el checkpoint **después del turno**. Un agente que cambie permisos a propósito cae en esa restauración.
225
237
  - **Memoria.** Todo lo dicho fuera de GHOST queda en `~/.pulse/rooms/<sala>/` y se reinyecta en los prompts de todos los agentes de esa sala. GHOST es la salida para lo que no debe recordarse.
226
238
 
239
+ ## Memoria desde MU/TH/UR
240
+
241
+ En `⚙ CONNECTIONS → MEMORY` se ajusta la memoria sin terminal y se aplica al siguiente turno: quién destila primero (`AUTO` elige al más barato permitido, con Ollama al frente cuando corre), quiénes pueden destilar, cada cuántos intercambios o tras cuántos minutos de reposo, dónde se calculan los embeddings (`AUTO`, `OLLAMA`, `GEMINI`, `OFF`) y qué parte del contexto puede ocupar el recall. Todo queda en `~/.pulse/config.json` bajo `memory`; las variables de entorno, si están puestas, mandan al siguiente arranque.
242
+
243
+ ## @madre, el quinto agente
244
+
245
+ Cuando Ollama corre con un modelo de chat, aparece en la sala un quinto agente: `@madre`. Es la memoria del proyecto convertida en interlocutor: responde desde el archivo completo, notas destiladas y citas exactas con su secuencia `[#n]`, más lo que lleve el mensaje. No escribe, no dibuja, no navega ni delega; cuando la sala nunca habló de algo, lo dice. Los demás agentes pueden delegarle un paso para comprobar qué se decidió. Sus tokens son locales y no cuentan contra ningún presupuesto. Entra y sale con Ollama, y MU/TH/UR lo anuncia; el interruptor `@MADRE IN THE ROOM` en MODULES → OLLAMA lo apaga.
246
+
247
+ ## El modelo del proyecto
248
+
249
+ `MEMORY → EXPORT DATASET` (o `madre dataset`) escribe junto al ledger los turnos reales de la sala como pares de chat redactados, más las notas destiladas, en el formato que leen `mlx-lm` y los demás entrenadores. [`docs/training/`](https://github.com/jossuealcacao-exe/madre/blob/main/docs/training/README.md) explica cómo entrenar un LoRA local sobre un modelo pequeño y registrarlo en Ollama como `madre-<proyecto>`; en cuanto existe, `@madre` responde con él. Es la destilación de MADRE AI: la sala produce el dato, tú decides cuándo entrenar, y el modelo se evalúa contra lo que la sala sí decidió antes de confiar en él.
250
+
251
+ ## Ollama: la inteligencia local
252
+
253
+ Si Ollama corre en la máquina, MADRE lo usa sin configurar nada: los embeddings de la memoria se calculan localmente con el modelo de embeddings disponible (`nomic-embed-text` recomendado) y la destilación de memorias la hace primero el modelo local (`qwen2.5:7b` en una Mac de 16 GB, `qwen2.5:3b` en 8 GB; cualquiera de la familia Qwen, Llama o Gemma sirve, y MADRE prefiere los de chat general sobre los `-coder`), antes que Gemini y el resto. Recordar deja de costar tokens y nada del proyecto sale de la máquina para eso. El módulo `OLLAMA` en MODULES muestra qué hay, permite descargar los modelos recomendados con `PULL`, apagar cada rol y desactivar el módulo. Sin Ollama, todo sigue como antes. `PULSE_OLLAMA_HOST`, `PULSE_OLLAMA_MODEL` y `PULSE_OLLAMA_EMBED_MODEL` eligen host y modelos; `PULSE_EMBED_PROVIDER=gemini` mantiene los embeddings en Gemini aunque Ollama exista.
254
+
227
255
  ## Sentinel de errores y feedback
228
256
 
229
257
  MU/TH/UR tiene una sección SENTINEL. Cuando un turno falla con un error que ninguna condición conocida explica, o el proceso de MADRE se cae, el sentinel guarda un reporte en el registro de la sala (`sentinel.report`): el error con rutas, nombres de usuario, correos y claves eliminados, la versión de MADRE y de Node, la plataforma y las versiones de los agentes detectados. Los repetidos se agrupan por huella durante 24 horas.
@@ -250,7 +278,12 @@ Cada agente tiene un timeout de 180 s por defecto; la burbuja de espera muestra
250
278
  | `PULSE_DISTILL_MAX_CHARS` | `6000` | Tamaño máximo del lote que lee el destilador |
251
279
  | `PULSE_DISTILL_AGENT` | — | Agente destilador preferido; por defecto el más barato disponible |
252
280
  | `PULSE_DISTILL_MODEL` | — | Modelo para la destilación |
253
- | `PULSE_EMBED` | `1` | Embeddings con la clave de Gemini para recall por significado (`0` lo apaga) |
281
+ | `PULSE_EMBED` | `1` | Embeddings para recall por significado (`0` lo apaga) |
282
+ | `PULSE_EMBED_PROVIDER` | `auto` | `ollama`, `gemini` o `auto` (Ollama si corre con modelo de embeddings, si no Gemini) |
283
+ | `PULSE_OLLAMA` | `1` | `0` ignora Ollama por completo |
284
+ | `PULSE_OLLAMA_HOST` | `http://127.0.0.1:11434` | Dónde escucha Ollama |
285
+ | `PULSE_OLLAMA_MODEL` | el mejor disponible | Modelo local para destilar |
286
+ | `PULSE_OLLAMA_EMBED_MODEL` | el mejor disponible | Modelo local de embeddings |
254
287
  | `PULSE_EMBED_MODEL` | `gemini-embedding-001` | Modelo de embeddings |
255
288
  | `PULSE_EMBED_DIMS` | `768` | Dimensiones del vector |
256
289
  | `PULSE_MEMORY_TOOLS` | `1` | Servidor MCP `pulse-memory` adjunto a cada turno (`0` lo quita) |
@@ -283,14 +316,14 @@ Gemini CLI lee `@algo` en el prompt como un archivo a incluir, incluso en modo h
283
316
 
284
317
  - Codex: consulta de solo lectura habilitada.
285
318
  - OpenCode: consulta restringida habilitada; MADRE inyecta permisos efímeros y no modifica la configuración global. Si tu configuración de OpenCode no fija modelo, `run` elige el proveedor por defecto, que puede no ser el que tiene sesión válida; fija `PULSE_OPENCODE_MODEL=proveedor/modelo` (por ejemplo `openai/gpt-5.6-sol`) al arrancar MADRE.
286
- - Claude Code: consulta restringida habilitada con Safe Mode, herramientas locales de lectura, MCP desactivado y sesiones no persistentes.
319
+ - Claude Code: consulta restringida habilitada con Safe Mode, herramientas locales de lectura, MCP limitado al servidor de memoria de MADRE (`pulse-memory`) y sesiones no persistentes.
287
320
  - Gemini CLI: consulta restringida habilitada con Plan Mode y una política efímera que solo permite herramientas locales de lectura. MADRE ejecuta Gemini con un `GEMINI_CLI_HOME` temporal que solo recibe las credenciales existentes (tokens OAuth y `~/.gemini/.env`; una API key guardada en el llavero del sistema funciona sin copia); hooks, extensiones, servidores MCP y memoria del `~/.gemini` real no se cargan. El relanzamiento interno del CLI se desactiva para que el timeout controle el proceso que hace la petición.
288
321
 
289
322
  Todos los adaptadores corren en su propio grupo de procesos. Si un agente no responde antes del timeout, MADRE termina el árbol completo (SIGTERM y, tras un periodo de gracia, SIGKILL), no solo el lanzador.
290
323
 
291
324
  ## Cambios
292
325
 
293
- Ver [CHANGELOG.md](CHANGELOG.md). La versión actual es 0.2.2, beta pública: el núcleo está probado y bajo CI, la superficie sigue cambiando y las decisiones que aún duelen están escritas en el modelo de amenazas. Los problemas se reportan desde MU/TH/UR (`✎ FEEDBACK` o el sentinel) o en [issues](https://github.com/jossuealcacao-exe/madre/issues); la seguridad, según [SECURITY.md](SECURITY.md).
326
+ Ver [CHANGELOG.md](https://github.com/jossuealcacao-exe/madre/blob/main/CHANGELOG.md) y el [roadmap](https://github.com/jossuealcacao-exe/madre/blob/main/docs/ROADMAP.md). La versión actual es 0.3.0, beta pública: el núcleo está probado y bajo CI, la superficie sigue cambiando y las decisiones que aún duelen están escritas en el modelo de amenazas. Los problemas se reportan desde MU/TH/UR (`✎ FEEDBACK` o el sentinel) o en [issues](https://github.com/jossuealcacao-exe/madre/issues); la seguridad, según [SECURITY.md](https://github.com/jossuealcacao-exe/madre/blob/main/SECURITY.md).
294
327
 
295
328
  ## Licencia
296
329
 
package/SECURITY.md CHANGED
@@ -13,7 +13,7 @@ Please include the MADRE version (`madre doctor --json`), the platform, which ag
13
13
 
14
14
  ## Known, accepted for the beta
15
15
 
16
- - In `#3 CONTROL`, Codex's `workspace-write` sandbox cannot exclude paths inside the project; `.git/`, `.pulse/` and `.env` files are restored from the checkpoint **after** the turn instead of being blocked before. Do not grant CONTROL to Codex where an intermediate effect on those files would matter.
16
+ - In `#3 CONTROL`, `.env` files, `.pulse/`, `.madre/` and `.claude/settings.local.json` are made read-only for the length of the turn and restored from the checkpoint afterwards; `.git/` stays writable because the CLIs need it and is only restored after the turn. An agent that changes permissions on purpose is caught by that restoration, not prevented.
17
17
  - Everything said outside `#0 GHOST` is kept in the room's memory and reaches every agent of that room. Use GHOST for what must not be remembered.
18
18
 
19
19
  NOBODY DELETES MOTHER'S MEMORY. EVERYTHING ELSE IS FAIR GAME.
package/bin/madre.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { resolve } from 'node:path';
4
- import { stat } from 'node:fs/promises';
4
+ import { execFileSync } from 'node:child_process';
5
+ import { readFile, stat } from 'node:fs/promises';
5
6
  import { startPulse } from '../src/server.mjs';
6
7
  import { detectAgents } from '../src/runtime-detection.mjs';
7
8
  import { probeAll } from '../src/auth-probe.mjs';
@@ -42,6 +43,14 @@ if (nodeMajor < 22 || (nodeMajor === 22 && nodeMinor < 5)) {
42
43
  console.error(`\n MOTHER › MADRE needs Node 22.5 or newer (found ${process.version}): the room's memory runs on node:sqlite.\n`);
43
44
  process.exit(2);
44
45
  }
46
+ if (has('--version') || has('-v') || command === 'version') {
47
+ // Version and commit, so a report can name exactly what ran.
48
+ const pkg = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
49
+ let commit = '';
50
+ try { commit = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd: new URL('..', import.meta.url).pathname, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { /* installed from npm: no repo */ }
51
+ console.log(`madre ${pkg.version}${commit ? ` · ${commit}` : ''} · node ${process.version}`);
52
+ process.exit(0);
53
+ }
45
54
  const stateRoot = process.env.PULSE_HOME;
46
55
  if (command !== 'help') {
47
56
  const stats = await stat(projectRoot).catch(() => null);
@@ -70,15 +79,38 @@ if (command === 'doctor' && (has('--catalog') || has('--conditions'))) {
70
79
  console.log('');
71
80
  }
72
81
  process.exitCode = 0;
82
+ } else if (command === 'dataset') {
83
+ // Export the room's dataset without opening the room: the same files the server writes.
84
+ const { createHash } = await import('node:crypto');
85
+ const { join, basename, resolve: resolvePath } = await import('node:path');
86
+ const { homedir } = await import('node:os');
87
+ const { realpath } = await import('node:fs/promises');
88
+ const { EventStore } = await import('../src/event-store.mjs');
89
+ const { RoomMemory } = await import('../src/memory.mjs');
90
+ const { exportDataset } = await import('../src/dataset.mjs');
91
+ const root = stateRoot ?? process.env.PULSE_HOME ?? join(homedir(), '.pulse');
92
+ const canonical = await realpath(projectRoot).catch(() => resolvePath(projectRoot));
93
+ const roomId = `${basename(canonical) || 'root'}-${createHash('sha256').update(canonical).digest('hex').slice(0, 16)}`;
94
+ const roomDir = join(root, 'rooms', roomId);
95
+ const store = await new EventStore(join(roomDir, 'events.jsonl')).initialize();
96
+ const memory = await new RoomMemory(join(roomDir, 'memory.sqlite')).initialize(store).catch(() => null);
97
+ const result = await exportDataset({ events: await store.readAll(), notes: memory ? memory.memories({ limit: 5000 }) : [], dir: join(roomDir, 'dataset'), project: basename(canonical), home: homedir() });
98
+ memory?.close();
99
+ if (has('--json')) console.log(JSON.stringify(result, null, 2));
100
+ else console.log(`\nMADRE dataset · ${result.project}\n\n pairs ${result.pairs} (${result.turns} turns · ${result.notes} notes)\n train ${result.train}\n valid ${result.valid}\n by agent ${Object.entries(result.byAgent).map(([id, n]) => `@${id} ${n}`).join(' · ') || '-'}\n folder ${result.dir}\n\n Train it: docs/training/README.md (mlx-lm on Apple Silicon), then \`ollama create madre-${result.project.toLowerCase().replace(/[^a-z0-9]+/g, '-')}\` and @madre picks it up.\n`);
101
+ process.exitCode = 0;
73
102
  } else if (command === 'doctor') {
74
103
  const agents = await detectAgents();
75
104
  const probes = await probeAll(agents);
105
+ const { localIntelligence, madreOnline } = await import('../src/setup.mjs');
106
+ const ollama = await localIntelligence();
76
107
  const report = agents.map((agent) => ({ ...agent, session: probes[agent.id] }));
77
108
  const result = {
78
- ok: report.some((agent) => isOnline(agent, agent.session)),
109
+ ok: report.some((agent) => isOnline(agent, agent.session)) || madreOnline(ollama),
79
110
  node: process.version,
80
111
  project: projectRoot,
81
112
  agents: report,
113
+ ollama: { running: Boolean(ollama.running), chatModel: ollama.chatModel ?? null, embedModel: ollama.embedModel ?? null, madre: madreOnline(ollama) },
82
114
  };
83
115
 
84
116
  if (has('--json')) {
@@ -90,6 +122,7 @@ if (command === 'doctor' && (has('--catalog') || has('--conditions'))) {
90
122
  const session = agent.detected ? ` · ${agent.session.state}${agent.session.detail ? ` (${agent.session.detail})` : ''}` : '';
91
123
  console.log(` ${agent.label.padEnd(10)} ${mark}${agent.version ? ` · ${agent.version}` : ''}${session}`);
92
124
  }
125
+ console.log(` ${'Ollama'.padEnd(10)} ${ollama.disabled ? 'ignored (PULSE_OLLAMA=0)' : ollama.running ? `running${ollama.chatModel ? ` · @madre with ${ollama.chatModel}` : ' · no chat model yet'}${ollama.embedModel ? ` · embeddings ${ollama.embedModel}` : ''}` : 'not running · optional'}`);
93
126
  console.log(`\n Project ${result.project}`);
94
127
  console.log(result.ok ? '\nReady to start. Known conditions and fixes: `madre doctor --catalog [query]`.\n' : '\nNo agent is online. Run `madre setup`. Known conditions and fixes: `madre doctor --catalog`.\n');
95
128
  }
@@ -109,7 +142,8 @@ if (command === 'doctor' && (has('--catalog') || has('--conditions'))) {
109
142
  if (process.stdin.isTTY && process.stdout.isTTY && !has('--no-setup')) {
110
143
  const agents = await detectAgents();
111
144
  const probes = await probeAll(agents);
112
- if (!agents.some((agent) => isOnline(agent, probes[agent.id]))) {
145
+ const { localIntelligence, madreOnline } = await import('../src/setup.mjs');
146
+ if (!agents.some((agent) => isOnline(agent, probes[agent.id])) && !madreOnline(await localIntelligence())) {
113
147
  const { action } = await runSetup({ projectRoot, stateRoot });
114
148
  if (action !== 'start') process.exit(0);
115
149
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jossuealcala/madre",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "MADRE: one local room where the AI coding agents already installed on your machine (Codex, Claude Code, Gemini CLI, OpenCode) work on a project together over the PULSE channel: read-only by default, per-message permission modes up to a checkpointed CONTROL, a shared memory every agent recalls and queries, and NOSTROMO to browse it.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -55,9 +55,10 @@
55
55
  "scripts": {
56
56
  "start": "node ./bin/madre.mjs start",
57
57
  "doctor": "node ./bin/madre.mjs doctor",
58
- "test": "node --test",
58
+ "test": "node --import ./test/setup.mjs --test",
59
59
  "check": "npm test && node ./bin/madre.mjs doctor --json",
60
- "pack:check": "node ./scripts/pack-check.mjs"
60
+ "pack:check": "node ./scripts/pack-check.mjs",
61
+ "release": "node ./scripts/release.mjs"
61
62
  },
62
63
  "publishConfig": {
63
64
  "access": "public"