Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .agents/comunidad.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
id: "ai-radar-comunidad"
display_name: "Comunidad"
agent_type: "default"
reasoning_effort: "medium"
source_type: "community"
scope:
include:
- "Hacker News, Reddit tecnico, foros de Hugging Face y GitHub discussions."
- "Blogs personales tecnicos, posts publicos y reportes de usuarios con reproduccion."
- "Incidentes, adopcion temprana, fricciones reales y feedback operativo."
exclude:
- "Rumores sin enlace o sin evidencia reproducible."
- "Opiniones que no indiquen impacto practico para builders."
instructions: >-
Busca senales recientes de IA nacidas en comunidad. Distingue hechos
verificables de testimonios u opiniones. Cuando una senal sea autoinformada,
marcala claramente y busca una segunda fuente tecnica u oficial si existe.
output:
language: "es"
fields:
- "titulo"
- "url"
- "fecha"
- "fuente"
- "evidencia"
- "impacto"
- "accion"
- "estado"
29 changes: 29 additions & 0 deletions .agents/fuentes-oficiales.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
id: "ai-radar-fuentes-oficiales"
display_name: "Fuentes oficiales"
agent_type: "default"
reasoning_effort: "high"
source_type: "official"
scope:
include:
- "Anuncios, blogs, changelogs y documentacion oficial de companias de IA."
- "Comunicados de organismos regulatorios, gobiernos, estandares y laboratorios."
- "Paginas primarias de producto, API, seguridad, compliance o investigacion."
exclude:
- "Cobertura secundaria sin enlace a fuente primaria."
- "Opinion, rumor o resumen no verificable."
instructions: >-
Busca senales recientes de IA en fuentes primarias. Prioriza lanzamientos de
modelos, cambios de API, regulacion, seguridad, agentes, compute,
herramientas developer y adopcion enterprise. Verifica fecha exacta,
deduplica anuncios repetidos y evita hype.
output:
language: "es"
fields:
- "titulo"
- "url"
- "fecha"
- "fuente"
- "evidencia"
- "impacto"
- "accion"
- "estado"
28 changes: 28 additions & 0 deletions .agents/medios-secundarios.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
id: "ai-radar-medios-secundarios"
display_name: "Medios secundarios"
agent_type: "default"
reasoning_effort: "medium"
source_type: "secondary_media"
scope:
include:
- "Medios tecnologicos, financieros y de negocio confiables."
- "Reportes sobre regulacion, chips, data centers, adopcion enterprise y mercado."
- "Contexto que complemente fuentes primarias o revele impacto estrategico."
exclude:
- "Notas sin fecha clara."
- "Contenido puramente promocional o sin evidencia enlazable."
instructions: >-
Busca reportes recientes de IA en medios secundarios. Prioriza informacion
con impacto para estrategia, producto o infraestructura. Marca siempre que la
fuente sea secundaria y, cuando sea posible, enlaza tambien la fuente primaria.
output:
language: "es"
fields:
- "titulo"
- "url"
- "fecha"
- "fuente"
- "evidencia"
- "impacto"
- "accion"
- "estado"
29 changes: 29 additions & 0 deletions .agents/repo-tecnico.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
id: "ai-radar-repo-tecnico"
display_name: "Repo tecnico"
agent_type: "default"
reasoning_effort: "xhigh"
source_type: "technical_repository"
scope:
include:
- "Releases, changelogs, issues y discussions de repositorios tecnicos."
- "Model cards, datasets, papers, arXiv, Hugging Face y benchmarks."
- "Frameworks de inferencia, entrenamiento, agentes, evals y tooling developer."
exclude:
- "Repos sin evidencia tecnica reciente."
- "Papers sin artefacto, benchmark o consecuencia practica clara."
instructions: >-
Busca senales recientes de IA con evidencia tecnica verificable. Prioriza
cambios que afecten builders: compatibilidad, rendimiento, licencias,
migraciones, nuevos modelos, bugs bloqueantes y patrones emergentes. Revisa
fechas, estado del artefacto y riesgos de adopcion.
output:
language: "es"
fields:
- "titulo"
- "url"
- "fecha"
- "fuente"
- "evidencia"
- "impacto"
- "accion"
- "estado"
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
SUPABASE_URL=
SUPABASE_SERVICE_ROLE_KEY=
AI_RADAR_API_TOKEN=
AI_RADAR_API_BASE_URL=http://localhost:3000
NOTION_API_KEY=
NOTION_DATA_SOURCE_ID=
NOTION_DATABASE_ID=
NOTION_DATABASE_URL=
NOTION_DATA_SOURCE_URL=
NEXT_PUBLIC_APP_ENV=development
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
.DS_Store
node_modules/
.pnpm-store/
.next/
dist/
coverage/
.env
.env.*
!.env.example
.airadar/
data/searches/
data/reports/
snapshots/weekly/
config/sources.json
supabase/.temp/
frameio/
recordings/
*.log
Expand Down
53 changes: 53 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Guia del Repositorio

## Estructura del Proyecto y Organizacion

Este workspace contiene AI Radar en `platzi-codex-clase-02-agents-md/`. El proyecto sigue siendo pequeno: `README.md` define la direccion del producto, `AGENTS.md` define reglas para agentes y `.gitignore` excluye caches locales, secretos, datos generados y salidas de build. La implementacion actual agrega un runtime minimo Next.js con API routes protegidas, dashboard visual en `app/page.js`, helpers server-side en `lib/`, migraciones en `supabase/migrations/`, pruebas en `tests/`, fixtures en `fixtures/` y scripts locales en `scripts/`.

## Comandos de Build, Prueba y Desarrollo

Usa estos comandos mientras trabajas:

```powershell
cd platzi-codex-clase-02-agents-md
git status --short
git log --oneline -5
npm test
npm run build
npm run sources:refresh
npm run supabase:check
```

Para desarrollo local de la API usa `npm run dev`. No ejecutes ni documentes comandos nuevos hasta que existan en `package.json`.

## Estilo de Codigo y Convenciones de Nombres

Manten encabezados claros, parrafos breves y nombres descriptivos para archivos nuevos, por ejemplo `fixtures/signals.json` o `scripts/normalize-sources.js`. La API actual usa JavaScript ESM, validacion con esquemas y funciones pequenas en `lib/`. No incluyas en control de versiones salidas generadas, snapshots temporales, grabaciones, credenciales ni bases de datos locales.

## Guia de Pruebas

Las pruebas usan `node:test` y viven en `tests/`. Mantén pruebas enfocadas en validacion de contratos, normalizacion, endpoints y adaptadores server-side. Para cambios de interfaz visual, agrega o ejecuta verificaciones con Playwright para flujos de usuario cuando el alcance lo justifique.

## Guia de Commits y Pull Requests

El historial existente usa prefijos convencionales cortos como `docs:` y `chore:`. Manten ese estilo, por ejemplo `docs: aclarar objetivos de AI Radar` o `chore: actualizar reglas de ignore`. Los pull requests deben describir que cambio, como se verifico y que queda intencionalmente pendiente. Incluye capturas solo cuando exista una interfaz.

## Instrucciones Especificas para Agentes

Inspecciona el repositorio antes de editar. Trata el README como direccion de producto, no como prueba de funcionalidades implementadas. Manten los cambios acotados a la leccion actual y evita inventar servicios, scripts, bases de datos o automatizaciones que no esten presentes.

La integracion Supabase actual es server-side: los endpoints requieren `Authorization: Bearer $AI_RADAR_API_TOKEN` y usan `SUPABASE_SERVICE_ROLE_KEY` solo en el servidor. El dashboard puede leer Supabase desde server components mediante helpers de `lib/`; si faltan variables o tablas, debe caer a fixture declarado sin exponer secretos al navegador. No uses secretos con prefijo `NEXT_PUBLIC_`. No apliques migraciones DDL ni crees proyectos Supabase remotos sin aprobacion explicita.

Cuando una tarea requiera buscar senales recientes de IA con subagentes, usa las configuraciones en `.agents/` y genera el plan de llamadas con:

```powershell
python scripts\llamar_subagentes.py "senales recientes de IA"
```

Antes de generar el plan, consulta Notion primero usando la tabla `AI radar Sources`. Refresca `config/sources.json` como cache local con las fuentes activas agrupadas por subagente. Usa `npm run sources:refresh` cuando existan `NOTION_API_KEY` y `NOTION_DATA_SOURCE_ID`; si el conector Notion no permite consultas SQL, usa `search` + `fetch` como fallback de lectura y reporta el motivo. Ese archivo esta ignorado por git y no debe tratarse como artefacto versionado salvo instruccion explicita.

Ese script valida los YAML, lee `config/sources.json` si existe y produce payloads para `multi_agent_v1.spawn_agent`; ejecuta esos payloads en paralelo desde Codex, deduplica resultados y normaliza las senales antes de responder o guardar snapshots.

Si Notion no responde, la tabla no esta indexada, una fuente falla o un subagente no devuelve resultado, continua con el fallback indicado por el script y reporta el motivo en la respuesta final.

Trata frases naturales como "busca las noticias de esta semana", "busca noticias recientes de IA" o "dame las senales de IA de la semana" como solicitudes para activar ese flujo. Para "esta semana", calcula la ventana de los ultimos 7 dias con fechas exactas y pasala al script con `--desde` y `--hasta`.
60 changes: 56 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ AI Radar es el proyecto del curso avanzado de Codex.

El objetivo del producto es organizar noticias, herramientas, papers, repos y lanzamientos de IA para convertirlos en senales accionables para builders: que paso, por que importa, que tan confiable es y que vale la pena probar.

Estado inicial: definicion de producto, stack objetivo y reglas iniciales. La implementacion se construye por capas durante el curso con Codex.
Estado actual: definicion de producto, contrato local de senales, scripts de subagentes, una API minima para persistir runs y senales en Supabase y un dashboard visual inicial. El dashboard puede leer datos reales server-side desde Supabase cuando el entorno esta configurado; si faltan credenciales, cae al fixture declarado en `fixtures/dashboard.json`.

## Problema

Expand Down Expand Up @@ -32,14 +32,66 @@ Al final del curso, AI Radar debe poder:
- guardar trazas de decisiones y validaciones,
- desplegarse con infraestructura controlada.

## Estado Inicial
## Estado Actual

El starter contiene:
El repo contiene:

- `README.md`
- `.gitignore`
- `AGENTS.md`
- `.agents/` con configuraciones de subagentes
- `contracts/ai-radar-daily-signals.schema.json`
- `data/daily/` con snapshots diarios
- `scripts/` con utilidades locales
- `app/api/` con endpoints Next.js protegidos
- `app/page.js` con dashboard visual basado en API server-side o fixture declarado
- `lib/` con validacion y acceso server-side a Supabase
- `supabase/migrations/` con el esquema core
- `tests/` con pruebas `node:test`

La primera clase usa este estado para mostrar como `AGENTS.md` cambia la forma en que Codex entiende un proyecto antes de escribir codigo.
El dashboard visual existe en modo operacional inicial. Aun no hay ranking persistido con scores propios; cuando usa Supabase, la capa visual adapta `signals` y `sources` al contrato de UI y declara ese mapeo en los datos entregados a la pagina.

## Desarrollo Local

```powershell
npm install
npm test
npm run build
npm run dev
npm run sources:refresh
npm run supabase:check
```

Configura `.env.local` a partir de `.env.example`. No guardes claves reales en git.

## Fuentes Notion

La tabla de fuentes vive en Notion como `AI radar Sources`. Para refrescar el cache local ignorado por git:

```powershell
npm run sources:refresh
```

El script usa la API publica de Notion y evita depender de consultas SQL del conector Notion. Configura `NOTION_API_KEY` y preferentemente `NOTION_DATA_SOURCE_ID`; `NOTION_DATABASE_ID` queda como fallback legacy. El cache resultante se escribe en `config/sources.json`, agrupado por subagente y solo con filas `Status = activa`.

## API Supabase

Los endpoints requieren `Authorization: Bearer $AI_RADAR_API_TOKEN`:

- `POST /api/runs`: guarda un run completo con senales normalizadas.
- `GET /api/runs/:id`: consulta un run y sus senales.
- `GET /api/signals?fecha=&source_type=&limit=`: lista senales persistidas.
- `POST /api/sources/sync`: sincroniza fuentes activas desde el cache de Notion.

Supabase se usa solo server-side con `SUPABASE_SERVICE_ROLE_KEY`. La migracion local habilita RLS y no crea politicas publicas.

Para validar que el entorno apunta al proyecto correcto y que existen las tablas esperadas:

```powershell
npm run supabase:check
```

El diagnostico no imprime secretos. Si faltan variables o el proyecto no tiene `public.sources`, `public.runs` y `public.signals`, reporta el problema antes de intentar sincronizar fuentes o persistir snapshots.

## Stack Objetivo

Expand Down
27 changes: 27 additions & 0 deletions app/api/runs/[id]/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { jsonError, requireBearerToken } from "../../../../lib/api/auth.js";
import { getSupabaseAdmin } from "../../../../lib/supabase/client.js";
import { getRunWithSignals } from "../../../../lib/supabase/runs.js";
import { validationIssues, uuidSchema } from "../../../../lib/validation.js";

export const runtime = "nodejs";

export async function GET(request, context) {
const auth = requireBearerToken(request);
if (!auth.ok) {
return auth.response;
}

const params = await context.params;
const result = uuidSchema.safeParse(params.id);
if (!result.success) {
return jsonError(400, "invalid_run_id", "id de run invalido", validationIssues(result.error));
}

try {
const supabase = getSupabaseAdmin();
const payload = await getRunWithSignals(supabase, result.data);
return Response.json(payload);
} catch (error) {
return jsonError(500, "get_run_failed", error.message);
}
}
38 changes: 38 additions & 0 deletions app/api/runs/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { jsonError, requireBearerToken } from "../../../lib/api/auth.js";
import { getSupabaseAdmin } from "../../../lib/supabase/client.js";
import { saveRun } from "../../../lib/supabase/runs.js";
import { PayloadValidationError, normalizeRunPayload } from "../../../lib/validation.js";

export const runtime = "nodejs";

export async function POST(request) {
const auth = requireBearerToken(request);
if (!auth.ok) {
return auth.response;
}

let body;
try {
body = await request.json();
} catch {
return jsonError(400, "invalid_json", "el body debe ser JSON valido");
}

let run;
try {
run = normalizeRunPayload(body);
} catch (error) {
if (error instanceof PayloadValidationError) {
return jsonError(400, "invalid_payload", error.message, error.issues);
}
throw error;
}

try {
const supabase = getSupabaseAdmin();
const result = await saveRun(supabase, run);
return Response.json(result, { status: 201 });
} catch (error) {
return jsonError(500, "save_run_failed", error.message);
}
}
31 changes: 31 additions & 0 deletions app/api/signals/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { jsonError, requireBearerToken } from "../../../lib/api/auth.js";
import { getSupabaseAdmin } from "../../../lib/supabase/client.js";
import { listSignals } from "../../../lib/supabase/signals.js";
import { PayloadValidationError, normalizeSignalQuery } from "../../../lib/validation.js";

export const runtime = "nodejs";

export async function GET(request) {
const auth = requireBearerToken(request);
if (!auth.ok) {
return auth.response;
}

let query;
try {
query = normalizeSignalQuery(new URL(request.url).searchParams);
} catch (error) {
if (error instanceof PayloadValidationError) {
return jsonError(400, "invalid_query", error.message, error.issues);
}
throw error;
}

try {
const supabase = getSupabaseAdmin();
const signals = await listSignals(supabase, query);
return Response.json({ signals, count: signals.length });
} catch (error) {
return jsonError(500, "list_signals_failed", error.message);
}
}
Loading