claude-code-arcane 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +2 -0
  3. package/agents/engineering/dotnet-engineer.md +78 -0
  4. package/dist/cli.js +284 -72
  5. package/docs/RELEASE-SETUP.md +99 -0
  6. package/docs/SKILLS-CATALOG.md +26 -3
  7. package/docs/presentations/arcane-overview-12.pptx +0 -0
  8. package/docs/presentations/arcane-overview.pptx +0 -0
  9. package/docs/presentations/build_arcane_deck.py +310 -0
  10. package/docs/presentations/build_arcane_deck_12.py +399 -0
  11. package/package.json +1 -1
  12. package/profiles/backend-dotnet.yaml +54 -0
  13. package/profiles/job-hunt.yaml +35 -0
  14. package/profiles/unity-design.yaml +1 -0
  15. package/profiles/unity-dev.yaml +1 -0
  16. package/rules/dotnet-code.md +64 -0
  17. package/skills/cold-outreach/SKILL.md +65 -0
  18. package/skills/cold-outreach/references/recruiter-playbook.md +65 -0
  19. package/skills/cover-letter/SKILL.md +66 -0
  20. package/skills/cv-ats-export/SKILL.md +64 -0
  21. package/skills/cv-ats-export/scripts/cv_export.py +306 -0
  22. package/skills/cv-tailor/SKILL.md +70 -0
  23. package/skills/cv-tailor/references/ats-keywords.md +46 -0
  24. package/skills/dotnet-architecture/SKILL.md +66 -0
  25. package/skills/dotnet-architecture/references/anti-patterns.md +12 -0
  26. package/skills/dotnet-architecture/references/checklist.md +19 -0
  27. package/skills/dotnet-architecture/references/patterns.md +118 -0
  28. package/skills/dotnet-architecture/references/project-structure.md +78 -0
  29. package/skills/dotnet-best-practices/SKILL.md +76 -0
  30. package/skills/dotnet-best-practices/references/api-design.md +75 -0
  31. package/skills/dotnet-best-practices/references/architecture.md +62 -0
  32. package/skills/dotnet-best-practices/references/async.md +62 -0
  33. package/skills/dotnet-best-practices/references/database.md +69 -0
  34. package/skills/dotnet-best-practices/references/dependency-injection.md +73 -0
  35. package/skills/dotnet-best-practices/references/devops.md +76 -0
  36. package/skills/dotnet-best-practices/references/error-handling.md +72 -0
  37. package/skills/dotnet-best-practices/references/performance.md +63 -0
  38. package/skills/dotnet-best-practices/references/security.md +73 -0
  39. package/skills/dotnet-best-practices/references/testing.md +76 -0
  40. package/skills/dotnet-scaffold/SKILL.md +99 -0
  41. package/skills/install-mcp/SKILL.md +107 -0
  42. package/skills/install-mcp/references/manual-setup.md +92 -0
  43. package/skills/interview-prep/SKILL.md +69 -0
  44. package/skills/interview-prep/references/star-framework.md +42 -0
  45. package/skills/job-hunt/SKILL.md +92 -0
  46. package/skills/job-hunt/references/templates/Aplicacion.md +48 -0
  47. package/skills/job-hunt/references/templates/CV Custom.md +53 -0
  48. package/skills/job-hunt/references/templates/Contacto.md +30 -0
  49. package/skills/job-hunt/references/templates/Dashboard.md +45 -0
  50. package/skills/job-hunt/references/templates/Empresa.md +36 -0
  51. package/skills/job-hunt/references/templates/Entrevista.md +44 -0
  52. package/skills/job-hunt/references/templates/Perfil.md +38 -0
  53. package/skills/job-search/SKILL.md +83 -0
  54. package/skills/job-search/references/scoring-rubric.md +43 -0
  55. package/skills/linkedin-optimize/SKILL.md +79 -0
  56. package/skills/master-profile/SKILL.md +69 -0
  57. package/skills/network-map/SKILL.md +61 -0
  58. package/skills/network-map/scripts/network_map.py +109 -0
  59. package/skills/personal-brand/SKILL.md +54 -0
  60. package/skills/personal-brand/references/post-pillars.md +66 -0
  61. package/skills/portfolio-site/SKILL.md +59 -0
@@ -0,0 +1,73 @@
1
+ # 2. Dependency Injection (CRITICAL)
2
+
3
+ ## 2.1 Prefer Constructor Injection (Primary Constructors) — CRITICAL
4
+
5
+ Dependencias explícitas, type-safe y testeables. Con primary constructors (C# 12+) el boilerplate desaparece.
6
+
7
+ **Correcto:**
8
+ ```csharp
9
+ public sealed class CreateOrderHandler(IOrderRepository repo, ILogger<CreateOrderHandler> logger)
10
+ {
11
+ public async Task HandleAsync(CreateOrderRequest req, CancellationToken ct)
12
+ {
13
+ logger.LogInformation("Creating order for {Customer}", req.CustomerId);
14
+ await repo.AddAsync(req.ToOrder(), ct);
15
+ }
16
+ }
17
+ ```
18
+
19
+ ## 2.2 Use Correct Lifetimes — CRITICAL
20
+
21
+ `Scoped` para `DbContext` y handlers (una instancia por request). `Singleton` solo para servicios stateless y thread-safe. `Transient` para servicios livianos sin estado.
22
+
23
+ ```csharp
24
+ builder.Services.AddDbContext<AppDbContext>(o => o.UseNpgsql(cs)); // Scoped por defecto
25
+ builder.Services.AddScoped<CreateOrderHandler>();
26
+ builder.Services.AddSingleton<IClock, SystemClock>(); // stateless
27
+ ```
28
+
29
+ ## 2.3 Never Inject Scoped into Singleton — CRITICAL
30
+
31
+ Es la "captured dependency": el Singleton retiene una instancia Scoped muerta tras el primer request → datos corruptos o `ObjectDisposedException`. Resolverlo bajo demanda con un scope.
32
+
33
+ **Incorrecto:**
34
+ ```csharp
35
+ public sealed class CacheWarmer(AppDbContext db) : IHostedService { } // Singleton captura DbContext Scoped
36
+ ```
37
+ **Correcto:**
38
+ ```csharp
39
+ public sealed class CacheWarmer(IServiceScopeFactory scopeFactory) : IHostedService
40
+ {
41
+ public async Task StartAsync(CancellationToken ct)
42
+ {
43
+ await using var scope = scopeFactory.CreateAsyncScope();
44
+ var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## 2.4 No Service Locator / No Manual BuildServiceProvider() — HIGH
50
+
51
+ `provider.GetService<T>()` dentro de la lógica oculta dependencias y rompe los tests. `BuildServiceProvider()` en `Program.cs` crea un container paralelo (singletons duplicados, fugas). Inyectar por constructor (2.1).
52
+
53
+ ## 2.5 Register by Interface — HIGH
54
+
55
+ Registrar contra la abstracción permite sustituir implementaciones (tests, features) sin tocar consumidores.
56
+
57
+ ```csharp
58
+ builder.Services.AddScoped<IOrderRepository, EfOrderRepository>();
59
+ ```
60
+
61
+ ## 2.6 IOptions<T> Pattern for Config — MEDIUM-HIGH
62
+
63
+ Bind tipado y validado de configuración, en vez de leer `IConfiguration` con strings por todo el código.
64
+
65
+ ```csharp
66
+ builder.Services.AddOptions<JwtOptions>()
67
+ .Bind(builder.Configuration.GetSection("Jwt"))
68
+ .ValidateDataAnnotations()
69
+ .ValidateOnStart();
70
+ // constructor(IOptions<JwtOptions> options) => _opts = options.Value;
71
+ ```
72
+
73
+ _Ref: https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection · https://learn.microsoft.com/en-us/dotnet/core/extensions/options_
@@ -0,0 +1,76 @@
1
+ # 10. DevOps & Deployment (LOW-MEDIUM)
2
+
3
+ ## 10.1 Multi-Stage Dockerfile, Non-Root User — MEDIUM
4
+
5
+ Compilar con la imagen SDK y correr sobre la imagen `aspnet` runtime reduce el tamaño y la superficie de ataque. Usar un usuario no-root limita el blast radius.
6
+
7
+ **Correcto:**
8
+ ```dockerfile
9
+ FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
10
+ WORKDIR /src
11
+ COPY . .
12
+ RUN dotnet publish -c Release -o /app
13
+
14
+ FROM mcr.microsoft.com/dotnet/aspnet:10.0
15
+ WORKDIR /app
16
+ COPY --from=build /app .
17
+ USER $APP_UID
18
+ ENTRYPOINT ["dotnet", "Api.dll"]
19
+ ```
20
+
21
+ ## 10.2 Health Checks (Liveness/Readiness) — MEDIUM
22
+
23
+ Los orquestadores (Kubernetes) necesitan endpoints separados: liveness (¿está vivo?) y readiness (¿puede recibir tráfico?, p. ej. DB conectada).
24
+
25
+ **Correcto:**
26
+ ```csharp
27
+ builder.Services.AddHealthChecks().AddNpgSql(conn, tags: ["ready"]);
28
+ app.MapHealthChecks("/health/live", new() { Predicate = _ => false });
29
+ app.MapHealthChecks("/health/ready", new() { Predicate = c => c.Tags.Contains("ready") });
30
+ ```
31
+
32
+ ## 10.3 Structured Logging (Serilog JSON) — MEDIUM
33
+
34
+ Logs en JSON son parseables por agregadores (Loki, ELK). Serilog con sink de consola en formato compacto facilita la observabilidad.
35
+
36
+ **Correcto:**
37
+ ```csharp
38
+ builder.Host.UseSerilog((ctx, cfg) =>
39
+ cfg.WriteTo.Console(new Serilog.Formatting.Compact.CompactJsonFormatter()));
40
+ ```
41
+
42
+ ## 10.4 OpenTelemetry for Traces & Metrics — MEDIUM
43
+
44
+ OpenTelemetry instrumenta traces y métricas de forma estándar y exportable a cualquier backend OTLP.
45
+
46
+ **Correcto:**
47
+ ```csharp
48
+ builder.Services.AddOpenTelemetry()
49
+ .WithTracing(t => t.AddAspNetCoreInstrumentation().AddOtlpExporter())
50
+ .WithMetrics(m => m.AddAspNetCoreInstrumentation().AddOtlpExporter());
51
+ ```
52
+
53
+ ## 10.5 Config per Environment & Graceful Shutdown — LOW-MEDIUM
54
+
55
+ Nunca hardcodear secrets ni connection strings: usar `appsettings.{Environment}.json` y variables de entorno. Registrar limpieza ante el shutdown con `IHostApplicationLifetime`.
56
+
57
+ **Correcto:**
58
+ ```csharp
59
+ var conn = builder.Configuration.GetConnectionString("Default"); // env / appsettings, no hardcoded
60
+ app.Lifetime.ApplicationStopping.Register(() => Log.Information("Draining connections..."));
61
+ ```
62
+
63
+ ## 10.6 CI: build, test, format — LOW
64
+
65
+ El pipeline debe fallar ante código que no compila, tests rojos o formato inconsistente.
66
+
67
+ **Correcto:**
68
+ ```yaml
69
+ steps:
70
+ - run: dotnet restore
71
+ - run: dotnet build --no-restore -c Release
72
+ - run: dotnet test --no-build -c Release
73
+ - run: dotnet format --verify-no-changes
74
+ ```
75
+
76
+ _Ref: https://learn.microsoft.com/en-us/dotnet/core/docker/build-container · https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks · https://learn.microsoft.com/en-us/dotnet/core/diagnostics/observability-with-otel · https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host_
@@ -0,0 +1,72 @@
1
+ # 4. Error Handling (HIGH)
2
+
3
+ ## 4.1 Global IExceptionHandler + ProblemDetails — CRITICAL
4
+
5
+ Respuestas de error consistentes y estandarizadas (RFC 7807) sin try/catch repetido en cada endpoint. Registrar `AddProblemDetails()` y un `IExceptionHandler`.
6
+
7
+ **Correcto:**
8
+ ```csharp
9
+ public sealed class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger) : IExceptionHandler
10
+ {
11
+ public async ValueTask<bool> TryHandleAsync(HttpContext ctx, Exception ex, CancellationToken ct)
12
+ {
13
+ logger.LogError(ex, "Unhandled exception on {Path}", ctx.Request.Path);
14
+ await Results.Problem(statusCode: StatusCodes.Status500InternalServerError, title: "An error occurred")
15
+ .ExecuteAsync(ctx);
16
+ return true;
17
+ }
18
+ }
19
+ // Program.cs
20
+ builder.Services.AddProblemDetails();
21
+ builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
22
+ app.UseExceptionHandler();
23
+ ```
24
+
25
+ ## 4.2 Don't Leak Stack Traces — CRITICAL
26
+
27
+ En producción no exponer `ex.Message`, stack traces ni detalles internos: filtran rutas, versiones y vectores de ataque. Loggear el detalle, devolver un mensaje genérico (ver 4.1).
28
+
29
+ ## 4.3 Result Pattern for Expected Failures — HIGH
30
+
31
+ Fallos esperados (validación, "not found", reglas de negocio) no son excepciones: las excepciones son caras y para lo excepcional. Devolver un `Result`.
32
+
33
+ **Incorrecto:**
34
+ ```csharp
35
+ if (order is null) throw new NotFoundException(); // control de flujo con excepciones
36
+ ```
37
+ **Correcto:**
38
+ ```csharp
39
+ public readonly record struct Result<T>(bool IsSuccess, T? Value, string? Error)
40
+ {
41
+ public static Result<T> Ok(T value) => new(true, value, null);
42
+ public static Result<T> Fail(string error) => new(false, default, error);
43
+ }
44
+ return order is null ? Result<Order>.Fail("Order not found") : Result<Order>.Ok(order);
45
+ ```
46
+
47
+ ## 4.4 Validation → ValidationProblemDetails — HIGH
48
+
49
+ Los errores de validación se devuelven como `ValidationProblemDetails` (400) con el mapa campo→errores, no como 500.
50
+
51
+ ```csharp
52
+ return Results.ValidationProblem(new Dictionary<string, string[]>
53
+ {
54
+ ["email"] = ["Email is required"]
55
+ });
56
+ ```
57
+
58
+ ## 4.5 No Empty Catch; Log With Context — MEDIUM-HIGH
59
+
60
+ `catch {}` oculta fallos y vuelve la app indebuggeable. Capturar tipos específicos, loggear con structured logging y re-lanzar o devolver un `Result`.
61
+
62
+ **Incorrecto:**
63
+ ```csharp
64
+ try { await Pay(ct); } catch { } // se traga el error
65
+ ```
66
+ **Correcto:**
67
+ ```csharp
68
+ try { await Pay(ct); }
69
+ catch (PaymentException ex) { logger.LogError(ex, "Payment failed for {OrderId}", orderId); throw; }
70
+ ```
71
+
72
+ _Ref: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling · https://learn.microsoft.com/en-us/aspnet/core/web-api/handle-errors_
@@ -0,0 +1,63 @@
1
+ # 6. Performance (HIGH)
2
+
3
+ ## 6.1 Output Caching & HybridCache — HIGH
4
+
5
+ Cachear respuestas caras evita recomputar en cada request. `HybridCache` combina cache en memoria (L1) y distribuida (L2) con stampede protection.
6
+
7
+ **Incorrecto:**
8
+ ```csharp
9
+ app.MapGet("/stats", async (StatsService s) => await s.ComputeAsync()); // recomputa siempre
10
+ ```
11
+ **Correcto:**
12
+ ```csharp
13
+ builder.Services.AddHybridCache();
14
+
15
+ app.MapGet("/stats", async (HybridCache cache, StatsService s, CancellationToken ct) =>
16
+ await cache.GetOrCreateAsync("stats", async token => await s.ComputeAsync(token), cancellationToken: ct));
17
+ ```
18
+
19
+ ## 6.2 Response Compression — HIGH
20
+
21
+ Comprimir respuestas reduce el ancho de banda y la latencia percibida en payloads JSON grandes.
22
+
23
+ **Correcto:**
24
+ ```csharp
25
+ builder.Services.AddResponseCompression(o => o.EnableForHttps = true);
26
+ var app = builder.Build();
27
+ app.UseResponseCompression();
28
+ ```
29
+
30
+ ## 6.3 Never Block on Async (sync-over-async) — HIGH
31
+
32
+ `.Result` o `.Wait()` bloquean un hilo del thread pool y pueden causar deadlocks y thread starvation bajo carga.
33
+
34
+ **Incorrecto:**
35
+ ```csharp
36
+ var user = _repo.GetUserAsync(id).Result; // bloquea el hilo
37
+ ```
38
+ **Correcto:**
39
+ ```csharp
40
+ var user = await _repo.GetUserAsync(id);
41
+ ```
42
+
43
+ ## 6.4 DbContext Pooling — MEDIUM-HIGH
44
+
45
+ Reusar instancias de `DbContext` desde un pool reduce las allocations y el costo de setup por request.
46
+
47
+ **Correcto:**
48
+ ```csharp
49
+ builder.Services.AddDbContextPool<AppDbContext>(o =>
50
+ o.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
51
+ ```
52
+
53
+ ## 6.5 Stream Large Results with IAsyncEnumerable — MEDIUM
54
+
55
+ Devolver `IAsyncEnumerable<T>` transmite filas a medida que llegan en vez de materializar toda la colección en memoria.
56
+
57
+ **Correcto:**
58
+ ```csharp
59
+ app.MapGet("/orders", (AppDbContext db) =>
60
+ db.Orders.AsNoTracking().AsAsyncEnumerable()); // streaming, server GC ayuda en cargas altas
61
+ ```
62
+
63
+ _Ref: https://learn.microsoft.com/en-us/aspnet/core/performance/caching/hybrid · https://learn.microsoft.com/en-us/aspnet/core/performance/response-compression · https://learn.microsoft.com/en-us/ef/core/performance/advanced-performance-topics_
@@ -0,0 +1,73 @@
1
+ # 5. Security (HIGH)
2
+
3
+ ## 5.1 Short-Lived JWT + Refresh Tokens — CRITICAL
4
+
5
+ Access tokens cortos (5-15 min) limitan la ventana si se filtra uno; el refresh token (revocable, server-side) renueva sin re-login. Tokens de larga vida no se pueden invalidar.
6
+
7
+ ```csharp
8
+ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
9
+ .AddJwtBearer(o => o.TokenValidationParameters = new()
10
+ {
11
+ ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true,
12
+ ValidateIssuerSigningKey = true, ClockSkew = TimeSpan.FromSeconds(30)
13
+ });
14
+ ```
15
+
16
+ ## 5.2 Secrets in User-Secrets / Key Vault — CRITICAL
17
+
18
+ Nunca claves ni connection strings en `appsettings.json` (van al repo). En dev `dotnet user-secrets`; en prod Azure Key Vault o variables de entorno.
19
+
20
+ **Incorrecto:**
21
+ ```json
22
+ { "Jwt": { "Key": "super-secret-signing-key" } } // en el repo
23
+ ```
24
+ **Correcto:**
25
+ ```csharp
26
+ // dotnet user-secrets set "Jwt:Key" "..." (dev)
27
+ builder.Configuration.AddAzureKeyVault(vaultUri, new DefaultAzureCredential()); // prod
28
+ ```
29
+
30
+ ## 5.3 Authorize by Policies, Not Manual Role Checks — HIGH
31
+
32
+ Las policies centralizan la regla de autorización; los `if (user.Role == "Admin")` dispersos se desincronizan y se olvidan.
33
+
34
+ **Incorrecto:**
35
+ ```csharp
36
+ if (!ctx.User.IsInRole("Admin")) return Results.Forbid(); // lógica esparcida
37
+ ```
38
+ **Correcto:**
39
+ ```csharp
40
+ builder.Services.AddAuthorizationBuilder()
41
+ .AddPolicy("CanManageOrders", p => p.RequireRole("Admin").RequireClaim("scope", "orders:write"));
42
+ app.MapPost("/orders", Handler).RequireAuthorization("CanManageOrders");
43
+ ```
44
+
45
+ ## 5.4 HTTPS, HSTS and Rate Limiting — HIGH
46
+
47
+ Forzar HTTPS + HSTS evita downgrade/MITM. El middleware `RateLimiter` integrado protege de abuso y brute-force.
48
+
49
+ ```csharp
50
+ builder.Services.AddRateLimiter(o => o.AddFixedWindowLimiter("api", w =>
51
+ { w.PermitLimit = 100; w.Window = TimeSpan.FromMinutes(1); }));
52
+ app.UseHsts();
53
+ app.UseHttpsRedirection();
54
+ app.UseRateLimiter();
55
+ ```
56
+
57
+ ## 5.5 Don't Log Sensitive Data; Validate Input — HIGH
58
+
59
+ Nunca loggear passwords, tokens ni PII. Validar todo input con FluentValidation. Las queries de EF Core son parametrizadas por defecto (no concatenar SQL crudo) y `[ValidateAntiForgeryToken]` donde haya cookies/formularios.
60
+
61
+ ```csharp
62
+ public sealed class CreateOrderValidator : AbstractValidator<CreateOrderRequest>
63
+ {
64
+ public CreateOrderValidator()
65
+ {
66
+ RuleFor(x => x.CustomerId).NotEmpty();
67
+ RuleFor(x => x.Items).NotEmpty();
68
+ }
69
+ }
70
+ // EF Core parametriza: db.Users.Where(u => u.Email == input) — nunca string-interpolated FromSqlRaw
71
+ ```
72
+
73
+ _Ref: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/configure-jwt-bearer-authentication · https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit_
@@ -0,0 +1,76 @@
1
+ # 8. Testing (MEDIUM-HIGH)
2
+
3
+ ## 8.1 Integration Tests with WebApplicationFactory — HIGH
4
+
5
+ `WebApplicationFactory<T>` levanta la app en memoria con el pipeline real (DI, middleware, routing), probando el comportamiento de verdad y no mocks.
6
+
7
+ **Correcto:**
8
+ ```csharp
9
+ public class OrdersApiTests(WebApplicationFactory<Program> factory)
10
+ : IClassFixture<WebApplicationFactory<Program>>
11
+ {
12
+ [Fact]
13
+ public async Task GetOrders_ReturnsOk()
14
+ {
15
+ var client = factory.CreateClient();
16
+ var response = await client.GetAsync("/api/v1/orders");
17
+ response.StatusCode.Should().Be(HttpStatusCode.OK);
18
+ }
19
+ }
20
+ ```
21
+
22
+ ## 8.2 Real Postgres via Testcontainers — HIGH
23
+
24
+ El provider in-memory miente sobre el comportamiento relacional (transacciones, constraints, SQL). Testcontainers levanta un Postgres real y desechable.
25
+
26
+ **Incorrecto:**
27
+ ```csharp
28
+ options.UseInMemoryDatabase("test"); // no valida FKs, constraints ni SQL real
29
+ ```
30
+ **Correcto:**
31
+ ```csharp
32
+ var pg = new PostgreSqlBuilder().WithImage("postgres:17").Build();
33
+ await pg.StartAsync();
34
+ options.UseNpgsql(pg.GetConnectionString());
35
+ ```
36
+
37
+ ## 8.3 Don't Mock DbContext — MEDIUM-HIGH
38
+
39
+ Mockear `DbContext` o `IQueryable` reimplementa LINQ-to-SQL en LINQ-to-Objects y da falsos verdes. Probar handlers/slices contra una base real.
40
+
41
+ **Incorrecto:**
42
+ ```csharp
43
+ var mock = new Mock<AppDbContext>(); // no traduce queries como el provider real
44
+ ```
45
+ **Correcto:**
46
+ ```csharp
47
+ var handler = new CreateOrderHandler(realDbContext);
48
+ var result = await handler.Handle(command, CancellationToken.None);
49
+ ```
50
+
51
+ ## 8.4 Arrange-Act-Assert with FluentAssertions — MEDIUM
52
+
53
+ Estructurar cada test en AAA y usar `FluentAssertions` para aserciones legibles y mensajes de error claros.
54
+
55
+ **Correcto:**
56
+ ```csharp
57
+ [Fact]
58
+ public void Discount_AppliesPercentage()
59
+ {
60
+ var order = new Order(100m); // Arrange
61
+ order.ApplyDiscount(0.10m); // Act
62
+ order.Total.Should().Be(90m); // Assert
63
+ }
64
+ ```
65
+
66
+ ## 8.5 Deterministic Tests — MEDIUM
67
+
68
+ Evitar `DateTime.Now`, GUIDs aleatorios o dependencias de orden: inyectar `TimeProvider` para que los tests sean reproducibles.
69
+
70
+ **Correcto:**
71
+ ```csharp
72
+ var time = new FakeTimeProvider(new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero));
73
+ var service = new SubscriptionService(time); // sin reloj real, resultado estable
74
+ ```
75
+
76
+ _Ref: https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests · https://learn.microsoft.com/en-us/dotnet/core/testing/ · https://learn.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/test-aspnet-core-services-web-apps_
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: dotnet-scaffold
3
+ description: "Scaffold de API ASP.NET Core (.NET 10) production-ready: Vertical Slice o Clean Architecture, Minimal APIs, EF Core + PostgreSQL, JWT + Identity, ProblemDetails, health checks y testing. Usar para iniciar un backend .NET nuevo."
4
+ category: "backend"
5
+ argument-hint: "[project-name]"
6
+ user-invocable: true
7
+ allowed-tools: Read, Glob, Grep, Bash, Write, Edit, Task
8
+ ---
9
+ # dotnet-scaffold — ASP.NET Core API Scaffolder
10
+
11
+ ## MANDATORY WORKFLOW
12
+
13
+ **Antes de generar cualquier código, completar estos pasos en orden.** Confirmar las decisiones del Step 0 con el usuario antes de escribir archivos (Question → Decision → Approval).
14
+
15
+ ### Step 0: Gather Requirements
16
+ 1. **Nombre del servicio** (PascalCase para la solución, ej. `OrdersApi`)
17
+ 2. **Arquitectura:** Vertical Slice (default) / Clean Architecture — ver skill `dotnet-architecture` para elegir
18
+ 3. **Transport:** Web API REST con Minimal APIs (default) / Controllers / gRPC
19
+ 4. **ORM:** EF Core (default) / Dapper
20
+ 5. **DB:** PostgreSQL (default) / SQL Server
21
+ 6. **Auth:** JWT + ASP.NET Identity (default) / OAuth / ninguna
22
+ 7. **Deploy:** Docker (default) / Azure App Service
23
+
24
+ > Si el dominio es CRUD/simple → **Vertical Slice**. Si es complejo y long-lived con varios devs → **Clean Architecture**.
25
+
26
+ ### Step 1: Crear base
27
+ ```bash
28
+ dotnet new sln -n <Name>
29
+ dotnet new webapi -n <Name>.Api # Minimal APIs (default en .NET 8+); para Controllers: --use-controllers
30
+ dotnet sln add <Name>.Api
31
+ dotnet new xunit -n <Name>.Tests && dotnet sln add <Name>.Tests
32
+ cd <Name>.Api
33
+ dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
34
+ dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
35
+ dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
36
+ dotnet add package FluentValidation.DependencyInjectionExtensions # core + DI; FluentValidation.AspNetCore (auto-validation) está deprecado
37
+ dotnet add package Serilog.AspNetCore
38
+ ```
39
+
40
+ ### Step 2: Estructura
41
+
42
+ **Vertical Slice (default)** — un folder por feature/use-case:
43
+ ```
44
+ src/
45
+ Features/
46
+ Orders/ { CreateOrder.cs, GetOrder.cs, OrderEndpoints.cs } # request+handler+validator+endpoint por slice
47
+ Users/ { ... }
48
+ Infrastructure/ { AppDbContext.cs, Migrations/ }
49
+ Common/ { Behaviors/, Results/, ProblemDetails/ }
50
+ Program.cs
51
+ ```
52
+
53
+ **Clean Architecture** — proyectos por capa (deps apuntan hacia adentro):
54
+ ```
55
+ <Name>.Domain/ # entities, value objects — cero deps externas
56
+ <Name>.Application/ # use cases, interfaces (IAppDbContext), DTOs
57
+ <Name>.Infrastructure/ # EF Core, Identity, implementaciones
58
+ <Name>.Api/ # endpoints/controllers, DI, Program.cs
59
+ ```
60
+
61
+ ### Step 3: Defaults no negociables (`Program.cs` / `.csproj`)
62
+ - `<Nullable>enable</Nullable>` + `<TreatWarningsAsErrors>true</TreatWarningsAsErrors>` en el `.csproj`
63
+ - **Async end-to-end**: handlers `async Task<>`, `CancellationToken` propagado a EF Core y HTTP; nunca `.Result`/`.Wait()`
64
+ - **DI por constructor** (primary constructors); registrar servicios con scope correcto (`AddScoped` para DbContext/handlers)
65
+ - **Validación**: FluentValidation por slice, invocada manualmente (pipeline behavior MediatR o endpoint filter — no auto-validation) → responder `ValidationProblemDetails` (RFC 7807)
66
+ - **Errores**: `AddProblemDetails()` + exception handler global → `ProblemDetails` consistente, sin filtrar stack traces
67
+ - **EF Core**: migraciones versionadas (`dotnet ef migrations add`), **nunca** `EnsureCreated()` en prod; `DbContext` como Unit of Work (sin repos genéricos salvo necesidad)
68
+ - **Auth**: JWT de vida corta + refresh, secretos en `user-secrets`/secret manager, authorization por policies declarativas
69
+ - **Observabilidad**: Serilog structured logging (JSON) + health checks (`AddHealthChecks`) + OpenTelemetry opcional
70
+ - **Graceful shutdown**: respetar `IHostApplicationLifetime` / `CancellationToken` del host
71
+
72
+ ### Step 4: Agregar piezas (invocar skills relacionadas)
73
+ - `dotnet-architecture` — estructura de slices/capas en detalle
74
+ - `database` / `data-migrations` — modelado EF Core + migraciones
75
+ - `jwt-strategy` / `auth-strategy` / `rbac-abac` — auth y authorization
76
+ - `api-design` / `api-versioning` / `api-docs` — contrato REST + OpenAPI
77
+ - `testing` / `contract-testing` — xUnit + Testcontainers (Postgres real en tests)
78
+
79
+ ### Step 5: Verificar
80
+ `dotnet build` + `dotnet test`. Revisar contra la rule `dotnet-code` y el skill `dotnet-best-practices`. Build + tests verdes → scaffold **READY**.
81
+
82
+ ## Stack Defaults
83
+
84
+ | Componente | Default |
85
+ |------------|---------|
86
+ | Runtime | .NET 10 LTS (C# 14) |
87
+ | Framework | ASP.NET Core — Minimal APIs |
88
+ | Arquitectura | Vertical Slice (Clean opcional) |
89
+ | ORM | EF Core 10 |
90
+ | DB | PostgreSQL (Npgsql) |
91
+ | Auth | JWT + ASP.NET Identity |
92
+ | Validación | FluentValidation → ProblemDetails |
93
+ | Logging | Serilog (JSON) |
94
+ | Testing | xUnit + Testcontainers |
95
+ | Deploy | Docker |
96
+
97
+ ---
98
+
99
+ _Inspirado en [dotnet/skills](https://github.com/dotnet/skills) (oficial Microsoft), [github/awesome-copilot](https://github.com/github/awesome-copilot) (dotnet-best-practices) y los templates [ardalis/CleanArchitecture](https://github.com/ardalis/CleanArchitecture) y [nadirbad/VerticalSliceArchitecture](https://github.com/nadirbad/VerticalSliceArchitecture). Adaptado al formato Arcane._
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: install-mcp
3
+ description: "Instala y registra el MCP de Unity (CoplayDev MCP for Unity) en un proyecto Unity + Claude Code: agrega el package UPM, registra el server MCP y verifica la conexion. Usar para: instalar mcp unity, conectar Claude con Unity, setup MCP Unity."
4
+ category: "gamedev"
5
+ argument-hint: "[--secondary] [project-path]"
6
+ user-invocable: true
7
+ allowed-tools: Read, Glob, Grep, Bash, Write, Edit
8
+ ---
9
+ # install-mcp — Unity MCP Setup
10
+
11
+ Instala el package MCP del lado Unity y registra el server MCP en Claude Code, replicando el setup usado en los proyectos de tesis (TesisUade).
12
+
13
+ - **Package primario:** `com.coplaydev.unity-mcp` (CoplayDev "MCP for Unity"). Server Python vía `uv`/`uvx`, transport HTTP en `127.0.0.1:8080`. El server se levanta y auto-configura desde Unity en `Window → MCP for Unity`.
14
+ - **Package secundario (opcional, flag `--secondary`):** `com.gamelovers.mcp-unity` (CoderGamester). Server Node.
15
+
16
+ > El flujo de Unity (`Window → MCP for Unity`) requiere la GUI del Editor — Claude no puede manejarla. La skill automatiza lo que sí puede (editar `manifest.json`, registrar el cliente MCP, verificar) y **guía** el paso del Editor.
17
+
18
+ ## Input
19
+
20
+ - `project-path` (opcional): raíz del proyecto Unity. Default: directorio actual.
21
+ - `--secondary` (opcional): además del primario, instala el package CoderGamester.
22
+
23
+ ---
24
+
25
+ ## Workflow
26
+
27
+ ### 1. Detectar proyecto Unity
28
+
29
+ Desde `project-path` (o cwd), confirmar que existen:
30
+ - `Packages/manifest.json`
31
+ - `ProjectSettings/ProjectVersion.txt`
32
+
33
+ Si falta alguno, **abortar** con: "No parece un proyecto Unity (falta Packages/manifest.json o ProjectSettings/ProjectVersion.txt). Pasá la ruta del proyecto con `/install-mcp <project-path>`."
34
+
35
+ ### 2. Verificar prerrequisitos
36
+
37
+ - `uv --version` y `uvx --version` (server del primario). Si falta `uv`, **no abortar**: avisar y dar el comando de install y seguir editando el manifest:
38
+ - Windows: `winget install astral-sh.uv`
39
+ - alternativa: `pipx install uv`
40
+ - Solo con `--secondary`: `node --version`. Si falta, avisar (el server CoderGamester no podrá correr).
41
+
42
+ ### 3. Agregar el package UPM (lado Unity)
43
+
44
+ Leer `Packages/manifest.json`. Dentro de `"dependencies"`, agregar **solo si la key no existe** (idempotente — nunca duplicar):
45
+
46
+ ```json
47
+ "com.coplaydev.unity-mcp": "https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#main"
48
+ ```
49
+
50
+ Con `--secondary`, agregar también:
51
+
52
+ ```json
53
+ "com.gamelovers.mcp-unity": "https://github.com/CoderGamester/mcp-unity.git"
54
+ ```
55
+
56
+ Usar `Edit` puntual sobre el bloque `dependencies` preservando el JSON válido (indentación, comas). Si la key ya estaba, reportar "ya presente, sin cambios".
57
+
58
+ > **Antes de escribir**, mostrar el diff propuesto sobre `manifest.json` (y el `.mcp.json` del paso 4B si aplica) y pedir aprobación al usuario. No editar sin confirmación.
59
+
60
+ ### 4. Registrar el server MCP en Claude Code
61
+
62
+ Dos caminos, en orden de preferencia:
63
+
64
+ **A. Recomendado (auto-config desde Unity).** Instruir al usuario:
65
+ 1. Abrir el proyecto en Unity (Unity importará el package nuevo).
66
+ 2. `Window → MCP for Unity`.
67
+ 3. En esa ventana, usar el botón de auto-configuración para **Claude Code** — instala el server `uv` y registra el cliente MCP automáticamente.
68
+
69
+ **B. Fallback manual.** Si el usuario no puede usar el Editor ahora, registrar el transport HTTP que usa CoplayDev:
70
+
71
+ ```bash
72
+ claude mcp add --transport http unity-mcp http://127.0.0.1:8080
73
+ ```
74
+
75
+ (equivalente: escribir un `.mcp.json` de proyecto con
76
+ `{"mcpServers":{"unity-mcp":{"type":"http","url":"http://127.0.0.1:8080"}}}`).
77
+
78
+ > El server solo responde cuando Unity está abierto con `MCP for Unity` activo. Registrar el cliente no levanta el server.
79
+
80
+ ### 5. Verificar
81
+
82
+ Correr:
83
+
84
+ ```bash
85
+ claude mcp list
86
+ ```
87
+
88
+ Reportar si `unity-mcp` aparece **conectado**. Seguir la guía de TesisUade: **si NO está conectado, avisar explícitamente** y aclarar que las operaciones de engine (scenes, SOs, prefabs, UI wiring) NO se aplicaron — no asumir éxito.
89
+
90
+ ### 6. Resumen
91
+
92
+ Reportar:
93
+ - Qué keys se agregaron a `manifest.json` (o si ya estaban).
94
+ - Estado del registro MCP (auto-config pendiente en Unity vs. fallback aplicado).
95
+ - Resultado de `claude mcp list`.
96
+ - Próximo paso manual: abrir Unity → `Window → MCP for Unity`.
97
+
98
+ **Verdict:**
99
+ - **READY** — package en `manifest.json` + cliente registrado + `unity-mcp` conectado en `claude mcp list`.
100
+ - **PENDING** — package y cliente listos, pero falta abrir Unity (`Window → MCP for Unity`) para que el server conecte. Avisar que las operaciones de engine aún NO están disponibles.
101
+ - **BLOCKED** — falta un prerrequisito (`uv` ausente) o no es un proyecto Unity válido. Indicar el fix.
102
+
103
+ Tras conectar, seguir con `/unity-game-architecture` o `/scaffold-unity` para trabajar el proyecto.
104
+
105
+ ---
106
+
107
+ > → Read references/manual-setup.md for [pasos detallados del flujo Window → MCP for Unity, troubleshooting (uv no encontrado, puerto 8080 ocupado, MCP no conecta) y la variante secundaria CoderGamester]