- Add path-specific rules for commands/queries, dynamic queries, validation, and gRPC - Add /add-command, /add-query, /add-dynamic-query scaffolding skills - Add project settings with post-edit formatting, proto validation, and build-gate hooks - Add .editorconfig codifying existing code style conventions - Trim CLAUDE.md from 414 to 130 lines (domain details moved to rules) - Add .harness-version tracking for the shared claude-harness repo Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
104 lines
3.4 KiB
Markdown
104 lines
3.4 KiB
Markdown
---
|
|
name: add-command
|
|
description: Scaffold a new CQRS command with handler, validator, and DI registration. Use when the user wants to add a new command to a project.
|
|
argument-hint: <description of the command in natural language>
|
|
---
|
|
|
|
# Add Command
|
|
|
|
Scaffold a new command based on: $ARGUMENTS
|
|
|
|
## Instructions
|
|
|
|
### 1. Determine the command name
|
|
|
|
The name must express **domain intent**, not CRUD. If the user's description sounds like CRUD, translate it:
|
|
- "create a user" → `RegisterUserCommand`
|
|
- "update the order status" → contextual: `ShipOrderCommand`, `CancelOrderCommand`, `ApproveOrderCommand`
|
|
- "delete an account" → `DeactivateAccountCommand` or `CloseAccountCommand`
|
|
|
|
Ask the user to clarify if the intent is ambiguous (e.g., "update order" could mean many things).
|
|
|
|
### 2. Determine the feature folder
|
|
|
|
Place the file in the appropriate feature folder: `Features/{DomainArea}/`
|
|
If the folder doesn't exist, create it. If the project doesn't use `Features/` yet, check the existing structure and follow the same pattern.
|
|
|
|
### 3. Create the file
|
|
|
|
Create a single file `Features/{DomainArea}/{CommandName}Command.cs` containing all three classes:
|
|
|
|
```csharp
|
|
using FluentValidation;
|
|
using Svrnty.CQRS.Abstractions;
|
|
|
|
namespace {ProjectNamespace}.Features.{DomainArea};
|
|
|
|
// 1. Command POCO — record type, properties with defaults
|
|
public record {CommandName}Command
|
|
{
|
|
// Properties based on user description
|
|
// Strings default to string.Empty, collections to []
|
|
}
|
|
|
|
// 2. Validator — rules in constructor, always include .WithMessage()
|
|
public class {CommandName}CommandValidator : AbstractValidator<{CommandName}Command>
|
|
{
|
|
public {CommandName}CommandValidator()
|
|
{
|
|
// Validation rules based on command properties
|
|
}
|
|
}
|
|
|
|
// 3. Handler — always async with CancellationToken
|
|
public class {CommandName}CommandHandler : ICommandHandler<{CommandName}Command, {ResultType}>
|
|
{
|
|
public Task<{ResultType}> HandleAsync({CommandName}Command command, CancellationToken cancellationToken = default)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
```
|
|
|
|
If the command has **no return value**, use `ICommandHandler<{CommandName}Command>` (single generic param) with `Task HandleAsync(...)`.
|
|
|
|
### 4. Register in DI
|
|
|
|
Find the project's service registration (typically `Program.cs` or a dedicated registration method) and add:
|
|
|
|
```csharp
|
|
// With validator (preferred)
|
|
builder.Services.AddCommand<{CommandName}Command, {ResultType}, {CommandName}CommandHandler, {CommandName}CommandValidator>();
|
|
|
|
// Without validator
|
|
builder.Services.AddCommand<{CommandName}Command, {ResultType}, {CommandName}CommandHandler>();
|
|
|
|
// No return value + validator
|
|
builder.Services.AddCommand<{CommandName}Command, {CommandName}CommandHandler, {CommandName}CommandValidator>();
|
|
```
|
|
|
|
Ensure the `using Svrnty.CQRS.FluentValidation;` namespace is imported if using the validator overload.
|
|
|
|
### 5. Proto message (if project uses gRPC)
|
|
|
|
If the project has a `Protos/` directory, add the corresponding proto message:
|
|
|
|
```protobuf
|
|
// In the CommandService definition
|
|
rpc {CommandName} ({CommandName}CommandRequest) returns ({CommandName}CommandResponse);
|
|
|
|
// Request message
|
|
message {CommandName}CommandRequest {
|
|
// fields matching command properties, snake_case, numbered sequentially
|
|
}
|
|
|
|
// Response message
|
|
message {CommandName}CommandResponse {
|
|
{result_type} result = 1; // omit if command has no return value
|
|
}
|
|
```
|
|
|
|
### 6. Summary
|
|
|
|
After creating everything, list what was created and where.
|