Skip to main content

adela-new-module

Scaffold y creación de nuevos módulos Adela siguiendo el ecosistema de piezas intercambiables

Ir para a instalação

Informações da origem

Repositório
Ntizar/NtizarBrainMasterMind
Última atividade na origem
26 de junho de 2026 às 12:05
Idioma detectado do SKILL.md
espanhol
Estrelas
2
Forks
0

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
14 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
adela-new-module
description
Scaffold y creación de nuevos módulos Adela siguiendo el ecosistema de piezas intercambiables
## Adela New Module Skill para que un LLM cree un nuevo módulo Adela desde cero, siguiendo los estándares del ecosistema (TypeScript strict, zero-deps, TODO en castellano). ### Cuándo usarlo - El usuario dice "crea un módulo Adela para X" - Necesitas añadir una nueva pieza al ecosistema Adela - El usuario pide "otro módulo como los que ya tenemos" - El usuario da un roadmap multi-fase para mejorar Adela (seguridad, observabilidad, escalabilidad, API) — cargar `references/backend-roadmap.md` para entender las fases y categorías ### ⚠️ Regla CRÍTICA: Todos los repos Adela son PRIVADOS **Cada módulo Adela se crea como repositorio PRIVADO en GitHub.** El usuario lo exige explícitamente. NO crear repos públicos. ### Categorías de módulos (nuevas desde v2.0) | Categoría | Descripción | Ejemplos | |-----------|-------------|----------| | `infra` | Capa base — sin dependencias de otros Adela | time, env, http, cache, db | | `core` | Capa funcional — depende de infra | auth, health, ai | | `export` | Capa de exportación | export | | `presentation` | Capa de interfaz | i18n, admin | | `seguridad` | **NUEVA** — Seguridad y validación | security, rateLimit, validation | | `observabilidad` | **NUEVA** — Logging y errores | logger, errors | | `escalabilidad` | **NUEVA** — Escalado horizontal | db_pg, cache_redis | | `api-layer` | **NUEVA** — Capa de API | pagination, router | ### Principio de explicación El usuario quiere que **le expliques cómo funciona cada módulo**. Al crear un módulo nuevo: - README.md debe explicar la arquitectura y el flujo - Explicar por qué se eligió ese enfoque (no solo el qué, sino el porqué) - Incluir diagramas de flujo ASCII cuando sea relevante ### Pasos #### 1. Copiar template scaffold ```bash cp -r /root/workspace/AdelaMasterMind/templates/adela-module-scaffold/ /root/workspace/Adela/Adela_<NOMBRE>/ ``` #### 2. Rellenar package.json Reemplazar placeholders `{{MODULE_NAME}}`, `{{MODULE_NAME_LOWERCASE}}`, `{{MODULE_DESCRIPTION}}`. Reglas de dependencias: - **Zero runtime deps** siempre que sea posible - Si necesita base de datos → `sql.js` - Si necesita auth → `bcryptjs`, `jsonwebtoken` - Si necesita export → `csv-parse`, `csv-stringify`, `pdfkit` - Si necesita fetch → usar `fetch()` nativo de Node (18+) #### 3. Implementar src/ Estructura estándar: ``` src/ ├── index.ts # Barrel export: exporta todo ├── <modulo>.ts # Implementación principal con createX() factory └── types.ts # Interfaces públicas ``` **Patrón multi-archivo:** Cuando un módulo tiene responsabilidades separadas (ej: implementación + formato de salida), dividir en archivos: ``` src/ ├── index.ts # Barrel export ├── metrics.ts # Lógica principal (createMetrics, counter, histogram, gauge, middleware) ├── prometheus.ts # Formato de exportación (toPrometheusFormat, toPrometheusJSON) └── types.ts # Tipos públicos ``` Cada archivo debe ser autocontenido y testable por separado. `index.ts` solo re-exporta. **🔴 PITFALL — Token GitHub enmascarado:** El `GITHUB_TOKEN` en `/hermes-home/.env` se muestra como `***` (enmascaramiento visual) pero es un PAT real de 40 caracteres. Sin embargo, **puede expirar entre llamadas consecutivas**, causando "Bad credentials" intermitentes. **Patrón seguro:** siempre sourcear `.env` y usar el token en el mismo bloque de comando: ```bash source /hermes-home/.env 2>/dev/null # Usar $GITHUB_TOKEN inmediatamente en el mismo bloque curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" ... ``` Si el token falla, verificar si se expiró y reemplazarlo en `.env` con un nuevo PAT desde GitHub Settings → Developer Settings → Personal access tokens. **🔴 PITFALL CRÍTICO — JWT en ESM con jsonwebtoken:** `import * as jwt from 'jsonwebtoken'` crea un **namespace object** donde `jwt.verify` funciona pero `jwt.sign` NO existe como método directo. Esto causa fallos silenciosos: el login genera token OK pero el middleware lo rechaza como "inválido". **Solución:** usar `createRequire` en TODOS los archivos que usen jsonwebtoken (routes Y middleware): ```typescript import { createRequire } from 'node:module' const require = createRequire(import.meta.url) const jwt = require('jsonwebtoken') // Ahora jwt.sign Y jwt.verify funcionan ``` **NUNCA mezclar:** un archivo con `import * as jwt` y otro con `require('jsonwebtoken')` = tokens que no se verifican. **🔴 PITFALL CRÍTICO — JWT_SECRET compartido entre módulos:** Si cada archivo (routes/auth.ts, middleware/auth.ts) genera su propio `process.env.JWT_SECRET || crypto.randomUUID()`, cada uno crea un UUID diferente. Resultado: el token se firma con el UUID de auth.ts pero se verifica con el UUID de middleware/auth.ts → siempre falla como "Token inválido". **Solución:** un solo `config.ts` exporta el JWT_SECRET, todos los archivos importan de ahí: ```typescript // src/config.ts export const JWT_SECRET = process.env.JWT_SECRET || crypto.randomUUID() // routes/auth.ts import { JWT_SECRET } from '../config.js' // middleware/auth.ts import { JWT_SECRET } from '../config.js' ``` **NUNCA** generar JWT_SECRET inline en cada archivo. Siempre un módulo compartido. **🔴 PITFALL — better-sqlite3 vs sql.js:** `better-sqlite3` requiere compilación nativa (node-gyp + make). Si no hay `make` en el sistema, el `npm install` falla. En entornos sin herramientas de compilación, usar **`sql.js`** (JS puro, sin compilación): ```bash npm install sql.js ``` Importación: `import initSqlJs from 'sql.js'` (async init). **🔴 PITFALL — sql.js es en memoria: persistencia en archivo:** sql.js por defecto guarda todo en memoria. Si el contenedor se reinicia, se pierden TODOS los datos. Para persistencia, exportar a archivo tras cada operación de escritura: ```typescript import fs from 'fs' import path from 'path' const DB_PATH = process.env.DB_PATH || path.join(__dirname, '..', 'data', 'datos.db') // Al iniciar: cargar desde archivo si existe if (fs.existsSync(DB_PATH)) { const buffer = fs.readFileSync(DB_PATH) db = new SQL.Database(buffer) } else { db = new SQL.Database() } // Después de CADA db.run() (INSERT, UPDATE, DELETE): function saveDatabase() { const data = db.export() const buffer = Buffer.from(data) const dir = path.dirname(DB_PATH) if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }) fs.writeFileSync(DB_PATH, buffer) } // Wrapper que guarda automáticamente: function run(sql: string, params: any[] = []) { db.run(sql, params) saveDatabase() // ← persistir tras cada escritura } ``` **Dockerfile:** crear directorio de datos y dar permisos: ```dockerfile RUN mkdir -p /app/data RUN chown -R appuser:appgroup /app ``` **Env var:** `DB_PATH=/app/data/datos.db` para rutas personalizadas. **🔴 PITFALL — sql.js INSERT con valores undefined:** Al hacer INSERT con sql.js, pasar `undefined` como valor de parámetro causa fallos silenciosos o errores de tipo. Siempre usar fallbacks: ```typescript // ❌ MAL — data.descripcion puede ser undefined db.run('INSERT INTO table (a, b, c) VALUES (?, ?, ?)', [id, data.nombre, data.descripcion]) // ✅ BIEN — fallbacks explícitos db.run('INSERT INTO table (a, b, c) VALUES (?, ?, ?)', [id, data.nombre, data.descripcion || '']) ``` Regla: `|| ''` para strings, `|| 0` para números, `|| null` para NULL explícito. **🔴 PITFALL — sql.js INSERT column ordering:** Verificar que las columnas del INSERT coinciden con los valores. Un error común es poner `rol` como entero `1` en vez de string `'admin'` en el INSERT del usuario admin. **🔴 PITFALL — sql.js db.run() devuelve void/Promise, no el objeto creado:** `db.run()` NO devuelve el registro insertado. Si una función como `crearLead()` llama `db.run()` y luego construye el objeto manualmente, la función DEBE ser `async` y hacer `await db.run(...)`. Si la función del route no hace `await`, Express serializa la Promise como `{}` → respuesta vacía. Síntoma: `{"lead":{}}` en POST, `{"leads":{}}` en GET, `{"stats":{}}` en stats. El objeto se construye bien internamente pero Express lo serializa vacío porque la función devuelve una Promise sin await. **✅ Fix:** TODAS las funciones de route que llaman a `db.*()` deben ser `async` + `await`. TODAS las funciones en db.ts que llaman `db.run()` o `db.exec()` deben ser `async` + `await`. **🔴 PITFALL — Template literal SQL: backtick de cierre al añadir tablas con patch:** Al añadir un nuevo bloque `db.run(\`...\`)` al final de un bloque SQL existente usando `patch()`, el `old_string` DEBE incluir el backtick de cierre `\`)` del bloque anterior. Si no, se deja un `db.run()` sin cerrar, causando TS1005 '`,` expected' en todas las líneas SQL siguientes. ```typescript // ❌ MAL — old_string sin el backtick de cierre del bloque anterior // old_string: "CREATE INDEX ... ;\n\n // Admin user" // Resultado: el db.run() de las tablas base se queda abierto → TS1005 en todas las tablas nuevas // ✅ BIEN — old_string incluye el cierre del bloque anterior // old_string: "CREATE INDEX ... ;\n `)\n\n // Admin user" // Resultado: el bloque anterior se cierra correctamente, el nuevo abre su propio db.run() // new_string correcto: // "CREATE INDEX ... ;\n `)\n\n // === Nuevo módulo ===\n db.run(`\n ...\n `)\n\n // Admin user" ``` **🔴 PITFALL — createRequire no va en types.ts:** `createRequire` es para archivos runtime (db.ts, routes, middleware). Si se pone en `types.ts`, causa error TS1343: `import.meta` meta-property only allowed with module es2020/esnext/node16+. types.ts solo debe tener interfaces y tipos, nunca imports de runtime. **🔴 PITFALL — Migración de datos al cambiar schema (admin pin_hash NULL):** Cuando se actualiza el código para usar bcrypt en PINs, los usuarios existentes tienen `pin_hash = NULL` (creados con el código viejo). El login falla silenciosamente porque `bcrypt.compareSync(pin, null)` siempre devuelve false. **Solución — migración automática en initDatabase():** ```typescript // Después de CREATE TABLE, detectar y migrar: const adminRow = db.exec("SELECT id, pin_hash FROM usuarios WHERE email = 'admin@adelacrm.local'") if (adminRow[0]?.values[0][1] === null) { const pinHash = bcrypt.hashSync(String(process.env.ADMIN_PIN || '1234'), 10) db.run("UPDATE usuarios SET pin_hash = ? WHERE email = 'admin@adelacrm.local'", [pinHash]) saveDatabase() } ``` **Regla:** Siempre que se cambie el formato de un campo (plain → hash, string → enum, etc.), añadir migración automática en initDatabase() que detecte el formato viejo y lo actualice. Nunca asumir que la BD está vacía. **🔴 PITFALL — API devuelve campos sensibles (pin_hash, password):** Nunca devolver `pin_hash`, `password`, ni otros campos sensibles en respuestas API. Crear un helper `sanitizeUser()` en la ruta: ```typescript function sanitizeUser(u: any) { const { pin_hash, ...rest } = u return rest } // En GET /usuarios: const usuarios = await obtenerUsuarios() res.json({ usuarios: usuarios.map(sanitizeUser) }) // En POST /usuarios (respuesta): res.status(201).json({ usuario: sanitizeUser(usuario) }) ``` **🔴 PITFALL — Admin PIN hardcodeado:** Nunca hardcodear PINs de admin en el código fuente. Usar variable de entorno: ```typescript // En db.ts, al crear el admin inicial: const adminPin = process.env.ADMIN_PIN || '1234' const pinHash = bcrypt.hashSync(String(adminPin), 10) db.run("INSERT INTO usuarios ... VALUES (?, ?, ?, ?, ?, ?, ?)", ['admin-001', 'Administrador', 'admin@adelacrm.local', pinHash, 'admin', 1, ahora]) ``` Documentar en `.env.example`: ``` ADMIN_PIN=1234 JWT_SECRET=tu-secreto-super-seguro DB_PATH=/app/data/datos.db ``` **🔴 PITFALL — PIN visible en HTML (login hint):** Nunca mostrar el PIN real en el HTML del login. En vez de `Demo: admin@local / 1234`, usar: ```html <p class="hint">Credenciales de demostración en el README</p> ``` **🔴 PITFALL — Healthcheck en Dockerfile apunta a endpoint inexistente:** Verificar que el healthcheck del Dockerfile usa el endpoint REAL del servidor. Error común: Dockerfile dice `/healthz` pero el servidor expone `/health`. ```dockerfile # ❌ MAL — endpoint no existe CMD wget -qO- http://localhost:9000/healthz || exit 1 # ✅ BIEN — coincide con el servidor CMD wget -qO- http://localhost:9000/health || exit 1 ``` **🔴 PITFALL — Express req.params.id es string | string[], no string:** Al leer parámetros de ruta en Express, `req.params.id` devuelve `string | string[]` (el tipo de Express), no `string` directamente. Si se pasa a una función que espera `string`, TypeScript lanza TS2345. ```typescript // ❌ MAL const item = await obtenerPorId(req.params.id) // TS2345: string | string[] no asignable a string // ✅ BIEN — castear siempre const id = req.params.id as string const item = await obtenerPorId(id) ``` Regla: SIEMPRE hacer `const id = req.params.id as string` en handlers de rutas Express, tanto en GET/PUT/DELETE de una entidad como en rutas anidadas (`req.params.presupuestoId`, `req.params.lineaId`, etc.). Las rutas estáticas (ej: `/stats`) deben definirse ANTES de las rutas con parámetros (ej: `/:id`). Si no, Express captura `/stats` como un ID. Esto es la causa #1 de "endpoint no encontrado" en Express. **🔴 PITFALL — TypeScript module resolution:** La combinación que funciona para proyectos ESM con `import.meta`, `createRequire`, y default imports depende del tipo de módulo: **Para módulos backend (Express, sql.js, JWT):**
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub