feat(integrations): add skills endpoint, enrich creative context with client info, and support meta in creatives
This commit is contained in:
66
skills/clients/blue-parrot-school.md
Normal file
66
skills/clients/blue-parrot-school.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Skill de cliente · Blue Parrot School (slug: bps)
|
||||
|
||||
> Esta skill define cómo habla y cómo se ve Blue Parrot School en cualquier creatividad. Se inyecta como contexto fijo a Claude en cada petición de relevo. Si algo aquí contradice un brief puntual, manda esta skill.
|
||||
|
||||
## Quién es
|
||||
|
||||
Academia de idiomas con sede en Madrid y Málaga, clases presenciales, online e in-company. Dos líneas:
|
||||
|
||||
- **B2B (empresas):** programas de inglés profesional para equipos. Diferencia frente a apps conversacionales tipo Twenix: profesor fijo, sesiones de 60 minutos, programas por sector (Inglés Legal, IT, Financiero, Comercial Internacional), modalidad híbrida. Es **Entidad Organizadora de FUNDAE** (gestiona la bonificación directamente, no a través de un partner).
|
||||
- **B2C (particulares):** cursos de inglés para adultos, preparación de exámenes oficiales, intensivos.
|
||||
|
||||
Web: blueparrotschool.com
|
||||
|
||||
## A quién le hablamos
|
||||
|
||||
**B2B**
|
||||
- *Marta* — RRHH / Formación. Quiere un proveedor que no le dé trabajo: gestión FUNDAE resuelta, informes de asistencia, un interlocutor.
|
||||
- *Carlos* — director o socio de despacho / consultora. Le importa que su equipo hable con clientes internacionales sin hacer el ridículo. Compra rigor, no "clases".
|
||||
- *Lucía* — directora comercial / internacional. Necesita resultados medibles en poco tiempo: presentaciones, negociación, llamadas.
|
||||
|
||||
**B2C**
|
||||
- Adultos 25-45 que necesitan el inglés para el trabajo o para un examen (Cambridge, Aptis, IELTS) y han fracasado con apps.
|
||||
|
||||
## Cómo habla
|
||||
|
||||
- **Tono:** profesional, seguro, directo. Cercano sin ser coloquial. Nunca infantil, nunca grito de descuento.
|
||||
- **Postura:** "formación profesional estructurada", no "aprende inglés fácil". Vendemos **programas**, no clases sueltas.
|
||||
- **Español de España**, tú o usted según el público: *tú* en B2C, *tú* en B2B cuando hablamos a la persona, *usted* nunca.
|
||||
- **Frases cortas.** Un dato concreto vale más que un adjetivo.
|
||||
- **Inglés en el copy:** solo nombres propios de exámenes o términos sin traducción natural. Nada de "boost", "unlock", "level up".
|
||||
|
||||
### Argumentos que funcionan
|
||||
1. Profesor fijo + 60 minutos: progreso real, no mantenimiento.
|
||||
2. FUNDAE gestionado por nosotros: coste cero o casi cero para la empresa.
|
||||
3. Programas por sector: tu equipo aprende el inglés de su trabajo.
|
||||
4. Presencial en Madrid y Málaga + online: híbrido de verdad.
|
||||
5. Informes de asistencia y progreso para RRHH.
|
||||
|
||||
### Objeciones a desactivar
|
||||
- "Ya usamos una app" → la app mantiene, no avanza.
|
||||
- "Es caro" → FUNDAE; coste por empleado y mes.
|
||||
- "No tenemos tiempo" → sesiones in-company en horario laboral.
|
||||
|
||||
## Prohibido
|
||||
- Prometer niveles o plazos ("B2 en 3 meses").
|
||||
- Comparar nombrando a la competencia (ni Twenix ni ninguna).
|
||||
- Descuentos agresivos, cuentas atrás, "última oportunidad".
|
||||
- Emojis en creatividades de imagen. En copy, como mucho uno al inicio y solo en B2C.
|
||||
- Fotografía de stock genérica de "gente feliz con portátil" con aspecto de banco de imágenes.
|
||||
- Testimonios inventados o con nombre y cargo de personas que no existen. Si no hay un caso real aprobado, no se usa testimonio.
|
||||
- Deformar el logo, recolorearlo o recrearlo. Se coloca el archivo oficial.
|
||||
|
||||
## Identidad visual
|
||||
|
||||
- **Paleta exacta y única:** Azul principal `#2A4D9C` · Amarillo `#FBC02D` · Negro `#000000` · Blanco `#FFFFFF`. Ningún otro color.
|
||||
- **Uso de superficies:** azul y blanco como base. Amarillo solo para resaltar una palabra, un dato o el CTA. Negro reservado a mensajes de formalidad o urgencia (FUNDAE que caduca, manifiesto).
|
||||
- **Tipografía:** Poppins. Extrabold en titulares, Regular en cuerpo. Nunca otra fuente.
|
||||
- **Logo:** lockup oficial *Blue Parrot* + *SCHOOL* en amarillo; versión con filete blanco sobre fondos azul o negro. Área de protección respetada, esquina inferior o superior, nunca centrado ni dominando.
|
||||
- **Estilo de imagen:** fotografía real de aula, oficina o reunión con luz natural; personas adultas de 25-50 en contexto de trabajo; sin poses de catálogo. Si es ilustración o composición gráfica, geometría limpia, mucho aire, una sola idea.
|
||||
- **Texto en imagen:** máximo 2 niveles (titular + apoyo). El titular no supera 8 palabras. Nada de texto tocando bordes: margen mínimo 8 % del lado corto.
|
||||
|
||||
## Ejemplos de hooks que han funcionado (referencia de registro, no para copiar)
|
||||
- "Tu equipo no necesita más inglés. Necesita el inglés de su trabajo."
|
||||
- "FUNDAE lo paga. Nosotros lo gestionamos."
|
||||
- "Un profesor. Sesenta minutos. Cada semana. Así se avanza."
|
||||
- "El inglés legal no se aprende en una app."
|
||||
66
skills/clients/bps.md
Normal file
66
skills/clients/bps.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Skill de cliente · Blue Parrot School (slug: bps)
|
||||
|
||||
> Esta skill define cómo habla y cómo se ve Blue Parrot School en cualquier creatividad. Se inyecta como contexto fijo a Claude en cada petición de relevo. Si algo aquí contradice un brief puntual, manda esta skill.
|
||||
|
||||
## Quién es
|
||||
|
||||
Academia de idiomas con sede en Madrid y Málaga, clases presenciales, online e in-company. Dos líneas:
|
||||
|
||||
- **B2B (empresas):** programas de inglés profesional para equipos. Diferencia frente a apps conversacionales tipo Twenix: profesor fijo, sesiones de 60 minutos, programas por sector (Inglés Legal, IT, Financiero, Comercial Internacional), modalidad híbrida. Es **Entidad Organizadora de FUNDAE** (gestiona la bonificación directamente, no a través de un partner).
|
||||
- **B2C (particulares):** cursos de inglés para adultos, preparación de exámenes oficiales, intensivos.
|
||||
|
||||
Web: blueparrotschool.com
|
||||
|
||||
## A quién le hablamos
|
||||
|
||||
**B2B**
|
||||
- *Marta* — RRHH / Formación. Quiere un proveedor que no le dé trabajo: gestión FUNDAE resuelta, informes de asistencia, un interlocutor.
|
||||
- *Carlos* — director o socio de despacho / consultora. Le importa que su equipo hable con clientes internacionales sin hacer el ridículo. Compra rigor, no "clases".
|
||||
- *Lucía* — directora comercial / internacional. Necesita resultados medibles en poco tiempo: presentaciones, negociación, llamadas.
|
||||
|
||||
**B2C**
|
||||
- Adultos 25-45 que necesitan el inglés para el trabajo o para un examen (Cambridge, Aptis, IELTS) y han fracasado con apps.
|
||||
|
||||
## Cómo habla
|
||||
|
||||
- **Tono:** profesional, seguro, directo. Cercano sin ser coloquial. Nunca infantil, nunca grito de descuento.
|
||||
- **Postura:** "formación profesional estructurada", no "aprende inglés fácil". Vendemos **programas**, no clases sueltas.
|
||||
- **Español de España**, tú o usted según el público: *tú* en B2C, *tú* en B2B cuando hablamos a la persona, *usted* nunca.
|
||||
- **Frases cortas.** Un dato concreto vale más que un adjetivo.
|
||||
- **Inglés en el copy:** solo nombres propios de exámenes o términos sin traducción natural. Nada de "boost", "unlock", "level up".
|
||||
|
||||
### Argumentos que funcionan
|
||||
1. Profesor fijo + 60 minutos: progreso real, no mantenimiento.
|
||||
2. FUNDAE gestionado por nosotros: coste cero o casi cero para la empresa.
|
||||
3. Programas por sector: tu equipo aprende el inglés de su trabajo.
|
||||
4. Presencial en Madrid y Málaga + online: híbrido de verdad.
|
||||
5. Informes de asistencia y progreso para RRHH.
|
||||
|
||||
### Objeciones a desactivar
|
||||
- "Ya usamos una app" → la app mantiene, no avanza.
|
||||
- "Es caro" → FUNDAE; coste por empleado y mes.
|
||||
- "No tenemos tiempo" → sesiones in-company en horario laboral.
|
||||
|
||||
## Prohibido
|
||||
- Prometer niveles o plazos ("B2 en 3 meses").
|
||||
- Comparar nombrando a la competencia (ni Twenix ni ninguna).
|
||||
- Descuentos agresivos, cuentas atrás, "última oportunidad".
|
||||
- Emojis en creatividades de imagen. En copy, como mucho uno al inicio y solo en B2C.
|
||||
- Fotografía de stock genérica de "gente feliz con portátil" con aspecto de banco de imágenes.
|
||||
- Testimonios inventados o con nombre y cargo de personas que no existen. Si no hay un caso real aprobado, no se usa testimonio.
|
||||
- Deformar el logo, recolorearlo o recrearlo. Se coloca el archivo oficial.
|
||||
|
||||
## Identidad visual
|
||||
|
||||
- **Paleta exacta y única:** Azul principal `#2A4D9C` · Amarillo `#FBC02D` · Negro `#000000` · Blanco `#FFFFFF`. Ningún otro color.
|
||||
- **Uso de superficies:** azul y blanco como base. Amarillo solo para resaltar una palabra, un dato o el CTA. Negro reservado a mensajes de formalidad o urgencia (FUNDAE que caduca, manifiesto).
|
||||
- **Tipografía:** Poppins. Extrabold en titulares, Regular en cuerpo. Nunca otra fuente.
|
||||
- **Logo:** lockup oficial *Blue Parrot* + *SCHOOL* en amarillo; versión con filete blanco sobre fondos azul o negro. Área de protección respetada, esquina inferior o superior, nunca centrado ni dominando.
|
||||
- **Estilo de imagen:** fotografía real de aula, oficina o reunión con luz natural; personas adultas de 25-50 en contexto de trabajo; sin poses de catálogo. Si es ilustración o composición gráfica, geometría limpia, mucho aire, una sola idea.
|
||||
- **Texto en imagen:** máximo 2 niveles (titular + apoyo). El titular no supera 8 palabras. Nada de texto tocando bordes: margen mínimo 8 % del lado corto.
|
||||
|
||||
## Ejemplos de hooks que han funcionado (referencia de registro, no para copiar)
|
||||
- "Tu equipo no necesita más inglés. Necesita el inglés de su trabajo."
|
||||
- "FUNDAE lo paga. Nosotros lo gestionamos."
|
||||
- "Un profesor. Sesenta minutos. Cada semana. Así se avanza."
|
||||
- "El inglés legal no se aprende en una app."
|
||||
65
skills/formats/estatico-4-5.md
Normal file
65
skills/formats/estatico-4-5.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Skill de formato · Estático para Meta Ads (4:5)
|
||||
|
||||
> Se aplica junto a la skill del cliente. Define la estructura de un anuncio estático de imagen para feed de Instagram y Facebook. Siempre 4:5 (1080×1350).
|
||||
|
||||
## Qué entregas por cada variante
|
||||
|
||||
Un objeto JSON con exactamente estos campos:
|
||||
|
||||
```json
|
||||
{
|
||||
"angle": "nombre corto del ángulo (p. ej. 'objeción tiempo', 'prueba social', 'coste FUNDAE', 'antes/después', 'autoridad')",
|
||||
"hook": "titular que va EN la imagen, máx. 8 palabras",
|
||||
"support": "línea de apoyo en la imagen, máx. 12 palabras, opcional (null si no hace falta)",
|
||||
"headline": "título del anuncio en Meta, máx. 40 caracteres",
|
||||
"primary_text": "texto principal del anuncio, 2-4 frases, máx. 300 caracteres, la primera frase es el gancho",
|
||||
"cta": "uno de: 'Más información', 'Solicitar presupuesto', 'Reservar', 'Contactar', 'Registrarte'",
|
||||
"image_prompt": "prompt en inglés para el generador de imagen, SIN texto ni logo, describiendo escena, personas, luz, encuadre y paleta",
|
||||
"layout": "una de: 'foto-con-franja' (foto + franja de color con titular), 'tipografico' (solo color y texto), 'dato-grande' (una cifra enorme + contexto)",
|
||||
"rationale": "una frase: por qué este ángulo puede relevar a la fatigada"
|
||||
}
|
||||
```
|
||||
|
||||
## Reglas de ángulo
|
||||
|
||||
- Cada variante ataca un **ángulo distinto** entre sí y distinto del de la creatividad fatigada (viene en `avoid_angles`).
|
||||
- Prioriza ángulos que aún no se hayan probado en el cliente (mira `top_creatives` del contexto: lo que ya funciona no se repite, se complementa).
|
||||
- Rotación recomendada: dolor → solución → prueba → objeción → oferta. Si la fatigada era "oferta", el relevo vuelve a "dolor" o "prueba".
|
||||
|
||||
## Reglas de hook (titular en imagen)
|
||||
|
||||
- Máximo 8 palabras. Si necesita más, no es un hook.
|
||||
- Una sola idea. Un verbo o un dato.
|
||||
- Sin signos de exclamación. Sin mayúsculas completas.
|
||||
- Se lee en 1,5 segundos en móvil con el pulgar en movimiento.
|
||||
- Prohibido empezar por "¿Sabías que", "Descubre", "Imagina".
|
||||
|
||||
## Reglas de copy (primary_text)
|
||||
|
||||
- Frase 1: repite o amplía el hook, nunca lo contradice.
|
||||
- Frase 2-3: un beneficio concreto o un dato.
|
||||
- Frase final: el CTA en palabras, coherente con el botón.
|
||||
- Sin hashtags. Sin enlaces en el texto.
|
||||
|
||||
## Reglas de imagen (image_prompt)
|
||||
|
||||
- Siempre en inglés, 40-80 palabras.
|
||||
- Describe: sujeto, acción, entorno, luz, encuadre (vertical 4:5, espacio libre arriba o abajo para texto), paleta en términos de la skill del cliente.
|
||||
- Termina siempre con: `no text, no logos, no watermarks, photorealistic, vertical 4:5 composition with clear negative space for a headline`.
|
||||
- Nunca pidas personas reconocibles, marcas de terceros, ni niños.
|
||||
- Una generación por variante. No pidas variaciones.
|
||||
|
||||
## Reglas de layout
|
||||
|
||||
- Zona segura: 8 % de margen en todos los lados. Texto nunca en el 15 % superior ni inferior (lo tapan la interfaz y el CTA de Meta).
|
||||
- Jerarquía: hook ≥ 2× el tamaño del support.
|
||||
- Contraste mínimo 4,5:1 entre texto y fondo.
|
||||
- El logo ocupa como máximo el 12 % del ancho.
|
||||
|
||||
## Autocheck antes de entregar
|
||||
|
||||
- [ ] ¿Las variantes tienen ángulos distintos entre sí y del `avoid_angles`?
|
||||
- [ ] ¿Cada hook tiene ≤ 8 palabras?
|
||||
- [ ] ¿El primary_text cabe en 300 caracteres?
|
||||
- [ ] ¿El image_prompt no pide texto ni logo?
|
||||
- [ ] ¿Se respeta la lista de prohibiciones de la skill del cliente?
|
||||
65
skills/formats/estatico.md
Normal file
65
skills/formats/estatico.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Skill de formato · Estático para Meta Ads (4:5)
|
||||
|
||||
> Se aplica junto a la skill del cliente. Define la estructura de un anuncio estático de imagen para feed de Instagram y Facebook. Siempre 4:5 (1080×1350).
|
||||
|
||||
## Qué entregas por cada variante
|
||||
|
||||
Un objeto JSON con exactamente estos campos:
|
||||
|
||||
```json
|
||||
{
|
||||
"angle": "nombre corto del ángulo (p. ej. 'objeción tiempo', 'prueba social', 'coste FUNDAE', 'antes/después', 'autoridad')",
|
||||
"hook": "titular que va EN la imagen, máx. 8 palabras",
|
||||
"support": "línea de apoyo en la imagen, máx. 12 palabras, opcional (null si no hace falta)",
|
||||
"headline": "título del anuncio en Meta, máx. 40 caracteres",
|
||||
"primary_text": "texto principal del anuncio, 2-4 frases, máx. 300 caracteres, la primera frase es el gancho",
|
||||
"cta": "uno de: 'Más información', 'Solicitar presupuesto', 'Reservar', 'Contactar', 'Registrarte'",
|
||||
"image_prompt": "prompt en inglés para el generador de imagen, SIN texto ni logo, describiendo escena, personas, luz, encuadre y paleta",
|
||||
"layout": "una de: 'foto-con-franja' (foto + franja de color con titular), 'tipografico' (solo color y texto), 'dato-grande' (una cifra enorme + contexto)",
|
||||
"rationale": "una frase: por qué este ángulo puede relevar a la fatigada"
|
||||
}
|
||||
```
|
||||
|
||||
## Reglas de ángulo
|
||||
|
||||
- Cada variante ataca un **ángulo distinto** entre sí y distinto del de la creatividad fatigada (viene en `avoid_angles`).
|
||||
- Prioriza ángulos que aún no se hayan probado en el cliente (mira `top_creatives` del contexto: lo que ya funciona no se repite, se complementa).
|
||||
- Rotación recomendada: dolor → solución → prueba → objeción → oferta. Si la fatigada era "oferta", el relevo vuelve a "dolor" o "prueba".
|
||||
|
||||
## Reglas de hook (titular en imagen)
|
||||
|
||||
- Máximo 8 palabras. Si necesita más, no es un hook.
|
||||
- Una sola idea. Un verbo o un dato.
|
||||
- Sin signos de exclamación. Sin mayúsculas completas.
|
||||
- Se lee en 1,5 segundos en móvil con el pulgar en movimiento.
|
||||
- Prohibido empezar por "¿Sabías que", "Descubre", "Imagina".
|
||||
|
||||
## Reglas de copy (primary_text)
|
||||
|
||||
- Frase 1: repite o amplía el hook, nunca lo contradice.
|
||||
- Frase 2-3: un beneficio concreto o un dato.
|
||||
- Frase final: el CTA en palabras, coherente con el botón.
|
||||
- Sin hashtags. Sin enlaces en el texto.
|
||||
|
||||
## Reglas de imagen (image_prompt)
|
||||
|
||||
- Siempre en inglés, 40-80 palabras.
|
||||
- Describe: sujeto, acción, entorno, luz, encuadre (vertical 4:5, espacio libre arriba o abajo para texto), paleta en términos de la skill del cliente.
|
||||
- Termina siempre con: `no text, no logos, no watermarks, photorealistic, vertical 4:5 composition with clear negative space for a headline`.
|
||||
- Nunca pidas personas reconocibles, marcas de terceros, ni niños.
|
||||
- Una generación por variante. No pidas variaciones.
|
||||
|
||||
## Reglas de layout
|
||||
|
||||
- Zona segura: 8 % de margen en todos los lados. Texto nunca en el 15 % superior ni inferior (lo tapan la interfaz y el CTA de Meta).
|
||||
- Jerarquía: hook ≥ 2× el tamaño del support.
|
||||
- Contraste mínimo 4,5:1 entre texto y fondo.
|
||||
- El logo ocupa como máximo el 12 % del ancho.
|
||||
|
||||
## Autocheck antes de entregar
|
||||
|
||||
- [ ] ¿Las variantes tienen ángulos distintos entre sí y del `avoid_angles`?
|
||||
- [ ] ¿Cada hook tiene ≤ 8 palabras?
|
||||
- [ ] ¿El primary_text cabe en 300 caracteres?
|
||||
- [ ] ¿El image_prompt no pide texto ni logo?
|
||||
- [ ] ¿Se respeta la lista de prohibiciones de la skill del cliente?
|
||||
@@ -108,12 +108,32 @@ export async function GET(
|
||||
topCreatives = fallbackRows || [];
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
client_id: tenantId,
|
||||
tenant_id: tenantId,
|
||||
// 5. Obtener datos del cliente (drive_folder_id, slug, nombre)
|
||||
const { data: tenant } = await adminClient
|
||||
.from('tenants')
|
||||
.select('id, name, slug, drive_folder_id, meta_ad_account_id')
|
||||
.eq('id', tenantId)
|
||||
.maybeSingle();
|
||||
|
||||
const contextData = {
|
||||
source_creative: creative,
|
||||
creative,
|
||||
metrics,
|
||||
rules: rules || null,
|
||||
top_creatives: topCreatives,
|
||||
top_historical_creatives: topCreatives,
|
||||
client: {
|
||||
id: tenantId,
|
||||
name: tenant?.name || null,
|
||||
slug: tenant?.slug || null,
|
||||
drive_folder_id: tenant?.drive_folder_id || null,
|
||||
},
|
||||
};
|
||||
|
||||
return NextResponse.json({
|
||||
data: contextData,
|
||||
client_id: tenantId,
|
||||
tenant_id: tenantId,
|
||||
...contextData,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ export async function POST(req: NextRequest) {
|
||||
created_by: 'claude', // creadas por Claude
|
||||
parent_id: c.parent_id || defaultParentId,
|
||||
generation_request_id: c.generation_request_id || genRequestId || null,
|
||||
meta: c.meta || {},
|
||||
}));
|
||||
|
||||
const { data: inserted, error } = await adminClient
|
||||
|
||||
73
src/app/api/integrations/skills/route.ts
Normal file
73
src/app/api/integrations/skills/route.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { authenticateIntegrationRequest } from '@/lib/integrations-auth';
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const auth = await authenticateIntegrationRequest(req, { requireTenant: true });
|
||||
if (!auth.ok) {
|
||||
return auth.errorResponse!;
|
||||
}
|
||||
|
||||
const tenantId = auth.tenantId!;
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
||||
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;
|
||||
const adminClient = createClient(supabaseUrl, serviceRoleKey);
|
||||
|
||||
// 1. Obtener slug del cliente
|
||||
const { data: tenant, error: tErr } = await adminClient
|
||||
.from('tenants')
|
||||
.select('id, name, slug')
|
||||
.eq('id', tenantId)
|
||||
.maybeSingle();
|
||||
|
||||
if (tErr || !tenant) {
|
||||
return NextResponse.json({ error: 'Tenant not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const slug = (tenant.slug || 'bps').toLowerCase().trim();
|
||||
const formatParam = (req.nextUrl.searchParams.get('format') || 'estatico').toLowerCase().trim();
|
||||
|
||||
// 2. Resolver skill del cliente (skills/clients/<slug>.md)
|
||||
const skillsDir = path.join(process.cwd(), 'skills');
|
||||
const clientCandidates = [
|
||||
path.join(skillsDir, 'clients', `${slug}.md`),
|
||||
path.join(skillsDir, 'clients', slug === 'bps' ? 'blue-parrot-school.md' : `${slug}.md`),
|
||||
path.join(skillsDir, 'clients', 'blue-parrot-school.md'),
|
||||
];
|
||||
|
||||
let clientSkill = '';
|
||||
for (const p of clientCandidates) {
|
||||
if (fs.existsSync(p)) {
|
||||
clientSkill = fs.readFileSync(p, 'utf-8');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Resolver skill del formato (skills/formats/<format>-4-5.md o skills/formats/<format>.md, fallback a estatico)
|
||||
const formatCandidates = [
|
||||
path.join(skillsDir, 'formats', `${formatParam}-4-5.md`),
|
||||
path.join(skillsDir, 'formats', `${formatParam}.md`),
|
||||
path.join(skillsDir, 'formats', 'estatico-4-5.md'),
|
||||
path.join(skillsDir, 'formats', 'estatico.md'),
|
||||
];
|
||||
|
||||
let formatSkill = '';
|
||||
for (const p of formatCandidates) {
|
||||
if (fs.existsSync(p)) {
|
||||
formatSkill = fs.readFileSync(p, 'utf-8');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
data: {
|
||||
client_skill: clientSkill,
|
||||
format_skill: formatSkill,
|
||||
},
|
||||
// Compatibilidad en primer nivel
|
||||
client_skill: clientSkill,
|
||||
format_skill: formatSkill,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user