@jossuealcala/madre 0.3.3 → 0.4.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +497 -3
  2. package/CONTRIBUTING.md +3 -1
  3. package/README.md +68 -186
  4. package/SECURITY.md +2 -1
  5. package/bin/madre.mjs +56 -13
  6. package/docs/INTERNALS.md +16 -0
  7. package/docs/REFERENCE.md +249 -0
  8. package/docs/SDK.md +121 -0
  9. package/docs/room.png +0 -0
  10. package/docs/sdk/hello-module.mjs +51 -0
  11. package/package.json +9 -1
  12. package/public/app.js +3979 -867
  13. package/public/es.js +2258 -0
  14. package/public/i18n.js +66 -0
  15. package/public/index.html +96 -15
  16. package/public/inquiry.js +220 -0
  17. package/public/resay.js +77 -0
  18. package/public/styles.css +622 -65
  19. package/public/troubleshooting.js +255 -46
  20. package/src/adapters/claude.mjs +2 -1
  21. package/src/adapters/codex.mjs +2 -1
  22. package/src/adapters/gemini.mjs +6 -5
  23. package/src/adapters/opencode.mjs +2 -1
  24. package/src/adapters/process.mjs +79 -20
  25. package/src/asking.mjs +128 -0
  26. package/src/auth-probe.mjs +58 -1
  27. package/src/chats.mjs +193 -0
  28. package/src/checkpoint.mjs +1 -1
  29. package/src/cold.mjs +56 -0
  30. package/src/commands.mjs +6 -0
  31. package/src/conversation-context.mjs +35 -3
  32. package/src/credentials.mjs +145 -0
  33. package/src/dataset.mjs +56 -4
  34. package/src/distiller.mjs +12 -5
  35. package/src/event-store.mjs +14 -8
  36. package/src/exam.mjs +240 -0
  37. package/src/extensions.mjs +3 -2
  38. package/src/eyecat-watch.mjs +100 -0
  39. package/src/eyecat.mjs +169 -0
  40. package/src/i18n.mjs +47 -0
  41. package/src/image-studio.mjs +2 -0
  42. package/src/launch.mjs +61 -0
  43. package/src/maturity.mjs +94 -0
  44. package/src/mcp/image-server.mjs +36 -3
  45. package/src/mcp/memory-server.mjs +1 -1
  46. package/src/memory.mjs +325 -17
  47. package/src/modules/ahp.mjs +9 -7
  48. package/src/modules/ash.mjs +36 -0
  49. package/src/modules/git-pulse.mjs +5 -3
  50. package/src/modules/helpers.mjs +31 -0
  51. package/src/modules/image-studio.mjs +10 -4
  52. package/src/modules/index.mjs +141 -9
  53. package/src/modules/ollama.mjs +66 -10
  54. package/src/modules/playwright.mjs +44 -23
  55. package/src/modules/ripley.mjs +5 -3
  56. package/src/modules/sdk.mjs +93 -2
  57. package/src/modules/updates.mjs +81 -0
  58. package/src/ollama.mjs +5 -2
  59. package/src/outbound.mjs +297 -0
  60. package/src/privacy.mjs +54 -7
  61. package/src/room/context.mjs +4 -4
  62. package/src/room/economy.mjs +161 -0
  63. package/src/room/prompt.mjs +118 -46
  64. package/src/room.mjs +443 -44
  65. package/src/runtime-detection.mjs +27 -8
  66. package/src/sentinel-errors.mjs +19 -1
  67. package/src/server.mjs +709 -71
  68. package/src/setup.mjs +1 -1
  69. package/src/updates.mjs +4 -2
  70. package/src/usage-sentinel.mjs +13 -8
  71. package/src/verdict.mjs +74 -0
  72. package/src/ashcode.mjs +0 -64
  73. package/src/modules/ashcode.mjs +0 -28
package/CONTRIBUTING.md CHANGED
@@ -22,6 +22,8 @@ node ./bin/madre.mjs start --no-open # the room from source, URL in the termi
22
22
 
23
23
  ## Writing a module
24
24
 
25
+ For a module of your own, outside this repository, read `docs/SDK.md`: one `.mjs` file in `~/.pulse/modules/` or `<project>/.madre/modules/`, no build, reloaded from MODULES. What follows is for modules that ship inside MADRE.
26
+
25
27
  A module is one file in `src/modules/`, registered in `src/modules/index.mjs`:
26
28
 
27
29
  ```js
@@ -71,7 +73,7 @@ docs/report-collector/ the Worker that turns sentinel reports into issues
71
73
 
72
74
  - 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.
73
75
  - One published version = one `vX.Y.Z` tag = one GitHub release = one changelog section. Tags and releases are created at publish time, never before.
74
- - 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.
76
+ - Patch (`0.x.Y`) 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.
75
77
  - `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.
76
78
 
77
79
  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
@@ -1,259 +1,141 @@
1
1
  <p align="center">
2
- <a href="https://jossuealcala.com/en/"><img src="https://raw.githubusercontent.com/jossuealcacao-exe/madre/main/docs/madre-banner.svg" alt="MADRE · MU/TH/UR 6000 · INTERFACE 2037" width="100%"></a>
2
+ <a href="https://madre.run"><img src="https://raw.githubusercontent.com/jossuealcacao-exe/madre/main/docs/madre-banner.svg" alt="MADRE · MU/TH/UR 6000 · INTERFACE 2037" width="100%"></a>
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
6
  <a href="https://www.npmjs.com/package/@jossuealcala/madre"><img alt="npm" src="https://img.shields.io/npm/v/@jossuealcala/madre?style=flat-square&label=npm&color=9bff66&labelColor=050605"></a>
7
7
  <a href="https://github.com/jossuealcacao-exe/madre/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/jossuealcacao-exe/madre/ci.yml?style=flat-square&label=CI&color=9bff66&labelColor=050605"></a>
8
- <img alt="status" src="https://img.shields.io/badge/status-beta-ffb000?style=flat-square&labelColor=050605">
9
8
  <img alt="node" src="https://img.shields.io/badge/node-%E2%89%A5%2022.5-9bff66?style=flat-square&labelColor=050605">
10
- <img alt="dependencies" src="https://img.shields.io/badge/dependencies-0-9bff66?style=flat-square&labelColor=050605">
11
- <img alt="license" src="https://img.shields.io/badge/license-Apache--2.0-9bff66?style=flat-square&labelColor=050605">
12
- <img alt="crew" src="https://img.shields.io/badge/crew-Codex%20%C2%B7%20Claude%20%C2%B7%20Gemini%20%C2%B7%20OpenCode%20%C2%B7%20%40madre-9bff66?style=flat-square&labelColor=050605">
9
+ <img alt="dependencies" src="https://img.shields.io/badge/dependencias-0-9bff66?style=flat-square&labelColor=050605">
10
+ <img alt="license" src="https://img.shields.io/badge/licencia-Apache--2.0-9bff66?style=flat-square&labelColor=050605">
13
11
  </p>
14
12
 
15
13
  ```
16
- MU/TH/UR 6000 · INTERFACE 2037 · MADRE IS READY · BETA
17
-
18
- ONE LOCAL ROOM · FOUR AI CODING AGENTS · ONE SHARED MEMORY · A FIFTH AGENT THAT IS THE MEMORY ITSELF
19
- READ-ONLY BY DEFAULT · CONTROL WHEN YOU SAY SO · NOTHING LEAVES THIS MACHINE ON ITS OWN
14
+ MU/TH/UR 6000 · INTERFACE 2037 · MADRE IS READY
20
15
  ```
21
16
 
22
- # MADRE
17
+ <br>
23
18
 
24
- Una sala local donde los agentes de IA que ya tienes instalados trabajan juntos sobre un proyecto real y comparten una memoria. *One local room where Codex, Claude Code, Gemini CLI and OpenCode work on a project together, with a memory every one of them recalls.*
19
+ # Los agentes que ya tienes.<br>Una sala. Una memoria.
25
20
 
26
- MADRE no es una nube ni una cuenta. Lanza el CLI de cada agente como proceso, con la sesión que ese CLI ya tiene, y mantiene el proyecto bajo tu control: los agentes leen por defecto, crean solo donde tú dices y editan solo cuando armas CONTROL.
21
+ Codex, Claude Code, Gemini CLI y OpenCode trabajando sobre el mismo proyecto,
22
+ en la misma conversación, recordando lo mismo.
27
23
 
28
24
  ```
29
25
  npx @jossuealcala/madre start
30
26
  ```
31
27
 
32
- ---
33
-
34
- ## ARRANQUE · LOS PRIMEROS CINCO MINUTOS
28
+ Eso es todo. No hay cuenta que crear, ni nube que configurar, ni llave que pegar.
29
+ MADRE usa las sesiones que esas CLIs ya tienen en tu máquina.
35
30
 
36
- Necesitas Node 22.5 o superior y al menos una de estas CLIs con sesión iniciada: **Codex**, **Claude Code**, **Gemini CLI**, **OpenCode**. Si además corre **Ollama** con un modelo de chat, la sala tiene un quinto agente local.
37
-
38
- 1. Entra a la carpeta del proyecto y corre `npx @jossuealcala/madre start`. MADRE detecta qué CLIs tienes y quién tiene sesión. Si nadie está en línea, abre un asistente en la terminal: un número corre el inicio de sesión de esa CLI, `s` abre la sala.
39
- 2. La sala abre en `http://127.0.0.1:4317`. Elige un agente en el selector o escribe `@claude …`. El chip junto a `to @agente` dice en qué modo sale el mensaje; `#1 EXCHANGE`, solo lectura, es el predeterminado.
40
- 3. Para conectar o reconectar una IA sin terminal: `MU/TH/UR → ⚙ CONNECTIONS`. Codex y Claude tienen `SIGN IN`; Gemini y OpenCode muestran el comando exacto para su propio prompt. Ninguna credencial pasa por MADRE.
41
- 4. Con Ollama, `MODULES → OLLAMA` ya está encendido: la memoria se embebe localmente y `@madre` aparece en la fila.
42
- 5. `⚙ CONNECTIONS → MEMORY` ajusta quién destila la memoria y cada cuánto. `◉ NOSTROMO` muestra lo que la sala recuerda.
31
+ <p align="center">
32
+ <img src="https://raw.githubusercontent.com/jossuealcacao-exe/madre/main/docs/room.png" alt="La sala de MADRE: Codex y Claude respondiendo en el mismo hilo sobre el mismo proyecto" width="100%">
33
+ </p>
43
34
 
44
- ```bash
45
- npx @jossuealcala/madre doctor # qué agentes hay, quién tiene sesión, si hay versión nueva
46
- npx @jossuealcala/madre setup # el asistente de conexión, cuando quieras
47
- npx @jossuealcala/madre start --no-open --project RUTA
48
- ```
35
+ <p align="center"><sub>Una pregunta. Dos agentes. El segundo leyó al primero.</sub></p>
49
36
 
50
- **Tres nombres.** **MADRE** es el producto. **MU/TH/UR** es su voz operativa: diagnóstico, conexiones, ajustes. **PULSE** es el canal sobre el que corre la sala: el registro de eventos, el bloque de delegación, la carpeta `.pulse/` donde caen los artefactos; por eso esos identificadores conservan su nombre.
37
+ <br>
51
38
 
52
39
  ---
53
40
 
54
- ## LA SALA
55
-
56
- **La barra.** La marca con su latido, la raíz del proyecto, la tripulación, `LIVE`, las alertas (`STOP ALL`, versión nueva), `MODULES`, `MU/TH/UR`, tema y panel de archivos. Cada esfera de agente lleva un anillo con su consumo: el límite real del proveedor cuando el CLI lo publica, si no, la ventana local de MADRE. Un clic la despliega.
41
+ <br>
57
42
 
58
- **Las burbujas.** Cada respuesta dice quién habla, a quién, en qué modo y con qué modelo; debajo, hora, tokens del turno y acumulado. Markdown completo, tablas, bloques de código con COPY. Las rutas que un agente menciona (`src/room.mjs:42`) abren el visor. Al pasar el ratón: valorar bien o mal, copiar, responder con otro agente.
43
+ ## Una conversación, no cuatro terminales
59
44
 
60
- **El compositor.**
45
+ Escribes `@codex` y contesta Codex. Escribes `@claude` y contesta Claude, **leyendo lo que Codex acaba de decir**. Pides una mesa redonda y cada uno opina por turno sobre lo mismo.
61
46
 
62
- | Escribes | Pasa |
63
- |---|---|
64
- | `@codex`, `@claude`, `@gemini`, `@opencode`, `@madre` | Menciona a un agente; `@` abre la lista |
65
- | `!src/room.mjs:12-20` | Adjunta un archivo del proyecto, con líneas si las das; `!` busca entre los archivos |
66
- | `#0` a `#3` | Fija el modo de permiso de ese mensaje |
67
- | `/create …`, `/image …`, `/stopall`, `/git …`, `/ahp …` | Comandos; `/` abre el menú. `/git push` muestra qué saldría; solo `/git push confirm` lo envía |
68
- | `STOPALL` | Freno maestro |
69
- | Clip, arrastrar o pegar una imagen | Adjunto para la sala, nunca en el proyecto |
70
- | `@madre, pregúntale al crew …` | Mesa redonda: un paso por agente en línea, cierre citado |
47
+ Hoy eso son cuatro ventanas, cuatro contextos y tú copiando y pegando entre ellas. MADRE lo vuelve un hilo.
71
48
 
72
- Un segundo clic en la esfera elegida, o el chip `default model ▾`, elige el modelo de esa petición: Codex, Claude, Gemini y OpenCode con sus listas reales, o un nombre libre. Se recuerda por agente.
49
+ ## Una memoria que sobrevive a la sesión
73
50
 
74
- **Archivos.** El icono de la derecha abre el árbol del proyecto en solo lectura. El visor numera líneas; clic y Shift+clic seleccionan un rango y `REVIEW WITH` lo manda a un agente. Con **RIPLEY** encendido en `MODULES`, HTML, SVG y Markdown se renderizan en un marco sellado en lugar de mostrar su código, con barra atrás/recargar y recarga automática cuando un agente cambia el archivo.
51
+ Todo lo que se dice queda indexado. Cuando la conversación pasa de la ventana de contexto, cada agente recibe de vuelta lo que hace falta, citado. Cada diez intercambios, la sala destila lo dicho en notas con tipo: una decisión, un hecho, una preferencia, una pregunta abierta.
75
52
 
76
- **Tema.** Sol/luna en la barra: automático, claro u oscuro. El claro no brilla; la pantalla de MU/TH/UR sigue siendo fósforo.
53
+ Preguntas *«¿por qué elegimos Postgres?»* tres semanas después y la sala contesta, aunque esa decisión la tomara otro agente en otra sesión.
77
54
 
78
- ---
55
+ Con [Ollama](https://ollama.com) corriendo, esa memoria además **habla**: `@madre` es un quinto agente, local, gratis, que responde desde todo el archivo con citas y no escribe nada.
79
56
 
80
- ## MODOS DE PERMISO
57
+ ## Leen por defecto. Escriben cuando tú lo dices.
81
58
 
82
- Cada mensaje sale con un modo. Tu modo es el techo de cualquier plan que ese mensaje arranque.
59
+ Cada mensaje sale con un modo de permiso, y tu modo es el techo de todo lo que ese mensaje arranque.
83
60
 
84
- | Modo | Nombre | Qué permite |
61
+ | | | |
85
62
  |---|---|---|
86
- | `#0` | **GHOST** | Fuera del registro. No se escribe en el ledger, nadie lo recuerda, desaparece al recargar. Los tokens sí cuentan. |
87
- | `#1` | **EXCHANGE** | Leer el proyecto y coordinar. El predeterminado. |
88
- | `#2` | **CREATE** | Añadir archivos y carpetas nuevos donde corresponda en el proyecto. Lo que ya existía no cambia: si un agente lo toca, MADRE lo restaura al terminar y lo dice. |
89
- | `#3` | **CONTROL** | Editar el proyecto real sin aprobación por acción. Un titular por sala, checkpoint antes, lista de cambios y `UNDO` después. |
90
- | `#4` | **AIRLOCK** | Todo lo de CONTROL más ejecutar comandos: pruebas, builds, `git push`, deploys con las CLIs y sesiones que ya hay en la máquina. Los archivos vuelven con `UNDO`; lo que sale de la nave, no. La anulación pide dos llaves: la designación y la palabra `AIRLOCK`. |
63
+ | `#1` | **EXCHANGE** | Leer y coordinar. El predeterminado. |
64
+ | `#2` | **CREATE** | Añadir archivos nuevos. Lo que ya existía se restaura al terminar. |
65
+ | `#3` | **CONTROL** | Editar el proyecto. Checkpoint antes, lista de cambios y `UNDO` después. |
66
+ | `#4` | **AIRLOCK** | Comandos, `git push`, deploys. Pide dos llaves. |
91
67
 
92
- **CREATE por dentro.** El agente decide dónde va lo nuevo según las convenciones del proyecto, y crea carpetas si hace falta; `.pulse/out/<turno>/` queda como borrador para lo que no tiene sitio. MADRE toma un checkpoint antes del turno y, al terminar, conserva lo que apareció, lo muestra bajo la respuesta como artefactos, y restaura cualquier archivo previo que se haya modificado, renombrado o borrado, avisando en la sala. Las CLIs reciben sus herramientas de escritura sobre el proyecto y la instrucción de no tocar lo existente; la garantía la da la restauración de MADRE al terminar, no la regla previa.
68
+ También hay `#0 GHOST`, para lo que no debe quedar en ninguna parte.
93
69
 
94
- **CONTROL por dentro.** Antes del turno, un commit real bajo `refs/madre/checkpoints/` que no toca tu rama, tu índice ni tu stash. Durante el turno, `.git/`, `.pulse/`, `.madre/`, los `.env` y `.claude/settings.local.json` quedan en solo lectura a nivel de sistema de archivos y recuperan sus permisos al terminar. Después, la lista de archivos añadidos, modificados y borrados, y `UNDO` restaura el checkpoint. Armar `#3` pide la designación del proyecto; armar `#4` pide además la palabra `AIRLOCK`. En `#4` cada CLI recibe su herramienta de comandos (Codex sin sandbox de red, Claude `Bash`, Gemini `run_shell_command`, OpenCode `bash`) y la instrucción de decir en una línea qué va a salir y adónde antes de que salga.
70
+ Subir a `#3` o `#4` es una ceremonia deliberada: MADRE te pide el nombre de la carpeta del proyecto, escrito por ti. Ningún agente puede concederse un permiso escribiéndolo en su respuesta.
95
71
 
96
- **Escalación.** Si un plan en `#1` llega a un paso que pide crear algo, la sala se detiene y pregunta: `GRANT ONCE · GRANT FOR PLAN · DENY`, con cronómetro de tres minutos. Solo el humano concede; un permiso escrito por un agente dentro de la conversación no cuenta.
97
-
98
- **Modo por paso.** Un orquestador puede pedir el modo de cada paso: `@codex #2: crea la página`, `@claude #3: arregla el router`. MADRE lo acota al modo de tu mensaje y al `MAX MODE` de ese agente. Con tu mensaje en `#3`, la palabra del orquestador basta; con tu mensaje en `#1`, un paso `#2` pasa por la escalación.
99
-
100
- **Dos controles por agente.** En `⚙ CONNECTIONS` cada agente tiene `MAX MODE`, hasta dónde puede llegar un mensaje dirigido a él, y `DEFAULT MODE`, dónde empieza: `#1` solo lectura hasta que armes CREATE, o `#2` para que cada turno pueda añadir archivos sin pedirlo. Aparte, dos habilidades: generar imágenes y web.
72
+ <br>
101
73
 
102
74
  ---
103
75
 
104
- ## QUÉ PUEDE CADA AGENTE
76
+ <br>
105
77
 
106
- | | Lee el proyecto | Recibe imágenes | Crea archivos acotado | Genera imágenes | Web |
107
- |---|---|---|---|---|---|
108
- | **Codex** | sí | `-i` | sandbox de escritura | sí, nativo, con su cuenta de ChatGPT | `web_search` |
109
- | **Claude Code** | sí | lectura de la ruta | reglas de permiso por ruta | sí, con **Image Studio** | WebFetch / WebSearch |
110
- | **Gemini CLI** | sí | `read_file` | política por patrón | sí, con **Image Studio** | `google_web_search` |
111
- | **OpenCode** | sí | `-f` | permisos `edit` por patrón | sí, con **Image Studio** | webfetch / websearch |
112
- | **@madre** | la memoria | no | no | no | no |
78
+ ## Lo que MADRE no es
113
79
 
114
- **Image Studio** es un módulo de MADRE: un servidor MCP propio que expone `generate_image` sobre los modelos de imagen de la API de Gemini, con tu propia key y tus créditos. Se conecta a Claude, Gemini y OpenCode solo dentro de un permiso CREATE con el alcance de imágenes encendido, en sus homes aislados, y la imagen cae en la carpeta del turno como cualquier artefacto. `/image <petición>` arma el permiso y enruta al agente que puede generar. Con los cuatro agentes puedes pedir una imagen; Codex la hace con lo suyo, los demás con Image Studio.
80
+ Esto importa más que cualquier función.
115
81
 
116
- El acceso web es un permiso permanente por agente, apagado por defecto, que se enciende en `⚙ CONNECTIONS`.
82
+ **No hay nube.** MADRE no tiene servidor, ni cuenta, ni backend. Corre en `127.0.0.1` y se muere cuando cierras la terminal.
117
83
 
118
- ---
84
+ **No guarda tus credenciales.** Cuando das una llave, MADRE la escribe en el archivo donde ese CLI la busca, cerrada a tu usuario, y no conserva copia. Nunca en su config, ni en su registro, ni en sus logs.
119
85
 
120
- ## DELEGACIÓN
86
+ **Nada sale por su cuenta.** Lo que un agente lee viaja a *su* proveedor, con tu cuenta y tus límites — como si lo hubieras corrido en tu terminal, porque eso es exactamente lo que MADRE hace. Los dos únicos envíos propios están bajo tu interruptor: una consulta diaria a npm por si hay versión nueva, y los reportes de fallos, apagados por defecto.
121
87
 
122
- Un agente puede poner a trabajar a los demás. Si le pides coordinar, termina su respuesta con un bloque `pulse`, un paso por línea:
88
+ **Y te deja leerlo.** Dentro de la sala puedes abrir el documento exacto que MADRE escribe en tu nombre cada turno, el comando exacto con el que lo entrega, y el registro de cada llamada que salió de esta máquina. No es una promesa: es una pantalla.
123
89
 
124
- ```
125
- @gemini: Sintetiza en un párrafo quién es el autor, separando hechos de inferencias.
126
- @codex: Misma pregunta; señala la afirmación menos sustentada.
127
- @claude: Compara ambas síntesis y marca dónde divergen.
128
- ```
129
-
130
- MADRE ejecuta los pasos como turnos normales: cada uno queda en el ledger, pasa por handoff, presupuesto y timeout. El paso dirigido al orquestador es su turno de cierre. Los delegados no delegan, así que todo plan termina. Máximo cuatro pasos más el cierre.
90
+ **Cero dependencias.** Node y nada más.
131
91
 
132
- **STOP ALL.** El botón de la barra, `STOPALL` en el compositor o `curl -X POST http://127.0.0.1:4317/api/stop-all` detienen todos los planes y matan todos los procesos de agente. MU/TH/UR avisa en rojo cuando la sala se escapa: tres agentes a la vez, un agente con dos turnos cruzados, un plan de más de cinco minutos.
92
+ <br>
133
93
 
134
94
  ---
135
95
 
136
- ## MEMORIA
137
-
138
- Todo lo dicho fuera de GHOST queda indexado junto al ledger, en `~/.pulse/rooms/<sala>/memory.sqlite`. Nada de esto requiere un comando: ocurre solo.
139
-
140
- **Recall.** Cuando la conversación excede la ventana de contexto, cada turno recibe los intercambios anteriores que coinciden con la petición, citados con su número de secuencia, para cualquier agente.
141
-
142
- **Notas destiladas.** Cada diez intercambios o tras diez minutos de reposo, el archivista lee lo no destilado y guarda hasta cinco notas tipadas: decisión, hecho, preferencia, pregunta abierta. El archivista es el agente más barato permitido; con Ollama es el modelo local y no cuesta nada.
143
-
144
- **Por significado.** Con Ollama (`nomic-embed-text`) o con una key de Gemini, intercambios y notas se embeben en segundo plano; una pregunta en español encuentra una decisión escrita en inglés.
145
-
146
- **Herramientas.** Cada turno lleva adjunto el servidor MCP `pulse-memory`: `memory_search`, `memory_recall`, `memory_notes`, `memory_timeline`, `project_state` y `memory_note`, que guarda una nota cuando tú pides explícitamente recordar algo. La burbuja muestra `◉ memory saved`.
147
-
148
- **NOSTROMO.** El mapa de la memoria, detrás de la designación del proyecto: MADRE es un sol rojo que late, cada memoria un planeta en el color de su tipo, unidos por venas que pulsan. Se navega, se lee y solo se puede borrar, con doble confirmación. Ocho toques al corazón activan CODE000 y sellan el archivo diez minutos.
149
-
150
- **@madre.** Con Ollama y un modelo de chat, la memoria habla: `@madre` responde desde todo el archivo con citas `[#n]`, dice cuando la sala nunca discutió algo, no escribe ni ejecuta, y convoca al crew si se lo pides. Sus tokens son locales y no cuentan. Cuando existe un modelo entrenado con la sala, lo usa.
151
-
152
- **Ajustes.** `⚙ CONNECTIONS → MEMORY`: quién destila, quiénes pueden, cada cuánto, dónde se embebe, cuánto contexto puede ocupar el recall. Todo en `~/.pulse/config.json`, sin reiniciar.
96
+ <br>
153
97
 
154
- **Privacidad.** Una CLI corre con su propio contexto privado y puede escribirlo en una respuesta. En `⚙ CONNECTIONS → PRIVACY` nombras los términos que no deben viajar por la sala: MADRE los sustituye por `[ENTIDAD-ORG]` antes de que lleguen al ledger, al archivista, a los demás agentes o al dataset, y `PURGE ROOM` limpia lo que la sala ya tenía. Los términos viven en `config.json`; la sala solo registra cuántos.
98
+ ## Empezar
155
99
 
156
- ---
157
-
158
- ## MADRE AI · TU PROPIO MODELO
100
+ Necesitas **Node 22.5** o superior y al menos una de estas CLIs con sesión iniciada: Codex, Claude Code, Gemini CLI u OpenCode.
159
101
 
160
- La sala acumula material de entrenamiento mientras trabajas. `MEMORY` muestra en vivo cuántos pares limpios lleva hacia un LoRA, con 300 como objetivo: turnos humano→agente, pasos delegados y notas destiladas; fuera quedan las respuestas de `@madre`, las enlatadas y lo que marques como mala respuesta con el pulgar de cada burbuja.
102
+ ```
103
+ cd tu-proyecto
104
+ npx @jossuealcala/madre start
105
+ ```
161
106
 
162
- `EXPORT DATASET` escribe `train.jsonl` y `valid.jsonl` junto al ledger, redactados. La tarjeta `TRAIN` trae los comandos ya rellenados con tu carpeta de sala y el modelo base que cabe en tu máquina. El entrenamiento corre fuera de MADRE, con mlx en Apple Silicon; la receta vive en [`docs/training/`](https://github.com/jossuealcacao-exe/madre/blob/main/docs/training/README.md). En cuanto Ollama tiene un modelo `madre-<proyecto>`, `@madre` responde con él y las demás CLIs reciben la indicación de preguntarle primero: la memoria del proyecto deja de costar tokens.
107
+ La sala abre en `http://127.0.0.1:4317`. **Si no tienes ninguna CLI, ábrela igual**: lo primero que ves es el puente, con un botón para instalar y firmar cada agente ahí mismo. MADRE enseña el comando antes de correrlo y transmite cada línea a la pantalla.
163
108
 
164
- **Ollama.** Si corre en la máquina, MADRE lo usa sin configurar nada: embeddings locales, archivista local, `@madre`. `MODULES → OLLAMA` muestra servidor, modelos y roles, descarga los recomendados con `PULL` y deja apagar cada rol. `qwen2.5:7b` en 16 GB, `qwen2.5:3b` en 8 GB; MADRE prefiere modelos de chat general sobre los `-coder`.
109
+ ```
110
+ npx @jossuealcala/madre doctor # qué hay instalado, quién tiene sesión
111
+ ```
165
112
 
166
- ---
113
+ La sala habla **español**. El botón `EN` la pasa a inglés.
167
114
 
168
- ## MU/TH/UR
115
+ <br>
169
116
 
170
- El botón de la barra abre la pantalla de diagnóstico. Escribe un síntoma, un agente o una palabra y MU/TH/UR clasifica las condiciones registradas en esta sala contra su catálogo, con el remedio para tu sistema operativo. El mismo catálogo en terminal: `madre doctor --catalog [texto]`.
117
+ **Cómo funciona cada cosa, con los números exactos:** [docs/REFERENCE.md](docs/REFERENCE.md)
118
+ **Cómo funciona por dentro:** [docs/INTERNALS.md](docs/INTERNALS.md) · **Escribir un módulo:** [docs/SDK.md](docs/SDK.md)
171
119
 
172
- | Sección | Qué hace |
173
- |---|---|
174
- | `⚙ CONNECTIONS` | Sesión, versión y ruta de cada CLI; `SIGN IN` y `RECHECK`; `MAX MODE`, `DEFAULT MODE` y habilidades; timeouts, presupuesto, delegación, modelo de OpenCode; MEMORY y PRIVACY |
175
- | `◉ NOSTROMO` | El mapa de la memoria |
176
- | `SENTINEL` | Fallos que ninguna condición explica y caídas del proceso, con rutas, usuarios, correos y claves eliminados. Cada reporte tiene `REPORT ON GITHUB ↗` para leerlo antes de publicarlo; `AUTO-REPORT`, apagado por defecto, envía los nuevos al colector del proyecto |
177
- | `RELEASE CHANNEL` | Una consulta a npm al día. Si hay versión nueva, una alerta en la barra y aquí `RESTART WITH x.y.z`: la sala cierra, instala y vuelve en la misma dirección; o el comando para hacerlo tú. Nunca mientras los agentes trabajan |
178
- | `✎ FEEDBACK` | Un issue en blanco con tu entorno ya escrito |
120
+ <br>
179
121
 
180
122
  ---
181
123
 
182
- ## MÓDULOS
183
-
184
- `MODULES` en la barra lista los disponibles y su estado. Ninguno escribe en el proyecto salvo la instalación de AHP+, que muestra el comando y pide confirmación.
124
+ <br>
185
125
 
186
- | Módulo | Qué añade |
187
- |---|---|
188
- | **Git Pulse** | `/git status`, `/git log [n]`, `/git diff`, `/git branches`: hechos del repositorio como tarjeta en el hilo, que los agentes también leen. `/git commit "mensaje"` y `/git push confirm`: tu mano sobre el repositorio, con vista previa de lo que saldría |
189
- | **Image Studio** | `generate_image` para Claude, Gemini y OpenCode, con tu key de Gemini, dentro de CREATE |
190
- | **RIPLEY** | El visor renderiza HTML, SVG y Markdown en un marco sellado, con recarga automática |
191
- | **OLLAMA** | Embeddings, archivista y `@madre` en local |
192
- | **PLAYWRIGHT** | Un navegador headless por turno que solo alcanza esta MADRE: abrir la vista previa de RIPLEY, hacer clic, leer consola, capturas al borrador del turno. Requiere `@playwright/mcp` |
193
- | **AshCode** (beta) | `$ ash_code`: abrevia mensajes localmente antes de enviarlos y pide respuestas concisas. Puede cambiar el significado; el original siempre queda |
194
- | **AHP+** | Integración externa opcional: estado verificado del proyecto, checkpoints y handoffs en `.ahp/`; `/ahp status`, `/ahp check`, `/ahp context` |
126
+ ## Estado
195
127
 
196
- Cada módulo es un archivo en `src/modules/` declarado con `defineModule`; un módulo puede entregar herramientas MCP a cada turno con `toolsForTurn`, como hace PLAYWRIGHT. Cómo escribir uno, en [CONTRIBUTING.md](https://github.com/jossuealcacao-exe/madre/blob/main/CONTRIBUTING.md#writing-a-module).
128
+ Beta pública. El núcleo está probado y bajo CI en macOS y Linux con Node 22 y 24; la superficie sigue cambiando. Las decisiones que todavía duelen están escritas, sin adornos, en [*Lo que sale de la máquina*](docs/REFERENCE.md#lo-que-sale-de-la-máquina).
197
129
 
198
- ---
199
-
200
- ## LO QUE SALE DE LA MÁQUINA
130
+ Una versión se cierra cuando está en npm. El detalle de cada una en [CHANGELOG.md](CHANGELOG.md); lo que viene, en el [roadmap](https://github.com/jossuealcacao-exe/madre/blob/main/docs/ROADMAP.md).
201
131
 
202
- - **Nada por sí solo.** MADRE no tiene nube, cuenta ni backend. No guarda credenciales.
203
- - **Lo que un agente lee, viaja a su proveedor.** Codex a OpenAI, Claude Code a Anthropic, Gemini CLI a Google, OpenCode a quien tenga configurado. Aplican su cuenta, sus límites y sus términos. `@madre` y el archivista con Ollama no salen de la máquina.
204
- - **Dos envíos propios, ambos bajo tu interruptor.** El sentinel, apagado por defecto, envía reportes redactados al colector del proyecto. El canal de liberación, encendido por defecto, pregunta a npm por la última versión: viaja el nombre del paquete, nada más, la misma petición que hace `npx`. `PULSE_UPDATE_CHECK=0` lo apaga.
205
- - **Escritura.** En `#1` nadie escribe. En `#2` solo se añade: lo que existía se restaura al terminar el turno. En `#3` todo el proyecto salvo las zonas prohibidas, con checkpoint y `UNDO`. En un proyecto sin git, MADRE guarda sus fotografías en un repositorio sombra fuera del proyecto.
206
- - **Memoria.** Todo lo dicho fuera de GHOST queda en `~/.pulse/rooms/<sala>/` y vuelve a los prompts de todos los agentes de esa sala. GHOST es la salida para lo que no debe recordarse; PRIVACY, para los nombres que nunca deben aparecer.
132
+ Problemas: desde MU/TH/UR (`✎ FEEDBACK`) o en [issues](https://github.com/jossuealcacao-exe/madre/issues).
133
+ Seguridad: [SECURITY.md](SECURITY.md) · Contribuir: [CONTRIBUTING.md](CONTRIBUTING.md)
207
134
 
208
- ---
135
+ Apache-2.0 · [Jossué Alcalá](https://jossuealcala.com/en/)
209
136
 
210
- ## REFERENCIA
137
+ <br>
211
138
 
212
- **`~/.pulse/config.json`**, o `PULSE_HOME/config.json`. Lo escribe MU/TH/UR; las variables de entorno mandan al siguiente arranque.
213
-
214
- ```json
215
- {
216
- "opencode": { "model": "openai/gpt-5.6-sol" },
217
- "timeouts": { "default": 300000, "claude": 600000 },
218
- "room": { "softTokenBudget": 500000, "delegation": true, "maxPlanSteps": 4 },
219
- "memory": { "archivist": "auto", "every": 10, "idleMinutes": 10, "embedProvider": "auto", "recallShare": 0.3 },
220
- "privacy": { "terms": [], "marker": "[ENTIDAD-ORG]" },
221
- "updates": { "check": true },
222
- "telemetry": { "autoReport": false }
223
- }
224
139
  ```
225
-
226
- **Variables de entorno**
227
-
228
- | Variable | Predeterminado | Efecto |
229
- |---|---|---|
230
- | `PULSE_HOME` | `~/.pulse` | Raíz de las salas y del config |
231
- | `PULSE_SOFT_TOKEN_BUDGET` | `500000` | Presupuesto local de tokens por agente, ventana rodante de 5 h |
232
- | `PULSE_CONTEXT_MAX_CHARS` | `16000` | Ventana de transcript inyectada |
233
- | `PULSE_RECALL_SHARE` | `0.3` | Parte de la ventana para la memoria recordada (`0` la apaga) |
234
- | `PULSE_DISTILL` · `_EVERY` · `_IDLE_MS` · `_MAX_CHARS` · `_AGENT` · `_MODEL` | `1` · `10` · `600000` · `6000` · — · — | Destilación de notas |
235
- | `PULSE_EMBED` · `PULSE_EMBED_PROVIDER` | `1` · `auto` | Embeddings; `ollama`, `gemini`, `auto` |
236
- | `PULSE_OLLAMA` · `_HOST` · `_MODEL` · `_EMBED_MODEL` | `1` · `http://127.0.0.1:11434` · el mejor · el mejor | Ollama |
237
- | `PULSE_MEMORY_TOOLS` | `1` | Servidor MCP `pulse-memory` en cada turno |
238
- | `PULSE_PRIVATE_TERMS` · `PULSE_PRIVATE_MARKER` | — · `[ENTIDAD-ORG]` | Términos privados extra y su marcador |
239
- | `PULSE_UPDATE_CHECK` | `1` | `0` apaga la consulta diaria a npm |
240
- | `PULSE_REPORT_URL` · `PULSE_AUTO_REPORT` | colector del proyecto · `0` | Sentinel |
241
- | `PULSE_AGENT_TIMEOUT_MS` · `PULSE_<AGENTE>_TIMEOUT_MS` | `180000` · — | Timeouts |
242
- | `PULSE_MAX_MESSAGE_CHARS` | `20000` | Tamaño máximo de un mensaje |
243
- | `PULSE_DELEGATION` · `PULSE_MAX_PLAN_STEPS` | `1` · `4` | Delegación |
244
- | `PULSE_ESCALATION_MS` | `180000` | Cronómetro de la escalación |
245
- | `PULSE_OPENCODE_MODEL` | del config | `proveedor/modelo` para OpenCode |
246
- | `PULSE_CLAUDE_USAGE` · `PULSE_OFFICIAL_QUOTA` | `0` · `1` | Cuota oficial de Claude (lee el llavero); `0` apaga todas las lecturas de proveedor |
247
- | `PULSE_GEMINI_IDLE_MS` · `PULSE_GEMINI_RETRIES` · `PULSE_GEMINI_FALLBACK_MODEL` | `90000` · `1` · `gemini-2.5-flash` | Gemini bajo carga |
248
-
249
- Cómo funcionan por dentro el ledger, el stream, la memoria, los adaptadores y la recuperación: [`docs/INTERNALS.md`](https://github.com/jossuealcacao-exe/madre/blob/main/docs/INTERNALS.md).
250
-
251
- ---
252
-
253
- ## ESTADO
254
-
255
- Beta pública. El núcleo está probado y bajo CI en macOS y Linux con Node 22 y 24; la superficie sigue cambiando y las decisiones que aún duelen están escritas arriba, en *Lo que sale de la máquina*. Una versión se cierra cuando está en npm; el detalle de cada una, en [CHANGELOG.md](https://github.com/jossuealcacao-exe/madre/blob/main/CHANGELOG.md), y lo que viene, en el [roadmap](https://github.com/jossuealcacao-exe/madre/blob/main/docs/ROADMAP.md).
256
-
257
- Problemas: desde MU/TH/UR (`✎ FEEDBACK` o el sentinel) o en [issues](https://github.com/jossuealcacao-exe/madre/issues). Seguridad: [SECURITY.md](https://github.com/jossuealcacao-exe/madre/blob/main/SECURITY.md). Contribuir: [CONTRIBUTING.md](https://github.com/jossuealcacao-exe/madre/blob/main/CONTRIBUTING.md).
258
-
259
- Apache-2.0. Ver `LICENSE` y `NOTICE`.
140
+ NOBODY DELETES MOTHER'S MEMORY.
141
+ ```
package/SECURITY.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Security · MU/TH/UR 6000 · PRIORITY ONE
2
2
 
3
- MADRE runs on your machine, talks to no cloud of its own and stores no credentials. Its agents are CLIs you installed; they talk to their providers. The short threat model lives in the README under *Modelo de amenazas, en corto*.
3
+ MADRE runs on your machine, talks to no cloud of its own and stores no credentials. Its agents are CLIs you installed; they talk to their providers. What leaves the machine, and what never does, is written out in [`docs/REFERENCE.md`](docs/REFERENCE.md#lo-que-sale-de-la-máquina).
4
4
 
5
5
  ## Reporting
6
6
 
@@ -14,6 +14,7 @@ Please include the MADRE version (`madre doctor --json`), the platform, which ag
14
14
  ## Known, accepted for the beta
15
15
 
16
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
+ - `#4 AIRLOCK` runs commands with the CLIs and sessions already on the machine: tests, builds, `git push`, deploys. Files come back with `UNDO`; **what leaves the machine does not**. Arming it asks for two keys — the project designation and the word `AIRLOCK` — and the agent's `MAX MODE` in CONNECTIONS must already reach it, which is a separate deliberate act. No agent can raise its own ceiling, and a permission an agent writes into its own answer is not a permission. Treat `#4` as handing that agent your shell.
17
18
  - 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
19
 
19
20
  NOBODY DELETES MOTHER'S MEMORY. EVERYTHING ELSE IS FAIR GAME.
package/bin/madre.mjs CHANGED
@@ -3,27 +3,67 @@
3
3
  import { resolve } from 'node:path';
4
4
  import { execFileSync } from 'node:child_process';
5
5
  import { readFile, stat } from 'node:fs/promises';
6
- import { startPulse } from '../src/server.mjs';
6
+ import { openUrl, startPulse } from '../src/server.mjs';
7
+ import { realpath } from 'node:fs/promises';
7
8
  import { detectAgents } from '../src/runtime-detection.mjs';
8
9
  import { probeAll } from '../src/auth-probe.mjs';
9
10
  import { applyConfigToEnv, loadConfig } from '../src/config.mjs';
10
11
  import { isOnline, runSetup } from '../src/setup.mjs';
11
12
  import { parseArgs } from '../src/cli-args.mjs';
13
+ import { createServer } from 'node:net';
12
14
 
13
15
  const cli = parseArgs(process.argv.slice(2));
14
16
  const { command } = cli;
15
17
  const option = (name, fallback) => cli.option(name.replace(/^--/, ''), fallback);
18
+
19
+ // Is a MADRE already serving this very project nearby? `start` walks ports, so look along the
20
+ // same short stretch and compare the project each one reports.
21
+ async function roomAlreadyOpen(port, projectRoot, span = 8) {
22
+ const mine = await realpath(projectRoot).catch(() => resolve(projectRoot));
23
+ for (let candidate = port; candidate < port + span; candidate += 1) {
24
+ try {
25
+ const response = await fetch(`http://127.0.0.1:${candidate}/api/version`, { signal: AbortSignal.timeout(500) });
26
+ if (!response.ok) continue;
27
+ const info = await response.json();
28
+ const theirs = info?.project ? await realpath(info.project).catch(() => info.project) : null;
29
+ if (theirs && theirs === mine) return { url: `http://127.0.0.1:${candidate}`, version: info.current ?? null };
30
+ } catch {
31
+ // nothing listening there, or not a MADRE: keep looking.
32
+ }
33
+ }
34
+ return null;
35
+ }
36
+
37
+
16
38
  const has = (name) => cli.has(name.replace(/^--/, ''));
17
39
 
40
+ // Is somebody already on that port? Asked before anything is started, because opening a room
41
+ // means finding the agents, opening the archive and waking the memory, and doing all of that only
42
+ // to then refuse is a wait nobody needed. The real bind below stays the authority: this only
43
+ // saves the waiting.
44
+ function portTaken(port, host = '127.0.0.1') {
45
+ return new Promise((resolve) => {
46
+ const probe = createServer();
47
+ probe.once('error', (error) => resolve(error.code === 'EADDRINUSE'));
48
+ probe.listen(port, host, () => probe.close(() => resolve(false)));
49
+ });
50
+ }
51
+
18
52
  // With an explicit --port a busy port is an error the user asked for. Without
19
53
  // one, MOTHER walks up to the next free port so a second room just opens.
20
54
  async function openRoom(options) {
21
55
  const explicit = cli.explicit('port');
22
56
  let port = options.port;
57
+ if (explicit && await portTaken(port)) {
58
+ console.error(`\n MOTHER › port ${port} is already in use. Another MADRE may be open there; try --port ${port + 1} or omit --port to pick one automatically.\n`);
59
+ process.exit(2);
60
+ }
23
61
  for (let attempt = 0; attempt < 20; attempt += 1) {
24
62
  try {
25
63
  return await startPulse({ ...options, port });
26
64
  } catch (error) {
65
+ // The room itself says it is taken, whatever port this attempt used.
66
+ if (error.code === 'ROOM_IN_USE') { console.error(`\n MOTHER › ${error.message}\n`); process.exit(2); }
27
67
  if (error.code !== 'EADDRINUSE') throw error;
28
68
  if (explicit) {
29
69
  console.error(`\n MOTHER › port ${port} is already in use. Another MADRE may be open there; try --port ${port + 1} or omit --port to pick one automatically.\n`);
@@ -132,7 +172,7 @@ if (command === 'doctor' && (has('--catalog') || has('--conditions'))) {
132
172
  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'}`);
133
173
  console.log(` ${'MADRE'.padEnd(10)} ${update.current}${update.available ? ` · ${update.latest} available · ${update.command}` : update.latest ? ' · up to date' : update.enabled ? ' · npm not reachable' : ' · update check off (PULSE_UPDATE_CHECK=0)'}`);
134
174
  console.log(`\n Project ${result.project}`);
135
- 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');
175
+ console.log(result.ok ? '\nReady to start. Known conditions and fixes: `madre doctor --catalog [query]`.\n' : '\nNo agent is online yet. Run `madre start`: the room opens anyway and walks you through installing and signing one in. Known conditions and fixes: `madre doctor --catalog`.\n');
136
176
  }
137
177
  process.exitCode = result.ok ? 0 : 1;
138
178
  } else if (command === 'setup') {
@@ -145,22 +185,25 @@ if (command === 'doctor' && (has('--catalog') || has('--conditions'))) {
145
185
  } else if (command === 'start') {
146
186
  const port = Number(option('--port', '4317'));
147
187
  const noOpen = has('--no-open');
148
- // First contact: in a terminal with nobody online, MOTHER walks the user
149
- // through configuring an agent before the room opens.
150
- if (process.stdin.isTTY && process.stdout.isTTY && !has('--no-setup')) {
151
- const agents = await detectAgents();
152
- const probes = await probeAll(agents);
153
- const { localIntelligence, madreOnline } = await import('../src/setup.mjs');
154
- if (!agents.some((agent) => isOnline(agent, probes[agent.id])) && !madreOnline(await localIntelligence())) {
155
- const { action } = await runSetup({ projectRoot, stateRoot });
156
- if (action !== 'start') process.exit(0);
157
- }
188
+ // First contact happens in the room, not here: MADRE opens even with nobody online and the
189
+ // bridge walks the human through installing and signing in. `madre setup` keeps the terminal
190
+ // wizard for whoever prefers it, and `--setup` asks for it explicitly.
191
+ if (has('--setup')) {
192
+ const { action } = await runSetup({ projectRoot, stateRoot });
193
+ if (action !== 'start') process.exit(0);
194
+ }
195
+ // A room for this project may already be open. Show that one instead of starting a second.
196
+ const running = await roomAlreadyOpen(port, projectRoot);
197
+ if (running) {
198
+ console.log(`\nMADRE is already open for this project\n\n ${running.url}\n Project: ${projectRoot}\n Close that room first if you want a fresh one.\n`);
199
+ if (!noOpen) openUrl(running.url);
200
+ process.exit(0);
158
201
  }
159
202
  await openRoom({ port, projectRoot, openBrowser: !noOpen });
160
203
  } else if (command === 'help') {
161
204
  console.log(`MADRE
162
205
 
163
- madre start [--project PATH] [--port 4317] [--no-open] [--no-setup]
206
+ madre start [--project PATH] [--port 4317] [--no-open] [--setup]
164
207
  Without --port, a busy 4317 falls through to the next free port.
165
208
  madre setup [--project PATH] [--port 4317] [--no-open]
166
209
  madre doctor [--project PATH] [--json]
package/docs/INTERNALS.md CHANGED
@@ -26,8 +26,24 @@ La ventana de contexto que recibe un agente es de 16 000 caracteres (`PULSE_CONT
26
26
  - `memories` con FTS5: las notas destiladas, tipadas (`decision`, `fact`, `preference`, `question`), con las secuencias que las sustentan, quién las escribió y su origen (`distilled` o `noted`).
27
27
  - `entry_vectors` y `memory_vectors`: embeddings de 768 dimensiones, calculados en segundo plano y nunca en el camino crítico de un turno.
28
28
 
29
+ **Conversaciones.** Un proyecto tiene una memoria y muchas conversaciones. El archivo, la tripulación, los módulos y la lista de privacidad son del proyecto; una conversación es solo el registro de una línea de trabajo. La primera es el `events.jsonl` que siempre estuvo ahí —no se mueve ningún archivo para añadir esto—, las demás viven en `<sala>/chats/<id>/events.jsonl`, y `<sala>/chats.json` guarda sus nombres y cuál está abierta. La numeración es del proyecto, no de la conversación: al abrir una se arranca desde la secuencia más alta que alcanzó cualquier otra, así que `#1411` significa un intercambio de este proyecto y el índice de memoria, las citas y NOSTROMO siguen valiendo. Hay hueco en un ledger y eso está bien: las secuencias son un orden, no un conteo.
30
+
31
+ Una conversación a la vez maneja a la tripulación —dos hilos editando el mismo árbol de trabajo no es una función, es una forma de perder trabajo—, así que abrir otra se niega mientras haya un turno corriendo. Y una sala por proyecto: `<sala>/open.json` lleva el pid y el puerto del servidor que la tiene, y otro arranque sobre el mismo proyecto se niega con la dirección de la sala abierta. Sin eso, dos servidores escribiendo conversaciones distintas del mismo proyecto repartirían la misma secuencia dos veces y el archivo se quedaría callado con una de ellas.
32
+
29
33
  **Recall.** Cuando el transcript excede la ventana, cada turno recibe además, dentro del mismo presupuesto (`PULSE_RECALL_SHARE`, 30 % por defecto), las notas y los intercambios anteriores que coinciden con la petición. La búsqueda léxica pondera cada término por su rareza en la sala: una ruta o un nombre pesan más que una palabra común. Con vectores, se fusiona con la similitud de coseno.
30
34
 
35
+ **Activación en cascada.** Cada recuerdo que entra a un turno deja un renglón con el lote que compartió, así que la sala sabe qué notas llegan juntas. De ahí sale una asociación —Jaccard sobre los turnos donde cada una fue encontrada por la búsqueda misma— y el recall guarda dos huecos para ella: al traer una nota, trae también la que la acompaña, aunque no compartan una sola palabra. Solo votan los turnos donde la búsqueda las encontró por mérito propio, nunca los que la cascada creó, o la red se cerraría sobre sí misma en unos días. La fuerza es un cociente: una pareja que deja de coincidir se apaga sola. Umbral 0.34, mínimo dos coincidencias, y la búsqueda nunca pierde un hueco a manos de la asociación. `PULSE_RECALL_CASCADE=0` o el interruptor en MU/TH/UR la apagan.
36
+
37
+ **Zonas frías.** Un recuerdo está frío cuando se cumplen tres cosas a la vez: ningún turno lo ha llevado nunca, no comparte tema con ningún otro —así que tampoco se le puede llegar de lado— y el archivo se abrió al menos doce veces desde que se escribió. La tercera es la que importa: nunca-recordado es lo que toda nota es el día que nace; frío es la oportunidad que pasó de largo. Las oportunidades se cuentan en turnos que de verdad entraron al archivo, y solo desde el día en que la sala empezó a llevar el rastro. NOSTROMO los cuenta en su cabecera y los rodea de un anillo tenue cuando se le pide.
38
+
39
+ **Qué preguntar.** La sala escribe las preguntas cuya respuesta le falta, siempre con las palabras que ya tiene y nunca inventando un tema que nadie levantó. Tres pozos: las preguntas que el archivista registró como abiertas y nadie contestó, los recuerdos fríos —preguntar es la alternativa honesta a tirar algo que nunca tuvo su oportunidad— y la clase de nota de la que el archivo anda corto (decisiones, preferencias, hechos; preguntas no, porque un archivo corto de preguntas no se arregla pidiendo más). Se toman por turnos para que un pozo lleno no sea la lista entera, y dos formas de preguntar lo mismo cuentan como una. Nada se envía: la pregunta va al compositor y la humana decide quién responde.
40
+
41
+ **El veredicto.** `GET /api/maturity` junta las seis lecturas, las tres pruebas, las zonas frías y las preguntas pendientes, y devuelve una frase y una instrucción. El orden no lo decide el puntaje: primero lo falso (contradicciones abiertas), luego lo que el archivo no puede contestar —y ahí manda a las preguntas que la sala ya escribió—, luego la lectura más floja citando su propio consejo, y al final la prueba que nadie ha corrido. La consistencia se calcula al leer; la cobertura se refresca sola una vez al día, en segundo plano y solo con embeddings locales.
42
+
43
+ **Las tres pruebas.** La lectura de madurez cuenta de qué está hecho el archivo; no dice si funciona. Eso lo dice una prueba, y una prueba solo vale si puede fallar. `COVERAGE` toma preguntas reales del historial, corre el recall en el punto exacto en que cada una se hizo y lo compara con la respuesta que de verdad se dio. `CONSISTENCY` mira las contradicciones que EYECAT sigue sosteniendo, lo que quedó fuera de circulación y si las aberraciones se archivan más seguido últimamente o menos. `MATCH` vuelve a hacerle al modelo local preguntas que contestó una CLI de frontera y compara. Ninguna se gasta un turno de proveedor; la tercera corre en segundo plano y se puede detener.
44
+
45
+ Las tres se miden **contra un control**: dentro de una sala todo habla de los mismos temas, así que a un embedder cualquier par de textos le parece cercano —la primera corrida de COVERAGE dio treinta de treinta—. Para contar, lo que el archivo entregó tiene que ganarle a lo que habría entregado para otra pregunta, por un margen. Cada resultado dice cuántos casos tuvo, con qué midió (significado o palabras), la barra y la media contra el control.
46
+
31
47
  **Destilación.** Cada `PULSE_DISTILL_EVERY` intercambios sin destilar, o tras `PULSE_DISTILL_IDLE_MS` de reposo, el archivista lee un lote acotado por `PULSE_DISTILL_MAX_CHARS`, del más reciente hacia atrás, y escribe hasta cinco notas. El archivista es el más barato permitido en el orden Ollama, Gemini, OpenCode, Codex, Claude; el que falla queda en banca treinta minutos y pasa el siguiente. Una sola llamada por lote, nunca durante un turno. Para los modelos locales se pide JSON estricto.
32
48
 
33
49
  **Embeddings.** `PULSE_EMBED_PROVIDER=auto` usa Ollama si corre con un modelo de embeddings (`nomic-embed-text`), si no la key de Gemini (`gemini-embedding-001`, la misma key que usa el Gemini CLI, incluida la del llavero de macOS). Lotes de hasta 100 textos por llamada. `PULSE_EMBED=0` deja el recall léxico.