alegra-data-agent 0.6.3 → 0.7.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 (2) hide show
  1. package/README.md +432 -8
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,21 +1,445 @@
1
- # alegra-data-agent
1
+ # Data Agent Self Service
2
2
 
3
- Agente de Data interno de Alegra. Instala skills + MCPs en tu cliente de IA con un comando.
3
+ > **Cómo se llama cada cosa.** El producto es **Data Agent Self Service** — así aparece en el
4
+ > gateway y en el login. El repositorio es `Alegra-Team/data-agent-selfservice` y el paquete npm
5
+ > es `alegra-data-agent`: son tres registros independientes y no tienen por qué coincidir.
6
+ >
7
+ > El paquete **no se renombra**: su nombre está incrustado en el hook de arranque de cada máquina
8
+ > ya instalada, así que cambiarlo obligaría a migrar a todo el mundo a mano. Lo mismo vale para
9
+ > el `ISSUER`/`AUDIENCE` de los JWT (`gateway/lib/jwt.ts`) — tocarlos invalidaría los tokens ya
10
+ > emitidos.
11
+
12
+ Agente de Data interno de Alegra. Una línea instala las skills + MCPs del equipo BI Data en tu herramienta de IA (Claude Code, Cursor, OpenCode, Antigravity, Codex, Hermes), gated por OAuth `@alegra.com`.
13
+
14
+ > Uso interno de Alegra. No publicar, no promocionar, no contribuciones externas.
15
+
16
+ ## Estado
17
+
18
+ - **CLI**: [`alegra-data-agent`](https://www.npmjs.com/package/alegra-data-agent) en npmjs.com.
19
+ - **Gateway en producción**: [https://alegra-data-agent.vercel.app](https://alegra-data-agent.vercel.app).
20
+ - **Healthcheck**: `https://alegra-data-agent.vercel.app/api/health`.
21
+ - **Guía de uso y documentación**: [wiki del agente](https://wiki.alegra.com/doc/alegra-data-agent-DKiPc9cvFS) — empezar, cómo preguntar, qué responde, problemas comunes, [arquitectura y seguridad](https://wiki.alegra.com/doc/arquitectura-y-seguridad-vWfNc90Myy) e [historial de cambios](https://wiki.alegra.com/doc/historial-de-cambios-J8uMHq1sfd). La página de entrada del gateway enlaza a todas.
22
+
23
+ ## Compatibilidad
24
+
25
+ | Sistema operativo | ✓ |
26
+ |---|---|
27
+ | macOS (Intel + Apple Silicon) | ✅ |
28
+ | Ubuntu / Debian / Linux | ✅ |
29
+ | WSL (Windows Subsystem for Linux) | ✅ |
30
+ | Windows 10/11 nativo | ✅ |
31
+
32
+ | Cliente AI | ✓ |
33
+ |---|---|
34
+ | [Claude Code](https://claude.com/claude-code) | ✅ |
35
+ | [Cursor](https://cursor.com) | ✅ |
36
+ | [OpenCode](https://opencode.ai) | ✅ |
37
+ | [Antigravity](https://antigravity.dev) | ✅ |
38
+ | [Codex](https://developers.openai.com/codex) | ✅ |
39
+ | [Hermes Agent](https://hermes-agent.nousresearch.com) | ✅ |
40
+
41
+ Si no tenés ningún cliente instalado, el bootstrap te instala Claude Code automáticamente.
42
+
43
+ ### Si no usás terminal
44
+
45
+ El comando de arriba es para clientes CLI/IDE. En **claude.ai, Claude Desktop, Cowork y móvil**
46
+ el agente llega sin instalar nada:
47
+
48
+ | | Cómo |
49
+ |---|---|
50
+ | MCPs | Configuración → **Conectores**. En Team/Enterprise el owner los agrega en *Organization settings* y le quedan disponibles a toda Alegra. |
51
+ | Skills | Configuración → **Habilidades** → subir el `.zip`. El owner puede provisionarlas para toda la organización y **aparecen solas**, sin que nadie suba nada. |
52
+
53
+ Los ZIP se generan con `pnpm run pack:web` (uno por skill, en `dist/skills-web/`).
54
+
55
+ Dos cosas que conviene saber:
56
+
57
+ - Un conector de la cuenta se sirve a **todas** las superficies, **Claude Code incluido** — ahí
58
+ aparecen en `/mcp` bajo `claude.ai`. Si ya tenés el MCP como server local, el conector queda
59
+ oculto por URL duplicada; el propio `/mcp` te dice cómo cambiar de uno al otro.
60
+ - Los ZIP subidos **no se auto-actualizan**, y no hay API para subirlos. El CLI sí (manifest
61
+ firmado). Después de un release, `pnpm run pack:web` dice cuáles cambiaron: solo esos se
62
+ vuelven a subir (paso 5 del «Flujo de release»).
63
+
64
+ Detalle y el porqué en [ADR-0010](./docs/adr/0010-superficies-no-code.md).
4
65
 
5
66
  ## Instalación
6
67
 
68
+ **El comando único es:**
69
+
7
70
  ```sh
8
71
  pnpm dlx alegra-data-agent
9
72
  ```
10
73
 
11
- Solo accesible para cuentas `@alegra.com` (OAuth Google). El CLI te abre el navegador para iniciar sesión.
74
+ Funciona en cualquier terminal (macOS Terminal, GNOME Terminal, WSL, PowerShell) y desde la terminal embebida de cualquier cliente AI (Claude Code, Cursor, OpenCode, Antigravity, Codex, Hermes).
75
+
76
+ ### Si no tenés Node o pnpm
77
+
78
+ Si al correr `pnpm dlx alegra-data-agent` te dice `pnpm: command not found`, primero corré el bootstrap que te instala Node 22+ y pnpm 11+ automáticamente:
79
+
80
+ **macOS / Linux / WSL** — en cualquier terminal:
81
+
82
+ ```sh
83
+ curl -fsSL https://alegra-data-agent.vercel.app/install.sh | bash
84
+ ```
85
+
86
+ **Windows nativo** — en PowerShell (sin admin):
87
+
88
+ ```powershell
89
+ irm https://alegra-data-agent.vercel.app/install.ps1 | iex
90
+ ```
91
+
92
+ El bootstrap deja Node + pnpm + git + Claude Code listos, y al final corre `pnpm dlx alegra-data-agent` por vos. Después de eso, el comando único es siempre `pnpm dlx alegra-data-agent`.
93
+
94
+ ### Después del install
95
+
96
+ 1. **Reiniciá tu cliente AI** (Claude Code / Cursor / OpenCode / Antigravity / Codex / Hermes).
97
+ 2. **Hacé tu primera pregunta** en español. Ejemplo: *"cuántos nuevos clientes en mayo en Colombia"*.
98
+ 3. **La primera vez** que el agente use un servicio (Metabase, Amplitude, Wiki) te va a pedir 1 click con tu cuenta `@alegra.com`. Después no te pide nada más.
99
+
100
+ > El agente nunca te va a pedir API keys, tokens, ni variables de entorno. Todo es OAuth `@alegra.com` automático.
101
+
102
+ > **HubSpot es la excepción: no lo instala el CLI, se habilita como conector de la cuenta.** El
103
+ > servidor de HubSpot no soporta el registro automático que usan los clientes AI, así que una entry
104
+ > local nunca autentica. El conector oficial sí funciona: Configuración → Conectores → HubSpot →
105
+ > Conectar, y queda disponible en Claude Code, Claude Desktop y claude.ai. En Cursor, OpenCode y
106
+ > Codex hoy no hay ruta. Detalle en [`agent/mcps/hubspot/README.md`](./agent/mcps/hubspot/README.md).
12
107
 
13
108
  ## Requisitos
14
109
 
15
- - Node ≥ 22.6.0
16
- - pnpm ≥ 11.1.2
17
- - Uno de: Claude Code, Cursor, OpenCode, Antigravity, Codex, Hermes Agent
110
+ - Cuenta Google `@alegra.com` verificada (lo único que sí tenés que tener antes).
111
+ - Node ≥ 22.6.0 — instala el bootstrap si te falta.
112
+ - pnpm ≥ 11.1.2 — instala el bootstrap si te falta.
113
+ - Un cliente AI compatible — el bootstrap instala Claude Code si no detecta ninguno.
114
+
115
+ ## Qué se instala
116
+
117
+ | Herramienta | Skills | Configuración MCP |
118
+ |---|---|---|
119
+ | Claude Code | `~/.claude/skills/<n>/` (carpeta) | `~/.claude.json` → `mcpServers` |
120
+ | Cursor | `~/.cursor/skills/<n>/` (carpeta) | `~/.cursor/mcp.json` → `mcpServers` |
121
+ | OpenCode | `~/.config/opencode/skills/<n>/` (carpeta) | `~/.config/opencode/opencode.json` → `mcp` |
122
+ | Antigravity | `~/.gemini/skills/<n>/` (carpeta) | `~/.gemini/config/mcp_config.json` → `mcpServers` |
123
+ | Codex | `~/.agents/skills/<n>/` (carpeta) | `~/.codex/config.toml` → `[mcp_servers.<n>]` |
124
+
125
+ ### Acotar los MCPs a una carpeta (opcional)
126
+
127
+ Por defecto los MCPs se instalan a nivel usuario: quedan disponibles en toda sesión, de
128
+ cualquier carpeta. Si en la misma máquina separás contextos —trabajo de Alegra, proyectos
129
+ propios, trabajo de un cliente— podés acotarlos a una sola carpeta:
130
+
131
+ ```bash
132
+ mkdir -p ~/.alegra
133
+ echo 'MCP_DIR=~/trabajo/alegra' >> ~/.alegra/agent.conf
134
+ pnpm dlx alegra-data-agent install --yes
135
+ ```
136
+
137
+ Los MCPs pasan a `~/trabajo/alegra/.mcp.json`, que Claude Code resuelve subiendo desde el
138
+ directorio actual: **cascadea a todo lo que esté dentro de esa carpeta y es invisible fuera
139
+ de ella**. Se pre-aprueban solos, así que no hay que aceptarlos repo por repo.
140
+
141
+ También funciona como env var (`ALEGRA_DATA_AGENT_MCP_DIR`, le gana al archivo), pero el
142
+ archivo es lo recomendado: el hook de auto-update corre en un shell no interactivo y no ve
143
+ lo que exportes en tu `.bashrc`, así que reinstalaría a nivel usuario.
144
+
145
+ **Qué clientes lo respetan.** Claude Code y Cursor pueden acotar un MCP a una carpeta, así
146
+ que con `MCP_DIR` los MCPs van solo a esos dos. OpenCode, Hermes, Codex y Antigravity no
147
+ pueden: OpenCode busca su config de proyecto solo hasta la raíz del repo git, y Hermes guarda
148
+ los MCPs por perfil. Ahí los MCPs quedarían globales, así que no se instalan y se retiran los
149
+ que ya tuvieran. Las skills sí se instalan en todos. Para consultar datos de Alegra, usá
150
+ Claude Code o Cursor dentro de la carpeta.
151
+
152
+ **Se corrige solo.** Cada arranque compara dónde debería estar cada MCP con dónde está y
153
+ retira lo que sobra, siempre que sea nuestro: una copia en `~/.claude.json` de antes de
154
+ configurar `MCP_DIR`, la de una carpeta vieja si lo cambiaste o lo sacaste, o la de un cliente
155
+ que no corresponde. Un server tuyo con el mismo nombre y otra configuración no se toca.
156
+ `alegra-data-agent status` te dice en qué archivo quedaron y avisa si ve algo fuera de lugar.
157
+
158
+ Detalle completo en [ADR-0012](docs/adr/0012-mcp-scope-por-workspace.md).
159
+
160
+ ### Dónde quedan los archivos que genera el agente
161
+
162
+ Cuando pedís un CSV, un Excel o un gráfico, el agente lo guarda en `alegra-data/` dentro de
163
+ la carpeta donde abriste la sesión, y te dice la ruta al terminar (en WSL, también la de
164
+ Windows). Nunca en los temporales del cliente. Para que vaya siempre a la misma carpeta:
165
+
166
+ ```bash
167
+ mkdir -p ~/.alegra
168
+ echo "OUTPUT_DIR=$HOME/Documentos/alegra-data" >> ~/.alegra/agent.conf
169
+ ```
170
+
171
+ Lo lee el agente, no el CLI, así que aplica desde la siguiente pregunta, sin reinstalar. En
172
+ claude.ai, Desktop y Cowork el archivo aparece para descargar en el chat.
173
+
174
+ El CLI:
175
+
176
+ 1. Verifica auth contra el gateway (OAuth Google `hd=alegra.com`).
177
+ 2. Descarga el manifest firmado Ed25519 y verifica firma antes de tocar disco.
178
+ 3. Verifica SHA-256 por archivo antes de escribirlo.
179
+ 4. Escribe atómicamente (tmp + rename) los configs JSON y respalda lo que ya existía (`*.bak-<ts>`).
180
+ 5. Registra en `~/.config/alegra-data-agent/lock.json` qué quedó instalado por target.
181
+ 6. Configura **auto-update en cada cliente instalado** (los 5, no solo Claude Code): Claude Code y Codex vía hook `SessionStart`, Cursor vía `workspaceOpen`, Antigravity vía `SessionStart` (Gemini), OpenCode vía plugin. Al abrir el cliente, el contenido se actualiza solo.
182
+
183
+ ## Comandos
184
+
185
+ ```sh
186
+ pnpm dlx alegra-data-agent # instala / actualiza todo (default)
187
+ pnpm dlx alegra-data-agent --target=claude-code,cursor
188
+ pnpm dlx alegra-data-agent --only=alegra-sales,alegra-growth
189
+ pnpm dlx alegra-data-agent --dry-run
190
+
191
+ pnpm dlx alegra-data-agent update # re-descarga lo que cambió
192
+ pnpm dlx alegra-data-agent status # estado actual + drift
193
+ pnpm dlx alegra-data-agent list # catálogo del gateway
194
+ pnpm dlx alegra-data-agent uninstall
195
+
196
+ pnpm dlx alegra-data-agent login
197
+ pnpm dlx alegra-data-agent logout
198
+ pnpm dlx alegra-data-agent whoami
199
+ ```
200
+
201
+ Ayuda completa: `pnpm dlx alegra-data-agent help`.
202
+
203
+ > El CLI siempre termina ejecutando la última versión publicada: si pnpm sirve una anterior (cooldown de 24h de pnpm 11), el comando detecta la nueva en el registry y se re-ejecuta solo con ella (self-bounce).
204
+
205
+ ## Skills
206
+
207
+ 16 skills. Cada equipo dueño mantiene la suya; BI Data mantiene la infraestructura y las skills horizontales.
208
+
209
+ | Skill | Dueño | Tipo |
210
+ |---|---|---|
211
+ | `alegra-bi` (router) | BI Data | Router |
212
+ | `alegra-strategic` | Strategic | Dominio |
213
+ | `alegra-product` | Producto | Dominio |
214
+ | `alegra-sales` | Sales | Dominio |
215
+ | `alegra-accountant` | Programa Contadores | Dominio |
216
+ | `alegra-cx` | Customer Experience | Dominio |
217
+ | `alegra-growth` | Growth | Dominio |
218
+ | `alegra-alanube` | Alanube | Dominio |
219
+ | `alegra-collection` | Collection | Dominio |
220
+ | `alegra-sr` | Strategic Relationships | Dominio |
221
+ | `alegra-board` | BI Data | Dominio |
222
+ | `dashboard-auditor` | BI Data | Horizontal (review) |
223
+ | `dataviz-reviewer` | BI Data | Horizontal (review) |
224
+ | `presentation-coach` | BI Data | Horizontal (coach) |
225
+ | `launch-measurement` | BI Data | Horizontal (proceso) |
226
+ | `agent-onboarding` | BI Data | Soporte |
227
+
228
+ Arquitectura: [`docs/skills-architecture.md`](./docs/skills-architecture.md). Contrato de ejecución (MBQL por defecto, SQL nativo de fallback, acceso MCP-first sin tokens): [`agent/skills/SKILL_STANDARD.md`](./agent/skills/SKILL_STANDARD.md) §3.1.
229
+
230
+ ### Cómo responde el agente
231
+
232
+ Tres reglas transversales que heredan todas las skills de dominio (detalle en [`agent/skills/SKILL_STANDARD.md`](./agent/skills/SKILL_STANDARD.md)):
233
+
234
+ - **Pregunta la población, asume los defaults.** Si falta el período, el país o el segmento, elige el más útil y lo avisa en una línea. Si no está claro **qué se cuenta** — "nuevos usuarios" puede ser signups (se registraron) o new logos (empezaron a pagar), que difieren en un orden de magnitud — pregunta antes de consultar. El vocabulario de poblaciones vive en el router (`alegra-bi`, sección «Vocabulario»).
235
+ - **La respuesta dice de dónde sale el número.** `New logos (empezaron a pagar · fact_customers_mrr) | 71`, no `Nuevos | 71`. Una cifra sin procedencia no se puede auditar, y las mismas palabras nombran poblaciones distintas según la tabla.
236
+ - **Una pregunta = una consulta.** Dos si hace falta una semilla o un delta. MBQL por defecto; cuando MBQL no lo expresa (CTEs, subqueries, window functions) va SQL nativo por `execute_sql` en una llamada, en vez de partirlo en varias.
237
+
238
+ **Fuentes de verdad, cuando dos tablas dan números distintos:** `dm_strategic.fact_customers_mrr` manda para todo lo que sea quién paga, quién empezó y quién dejó de pagar. `dm_sales.fact_closed_deals` es su vista comercial (quién cerró, contra qué meta), conectada por `id_company`. Para eventos de uso de producto, `amplitude_events_enriched` en Metabase — no el MCP de Amplitude, no las columnas `*_m` de retención.
239
+
240
+ ## MCPs
241
+
242
+ Los remotos van vía OAuth — el cliente abre el flow en el primer uso, **cero tokens manuales, cero env vars con secretos**. `salesforce` es la excepción: MCP stdio local del vendor, auth por la Salesforce CLI (`sf org login web --set-default`, requiere Node ≥ 22), solo lectura, para casos puntuales fuera del mirror de Metabase. No se autentica desde `/mcp` — soporte en el Caso 11 de `agent-onboarding`.
243
+
244
+ | MCP | Backend | Transporte | Auth |
245
+ |---|---|---|---|
246
+ | `amplitude` | Amplitude SaaS | remoto (http) | OAuth |
247
+ | `hubspot` | HubSpot SaaS | conector de cuenta | OAuth — **no se instala**: se habilita en Configuración → Conectores ([por qué](./agent/mcps/hubspot/README.md)) |
248
+ | `metabase` | Metabase self-hosted | remoto (http) | OAuth corporativo |
249
+ | `salesforce` | Salesforce DX (solo lectura) | stdio local | Salesforce CLI (`sf org login web`) — no por `/mcp` |
250
+ | `wiki-alegra` | Wiki self-hosted | remoto (http) | OAuth corporativo |
251
+
252
+ Llave universal para joins: `id_company`. Detalles en [`docs/joining-data-sources.md`](./docs/joining-data-sources.md).
253
+
254
+ ## Para mantenedores
255
+
256
+ Skills viven en `agent/skills/<name>/SKILL.md` (+ anexos). MCPs en `agent/mcps/<name>/config.example.json`. El manifest firmado se regenera cuando esos paths cambian — **hoy en local**, porque Actions está apagado (ver «Release sin Actions» más abajo).
257
+
258
+ ### Flujo de release
259
+
260
+ > Los pasos 1 y 2 describen el flujo **con CI**. Mientras Actions siga apagado, la validación y la firma van en local: ver «Release sin Actions» justo abajo.
261
+
262
+ 1. PR `<rama>-changes` o `feature/* → testing` modificando `agent/skills/**` o `agent/mcps/**` (CI valida standards, scan y frescura del manifest).
263
+ 2. PR `testing → main`: el workflow `sign-manifest.yml` se dispara en el PR (toca esos paths), regenera el manifest, lo escanea, lo firma con `ALEGRA_SIGNING_KEY` y commitea el manifest firmado de vuelta al branch del PR vía la REST API de GitHub (queda Verified, server-side). Al mergear, `main` ya tiene el manifest correcto.
264
+ 3. Gateway en Vercel se redespliega manualmente con `pnpm run deploy` desde `gateway/` (corre `prepare:skills`, que verifica firma y sha256, y después `vercel --prod`; nunca `vercel --prod` a secas). La integración Vercel ↔ GitHub no está habilitada: el proyecto de Vercel vive en su propio team y no depende de la organización de GitHub.
265
+ 4. Próximo `update` de cualquier alegrito trae los cambios — sin republicar el CLI a npm.
266
+ 5. **Superficies no-code (claude.ai, Desktop, Cowork).** Los pasos anteriores no llegan ahí: esos
267
+ ZIP se suben a mano y no hay API para automatizarlo (ADR-0010). `pnpm run pack:web` lista
268
+ exactamente cuáles cambiaron respecto de lo que ya está en la organización. Se suben esos, y
269
+ después `pnpm run pack:web -- --mark-published <skills>` registra lo subido en
270
+ `agent/web-published.json`, que se commitea. Sin este paso, quien usa Desktop sigue con las
271
+ definiciones viejas sin saberlo.
272
+
273
+ ### Release sin Actions
274
+
275
+ Con los workflows apagados, los dos pasos que hacía el CI se hacen en local. Requieren la clave
276
+ privada de firma (gestor del equipo) y el token de npm.
277
+
278
+ ```sh
279
+ # 1. Validar, escanear, regenerar y FIRMAR el manifest
280
+ ALEGRA_SIGNING_KEY="$(cat /ruta/a/la/clave)" pnpm run manifest
281
+
282
+ # 2. Verificar que el manifest quedó firmado y válido antes de commitear
283
+ node --experimental-strip-types -e "import('./src/signature.ts').then(async m => {
284
+ const man = JSON.parse((await import('node:fs')).readFileSync('agent/skills/index.json','utf-8'));
285
+ const r = m.verifyManifestSignature(man);
286
+ console.log(r.ok ? 'firma válida — ' + man.signature.keyId : 'INVÁLIDA: ' + r.reason);
287
+ })"
288
+
289
+ # 3. Publicar a npm: DESPUÉS del merge a main, del deploy del gateway verificado en producción
290
+ # con una sesión real (manifest nuevo, firma válida, update instala) y del tag. Lección de la 0.5.0.
291
+ pnpm run prepublishOnly && cp .github/README-npm.md README.md && pnpm publish --access public --no-git-checks
292
+ git checkout README.md # devolver el README completo al repo
293
+ ```
294
+
295
+ > `--no-git-checks` es obligatorio acá, no opcional: el swap del paso anterior deja el working tree
296
+ > "sucio" a propósito (es exactamente lo que hace `publish.yml` en `Swap to minimal README for npm
297
+ > tarball` + `--no-git-checks` en su paso `Publish`). Sin el flag, `pnpm publish` rechaza con
298
+ > `ERR_PNPM_GIT_UNCLEAN` — no es una falla real, es el propio swap documentado arriba.
299
+
300
+ El manifest firmado se commitea en el PR, en vez de que el bot lo commitee al branch. Todo lo
301
+ demás del flujo no cambia.
302
+
303
+ **Después de publicar, la wiki.** Copiar la entrada nueva del `CHANGELOG.md` **arriba** de la
304
+ página hija [Historial de cambios](https://wiki.alegra.com/doc/historial-de-cambios-J8uMHq1sfd) y
305
+ actualizar la línea **Estado** del home de la wiki (versión, skills, fecha). El home no lleva
306
+ historial: solo el enlace a esa página, para que no crezca con cada release. El CHANGELOG del repo
307
+ es la fuente; la wiki lo copia, nunca al revés.
308
+
309
+ ### Publicación de nueva versión del CLI
310
+
311
+ 1. Bump `version` en `package.json` y `gateway/package.json` + entrada en `CHANGELOG.md` + regenerar el manifest con **`pnpm run manifest`** (el pipeline completo: `validate` → `scan` → `generate` → `sign`; el `productVersion` sale de package.json). Todo dentro del mismo PR del release.
312
+ 2. Tras el merge a main, tag sobre el merge commit: `git tag -s vX.Y.Z && git push origin vX.Y.Z`.
313
+ 3. **Con Actions apagado este paso no ocurre:** el tag no publica nada y el publish se hace a mano (ver «Release sin Actions»). Cuando Actions vuelva, el workflow `publish.yml` valida que `package.json` coincida con el tag, corre los gates (validate → scan → tests) y publica: build → swap a `.github/README-npm.md` (README mínimo del tarball) → `pnpm publish --access public`. Requiere el secret `NPM_TOKEN` configurado.
314
+
315
+ > ⚠️ **Nunca corras `manifest:generate` solo.** Borra el bloque `signature` de
316
+ > `agent/skills/index.json`, y `verifyManifestSignature` es hard-fail: un manifest sin firma hace
317
+ > que el CLI rechace **toda** instalación y update, no solo la skill que tocaste. Siempre
318
+ > `pnpm run manifest`, que firma al final. `tests/security/manifest-committed.test.mjs` lo caza en
319
+ > local si se te pasa.
320
+
321
+ **Si el release sale mal:** mover el dist-tag `latest` hacia atrás **no revierte a nadie** — el
322
+ hook resuelve por rango semver y los tags no participan. El procedimiento real está en
323
+ [`docs/rollback.md`](./docs/rollback.md).
324
+
325
+ ### Local
326
+
327
+ ```sh
328
+ pnpm run manifest:scan # buscar prompt-injection / credenciales
329
+ pnpm run manifest:generate # regenera agent/skills/index.json
330
+ pnpm run manifest:sign # firma (requiere ALEGRA_SIGNING_KEY)
331
+ pnpm run manifest # los cuatro en orden (validate → scan → generate → sign)
332
+
333
+ pnpm install
334
+ pnpm typecheck
335
+ pnpm build # → dist/
336
+
337
+ pnpm test # Fase 1: SOLO valida el schema de los evals — no
338
+ # ejecuta ningún agente
339
+ pnpm test:security # la suite real: firma, paths, clientes, drift de MCPs
340
+ pnpm test:all # ambos
341
+
342
+ pnpm run eval:run -- --local # Fase 2: ejecuta los evals de verdad contra las skills
343
+ # del repo. Cuesta tokens y tarda horas (116 casos); para
344
+ # un caso puntual: --only <nombre-del-caso>
345
+
346
+ pnpm run verify:clients # E2E: instala en un HOME temporal con 5 de los 6 clientes
347
+ # simulados (Hermes aún no está cubierto) y verifica rutas, shapes de MCP, hooks
348
+ # e idempotencia (requiere sesión y red)
349
+ ```
350
+
351
+ > **`test:all` en verde no dice que las skills respondan bien.** La Fase 1 solo lee los YAML;
352
+ > si tardó un segundo, no ejecutó nada. Medir routing, tablas y números es `eval:run`: corre cada
353
+ > caso aislado (sin el repo ni la memoria del dev a la vista) y consulta Metabase de verdad.
354
+ > Detalle en [`docs/evals.md`](./docs/evals.md).
355
+
356
+ El grueso de la verificación corre en local, no en CI: la organización está en plan Free y el
357
+ pool de minutos de Actions es compartido. `pre-commit install && pre-commit install --hook-type
358
+ pre-push` deja los mismos gates enganchados a los commits.
359
+
360
+ ### Keypair de firma
361
+
362
+ ```sh
363
+ pnpm run signing-key:generate # escribe la privada a un archivo 600; NO la imprime
364
+ ```
365
+
366
+ La pública va en `TRUSTED_KEYS` (`src/signature.ts`); la privada, **en dos lugares**: el gestor
367
+ de contraseñas del equipo y el secret `ALEGRA_SIGNING_KEY` del repo. Un secret de GitHub no se
368
+ puede leer: si es la única copia, se pierde en cuanto haga falta usarla en otro lado — pasó con
369
+ la clave v1 y obligó a rotar.
370
+
371
+ El CLI acepta **varias claves a la vez**, que es lo que permite rotar sin ventana de rotura: con
372
+ una sola, el CLI nuevo rechaza el manifest viejo *y* el viejo rechaza el nuevo. Procedimiento
373
+ completo en [SECURITY.md](SECURITY.md#custodia-y-rotación-de-la-clave-de-firma).
374
+
375
+ ## Estructura del repo
376
+
377
+ ```
378
+ data-agent-selfservice/
379
+ ├── src/ # CLI TypeScript (publicado a npm como dist/)
380
+ ├── scripts/ # generate-manifest, sign-manifest, scan-skills, generate-signing-key, verify-clients
381
+ ├── agent/
382
+ │ ├── skills/ # SKILL.md + anexos, agrupados por dueño
383
+ │ └── mcps/ # config.example.json por MCP
384
+ ├── gateway/ # Next.js en Vercel — OAuth + manifest firmado + skills
385
+ ├── docs/ # ADRs + guías
386
+ ├── tests/ # evals + security tests
387
+ ├── install.sh # bootstrap para macOS/Linux/WSL (servido por gateway)
388
+ └── install.ps1 # bootstrap para Windows (servido por gateway)
389
+ ```
390
+
391
+ ## Troubleshooting
392
+
393
+ | Síntoma | Probable causa | Fix |
394
+ |---|---|---|
395
+ | `pnpm: command not found` | pnpm no instalado | `corepack enable && corepack prepare pnpm@11.1.2 --activate` |
396
+ | `Node.js >= 22.6.0` requerido | Node viejo | `nvm install 22` o reinstala desde nodejs.org |
397
+ | `Solo cuentas @alegra.com` | OAuth con cuenta personal | Login con tu cuenta corporativa |
398
+ | El cliente no ve los MCPs | Cambio de config no recargado | Reinicia el cliente |
399
+ | `keyId no reconocido` | El CLI no conoce la clave con la que se firmó el manifest (rotación en curso y CLI viejo) | `pnpm dlx alegra-data-agent@latest`. Durante una rotación el CLI acepta la clave nueva y la anterior, así que esto solo aparece en versiones muy atrasadas |
400
+ | Una versión recién publicada no se instala / no actualiza | Cooldown de pnpm 11 (`minimumReleaseAge`, ~24h) sobre versiones de <24h | Desde 0.2.2 el CLI hace self-bounce: aunque pnpm sirva una versión anterior, el comando detecta la última en el registry y se re-ejecuta con ella solo. El self-update (hook) y el bootstrap además corren con `--config.minimumReleaseAge=0`. Para forzarlo a mano: `pnpm dlx alegra-data-agent@<versión>` (pin exacto) |
401
+
402
+ Si nada funciona: la skill `agent-onboarding` (instalada por defecto) tiene una guía paso a paso de troubleshooting que el modelo puede correr contigo.
403
+
404
+ ## Branching
405
+
406
+ `<nombre>-changes → testing → main`. Cada quien trabaja en su rama personal (`jhon-changes`,
407
+ `luis-changes`…) y llega por PR: primero a `testing`, después a `main`. Nada entra directo a
408
+ `main`, y **el PR a `main` lo aprueba y mergea otra persona** — nadie mergea el suyo. Detalle en
409
+ [CONTRIBUTING.md](CONTRIBUTING.md).
410
+
411
+ Cambios a `agent/skills/**` o `agent/mcps/**` disparan el workflow de firma en el PR hacia `main`
412
+ (el manifest firmado se commitea al branch del PR antes del merge). Cambios a `scripts/**.mjs`
413
+ requieren review humano: no autoejecutan la firma, para que un PR a los scripts no pueda
414
+ exfiltrar la clave.
415
+
416
+ > En plan Free los repos privados no tienen rulesets, así que esto es **un acuerdo del equipo,
417
+ > no un candado técnico**. Lo mismo vale para los commits firmados. Ver
418
+ > [Cómo trabajamos en GitHub (Alegra-Team)](https://wiki.alegra.com/doc/como-trabajamos-en-github-alegra-team-2WuRXpYEmg).
419
+
420
+ ## CI / Workflows
421
+
422
+ > ⚠️ **Hoy Actions está deshabilitado en este repo.** La organización agotó su presupuesto
423
+ > (`the job was not started because recent account payments have failed or your spending limit
424
+ > needs to be increased`), así que ningún workflow puede correr y dejarlos activos solo llenaría
425
+ > los PRs de checks rojos que no dicen nada del código. **Mientras dure, todo es local**: la
426
+ > validación con pre-commit/pre-push, y la firma del manifest y la publicación a npm a mano —
427
+ > ver «Release sin Actions» abajo. Para reactivarlos cuando haya presupuesto:
428
+ > `gh api -X PUT /repos/Alegra-Team/data-agent-selfservice/actions/permissions -F enabled=true`.
429
+
430
+ Cuatro workflows, y solo uno corre por PR. El pool de minutos de Actions es compartido entre
431
+ todos los repos de la organización (plan Free), así que en GitHub queda únicamente lo que no se
432
+ puede garantizar en la máquina del autor; el resto vive en `.pre-commit-config.yaml`.
433
+
434
+ | Workflow | Cuándo dispara | Qué hace |
435
+ |---|---|---|
436
+ | `pr.yml` | PRs a `testing` y a `main` (`paths-ignore` docs-only) | **Un solo job**: gates pnpm-only, standards de skills/MCPs (strict), scan de secretos y prompt injection, frescura del manifest, typecheck y tests. El typecheck del gateway solo si el PR toca `gateway/` |
437
+ | `sign-manifest.yml` | PRs a `main` que tocan `agent/skills/**` o `agent/mcps/**` | Regenera + escanea + firma el manifest y lo commitea al branch del PR vía REST API de GitHub, no con `git push`: así queda firmado server-side y Verified. Necesita el secret `ALEGRA_SIGNING_KEY` |
438
+ | `security.yml` | Schedule semanal + `workflow_dispatch` | Gitleaks + OSV + Trivy + pnpm audit. CodeQL solo bajo dispatch explícito (es el job más caro). No corre por push ni por PR |
439
+ | `publish.yml` | Tag `v*.*.*` | Valida versión == tag + gates + publica el CLI a npmjs.com. Necesita el secret `NPM_TOKEN` |
18
440
 
19
- ## Soporte
441
+ Por qué un solo job y no varios: GitHub factura por job con un mínimo de un minuto, así que
442
+ partir la validación en cuatro costaba cuatro minutos aunque dos de ellos fueran un `grep` de
443
+ tres segundos.
20
444
 
21
- Guía de uso, documentación y soporte para el equipo de Alegra: entra a https://alegra-data-agent.vercel.app con tu cuenta `@alegra.com`.
445
+ Dependabot apunta a `testing` como cualquier otro cambio, no directo a main).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alegra-data-agent",
3
- "version": "0.6.3",
3
+ "version": "0.7.1",
4
4
  "private": false,
5
5
  "description": "Agente de Data interno de Alegra. Instala skills + MCPs en tu cliente de IA con un comando.",
6
6
  "license": "UNLICENSED",