Imported from dynamicdevs/laravel-react-specsmd-template (
AGENTS.md). Install upstream withnpx skills add dynamicdevs/laravel-react-specsmd-template. Copyright stays with the author.
AGENTS.md
Contexto maestro de Acme para agentes de IA (Claude Code, Cursor, Copilot, opencode, Codex, etc.). Este archivo es la fuente principal de contexto del proyecto; CLAUDE.md apunta aquí.
Qué es este repo
Template fullstack genérico para arrancar proyectos nuevos: monolito modular Laravel 13 + SPA React + admin Filament, con tooling de IA, worktrees paralelos, CI/CD y devcontainer ya cableados. acme es el placeholder de naming — al clonar el template, ./init.sh lo renombra interactivamente (slug, nombre visible y org de GHCR).
- El módulo Tasks (backend + Filament + pantalla SPA + tests) es un ejemplo end-to-end de todas las convenciones. Se borra al empezar las features reales (
init.shlista los archivos). - Documentación funcional (setup, endpoints, deploy): ver
README.md.
Stack
| Capa | Tecnología |
|---|---|
| Framework | Laravel 13 (laravel/framework ^13.9) |
| Lenguaje | PHP 8.5 |
| Admin / UI | Filament 5 (+ filament-shield) |
| Colas / jobs | Laravel Horizon (Redis) |
| Auth API | Sanctum + Socialite (Google OAuth vía Firebase) |
| Permisos | spatie/laravel-permission |
| Webhooks | spatie/laravel-webhook-client + patrón WebhookLog |
| Base de datos | PostgreSQL · Redis (cache/colas) |
| Testing | Pest 4 (+ plugin-laravel) sobre PHPUnit 12 · Vitest (front) |
| Formato | Laravel Pint · ESLint |
| Análisis estático | PHPStan + Larastan · tsc |
| Frontend | SPA React 19 + react-router + Tailwind v4, bundleada con Vite (build no commiteado) |
| Deploy | Docker → GHCR → servidor (Ansible) |
Arquitectura — monolito modular
Código de dominio organizado en módulos bajo app/Modules/ (PSR-4, namespace App\Modules\):
| Módulo | Responsabilidad |
|---|---|
| Auth | Email/password + Google OAuth (Sanctum tokens), reset de contraseña estándar, perfil |
| Tasks | CRUD de ejemplo: Service gordo, FormRequests, Job programado, patrón AppSetting. Borrable |
Fuera de Modules/, la estructura Laravel estándar vive en app/ (Filament, Http, Models, Policies, Providers).
- Rutas por módulo en
app/Modules/<X>/Routes/api.php, cableadas conrequiredesderoutes/api.php(públicas / webhooks / protegidas conauth:sanctum). - Models compartidos en
app/Models/; Controllers finos; la lógica vive en Services inyectados por constructor.
Frontend: la SPA (resources/js/)
La interfaz es una SPA React 19 + react-router + Tailwind v4 (componentes .tsx por feature, estilos en resources/css/app.css con cascade layers y tokens en @theme), servida en / por Blade (routes/web.php → resources/views/index.blade.php) con @vite(['resources/css/app.css', 'resources/js/main.tsx']): Vite emite módulos ESM hasheados bajo public/build (gitignoreado). Laravel además aporta /app-config (env vars no secretas que main.tsx lee en boot). La API se sirve del mismo origen, así que la SPA la llama directamente.
main.tsxes el composition root: carga/app-config, construye HttpClient + repos + adapter Firebase y renderiza<App/>. Las pantallas reciben sus dependencias por props (DI explícita, testeables en aislamiento). La sesión vive en un contexto React persistido en localStorage y revalidado contra/auth/me.- Cada feature en
resources/js/features/<f>/:<f>.repo.ts(API),<f>.screens.tsx(pantallas),<f>.test.tsx(Vitest + RTL). Ver el skill/react-feature. - El CORS vive en Laravel (
config/cors.php), no en nginx, porque la cabecera la pone quien emite los bytes:/api/*no existe en disco, así quelocation /cae en@phpy la respuesta la genera PHP. No muevas la política al proxy:add_headerañade en vez de reemplazar (dosAccess-Control-Allow-Originy el navegador rechaza ambas), y unadd_headerdentro de unlocationdeja de heredar los delserver. CORS no protege la API (de eso se encargaauth:sanctum): solo decide si el JS del navegador puede leer la respuesta. - Firebase (Google Sign-In) y las fuentes se cargan por CDN desde
index.blade.php; la app los consume como globals a través de adapters (adapters/firebase.adapter.ts). Excepción deliberada: Material Icons va vendoreado enresources/css/vendor/material-icons.csse importado conlayer(base)— como<link>su CSS sin capa pisaría los tamaños contextuales de las capas. - El design system es una paleta neutra placeholder (tokens en
@theme): se rebrandea editando los tokens, no los componentes. tests/Feature/Spa/BundleTest.phpprotege el contrato del bundle leyendopublic/build/manifest.json: ceroon*=en el bundle y canarios anti tree-shaking (un literal por feature) — requierepnpm buildprevio (sin manifest, se salta).
Convenciones
- Módulos primero: la lógica de negocio nueva va en
app/Modules/<Módulo>/, no enapp/Httpdirectamente. - PSR-4 y estilo Laravel Pint (
pint.json, presetlaravel). No formatear a mano. - Tests con Pest en
tests/(suites Unit y Feature; SQLite en memoria). - Context7: consultar documentación actualizada de cualquier librería externa antes de usarla (MCP server configurado, ver abajo).
- Código y comentarios en inglés; textos de UI en inglés; los docs de contexto (este archivo, README) en español.
Dónde vive cada parámetro
-
Parámetros de negocio ajustables (límites, flags): tres capas.
config/<módulo>.phpes el default versionado (conenv()para variar por entorno) → la tablaapp_settings(víaAppSetting::get/set) es la fuente de verdad en runtime, editable sin deploy → Filament es la UI para negocio.Si una clave tiene
AppSetting, léela siempre comoAppSetting::get('clave', config('...')), nunca conconfig()a secas — si no, el panel de admin cambia el valor y tu código sigue usando el default. Ejemplo canónico:TaskService::assertCanCreate().Claves con override en DB hoy:
tasks_max_open(módulo de ejemplo). -
Credenciales e integraciones externas →
config/services.php. -
env()solo dentro deconfig/. Fuera de ahí devuelvenullen cuanto alguien corraconfig:cache. Los seeders son la única excepción tolerada. -
Tablas de referencia estáticas sin lógica van en el config del módulo, no en DB.
Webhooks entrantes
Públicos (sin auth:sanctum), verifican su propia firma fail-closed con hash_equals, registran cada request en WebhookLog (fuente dinámica, visor en Filament) y deduplican por UNIQUE constraint. Patrón completo en el skill /inbound-webhook; auditoría con @integration-reviewer.
Verificaciones automatizadas
Antes de dar por terminado un cambio, corre y deja en verde:
| Comando | Qué valida |
|---|---|
pnpm typecheck |
tsc --noEmit sobre resources/js |
pnpm lint |
ESLint del front |
pnpm test:unit |
Vitest (jsdom) |
composer test |
Suite Pest/PHPUnit (php artisan test) |
composer lint |
Formato con Pint (pint --test, sin escribir) |
composer analyse |
Análisis estático PHPStan + Larastan |
Para autoformatear: vendor/bin/pint. Estas mismas verificaciones corren en el job quality de .github/workflows/ci.yml (que además hace pnpm build).
Si tocaste la SPA, además: pnpm build y php artisan test --group=spa (valida el bundle construido, no la fuente).
Gate de pre-push
- Hook
.husky/pre-push: espejo local del jobquality(typecheck + lint + vitest +composer analyse+composer test). Bypass de emergencia:git push --no-verify. - Subagente
@integration-reviewer: auditor de la superficie de integraciones y webhooks. Úsalo en cambios que toquen webhooks entrantes o llamadas HTTP salientes. Verifica firma (hash_equals, fail-closed), semántica 200-vs-401/403, deduplicación de eventos y aislamiento de secretos/PII.
Subagentes escritos a mano
La fuente de verdad de los subagentes hechos a mano vive versionada en .agents/subagents/*.md (formato markdown + frontmatter de Claude Code). .agents/scripts/generate-agents.mjs los materializa en el directorio de agentes de cada herramienta instalada (Claude Code → .claude/agents/, que está en .gitignore y se regenera).
Subagentes disponibles hoy:
| Subagente | Rol | Modo |
|---|---|---|
@integration-reviewer |
Audita webhooks e integraciones externas (firma, dedup, secretos) | revisa (read-only) |
@pest-tester |
Escribe tests Pest con las convenciones del repo y los deja en verde | escribe |
@laravel-module |
Implementa backend nuevo respetando el monolito modular | escribe |
- No edites
.claude/agents/*.mddirectamente: edita el fuente en.agents/subagents/y regenera connode .agents/scripts/generate-agents.mjs(opnpm generate:agents). - El devcontainer los genera automáticamente al aprovisionar (
post-create.sh), y un hook de pre-commit regenera cuando cambia algún fuente. - Verificar sincronía sin escribir:
node .agents/scripts/generate-agents.mjs --check. .agents/agents/(sinsub) la genera specs.md — no la uses para agentes a mano.
Skills
Los skills son procedimientos que la herramienta auto-carga y expone como slash command. La fuente de verdad única (versionada) vive en .agents/skills/<skill>/ y contiene dos orígenes que conviven en el mismo directorio:
- Escritos a mano (los de la tabla de abajo): propios del proyecto.
- De comunidad: instalados con
npx autoskillsdesde su registro curado; quedan registrados enskills-lock.json(nombre + repo origen + hash). Ver sección autoskills.
.agents/scripts/generate-skills.mjs los materializa en el directorio de cada herramienta de IA instalada (.claude/skills/, .cline/skills/, …), todos gitignored. No copia: crea symlinks <tool>/skills/<name> → .agents/skills/<name>. Por eso editar un SKILL.md se refleja al instante en todas las herramientas y no hay drift.
Skills escritos a mano disponibles hoy:
| Skill | Rol |
|---|---|
/worktree |
Crear/gestionar git worktrees aislados para lanzar agentes en paralelo (ver docs/parallel-agent-worktrees.md) |
/business-setting |
Añadir/leer un parámetro configurable (config → AppSetting → Filament) |
/inbound-webhook |
Añadir un webhook entrante (firma fail-closed, WebhookLog, 403/200, dedup) |
/commit-conventions |
Mensajes de commit gitmoji + commitlint y gates del repo |
/react-feature |
Añadir feature a la SPA React (repo/screens/test en .tsx + ruta en app.tsx + build & tests del bundle) |
/filament-resource |
Crear un Resource de Filament 5 (Schema/Table/Pages) + permisos shield |
/db-migration |
Migración con convenciones (uuid, decimal, append-only, VIEWs pgsql+sqlite, índices) |
/queue-job |
Escribir un Job encolado (Horizon): $tries/$backoff, poll auto-reprogramado, idempotencia |
/quality-gate |
Correr el gate local (typecheck/lint/vitest + phpstan/pest) antes de push |
Los
SKILL.md(lo que el agente auto-carga) están en inglés; esta tabla de referencia queda en español como el resto del documento.
- No edites
.claude/skills/**(ni.cline/skills/**, etc.) directamente: son symlinks. Edita el fuente en.agents/skills/y se refleja solo. - Solo al añadir un skill nuevo hace falta crear su symlink:
node .agents/scripts/generate-skills.mjs(opnpm generate:skills).pnpm generatecorre mcp+agents+skills. El devcontainer lo ejecuta al aprovisionar. - Elige a qué herramientas enlazar con
AGENTS_TOOLS=claude,cline(por defecto detecta las instaladas:claude,cline,continue,codebuddy,junie,kiro).
Skills de comunidad (autoskills)
Los skills de comunidad se gestionan con autoskills, que instala en el mismo directorio canónico .agents/skills/:
npx autoskills # detecta el stack e instala/actualiza
npx autoskills -a claude-code # solo para una herramienta concreta
autoskills escribe el bundle en .agents/skills/<name>, symlinkea a las herramientas detectadas y registra el skill en skills-lock.json. Solo instala skills de su registro; los del proyecto (tabla de arriba) no van ahí — los gestiona generate-skills.mjs.
Herramientas de IA — configuración MCP
.agents/ es la fuente de verdad para la configuración MCP. .agents/scripts/generate-mcp.mjs lee .agents/config/mcp/source.json y genera el archivo de config MCP para cada herramienta de IA instalada en el sistema (Claude Code → .claude/settings.json, Cursor → .cursor/mcp.json, opencode, Codex, Copilot, etc.).
Tras clonar (o al añadir un MCP server), ejecuta:
node .agents/scripts/generate-mcp.mjs # o: pnpm generate:mcp
Añadir un MCP server
- Editar
.agents/config/mcp/source.json(nueva entrada enservers). - Si necesita formato stdio propio, añadir un
caseentoStdioEntry()dentro de.agents/scripts/generate-mcp.mjs. - Correr
node .agents/scripts/generate-mcp.mjs.
Un hook de pre-commit puede regenerar y verificar que no haya drift entre .agents/ y los configs generados.
Desarrollo dirigido por specs: specs.md
El proyecto usa specs.md, un framework de desarrollo dirigido por especificaciones que instala comandos y agentes en cada herramienta de IA del proyecto.
El devcontainer instala specs.md automáticamente (.devcontainer/post-create.sh → .agents/scripts/install-specsmd.cjs). Los recursos compartidos del flujo quedan en .specsmd/ (no versionado; se regenera). El estado versionado vive en .specs-fire/.
Template: los standards de
.specs-fire/standards/no vienen pre-escritos — trasinit.sh, regenera con el project-init de FIRE (/specsmd-master-agent) sobre el código real del proyecto.
Flujo por defecto: FIRE
Se instala el flujo FIRE (ejecución adaptativa, apto para brownfield y monorepos). Para cambiarlo, pasa el flujo como primer argumento a post-create.sh (fire | simple | ideation) o usa la variable SPECSMD_FLOW; los agentes de destino se ajustan con SPECSMD_TOOLS. Instalación manual: pnpm specsmd:install.
Commits
Convención gitmoji validada por commitlint (commitlint.config.mjs → extends: ["gitmoji"]) en el hook commit-msg. Usa pnpm commit (gitmoji-cli) o escribe el mensaje siguiendo la convención. Ver el skill /commit-conventions (gotchas: footer-leading-blank y subject ≤ 100 caracteres).