Skip to content
Merged
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ Formato basado en [Keep a Changelog](https://keepachangelog.com/) y [SemVer](htt
## [No publicado]

### Added
- **Estándares de C# en el agente `coder`.** "Estándares por lenguaje" cubría TypeScript, Python, Go, Rust, Solidity, PHP, Java y Dart, pero **no C#**, pese a existir un stack `backend/dotnet` completo y a que la regla de nombres ya lo mencionaba. Se añade su sección (nullable reference types, `record`/`sealed`, `async` sin `.Result`/`async void`, `CancellationToken`, excepciones específicas, analyzers como error) y se incluye C# en la lista de lenguajes del agente.
- **Overlays de Spring, .NET y Angular más profundos.** Spring: virtual threads y `@Async` con executor explícito, Resilience4j, `@Cacheable` con clave y TTL, Actuator + Micrometer, `readOnly` en lecturas, la trampa del proxy de `@Transactional` en llamadas internas, y slices de test frente a `@SpringBootTest`. .NET: `CancellationToken` propagado, `IHttpClientFactory` con typed clients y resiliencia, `HybridCache`, OpenTelemetry y health checks, `MapGroup` con filtros, la captura de `Scoped` desde `Singleton`, `Nullable`/`TreatWarningsAsErrors`, y sustitución de dependencias vía `ConfigureTestServices`. Angular: `@defer`, `computed` frente a `effect`, `linkedSignal`, rutas lazy con guards funcionales y `withComponentInputBinding`, interceptores funcionales, formularios reactivos tipados, `NgOptimizedImage`, accesibilidad y presupuestos de bundle.

### Fixed
- **Detección de Spring incompleta**: se exigía la cadena literal `spring-boot` en el `pom.xml`, así que un proyecto que solo declara `org.springframework` (BOM o Spring Framework sin Boot) no se detectaba; y los `settings.gradle`/`settings.gradle.kts` no se miraban en absoluto. Ahora se buscan varias señales en los cinco archivos de build.
- **Un proyecto Java sin Spring quedaba en un callejón sin salida**: la detección devolvía `null` sin explicar nada y, con `--yes`, el instalador salía con código 1 a secas. Ahora imprime qué vio (`pom.xml`/`build.gradle`), aclara que el overlay de Java del kit es Spring Boot y avisa de que para Quarkus, Micronaut o Jakarta EE puro ese overlay no encaja. Misma pista para ecosistemas sin overlay (Go, Rust) y para un `package.json` sin framework reconocido.
- **`validate.cjs` verifica que el README no mienta.** El README se había desviado en silencio: listaba helpers borrados dos releases antes, 9 de los 13 stacks, "los 8 stacks" del CI y ejemplos pineados a `#v1.2.0`. Ahora se comprueban contra el repo los conteos de agentes, skills y stacks (los del árbol y los del CI), la lista de helpers, los stacks por categoría, el número de componentes descartados, y que todo ejemplo `#vX.Y.Z` apunte a la versión de `package.json`. Si una de esas frases desaparece, falla explícitamente en vez de dejar de comprobar: 38 → 49 comprobaciones.

## [2.1.0] - 2026-08-08
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ Flags: `--stack backend/nestjs` (fuerza stack) · `--yes` (no interactivo, conse
`--no-flow` · `--no-external` · `--update`/`--force` (trae la última capa base sin pisar lo que hayas editado) · `--dry-run` (simula) · `--help`.

El instalador:
1. **Detecta el stack** (pubspec→flutter, react-native, next, angular, nest, foundry, .csproj→dotnet, spring, django, composer→php, fastapi, react, express).
1. **Detecta el stack** (pubspec→flutter, react-native, next, angular, nest, foundry, `.csproj`/`.sln`→dotnet, Spring, django, composer→php, fastapi, react, express).
- **Java/Kotlin**: se reconoce Spring por sus señales (`spring-boot`, `org.springframework`, el plugin de Gradle o `io.spring.dependency-management`) en `pom.xml`, `build.gradle(.kts)` y `settings.gradle(.kts)`. Un proyecto Java **sin** Spring no se fuerza al overlay de Spring: el instalador lo dice y te deja elegir.
- Si la detección falla, imprime **qué vio** en el directorio en vez de rendirse sin explicación.
- En un proyecto **fullstack con un solo `package.json`** (p. ej. express + react juntos) gana el **frontend**; si el repo es principalmente API, fuerza con `--stack backend/express`.
2. Hace **backup** de tu `.claude/` si existe.
3. Aplica la **capa base** (`shared/.claude` → `.claude/`): agentes, skills, helpers, settings, comandos.
Expand Down
26 changes: 25 additions & 1 deletion install.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,29 @@ function dirHasExt(re) {
return false;
}
}
// Archivos de build de Java/Kotlin. Un proyecto Java NO es necesariamente Spring.
const JAVA_BUILD_FILES = ["pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts"];
const javaBuildFiles = () => JAVA_BUILD_FILES.filter((f) => fs.existsSync(path.join(CWD, f)));

// Spring se declara de muchas formas: starters, BOM, plugin de Gradle, imports del framework.
// Antes solo se miraba /spring-boot/ en pom.xml y /spring/ en build.gradle, así que un Gradle
// con Kotlin DSL o un proyecto Spring sin la palabra exacta se quedaba sin detectar.
const SPRING_SIGNALS = /spring-boot|spring-framework|org\.springframework|io\.spring\.dependency-management/i;
const isSpringProject = () => javaBuildFiles().some((f) => fileHas(path.join(CWD, f), SPRING_SIGNALS));

// Qué se vio en el directorio cuando la detección falla. Sin esto el usuario solo recibe
// "no se pudo detectar" y tiene que adivinar por qué.
function detectionHint() {
const java = javaBuildFiles();
if (java.length) {
return `Vi ${java.join(", ")}: es un proyecto Java/Kotlin, pero no encontré señales de Spring.\n El overlay de Java del kit es Spring Boot; úsalo con --stack backend/spring si aplica.\n Si es Quarkus, Micronaut o Jakarta EE puro, ese overlay no encaja: dinos cuál necesitas.`;
}
if (fs.existsSync(path.join(CWD, "go.mod"))) return "Vi go.mod: aún no hay overlay de Go en el kit.";
if (fs.existsSync(path.join(CWD, "Cargo.toml"))) return "Vi Cargo.toml: aún no hay overlay de Rust en el kit.";
if (fs.existsSync(path.join(CWD, "package.json"))) return "Vi package.json pero ningún framework reconocido en sus dependencias.";
return "No vi ningún archivo de proyecto reconocible en este directorio.";
}

function detectStack() {
const pkg = readJSON(path.join(CWD, "package.json")) || {};
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
Expand All @@ -185,7 +208,7 @@ function detectStack() {
if (fs.existsSync(path.join(CWD, "nest-cli.json")) || deps["@nestjs/core"]) return "backend/nestjs";
if (fs.existsSync(path.join(CWD, "foundry.toml"))) return "blockchain/solidity";
if (dirHasExt(/\.(csproj|sln)$/)) return "backend/dotnet";
if (fileHas(path.join(CWD, "pom.xml"), /spring-boot/i) || fileHas(path.join(CWD, "build.gradle"), /spring/i) || fileHas(path.join(CWD, "build.gradle.kts"), /spring/i)) return "backend/spring";
if (isSpringProject()) return "backend/spring";
if (fs.existsSync(path.join(CWD, "manage.py")) || fileHas(req, /(^|\n)\s*django\b/i) || fileHas(pyproj, /\bdjango\b/i)) return "backend/django";
if (fs.existsSync(path.join(CWD, "composer.json"))) return "backend/php";
if (fileHas(req, /fastapi/i) || fileHas(pyproj, /fastapi/i)) return "backend/fastapi";
Expand Down Expand Up @@ -748,6 +771,7 @@ async function main() {
if (!(await confirm("¿Es correcto?", true))) stackId = null;
}
if (!stackId) {
if (!FLAGS.stack) console.log(`\n🔍 ${detectionHint()}`);
console.log("\nStacks disponibles:\n " + Object.keys(STACKS).join("\n "));
const answer = await ask("\nElige stack (category/stack): ");
stackId = (answer || "").trim();
Expand Down
6 changes: 5 additions & 1 deletion shared/.claude/agents/coder.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: "Úsalo para implementar features o refactorizar con criterio senio
tools: Read, Write, Edit, Bash, Glob, Grep
---

Eres un ingeniero de software senior con más de 20 años de experiencia construyendo y operando sistemas de producción en muchos lenguajes (TypeScript/JavaScript, Python, Go, Rust, Java, PHP, Solidity, Dart) y paradigmas (orientado a objetos, funcional, reactivo). Tu enfoque abarca correctitud, legibilidad, rendimiento y mantenibilidad a largo plazo, con un fuerte sesgo hacia el cambio mínimo y coherente y hacia código que se lee como prosa. Escribes código que tu yo del futuro —y cualquier colega— entenderá sin preguntar.
Eres un ingeniero de software senior con más de 20 años de experiencia construyendo y operando sistemas de producción en muchos lenguajes (TypeScript/JavaScript, Python, Go, Rust, Java, C#, PHP, Solidity, Dart) y paradigmas (orientado a objetos, funcional, reactivo). Tu enfoque abarca correctitud, legibilidad, rendimiento y mantenibilidad a largo plazo, con un fuerte sesgo hacia el cambio mínimo y coherente y hacia código que se lee como prosa. Escribes código que tu yo del futuro —y cualquier colega— entenderá sin preguntar.

## Preparación

Expand Down Expand Up @@ -51,6 +51,10 @@ Al ser invocado, establece contexto primero: lee el archivo objetivo y sus vecin
### Java
- Inyección por constructor (no `@Autowired` en campos). DTOs en la API, no entidades JPA. `Optional` en vez de null donde aplique; `final` por defecto. Maneja `checked exceptions` con intención. Evita N+1 (fetch joins/`@EntityGraph`).

### C#
- **Nullable reference types** habilitados (`<Nullable>enable</Nullable>`); el operador `!` no es una solución, es callar al compilador. `record`/`readonly struct` para datos inmutables; `sealed` por defecto.
- `async`/`await` en todo I/O: **nunca** `.Result`/`.Wait()` (deadlock), nada de `async void` salvo event handlers, propaga `CancellationToken` hasta el fondo y usa `ConfigureAwait(false)` en librerías. Excepciones específicas, jamás un `catch (Exception)` que se traga el error. Analyzers activos con `TreatWarningsAsErrors`.

### Dart
- Null-safety estricto; modelos inmutables (`freezed`) e igualdad por valor. `const` en todo lo posible. Un solo paradigma de estado (Riverpod o BLoC), no mezclar. `analysis_options.yaml` con lints fuertes.

Expand Down
22 changes: 20 additions & 2 deletions stacks/backend/dotnet/.claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,42 @@
## Estructura y DI
- Minimal APIs o Controllers; capas con el **contenedor DI integrado** (constructor injection). Lifetimes correctos (`Scoped` para EF DbContext, no `Singleton`).
- DTOs/`record` en la API, **nunca** entidades EF expuestas. Mapea con Mapster/AutoMapper o a mano.
- Con Minimal APIs, agrupa por dominio (`MapGroup`) y aplica filtros por grupo en vez de repetir validación endpoint por endpoint.
- **Nunca** captures un servicio `Scoped` desde uno `Singleton`: es el bug de lifetime clásico y explota tarde, en producción.

## Persistencia (EF Core)
- Lecturas con `AsNoTracking()`. Evita **N+1**: `Include`/`ThenInclude` o proyecciones a DTO con `Select`. `await` en todo I/O.
- Migraciones con `dotnet ef migrations`; transacciones explícitas para operaciones multi-tabla. Concurrencia optimista con `[Timestamp]`/rowversion.
- Proyecta a DTO **dentro** de la query (`Select` antes de materializar): traer la entidad entera para usar dos columnas es el desperdicio más común.

## Async y cancelación
- `CancellationToken` en toda firma async, propagado hasta EF y `HttpClient`: sin él, el trabajo sigue corriendo después de que el cliente se fue.
- Nada de `.Result`/`.Wait()` (deadlock) ni `async void` fuera de event handlers. `IAsyncEnumerable` para streaming en vez de materializar listas grandes.

## API, validación, errores
- Validación con **FluentValidation** o DataAnnotations en el boundary. Manejo global con middleware de excepciones → `ProblemDetails` (RFC 7807) consistente.
- Versionado de API y paginación.
- Versionado de API y paginación, con tope de tamaño de página en el servidor.

## Resiliencia y caché
- `IHttpClientFactory` con **typed clients**: nunca `new HttpClient()` (agota sockets y no refresca DNS). Añade timeout + retry con backoff + circuit breaker (`Microsoft.Extensions.Http.Resilience` o Polly).
- `HybridCache`/`IMemoryCache` con TTL explícito; incluye en la clave todo lo que varíe la respuesta (usuario, tenant, cultura).

## Config y seguridad
- Config por `appsettings.{Environment}.json` + env vars + **user-secrets** en dev; secretos nunca versionados (usa Key Vault/secret manager en prod). **Options pattern** validado al boot.
- Config por `appsettings.{Environment}.json` + env vars + **user-secrets** en dev; secretos nunca versionados (usa Key Vault/secret manager en prod). **Options pattern** validado al boot (`ValidateOnStart`).
- Auth con ASP.NET Core Identity/JWT; **authorization policies deny-by-default**. Logging estructurado con **Serilog** (sin secretos/PII).

## Observabilidad
- **OpenTelemetry** para trazas y métricas; health checks (`/health/live`, `/health/ready`) diferenciados y conectados al orquestador.

## Calidad del proyecto
- `<Nullable>enable</Nullable>` y `<TreatWarningsAsErrors>true</TreatWarningsAsErrors>` en el `.csproj`; `.editorconfig` con analyzers. `dotnet format` en el hook de pre-commit.

## Comandos
- `dotnet run` · `dotnet build` · `dotnet test` · `dotnet format`
- `dotnet ef migrations add <N>` · `dotnet ef database update`

## Tests
- **xUnit** + Moq (unit); `WebApplicationFactory` (integración) + Testcontainers para DB real.
- Reemplaza dependencias externas en el `WebApplicationFactory` vía `ConfigureTestServices`, no con banderas de entorno dentro del código de producción.

> Aplica también el playbook común de backend.
17 changes: 16 additions & 1 deletion stacks/backend/spring/.claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,38 @@
## Estructura y DI
- Capas: `Controller` → `Service` → `Repository` (Spring Data JPA). **Inyección por constructor** (nunca `@Autowired` en campos: rompe testabilidad e inmutabilidad).
- DTOs en la API, **nunca** entidades JPA expuestas (evita fugas y problemas de serialización lazy). Mapea con MapStruct o a mano.
- DTOs como `record` (Java 17+): inmutables y con equals/hashCode correctos sin boilerplate. En Kotlin, `data class` con `val`.

## Persistencia (JPA)
- Evita **N+1**: `JOIN FETCH`, `@EntityGraph` o proyecciones; cuidado con `FetchType.EAGER`. `@Transactional` con límites claros (en el service, no en el controller).
- `@Transactional(readOnly = true)` en lecturas: habilita optimizaciones del proveedor y documenta la intención.
- Migraciones con **Flyway**/Liquibase (nunca `ddl-auto=update` en prod). Locking optimista con `@Version`.
- Cuidado con el proxy de `@Transactional`: una llamada interna al mismo bean **no** pasa por el proxy y se queda sin transacción.

## API, validación, errores
- Bean Validation (`@Valid`, `@NotNull`...) en los DTOs de entrada. Manejo global con `@RestControllerAdvice` → envelope de error consistente.
- Versionado y paginación (`Pageable`).
- Versionado y paginación (`Pageable`). Limita el tamaño de página en el servidor: un `size` sin tope es un DoS gratis.

## Concurrencia
- Java 21+: **virtual threads** (`spring.threads.virtual.enabled=true`) para I/O bloqueante; no los uses con código `synchronized` de bloqueo largo (pinning).
- `@Async` con un executor **explícito** (nunca el default sin acotar) y `CompletableFuture` para composición. Propaga el contexto de seguridad si lo necesitas.

## Resiliencia y caché
- **Resilience4j** para llamadas a terceros: timeout + retry con backoff + circuit breaker. Un cliente HTTP sin timeout acaba tumbando el pool.
- `@Cacheable` con clave explícita y TTL; invalida en la escritura (`@CacheEvict`). Nunca cachees respuestas que dependan del usuario sin incluirlo en la clave.

## Config y seguridad
- `application-{env}.yml` por perfil; secretos vía env vars / config server, **nunca** en el yml versionado. Fail-fast con `@ConfigurationProperties` validadas.
- **Spring Security** deny-by-default; passwords con BCrypt; authz por método/endpoint. Actuator con endpoints sensibles protegidos.

## Observabilidad
- Actuator + **Micrometer** para métricas; `/health` y `/health/readiness` diferenciados. Logging estructurado (JSON) con `traceId` correlacionado, sin secretos ni PII.

## Comandos
- Maven: `mvn spring-boot:run` · `mvn verify` — Gradle: `./gradlew bootRun` · `./gradlew test`

## Tests
- JUnit 5 + Mockito (unit), `@SpringBootTest` + **Testcontainers** (integración con DB real). `@DataJpaTest` para repos.
- Prefiere **slices** (`@WebMvcTest`, `@DataJpaTest`) al `@SpringBootTest` completo: arrancar todo el contexto en cada test es lo que vuelve lenta la suite.

> Aplica también el playbook común de backend.
21 changes: 21 additions & 0 deletions stacks/frontend/angular/.claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@
- **Standalone components** (sin NgModules nuevos). `ChangeDetectionStrategy.OnPush` por defecto.
- `@if`/`@for`/`@switch` (control flow nuevo) con `track` en los `@for` (clave estable, no índice).
- `inject()` sobre constructor cuando aporte; `DestroyRef`/`takeUntilDestroyed` para limpiar suscripciones.
- `@defer` para diferir lo que no es visible de entrada (bloques pesados, below the fold), con `@placeholder`/`@loading`.
- `effect()` es para sincronizar con el mundo exterior, **no** para derivar estado: si calculas un valor a partir de otros, eso es `computed`.

## Estado con signals
- `computed` para todo lo derivado; nada de duplicar estado que puedas calcular.
- `linkedSignal` cuando un estado local deba reinicializarse al cambiar su origen (p. ej. la selección al cambiar de lista).
- Las APIs `resource`/`rxResource`/`httpResource` cubren carga asíncrona declarativa con estado y recarga; confirma su nivel de estabilidad contra la versión exacta de tu proyecto antes de apoyarte en ellas.

## Rutas
- Lazy loading con `loadComponent`/`loadChildren` por ruta: el bundle inicial solo debe traer la primera pantalla.
- Guards y resolvers **funcionales** (`CanActivateFn`, `ResolveFn`) con `inject()`; los basados en clase son legado.
- `withComponentInputBinding()` para recibir parámetros de ruta como inputs en vez de leer `ActivatedRoute` a mano.

## HTTP y formularios
- Interceptores **funcionales** (`HttpInterceptorFn`) para auth, correlación y reintentos; el manejo de errores va en el stream, no en cada componente.
- Formularios **reactivos y tipados**; template-driven solo para casos triviales. Valida en el `FormGroup`, no en el template.

## RxJS con criterio
- Usa signals para estado; RxJS para flujos de eventos/async complejos. No subscribes manuales sin limpieza.
Expand All @@ -18,9 +34,14 @@
- Todo texto visible vía `| translate` (ngx-translate); nunca hardcodeado.
- Nada de acceso directo a `localStorage`: usa un `StorageService` inyectable.
- Tailwind para estilos; evita CSS suelto duplicado. Sanitiza con `DomSanitizer` si inyectas HTML.
- Accesibilidad como requisito: HTML semántico, foco visible y navegable por teclado, `aria-*` solo cuando el elemento nativo no alcanza.
- `NgOptimizedImage` (`ngSrc`) para imágenes: dimensiona, prioriza el LCP y evita layout shift.

## Comandos
- `ng serve` · `ng build` · `ng test` · `ng lint`

## Tests
- Testing Library Angular o TestBed con `OnPush`. Mockea servicios HTTP (`HttpTestingController`). Prueba comportamiento visible, no internals.

## Presupuesto de bundle
- Mantén los `budgets` del `angular.json` y trátalos como error en CI: sin tope, el bundle solo crece.
Loading
Loading