agentic-team-templates 0.13.2 → 0.14.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 (54) hide show
  1. package/README.md +6 -1
  2. package/package.json +1 -1
  3. package/src/index.js +22 -2
  4. package/src/index.test.js +5 -0
  5. package/templates/cpp-expert/.cursorrules/concurrency.md +211 -0
  6. package/templates/cpp-expert/.cursorrules/error-handling.md +170 -0
  7. package/templates/cpp-expert/.cursorrules/memory-and-ownership.md +220 -0
  8. package/templates/cpp-expert/.cursorrules/modern-cpp.md +211 -0
  9. package/templates/cpp-expert/.cursorrules/overview.md +87 -0
  10. package/templates/cpp-expert/.cursorrules/performance.md +223 -0
  11. package/templates/cpp-expert/.cursorrules/testing.md +230 -0
  12. package/templates/cpp-expert/.cursorrules/tooling.md +312 -0
  13. package/templates/cpp-expert/CLAUDE.md +242 -0
  14. package/templates/csharp-expert/.cursorrules/aspnet-core.md +311 -0
  15. package/templates/csharp-expert/.cursorrules/async-patterns.md +206 -0
  16. package/templates/csharp-expert/.cursorrules/dependency-injection.md +206 -0
  17. package/templates/csharp-expert/.cursorrules/error-handling.md +235 -0
  18. package/templates/csharp-expert/.cursorrules/language-features.md +204 -0
  19. package/templates/csharp-expert/.cursorrules/overview.md +92 -0
  20. package/templates/csharp-expert/.cursorrules/performance.md +251 -0
  21. package/templates/csharp-expert/.cursorrules/testing.md +282 -0
  22. package/templates/csharp-expert/.cursorrules/tooling.md +254 -0
  23. package/templates/csharp-expert/CLAUDE.md +360 -0
  24. package/templates/java-expert/.cursorrules/concurrency.md +209 -0
  25. package/templates/java-expert/.cursorrules/error-handling.md +205 -0
  26. package/templates/java-expert/.cursorrules/modern-java.md +216 -0
  27. package/templates/java-expert/.cursorrules/overview.md +81 -0
  28. package/templates/java-expert/.cursorrules/performance.md +239 -0
  29. package/templates/java-expert/.cursorrules/persistence.md +262 -0
  30. package/templates/java-expert/.cursorrules/spring-boot.md +262 -0
  31. package/templates/java-expert/.cursorrules/testing.md +272 -0
  32. package/templates/java-expert/.cursorrules/tooling.md +301 -0
  33. package/templates/java-expert/CLAUDE.md +325 -0
  34. package/templates/javascript-expert/.cursorrules/overview.md +5 -3
  35. package/templates/javascript-expert/.cursorrules/typescript-deep-dive.md +348 -0
  36. package/templates/javascript-expert/CLAUDE.md +34 -3
  37. package/templates/kotlin-expert/.cursorrules/coroutines.md +237 -0
  38. package/templates/kotlin-expert/.cursorrules/error-handling.md +149 -0
  39. package/templates/kotlin-expert/.cursorrules/frameworks.md +227 -0
  40. package/templates/kotlin-expert/.cursorrules/language-features.md +231 -0
  41. package/templates/kotlin-expert/.cursorrules/overview.md +77 -0
  42. package/templates/kotlin-expert/.cursorrules/performance.md +185 -0
  43. package/templates/kotlin-expert/.cursorrules/testing.md +213 -0
  44. package/templates/kotlin-expert/.cursorrules/tooling.md +258 -0
  45. package/templates/kotlin-expert/CLAUDE.md +276 -0
  46. package/templates/swift-expert/.cursorrules/concurrency.md +230 -0
  47. package/templates/swift-expert/.cursorrules/error-handling.md +213 -0
  48. package/templates/swift-expert/.cursorrules/language-features.md +246 -0
  49. package/templates/swift-expert/.cursorrules/overview.md +88 -0
  50. package/templates/swift-expert/.cursorrules/performance.md +260 -0
  51. package/templates/swift-expert/.cursorrules/swiftui.md +260 -0
  52. package/templates/swift-expert/.cursorrules/testing.md +286 -0
  53. package/templates/swift-expert/.cursorrules/tooling.md +285 -0
  54. package/templates/swift-expert/CLAUDE.md +275 -0
@@ -0,0 +1,311 @@
1
+ # ASP.NET Core Patterns
2
+
3
+ Modern ASP.NET Core web development. Minimal APIs, middleware, and production-grade service patterns.
4
+
5
+ ## Minimal APIs
6
+
7
+ ```csharp
8
+ var builder = WebApplication.CreateBuilder(args);
9
+
10
+ // Service registration
11
+ builder.Services.AddOrderServices();
12
+ builder.Services.AddOpenApi();
13
+
14
+ var app = builder.Build();
15
+
16
+ // Middleware pipeline — order matters
17
+ app.UseExceptionHandler();
18
+ app.UseStatusCodePages();
19
+ app.UseAuthentication();
20
+ app.UseAuthorization();
21
+
22
+ // Endpoint mapping
23
+ app.MapOrderEndpoints();
24
+ app.MapOpenApi();
25
+
26
+ app.Run();
27
+ ```
28
+
29
+ ### Endpoint Organization
30
+
31
+ ```csharp
32
+ public static class OrderEndpoints
33
+ {
34
+ public static void MapOrderEndpoints(this IEndpointRouteBuilder app)
35
+ {
36
+ var group = app.MapGroup("/orders")
37
+ .WithTags("Orders")
38
+ .RequireAuthorization();
39
+
40
+ group.MapGet("/", GetAllOrders)
41
+ .WithName("GetOrders")
42
+ .Produces<IReadOnlyList<OrderResponse>>();
43
+
44
+ group.MapGet("/{id:int}", GetOrderById)
45
+ .WithName("GetOrder")
46
+ .Produces<OrderResponse>()
47
+ .ProducesProblem(StatusCodes.Status404NotFound);
48
+
49
+ group.MapPost("/", CreateOrder)
50
+ .WithName("CreateOrder")
51
+ .Produces<OrderResponse>(StatusCodes.Status201Created)
52
+ .ProducesValidationProblem();
53
+
54
+ group.MapDelete("/{id:int}", DeleteOrder)
55
+ .RequireAuthorization("AdminOnly");
56
+ }
57
+
58
+ private static async Task<IResult> GetOrderById(
59
+ int id, IOrderService service, CancellationToken ct)
60
+ {
61
+ var order = await service.GetByIdAsync(id, ct);
62
+ return order is not null
63
+ ? Results.Ok(order)
64
+ : Results.Problem(statusCode: 404, title: "Order not found");
65
+ }
66
+
67
+ private static async Task<IResult> CreateOrder(
68
+ CreateOrderRequest request,
69
+ IValidator<CreateOrderRequest> validator,
70
+ IOrderService service,
71
+ CancellationToken ct)
72
+ {
73
+ var validation = await validator.ValidateAsync(request, ct);
74
+ if (!validation.IsValid)
75
+ return Results.ValidationProblem(validation.ToDictionary());
76
+
77
+ var result = await service.CreateAsync(request, ct);
78
+ return result.Match(
79
+ order => Results.Created($"/orders/{order.Id}", order),
80
+ error => Results.Problem(error.Message, statusCode: 400));
81
+ }
82
+ }
83
+ ```
84
+
85
+ ## Middleware
86
+
87
+ ```csharp
88
+ // Request timing middleware
89
+ public class RequestTimingMiddleware(RequestDelegate next, ILogger<RequestTimingMiddleware> logger)
90
+ {
91
+ public async Task InvokeAsync(HttpContext context)
92
+ {
93
+ var stopwatch = Stopwatch.StartNew();
94
+ try
95
+ {
96
+ await next(context);
97
+ }
98
+ finally
99
+ {
100
+ stopwatch.Stop();
101
+ logger.LogInformation(
102
+ "HTTP {Method} {Path} responded {StatusCode} in {ElapsedMs}ms",
103
+ context.Request.Method,
104
+ context.Request.Path,
105
+ context.Response.StatusCode,
106
+ stopwatch.ElapsedMilliseconds);
107
+ }
108
+ }
109
+ }
110
+
111
+ // Registration
112
+ app.UseMiddleware<RequestTimingMiddleware>();
113
+ ```
114
+
115
+ ## Configuration
116
+
117
+ ```csharp
118
+ // Strongly typed with validation
119
+ public class DatabaseOptions
120
+ {
121
+ public const string SectionName = "Database";
122
+
123
+ [Required]
124
+ public required string ConnectionString { get; init; }
125
+
126
+ [Range(1, 100)]
127
+ public int MaxPoolSize { get; init; } = 20;
128
+
129
+ [Range(1, 300)]
130
+ public int CommandTimeoutSeconds { get; init; } = 30;
131
+ }
132
+
133
+ // Registration with validation at startup
134
+ builder.Services
135
+ .AddOptions<DatabaseOptions>()
136
+ .BindConfiguration(DatabaseOptions.SectionName)
137
+ .ValidateDataAnnotations()
138
+ .ValidateOnStart();
139
+ ```
140
+
141
+ ## Health Checks
142
+
143
+ ```csharp
144
+ builder.Services.AddHealthChecks()
145
+ .AddDbContextCheck<AppDbContext>("database")
146
+ .AddRedis(connectionString, "redis")
147
+ .AddCheck<ExternalApiHealthCheck>("external-api");
148
+
149
+ app.MapHealthChecks("/healthz", new HealthCheckOptions
150
+ {
151
+ ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
152
+ });
153
+
154
+ // Liveness vs readiness
155
+ app.MapHealthChecks("/healthz/live", new HealthCheckOptions
156
+ {
157
+ Predicate = _ => false // Just checks if the app is running
158
+ });
159
+
160
+ app.MapHealthChecks("/healthz/ready", new HealthCheckOptions
161
+ {
162
+ Predicate = check => check.Tags.Contains("ready")
163
+ });
164
+ ```
165
+
166
+ ## Authentication & Authorization
167
+
168
+ ```csharp
169
+ // JWT Bearer
170
+ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
171
+ .AddJwtBearer(options =>
172
+ {
173
+ options.Authority = builder.Configuration["Auth:Authority"];
174
+ options.Audience = builder.Configuration["Auth:Audience"];
175
+ options.TokenValidationParameters = new TokenValidationParameters
176
+ {
177
+ ValidateIssuer = true,
178
+ ValidateAudience = true,
179
+ ValidateLifetime = true,
180
+ ClockSkew = TimeSpan.FromSeconds(30)
181
+ };
182
+ });
183
+
184
+ // Policy-based authorization
185
+ builder.Services.AddAuthorization(options =>
186
+ {
187
+ options.AddPolicy("AdminOnly", policy =>
188
+ policy.RequireClaim("role", "admin"));
189
+
190
+ options.AddPolicy("CanManageOrders", policy =>
191
+ policy.Requirements.Add(new OrderManagementRequirement()));
192
+ });
193
+ ```
194
+
195
+ ## Output Caching (.NET 7+)
196
+
197
+ ```csharp
198
+ builder.Services.AddOutputCache(options =>
199
+ {
200
+ options.AddBasePolicy(builder => builder.Expire(TimeSpan.FromMinutes(5)));
201
+ options.AddPolicy("Products", builder =>
202
+ builder.Tag("products").Expire(TimeSpan.FromMinutes(30)));
203
+ });
204
+
205
+ app.MapGet("/products", GetProducts)
206
+ .CacheOutput("Products");
207
+
208
+ // Invalidation
209
+ app.MapPost("/products", async (
210
+ CreateProductRequest request,
211
+ IOutputCacheStore store,
212
+ CancellationToken ct) =>
213
+ {
214
+ // ... create product ...
215
+ await store.EvictByTagAsync("products", ct);
216
+ });
217
+ ```
218
+
219
+ ## Rate Limiting
220
+
221
+ ```csharp
222
+ builder.Services.AddRateLimiter(options =>
223
+ {
224
+ options.AddFixedWindowLimiter("api", config =>
225
+ {
226
+ config.PermitLimit = 100;
227
+ config.Window = TimeSpan.FromMinutes(1);
228
+ config.QueueLimit = 0;
229
+ });
230
+
231
+ options.AddTokenBucketLimiter("uploads", config =>
232
+ {
233
+ config.TokenLimit = 10;
234
+ config.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
235
+ config.TokensPerPeriod = 2;
236
+ });
237
+
238
+ options.OnRejected = async (context, ct) =>
239
+ {
240
+ context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
241
+ await context.HttpContext.Response.WriteAsJsonAsync(
242
+ new ProblemDetails { Title = "Too many requests" }, ct);
243
+ };
244
+ });
245
+
246
+ app.UseRateLimiter();
247
+ app.MapGet("/api/data", GetData).RequireRateLimiting("api");
248
+ ```
249
+
250
+ ## Background Services
251
+
252
+ ```csharp
253
+ public class OrderProcessorService(
254
+ IServiceScopeFactory scopeFactory,
255
+ ILogger<OrderProcessorService> logger) : BackgroundService
256
+ {
257
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
258
+ {
259
+ logger.LogInformation("Order processor starting");
260
+
261
+ while (!stoppingToken.IsCancellationRequested)
262
+ {
263
+ try
264
+ {
265
+ using var scope = scopeFactory.CreateScope();
266
+ var processor = scope.ServiceProvider
267
+ .GetRequiredService<IOrderProcessor>();
268
+
269
+ await processor.ProcessPendingOrdersAsync(stoppingToken);
270
+ }
271
+ catch (Exception ex) when (ex is not OperationCanceledException)
272
+ {
273
+ logger.LogError(ex, "Error processing orders");
274
+ }
275
+
276
+ await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
277
+ }
278
+ }
279
+ }
280
+ ```
281
+
282
+ ## Anti-Patterns
283
+
284
+ ```csharp
285
+ // Never: business logic in endpoints
286
+ app.MapPost("/orders", async (CreateOrderRequest req, AppDbContext db) =>
287
+ {
288
+ // Validation, business rules, persistence all in one place
289
+ if (req.Items.Count == 0) return Results.BadRequest();
290
+ var order = new Order { /* ... */ };
291
+ db.Orders.Add(order);
292
+ await db.SaveChangesAsync();
293
+ return Results.Ok(order);
294
+ });
295
+ // Use services, keep endpoints thin
296
+
297
+ // Never: exposing entities directly
298
+ app.MapGet("/users/{id}", async (int id, AppDbContext db) =>
299
+ await db.Users.FindAsync(id)); // Exposes internal schema, password hashes, etc.
300
+ // Map to DTOs/response records
301
+
302
+ // Never: synchronous I/O in middleware
303
+ public void Configure(IApplicationBuilder app)
304
+ {
305
+ app.Use((context, next) =>
306
+ {
307
+ var data = File.ReadAllText("config.json"); // Blocks thread pool thread!
308
+ return next();
309
+ });
310
+ }
311
+ ```
@@ -0,0 +1,206 @@
1
+ # C# Async Patterns
2
+
3
+ async/await done right. Every pitfall known. Every pattern battle-tested.
4
+
5
+ ## The Golden Rules
6
+
7
+ 1. **Async all the way down.** Never mix sync and async.
8
+ 2. **Never block on async.** No `.Result`, `.Wait()`, `.GetAwaiter().GetResult()` in application code.
9
+ 3. **Always pass CancellationToken.** Every async method that does I/O should accept and honor one.
10
+ 4. **ConfigureAwait(false) in library code.** Not needed in ASP.NET Core app code (no SynchronizationContext).
11
+ 5. **Return Task, not void.** `async void` is only for event handlers.
12
+
13
+ ## Proper Async Methods
14
+
15
+ ```csharp
16
+ // Good: accepts CancellationToken, returns Task<T>
17
+ public async Task<User?> GetUserAsync(int id, CancellationToken cancellationToken = default)
18
+ {
19
+ return await _dbContext.Users
20
+ .AsNoTracking()
21
+ .FirstOrDefaultAsync(u => u.Id == id, cancellationToken);
22
+ }
23
+
24
+ // Good: elide async/await when just forwarding
25
+ public Task<User?> GetUserAsync(int id, CancellationToken ct = default)
26
+ => _dbContext.Users.AsNoTracking().FirstOrDefaultAsync(u => u.Id == id, ct);
27
+ // But: don't elide if there's a using/try-catch — the await ensures proper lifetime
28
+
29
+ // Bad: blocking on async
30
+ public User GetUser(int id)
31
+ {
32
+ return GetUserAsync(id).Result; // DEADLOCK RISK
33
+ }
34
+ ```
35
+
36
+ ## CancellationToken
37
+
38
+ ```csharp
39
+ // Thread it through every layer
40
+ public async Task<OrderResult> ProcessOrderAsync(
41
+ CreateOrderRequest request,
42
+ CancellationToken cancellationToken)
43
+ {
44
+ var user = await _userService.GetByIdAsync(request.UserId, cancellationToken);
45
+ var inventory = await _inventoryService.CheckAsync(request.Items, cancellationToken);
46
+
47
+ // Check cancellation before expensive operations
48
+ cancellationToken.ThrowIfCancellationRequested();
49
+
50
+ var order = Order.Create(user, inventory);
51
+ await _repository.SaveAsync(order, cancellationToken);
52
+
53
+ return new OrderResult(order.Id);
54
+ }
55
+
56
+ // Link cancellation tokens
57
+ using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
58
+ cancellationToken,
59
+ _applicationLifetime.ApplicationStopping);
60
+
61
+ // Timeout with cancellation
62
+ using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
63
+ using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
64
+ cancellationToken, timeoutCts.Token);
65
+ ```
66
+
67
+ ## Concurrent Operations
68
+
69
+ ```csharp
70
+ // Good: parallel independent operations with Task.WhenAll
71
+ public async Task<DashboardData> GetDashboardAsync(int userId, CancellationToken ct)
72
+ {
73
+ var userTask = _userService.GetByIdAsync(userId, ct);
74
+ var ordersTask = _orderService.GetRecentAsync(userId, ct);
75
+ var notificationsTask = _notificationService.GetUnreadAsync(userId, ct);
76
+
77
+ await Task.WhenAll(userTask, ordersTask, notificationsTask);
78
+
79
+ return new DashboardData(
80
+ User: await userTask,
81
+ Orders: await ordersTask,
82
+ Notifications: await notificationsTask);
83
+ }
84
+
85
+ // Good: bounded concurrency with SemaphoreSlim
86
+ public async Task ProcessBatchAsync(
87
+ IReadOnlyList<Item> items, CancellationToken ct)
88
+ {
89
+ using var semaphore = new SemaphoreSlim(maxConcurrency: 10);
90
+ var tasks = items.Select(async item =>
91
+ {
92
+ await semaphore.WaitAsync(ct);
93
+ try
94
+ {
95
+ await ProcessItemAsync(item, ct);
96
+ }
97
+ finally
98
+ {
99
+ semaphore.Release();
100
+ }
101
+ });
102
+
103
+ await Task.WhenAll(tasks);
104
+ }
105
+ ```
106
+
107
+ ## Channels
108
+
109
+ ```csharp
110
+ // Producer-consumer with bounded channels
111
+ public class EventProcessor
112
+ {
113
+ private readonly Channel<DomainEvent> _channel =
114
+ Channel.CreateBounded<DomainEvent>(new BoundedChannelOptions(1000)
115
+ {
116
+ FullMode = BoundedChannelFullMode.Wait,
117
+ SingleReader = true,
118
+ SingleWriter = false
119
+ });
120
+
121
+ public async ValueTask PublishAsync(DomainEvent @event, CancellationToken ct)
122
+ {
123
+ await _channel.Writer.WriteAsync(@event, ct);
124
+ }
125
+
126
+ public async Task ProcessAsync(CancellationToken ct)
127
+ {
128
+ await foreach (var @event in _channel.Reader.ReadAllAsync(ct))
129
+ {
130
+ await HandleEventAsync(@event, ct);
131
+ }
132
+ }
133
+ }
134
+ ```
135
+
136
+ ## ValueTask
137
+
138
+ ```csharp
139
+ // Use ValueTask when the result is often synchronous (cached, pooled)
140
+ public ValueTask<User?> GetCachedUserAsync(int id, CancellationToken ct)
141
+ {
142
+ if (_cache.TryGetValue(id, out var user))
143
+ return ValueTask.FromResult<User?>(user); // No allocation
144
+
145
+ return GetAndCacheUserAsync(id, ct); // Async path
146
+ }
147
+
148
+ private async ValueTask<User?> GetAndCacheUserAsync(int id, CancellationToken ct)
149
+ {
150
+ var user = await _repository.GetByIdAsync(id, ct);
151
+ if (user is not null) _cache.Set(id, user);
152
+ return user;
153
+ }
154
+
155
+ // Rules for ValueTask:
156
+ // - Never await a ValueTask more than once
157
+ // - Never use .Result or .GetAwaiter().GetResult() on an incomplete ValueTask
158
+ // - Never use Task.WhenAll with ValueTask (convert with .AsTask() first)
159
+ ```
160
+
161
+ ## IAsyncEnumerable
162
+
163
+ ```csharp
164
+ // Streaming results without buffering
165
+ public async IAsyncEnumerable<LogEntry> StreamLogsAsync(
166
+ string filter,
167
+ [EnumeratorCancellation] CancellationToken ct = default)
168
+ {
169
+ await foreach (var line in _logSource.ReadLinesAsync(ct))
170
+ {
171
+ if (line.Contains(filter, StringComparison.OrdinalIgnoreCase))
172
+ {
173
+ yield return ParseLogEntry(line);
174
+ }
175
+ }
176
+ }
177
+
178
+ // Consuming
179
+ await foreach (var entry in StreamLogsAsync("ERROR", ct))
180
+ {
181
+ await ProcessEntryAsync(entry, ct);
182
+ }
183
+ ```
184
+
185
+ ## Anti-Patterns
186
+
187
+ ```csharp
188
+ // Never: async void (unobservable exceptions)
189
+ async void OnButtonClick() { await DoWorkAsync(); }
190
+ // Use: async Task OnButtonClickAsync() { ... }
191
+
192
+ // Never: fire-and-forget without error handling
193
+ _ = DoWorkAsync(); // Exception silently swallowed
194
+ // Use: _ = Task.Run(async () => { try { ... } catch { _logger.LogError(...); } });
195
+
196
+ // Never: unnecessary async/await wrapper
197
+ async Task<int> GetValueAsync() { return await Task.FromResult(42); }
198
+ // Just: Task<int> GetValueAsync() => Task.FromResult(42);
199
+
200
+ // Never: Task.Run for I/O-bound work
201
+ await Task.Run(() => httpClient.GetAsync(url)); // Wastes a thread pool thread
202
+ // Just: await httpClient.GetAsync(url);
203
+
204
+ // Never: capturing loop variable in pre-C#5 style (modern C# handles this, but be aware)
205
+ // Never: using async lambdas with void-returning delegates (Action)
206
+ ```