code-timeline
by FlEtsv
README.md
# Code Timeline
[](https://github.com/FlEtsv/code-timeline/actions/workflows/ci.yml)
[](https://nodejs.org)
[](LICENSE)
**Git guarda el código. Los transcripts guardan la conversación. Code Timeline
guarda la decisión vinculada al cambio.**
Claude Code o Codex registran el porqué mientras todavía lo tienen en contexto;
Git aporta el antes/después exacto y tú revisas después solo lo pendiente.
También puede **proponer** cambios que no ha hecho: aparecen aparte, y tú los
aceptas o los descartas. Lo hecho y lo sugerido nunca se mezclan.
Todo corre en tu máquina: un servidor MCP para que Claude escriba, y una web
local en `localhost` para que tú leas. Nada se publica en ninguna parte — la
web escucha solo en `127.0.0.1`, así que ni siquiera se ve desde otro equipo
de tu red salvo que lo pidas tú con `--host`. El historial se exporta a JSON
(respaldo y traslado), a Markdown y a PDF.

La página abre por **Pendiente**: lo que reclama algo tuyo — propuestas por
decidir, aceptadas por escribir, cambios por revisar y pruebas en rojo. El
libro completo vive en la pestaña **Historial**, y las propuestas descartadas
en la suya. Imprimir saca las tres, esté abierta la que esté: una pestaña es
un estado de pantalla, no del documento.
## Pruébalo en 60 segundos
Desde cualquier carpeta, instala el MCP en Claude Code y Codex con un comando:
```bash
npx github:FlEtsv/code-timeline instalar --agente ambos
```
Reinicia el agente y dile: **«vincula este proyecto y registra los cambios que
hagas»**. Después abre la revisión con: **«abre el timeline»**.
Para que el historial viaje con un repositorio de equipo, vincúlalo con
`storageMode: "versioned"` o usa `code-timeline link ... --versionado`. Se
espeja en `.code-timeline/history.json`; revisa su contenido antes de
commitearlo porque contiene fragmentos y explicaciones del código.
## Cero coste en reposo. Menos del 1% al registrar. Y ahorra contexto después.
Una herramienta que se mete entre tú y tu agente tiene que responder a esto
antes que a nada. Y con una medida que puedas rehacer, no con una promesa:
```
node scripts/medir-coste.mjs
```
Lee **tus** transcripts de Claude Code, saca el `usage` real de cada sesión y
mide qué parte se fue en llamadas a Code Timeline. Esta es la medida actual
sobre **23 sesiones reales, 8.917.888 tokens de trabajo y 120 entradas**:
| Si una sesión sin Code Timeline es | 100% |
|---|---|
| Coste histórico observado | **101,98%** |
| Con la captura automática actual aplicada a todo | **100,93%** |
Las dos filas son reales y miden cosas distintas. La primera es lo que costó de
verdad, con el agente tecleando el código `before`/`after` a mano. La segunda es
esa misma medida descontando ese código, que desde la versión actual **ya no se
teclea**: se captura de `git diff`.
En números absolutos, aquellas sesiones produjeron 8.917.888 tokens de salida
de trabajo. Code Timeline añadió 176.760; de ellos, 93.663 eran código
`before`/`after` que la versión actual ya captura de Git. Aplicando el diseño
actual al mismo trabajo quedan **83.097 tokens: 692 por entrada y un 0,93%**.
### Medida real con Codex
También se midió con procesos reales de Codex CLI, modelo `gpt-5.6-sol`, login
mediante suscripción ChatGPT, sesiones efímeras y sandbox de solo lectura. No
son estimaciones de caracteres: son los contadores `usage` emitidos por Codex:
| Prueba | Entrada total | Entrada cacheada | Entrada no cacheada | Salida |
|---|---:|---:|---:|---:|
| MCP desactivado, sin herramientas | 14.374 | 10.624 | 3.750 | 8 |
| MCP cargado, sin usarlo | 14.374 | 10.624 | 3.750 | 8 |
| Una llamada a `estado`, catálogo completo | 62.699 | 48.704 | 13.995 | 161 |
| Una llamada a `estado`, perfil habitual de 7 herramientas | 49.188 | 41.984 | 7.204 | 131 |
**Tener Code Timeline instalado y disponible costó exactamente cero tokens
adicionales en la prueba.** Solo consume cuota cuando Codex lo llama. Una
llamada abre otra pasada del agente, por eso el total procesado es mayor que el
JSON que devuelve; gran parte queda cubierta por caché. Limitar el catálogo a
las herramientas de trabajo redujo un 66% la entrada adicional no cacheada
(de 10.245 a 3.454 tokens sobre la sesión base).
En esta máquina `codex login status` devuelve `Logged in using ChatGPT`: estas
pruebas consumen la cuota incluida de Codex, **no generan una factura de API**.
Con login por API sí se facturarían según los tokens y el modelo elegidos.
### Y a cambio, ahorra
No es un 1% que pagues por nada. Redactar el mensaje de commit desde el
historial en vez de leyéndose el diff, medido en el commit de este mismo repo:
| | Tokens de entrada |
|---|---|
| Leerse `git diff` + los archivos nuevos | **35.869** |
| `git_advice`, con el mensaje ya redactado | **1.259** |
Un 96% menos, en una sola operación — treinta veces lo que costó registrar toda
la tanda. El ahorro escala con el tamaño del diff: con un cambio suelto no
compensa (ver más abajo), con una sesión de trabajo sí.
### Cómo se consigue
**No te quitamos funcionalidad para que salga barato. Al revés: cada versión
añade, y encima cuesta menos.** Tres decisiones:
1. **El código no lo escribe el agente.** El `before`/`after` era el 59% de todo
lo que se tecleaba por MCP — código que ya estaba en el disco y en git. Ahora
`add_change` solo necesita la ruta del archivo y el fragmento se captura de
`git diff` (`lib/captura.mjs`): exacto, acotado y gratis.
2. **Leer el historial no arrastra el código.** `list_changes` devuelve título,
porqué, archivos y estado; `get_change` trae una entrada entera cuando la
necesitas.
3. **Nada de relleno.** Las respuestas van en JSON compacto (la indentación era
un 12% de puros espacios), `add_change` confirma en vez de repetirte lo que
acabas de escribir, y cualquier herramienta acepta la ruta del repo en lugar
del id, para que no tengas que llamar a `list_projects` antes.
Lo que queda son **unos 692 tokens por entrada** sobre los transcripts reales,
y más del 80% de eso es la explicación:
el porqué del cambio. Eso no se puede capturar de ningún sitio, porque no está
en el código — y es lo único que separa esto de un `git log`. **Lo único que
sigues pagando es exactamente lo que compras.**
### La medida, reproducible
Dos medidas distintas, las dos reproducibles: `node scripts/medir-coste.mjs`
saca el porcentaje sobre tus sesiones reales, y `node --test test/coste.test.mjs`
mide el coste de una entrada en un repo de laboratorio. La segunda son pruebas
que **fallan** si alguien encarece la herramienta sin darse cuenta.
```
escenario REGISTRAR COMMITEAR
antes ahora ahorro diff historial ahorro
────────────────────────────────────────────────────────────
Un cambio suelto (1) 785 443 44% 258 425 +65%
Una tanda normal (3) 2355 1329 44% 775 449 42%
Una sesión larga (8) 6280 3544 44% 2066 495 76%
```
Y lo que no vas a leer en el README de nadie: **con un cambio suelto, redactar
el commit desde el historial sale más caro que leerse el diff** (+65%). El
ahorro llega cuando el diff crece, que es justo cuando escribir el commit a mano
duele. Registrar, en cambio, ahorra un 44% siempre.
## Por qué, si ya existe `git log`
No lo sustituye, lo complementa. Un commit agrupa varios cambios de una
sentada y su mensaje cuenta el resultado, no el razonamiento. Aquí cada
entrada es **una unidad revisable**: se entiende y se verifica de una vez, sin
tener que reconstruir de qué iba.
Y hay una diferencia que en la práctica pesa más que ninguna: `git log` te dice
qué cambió, no **por qué** ni **qué se rompía antes**. Cuando el que escribe el
código es un agente, eso es justo lo que necesitas para poder revisarlo. Cada
entrada lleva la explicación que el agente redactó *mientras* hacía el cambio,
con el contexto todavía en la mano.
Cuando un cambio no tiene nada que ver con el anterior, se marca como **salto**
y hay que explicar por qué se cambió de tema. Al leer el historial seguido, eso
es lo que evita perder el hilo.
## Cómo se ve
### El índice de proyectos
Cuántos cambios lleva cada uno y cuántos has revisado ya.

### La vista a pantalla completa
El archivo **entero** leído del disco en vivo, con las líneas del cambio
resaltadas, un árbol de los archivos del proyecto a la izquierda (con qué
cambio tocó cada uno y si está revisado) y navegación anterior/siguiente para
recorrer el historial sin volver atrás.

### Propuestas
Claude también puede sugerir sin tocar nada (`propose_change`). Una propuesta
va **arriba, fuera del hilo cronológico**, con borde discontinuo: es lo único
de la página que te pide una decisión. El historial de abajo es cosa hecha.

Aceptarla **no** la mete en el historial: la deja en *aceptada, pendiente de
aplicar*. El historial dice lo que está en el código, y al aceptar todavía no
lo está — nadie la ha escrito. Entra cuando quien la escribe lo confirma con
`mark_applied`, y entonces vuelve a "pendiente de revisar" como cualquier otro
cambio.
Descartarla la archiva con tu motivo, no la borra: saber qué se rechazó y por
qué es lo que evita volver a proponerlo dentro de tres semanas.
### Cómo se entera Claude de que aceptaste
**La web no puede avisarle** — es una página en tu `localhost`, no tiene por
dónde llamarle. Así que el traspaso es explícito por los dos lados:
- La tarjeta aceptada te da la orden ya escrita y un botón para copiarla:
`aplica la propuesta "..."`. La pegas en Claude Code y listo.
- Claude puede verlo por su cuenta con `list_proposals` y `status: "accepted"`.
El `CLAUDE.md` del repo le dice que lo mire al ponerse a trabajar, así que
normalmente lo saca él solo sin que se lo pidas.

Una propuesta tiene una trampa que la vista completa avisa explícitamente: el
archivo que se lee del disco es el **estado actual**, no el propuesto. Las
líneas resaltadas marcan dónde iría, y el código propuesto va al lado.

### Cómo sabes que un cambio funciona
Revisar y probar no son lo mismo, y el historial los guarda por separado:
**revisado** es que lo has leído; **prueba** es que algo lo ha ejecutado. Un
cambio puede estar revisado y sin probar, y saber cuál de las dos falta es la
mitad de la pregunta al mirar un historial ajeno.
Cada entrada lleva su estado de prueba — *sin probar*, *prueba automática*
(con el comando que la repite), *probado a mano* (con cómo), o **falla**. Ese
último existe a propósito: un historial donde solo cabe lo que funciona miente
por omisión, y el contador de arriba se pone en rojo mientras haya alguno.
```bash
code-timeline test <projectId> <changeId> --status auto --command "npm test -- carrito"
```
Claude lo registra con `set_test` cuando escribe o ejecuta la prueba.
### Estado de QA de un arnés externo
Si usas un arnés de QA (qabot o el que sea), puede dejar constancia de sus
ejecuciones sin ensuciar el historial:
```bash
code-timeline qa --resultado verde|rojo [--comando "qabot ciclo"] [--entorno staging] [--detalle "..."]
code-timeline qa --listar [--limit N] # las últimas ejecuciones registradas
```
Se guardan **aparte**, como estado del proyecto, y se ven en una tira sobre el
historial. No son entradas: una entrada es una decisión de código con su
porqué, y meter "batería verde en staging" cada vez lo llenaría de ruido hasta
que dejara de poder leerse.
Está pensado para llamarlo desde otro script: resuelve el proyecto por la ruta
del repo — el directorio actual, o el que le digas con `--repo`, sin que haga
falta saber el `projectId` — y, **si el repo no está vinculado, no hace nada y
sale con 0**. Así quien lo invoque no depende de que
Code Timeline esté instalado ni se le rompe el ciclo si falta.
### Cuando no entiendes un trozo de código
El historial guarda el **porqué**. Pero al revisar te encuentras código del que
no sabes ni **qué** hace, y ahí el porqué no ayuda: falta el paso de antes.
La explicación llega ya escrita por el agente al registrar el cambio y nace
plegada. Cada panel que la contiene tiene un botón **«Revelar explicación»**.
Lo que sale es una explicación línea por línea, y cada explicación está **enlazada con su línea**:
pasas por encima de una y se ilumina la otra, en los dos sentidos. Las líneas
sin explicar se atenúan, así que se ve de un vistazo qué está cubierto.
- **La explicación no toca el código.** Es una capa de lectura que se guarda
aparte, en la entrada. Meter los comentarios en el archivo sería cambiar tu
código para que tú lo entiendas.
- **Se escribe al registrar el cambio, no después.** Quien acaba de escribir el
código lo tiene en contexto: medido, explicarlo ahí cuesta **0,0161 $ y 15 s**
frente a **0,0652 $ y 38 s** si se pide luego, porque eso arranca una sesión
aparte solo para leer el fragmento. Y sale mejor: quien lo escribió sabe por
qué está así.
- **Una calidad general para lo que venga después.** Se elige una vez arriba,
se recuerda en el navegador y se usa al generar o regenerar cualquier panel.
Los tres niveles, medidos sobre el mismo código: concisa (12 líneas,
0,0432 $), normal (18 líneas, 0,0506 $) y extensa (19 líneas con el porqué y
los casos límite, 0,0607 $). Entre niveles consecutivos hay +17% y +20% de
coste, con +119% y +81% de contenido.
- **Revelar y ocultar.** La explicación siempre está guardada y empieza oculta;
el botón solo decide si ocupa sitio, y se recuerda por panel.
- **Elige quién lo explica.** Sonnet por defecto —explicar código leído es
trabajo acotado y pagar un modelo mayor es gastar de más—, con Opus, Fable y
Haiku a un clic. Y si trabajas con **Codex**, también sale en la lista: no
hace falta tener Claude para leer una explicación.
- **Se ve lo que costó.** Tokens de entrada y salida, y el precio cuando el
motor lo publica, para decidir si compensa rehacerlo con otro modelo.
Los paneles de código **no scrollean en horizontal**: las líneas largas se
ajustan y siguen debajo. Para que se siga distinguiendo dónde empieza cada
línea, cada una lleva su número y un guion tenue al principio.
### El copiloto de git
Code Timeline sabe algo que `git` no sabe: el **porqué** de cada cambio,
escrito cuando estaba fresco. Con eso puede aconsejar sobre git de una forma
que un diff no permite.
En la cabecera de cada proyecto aparece un panel con la rama, el estado del
árbol y lo que convendría hacer:
- **Conviene un commit** — con el mensaje **redactado desde las entradas**, no
adivinado del diff: el asunto sale del título y el cuerpo del motivo que
registraste. Botón para copiar el `git commit` entero.
- **Esto son N commits, no uno** — cuando entre las entradas sin commitear hay
un `jump`, que es literalmente un cambio de contexto declarado por quien lo
escribió. Y comprueba si se pueden separar: si los dos grupos tocan los
mismos archivos no basta con repartirlos, y te lo dice antes de que lo
descubras a mitad del commit.
- **N entradas sin commitear sobre `main`** — una tanda larga en la rama
principal es difícil de revisar y de deshacer.
- **Pruebas en rojo a punto de entrar en git**, commits **sin subir**, y
entradas **ya commiteadas sin su commit apuntado** (que se arreglan con
`stamp_commits` o `code-timeline sellar`).
- **Deriva**: entradas cuyo código ya no se reconoce en el archivo — se
revirtió o se reescribió sin registrarlo. Un historial donde solo consta lo
que sigue vivo miente por omisión.
Todo son **consejos con el comando escrito para copiar**. Nada de esto ejecuta
git: ni commit, ni checkout, ni push. El repo lo mueve quien lo entiende.
El mismo copiloto habla por tres bocas más: la herramienta MCP `git_advice`
—para que Claude te lo diga mientras trabajáis—, el hook `Stop` que avisa una
vez por sesión, y `code-timeline consejo` en la terminal.
### Los botones que hacen cosas
La web dejó de ser solo de lectura. Tres acciones, y todas parten de un clic
tuyo:
- **Que la aplique Claude** en una propuesta aceptada. Hasta ahora esa tarjeta
te daba una orden para copiar al terminal porque "esta página no puede avisar
a Claude"; ahora lanza `claude -p` en el repo con el contexto de la propuesta
ya compuesto. Cuando termina, la entrada pasa sola al historial.
- **Hacerlo** junto al consejo de commit, que ejecuta el `git commit` con el
mensaje redactado desde tu historial. Este no pasa por Claude: el mensaje ya
está escrito y ejecutarlo son dos órdenes de git.
- **Subir**, para los commits que se quedaron en tu máquina.
Lo que le llega a Claude desde la web es **solo una propuesta que tú has
aceptado**. No hay campo de texto libre, y es deliberado: el prompt lo compone
la herramienta a partir de algo ya escrito y revisado.
Como ese puerto puede ahora tocar tu máquina, las rutas que escriben piden un
**token** que el servidor genera al arrancar y que viaja dentro de la página,
más una comprobación de `Origin`. Una web cualquiera que visites puede hacer
que tu navegador dispare una petición contra tu localhost, pero no puede leer
la página para sacar el token.
### El cuerpo de un PR
`pr_body`, el enlace **Cuerpo de PR** de la web o `code-timeline pr` redactan
en Markdown **qué cambia, por qué y cómo se ha probado** con las entradas de
la rama actual, y avisan de lo que no debería fusionarse a ciegas —pruebas en
rojo, cambios sin revisar—. Sirve igual para la descripción de un pull request
que para el comentario de handoff al cerrar la jornada.
Tampoco publica nada: devuelve el texto. Esta herramienta no habla con la API
de GitHub ni guarda credenciales.
### Exportar
El botón **Imprimir / PDF** abre el diálogo del navegador sobre una versión
para papel: A4, tema claro, el código envuelto en vez de recortado, sin
botones y con los paneles apilados para que quepan.

Además, **JSON** y **Markdown**, desde la web, el CLI o el MCP. El JSON es el
formato de respaldo y de traslado — `import_project` lo vuelve a montar en
otra máquina, y al fusionar compara por id, así que reimportar el mismo
fichero dos veces no duplica nada. Como `data/` no se versiona, esto es lo
único que hay entre tú y perder tus notas de revisión.
## Junto a qabot
[qabot](https://github.com/FlEtsv/qabot) es un arnés de QA y despliegue con su
propio servidor MCP. Los dos juntos cierran un ciclo: **decides, se registra,
se prueba, se despliega, y el resultado vuelve al historial.**
```
Claude / Codex ──> propone y registra ──> Code Timeline (qué cambió y por qué)
│
└────────> prueba y despliega ──> qabot (si funciona y dónde está)
│
└── el veredicto vuelve a Code Timeline
```
**Guía paso a paso: [INSTALACION-CONJUNTA.md](INSTALACION-CONJUNTA.md)** —
requisitos, los dos servidores MCP, cómo comprobar que se hablan, qué hacer si
algo no va, y cómo quitar uno sin tocar el otro.
En corto, son dos servidores MCP independientes:
```bash
claude mcp add --scope user code-timeline -- node /ruta/a/code-timeline/server.mjs
claude mcp add --scope user qabot -- node /ruta/a/qabot/mcp/servidor.mjs
```
Y en el PATH, para que qabot pueda registrar sus ciclos:
```bash
cd /ruta/a/code-timeline && npm link
```
**Ninguno de los dos necesita al otro.** qabot comprueba si `code-timeline`
está en el PATH y si el repositorio está vinculado; si falta cualquiera de las
dos cosas, sigue igual y no se entera. Code Timeline no sabe que qabot existe:
solo recibe ejecuciones de QA de quien quiera mandárselas. Puedes usar
cualquiera de los dos por separado sin instalar el otro.
## Requisitos
- **Node.js 18 o superior.** Sin base de datos y sin dependencias en tiempo de
ejecución para la web: el servidor HTTP es el `http` nativo de Node. La única
dependencia real es el SDK de MCP, y solo la usa `server.mjs`.
- [Claude Code](https://claude.com/claude-code) si quieres que sea un agente
quien registre los cambios (que es el caso de uso). La web funciona por su
cuenta.
## Instalar
```bash
git clone https://github.com/FlEtsv/code-timeline.git
cd code-timeline
npm install
```
### Pruébalo con el proyecto de ejemplo
Antes de enchufarle nada tuyo, siembra la demo: vincula `examples/demo-repo`
(un módulo de carrito minúsculo que viene en el repo) y le registra cinco
cambios de ejemplo, con su antes/después, su explicación y un salto.
```bash
npm run demo # siembra el proyecto "Demo Carrito"
npm start # levanta la web
```
Abre <http://localhost:4173>. Todo lo que ves en las capturas de arriba sale de
ahí, así que puedes trastear con ello sin miedo: marca cosas como revisadas,
deja notas, abre la pantalla completa. Para volver a empezar, borra `data/`.
### Instalar el CLI en el PATH (opcional)
```bash
npm link
code-timeline serve --port 4173 --open
```
### Conectarlo a Claude Code
Registra el servidor MCP una sola vez, con `--scope user` para tenerlo
disponible desde cualquier proyecto:
```bash
claude mcp add --scope user code-timeline -- node "$(pwd)/server.mjs"
```
A partir de ahí, en cualquier sesión de Claude Code puedes decirle "vincula
este proyecto" y que vaya registrando lo que hace.
### Conectarlo a Codex
Codex acepta el mismo servidor stdio. Regístralo una sola vez con la ruta
absoluta del clon:
```bash
codex mcp add code-timeline -- node "$(pwd)/server.mjs"
codex mcp get code-timeline
```
Para que Codex lo use continuamente —también en ejecuciones no interactivas
con `approval=never`— confía en las herramientas de este servidor añadiendo a
`~/.codex/config.toml`:
```toml
[mcp_servers.code-timeline]
command = "node"
args = ["/ruta/absoluta/a/code-timeline/server.mjs"]
default_tools_approval_mode = "approve"
```
Esta confianza se limita a `code-timeline`; no desactiva el sandbox ni cambia
la aprobación de shell u otros MCP. Reinicia la sesión de Codex después de
editar la configuración para que aparezcan las herramientas.
El botón **Aplicar** de la web usa Claude Code por defecto. Para delegarlo en
Codex, arranca la web así:
```bash
CODE_TIMELINE_AGENT=codex npm start
```
Se puede indicar otro ejecutable con `CODE_TIMELINE_CODEX=/ruta/a/codex`.
## El flujo, en la práctica
```
tú: vincula este proyecto
agente: link_project(name, repoPath) → guarda el projectId
[el agente cambia código]
agente: add_change(projectId, { files: [{ file, lineStart }],
unitName, title, explanation })
[el agente ve algo mejorable, pero fuera del encargo]
agente: propose_change(projectId, { ... }) → queda pendiente de tu decisión
tú: levanta el timeline
agente: web({ accion: "abrir" }) → http://localhost:4173
```
Y ya en la web: lees en orden, marcas "revisado" y dejas notas. Las notas y las
marcas se guardan en el `changes.json` del proyecto a través de la API del
servidor — no en el navegador, así que sobreviven a limpiar la caché o a
cambiar de equipo.
Añadir entradas es cosa del agente a propósito: el valor de cada una está en la
explicación, y esa hay que redactarla con el cambio fresco, no deducirla luego
de un diff.
## El CLI
```
code-timeline serve [--port N] [--host H] [--open] levanta la web (viva, con notas)
code-timeline projects lista los proyectos vinculados
code-timeline link --name N --path P [--remote R] vincula un proyecto
code-timeline changes <projectId> [--limit N] lista los cambios registrados
code-timeline proposals <projectId> pendientes (--accepted: sin aplicar; --rejected: descartadas)
code-timeline decide <id> <changeId> accept|reject [--note "..."]
code-timeline applied <id> <changeId> [--commit sha]
code-timeline test <id> <changeId> [--status untested|auto|manual|failing] [--command "..."] [--note "..."]
code-timeline qa --resultado verde|rojo [--comando "..."] [--entorno E] [--detalle "..."] [--repo ruta]
code-timeline qa --listar [--limit N] ejecuciones de QA de un arnés externo
code-timeline export <projectId> [--format json|md] [--out ruta|-]
code-timeline import <fichero.json> [--merge <projectId>] [--repo <ruta>]
code-timeline consejo [<projectId>] [--repo ruta] qué convendría hacer con git ahora
code-timeline sellar --proyecto <id> [--simular] apunta en cada entrada su commit
code-timeline pr [<projectId>] [--out ruta] cuerpo de PR desde las entradas de la rama
code-timeline render <projectId> exporta un timeline.html estático
code-timeline show <projectId> metadatos del proyecto (JSON)
code-timeline doctor diagnóstico: dónde están los datos y por qué
```
## Las herramientas MCP
| Herramienta | Para qué |
| --- | --- |
| `list_projects` | Todos los proyectos vinculados, con sus contadores |
| `link_project` | Registra un repo. Una vez por proyecto |
| `get_project` | Metadatos de uno |
| `estado` | Resumen inicial: pendientes, pruebas en rojo y situación de Git |
| `add_change` | Registra un cambio **ya aplicado**: archivos, antes/después, unidad y porqué |
| `propose_change` | Registra una **propuesta**: código que aún no ha tocado |
| `list_changes` | El historial en orden, **sin el código**: un 10% del coste |
| `buscar` | Busca algo concreto sin cargar el historial entero |
| `list_proposals` | Las pendientes, o las descartadas con su motivo |
| `decide_proposal` | Acepta o descarta (solo si se lo pides tú) |
| `set_test` | Registra cómo se comprueba un cambio, o que su prueba falla |
| `mark_applied` | Confirma que una aceptada ya está escrita: pasa al historial |
| `exchange_project` | Exporta a JSON/Markdown o importa un JSON |
| `get_change` | Una entrada entera, con su código. Para leer una sin traerse todas |
| `render_timeline` | Exporta el `timeline.html` estático |
| `git_advice` | Qué convendría hacer con git, con el mensaje de commit ya redactado |
| `stamp_commits` | Apunta en cada entrada el commit que la recogió |
| `pr_body` | Redacta el cuerpo de un PR desde las entradas de la rama |
| `web` | Abre, consulta o cierra el servidor web |
La frontera entre `add_change` y `propose_change` es la que sostiene todo lo
demás, y por eso está escrita en la descripción de las dos herramientas: una es
para código que ya existe en el repo, la otra para código que no. Si se
confunden, la vista completa enseña un archivo que no se parece a lo que
cuenta la tarjeta.
`add_change` obliga a dos cosas: `title` y `explanation` nunca pueden ir
vacíos, y un cambio marcado como `jump` tiene que traer una `relationNote` que
diga qué lo separa del anterior. Son las dos únicas formas que tiene el store
de defenderse de un historial que no se puede leer.
## Dónde viven tus datos
En un solo directorio, y en ningún sitio más:
```
projects.json los proyectos vinculados
projects/<id>/changes.json cambios, propuestas, notas y qué has revisado
projects/<id>/qa.json las últimas ejecuciones de QA
projects/<id>/timeline.html export estático (se regenera; no se versiona)
```
Cuál es ese directorio depende de cómo lo hayas instalado, y se decide en este
orden:
| | Directorio de datos |
|---|---|
| `CODE_TIMELINE_DATA` está definida | lo que diga ella |
| Trabajas desde un clon de este repo | `data/` dentro del propio repo |
| Instalado como paquete (npm, global) | `~/.code-timeline` |
La tercera regla no es un detalle: si los datos colgaran del paquete
instalado, un `npm update` se llevaría por delante tu historial. Si no sabes
en cuál de los tres casos estás, `code-timeline projects` te dice la ruta en
uso cuando todavía no hay ningún proyecto vinculado.
Cada fichero se guarda **de forma atómica** (se escribe aparte y se renombra
encima), dejando al lado un `.bak` con la versión anterior, y el ciclo entero
de leer-modificar-escribir va bajo un candado: el servidor MCP y la web
escriben los mismos ficheros a la vez, y sin eso una marca de "revisado" podía
borrar un cambio recién registrado. Si el fichero principal apareciera
corrupto, se recupera solo del `.bak` y aparta el ilegible como `.corrupto` en
vez de arrancar con el historial vacío.
Cambios y propuestas comparten fichero y comparten id. Una entrada recorre sus
estados sin moverse de sitio, así que nunca pierde su antes/después ni la nota
que dejaste al revisarla:
```
proposal ──aceptar──> accepted ──mark_applied──> change
└─────descartar─────> rejected
```
Son ficheros JSON planos, legibles y editables. **`data/` está en
`.gitignore`, y es a propósito**: cada entrada guarda fragmentos literales del
código del proyecto vinculado, así que tu historial no debe acabar dentro de
este repo ni de ningún otro que compartas. Si quieres respaldarlo, hazlo en un
repositorio privado tuyo.
## Pruebas
```bash
npm test
```
181 pruebas con el runner que trae Node (`node:test`), sin dependencias. Cubren
lo que puede romperse sin hacer ruido: el tokenizador del resaltado (lenguajes
desconocidos, cadenas y comentarios sin cerrar, escapado de HTML), la máquina
de estados de las propuestas con sus guardarraíles, el ciclo de export e
import incluida la fusión sin duplicados, la resolución del directorio de
datos en sus tres casos, y el almacén bajo presión — un fichero a medias, uno
corrupto que se recupera de la copia, y tres procesos de verdad escribiendo a
la vez sin perder ninguna entrada.
Los tests apuntan `CODE_TIMELINE_DATA` a un directorio temporal, así que nunca
tocan tu historial. Esa variable también te sirve para guardar tus datos fuera
del repo:
```bash
CODE_TIMELINE_DATA=~/timelines code-timeline serve
```
## Cómo está montado
| Archivo | Qué hace |
| --- | --- |
| `lib/store.mjs` | Persistencia. Ficheros JSON, sin base de datos |
| `lib/render.mjs` | Genera el HTML: índice, timeline, vista completa y el CSS de impresión |
| `lib/highlight.mjs` | Resaltado de sintaxis, sin dependencias |
| `lib/markdown.mjs` | El export a Markdown |
| `lib/httpserver.mjs` | Servidor HTTP nativo + API de "revisado" y notas |
| `lib/repofile.mjs` | Lee el archivo del repo: el del disco, y si ya no está, el del commit |
| `server.mjs` | Servidor MCP (stdio) |
| `bin/cli.mjs` | El CLI |
| `examples/demo-repo/` | El proyecto de ejemplo de `npm run demo` |
El código va resaltado, en los paneles del diff y en el archivo completo. El
resaltador (`lib/highlight.mjs`) es un tokenizador de una pasada sin
dependencias, y tiene una regla que lo mantiene honesto: cuando no conoce el
lenguaje **no se inventa palabras clave** — sigue marcando cadenas, números y
comentarios, que son casi universales, y deja el resto en el color del texto.
Conoce JavaScript, TypeScript, Python, SQL, CSS y JSON.
La vista a pantalla completa lee siempre el **estado actual** del archivo en tu
disco, no una copia congelada: es lo que abrirías hoy en el editor. Solo si el
archivo ya no existe (renombrado o borrado) cae de vuelta al `git show` del
commit que se registró con el cambio.
## Licencia
**Apache 2.0 + [Commons Clause](https://commonsclause.com/)** — ver [LICENSE](LICENSE).
En corto: úsalo para lo que quieras, también en tu empresa y en tu trabajo
diario. Modifícalo, cópialo, publica tus cambios. Lo único que no puedes es
**venderlo**: cobrar por el software, o por un producto o servicio de pago cuyo
valor venga entera o sustancialmente de él (hosting o soporte incluidos).
Que quede claro para que nadie pierda el tiempo: la Commons Clause hace que
esto **no** sea open source según la definición de la OSI, porque restringe el
uso. El código está disponible y es modificable, pero si tu política interna
exige licencias aprobadas por la OSI, esta no lo es.
Si quieres vender algo basado en esto, escribe y lo hablamos.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessUnresponsive