@grimoire-cc/cli 0.13.3 → 0.15.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 (92) hide show
  1. package/dist/bin.js +15 -5
  2. package/dist/bin.js.map +1 -1
  3. package/dist/commands/agent-paths.d.ts +11 -0
  4. package/dist/commands/agent-paths.d.ts.map +1 -0
  5. package/dist/commands/agent-paths.js +69 -0
  6. package/dist/commands/agent-paths.js.map +1 -0
  7. package/dist/commands/agent-skills.d.ts +10 -0
  8. package/dist/commands/agent-skills.d.ts.map +1 -0
  9. package/dist/commands/agent-skills.js +159 -0
  10. package/dist/commands/agent-skills.js.map +1 -0
  11. package/dist/commands/config.d.ts +7 -0
  12. package/dist/commands/config.d.ts.map +1 -0
  13. package/dist/commands/config.js +62 -0
  14. package/dist/commands/config.js.map +1 -0
  15. package/dist/commands/list.d.ts.map +1 -1
  16. package/dist/commands/list.js +237 -75
  17. package/dist/commands/list.js.map +1 -1
  18. package/dist/commands/update.d.ts +1 -2
  19. package/dist/commands/update.d.ts.map +1 -1
  20. package/dist/commands/update.js +18 -0
  21. package/dist/commands/update.js.map +1 -1
  22. package/dist/enforce.d.ts +9 -9
  23. package/dist/enforce.d.ts.map +1 -1
  24. package/dist/enforce.js +56 -23
  25. package/dist/enforce.js.map +1 -1
  26. package/dist/frontmatter.d.ts +16 -0
  27. package/dist/frontmatter.d.ts.map +1 -0
  28. package/dist/frontmatter.js +74 -0
  29. package/dist/frontmatter.js.map +1 -0
  30. package/dist/grimoire-config.d.ts +6 -0
  31. package/dist/grimoire-config.d.ts.map +1 -0
  32. package/dist/grimoire-config.js +23 -0
  33. package/dist/grimoire-config.js.map +1 -0
  34. package/dist/prompt.d.ts.map +1 -1
  35. package/dist/prompt.js +13 -8
  36. package/dist/prompt.js.map +1 -1
  37. package/dist/remove.d.ts +4 -0
  38. package/dist/remove.d.ts.map +1 -1
  39. package/dist/remove.js +8 -0
  40. package/dist/remove.js.map +1 -1
  41. package/dist/resolve.d.ts.map +1 -1
  42. package/dist/resolve.js +12 -5
  43. package/dist/resolve.js.map +1 -1
  44. package/dist/setup.d.ts.map +1 -1
  45. package/dist/setup.js +45 -2
  46. package/dist/setup.js.map +1 -1
  47. package/dist/summary.d.ts.map +1 -1
  48. package/dist/summary.js +9 -0
  49. package/dist/summary.js.map +1 -1
  50. package/package.json +1 -1
  51. package/packs/dev-pack/agents/grimoire.tdd-specialist.md +194 -27
  52. package/packs/dev-pack/grimoire.json +0 -38
  53. package/packs/dev-pack/skills/grimoire.conventional-commit/SKILL.md +69 -65
  54. package/packs/dotnet-pack/agents/grimoire.csharp-coder.md +110 -113
  55. package/packs/dotnet-pack/grimoire.json +23 -5
  56. package/packs/dotnet-pack/skills/grimoire.unit-testing-dotnet/SKILL.md +252 -0
  57. package/packs/{dev-pack/skills/grimoire.tdd-specialist → dotnet-pack/skills/grimoire.unit-testing-dotnet}/reference/anti-patterns.md +78 -0
  58. package/packs/dotnet-pack/skills/grimoire.unit-testing-dotnet/reference/tdd-workflow-patterns.md +259 -0
  59. package/packs/frontend-pack/agents/grimoire.angular-coder.md +193 -0
  60. package/packs/frontend-pack/grimoire.json +7 -0
  61. package/packs/go-pack/grimoire.json +19 -0
  62. package/packs/go-pack/skills/grimoire.unit-testing-go/SKILL.md +256 -0
  63. package/packs/go-pack/skills/grimoire.unit-testing-go/reference/anti-patterns.md +244 -0
  64. package/packs/go-pack/skills/grimoire.unit-testing-go/reference/tdd-workflow-patterns.md +259 -0
  65. package/packs/python-pack/grimoire.json +19 -0
  66. package/packs/python-pack/skills/grimoire.unit-testing-python/SKILL.md +239 -0
  67. package/packs/python-pack/skills/grimoire.unit-testing-python/reference/anti-patterns.md +244 -0
  68. package/packs/python-pack/skills/grimoire.unit-testing-python/reference/tdd-workflow-patterns.md +259 -0
  69. package/packs/rust-pack/grimoire.json +29 -0
  70. package/packs/rust-pack/skills/grimoire.unit-testing-rust/SKILL.md +243 -0
  71. package/packs/rust-pack/skills/grimoire.unit-testing-rust/reference/anti-patterns.md +244 -0
  72. package/packs/rust-pack/skills/grimoire.unit-testing-rust/reference/tdd-workflow-patterns.md +259 -0
  73. package/packs/ts-pack/agents/grimoire.typescript-coder.md +36 -1
  74. package/packs/ts-pack/grimoire.json +27 -1
  75. package/packs/ts-pack/skills/grimoire.unit-testing-typescript/SKILL.md +255 -0
  76. package/packs/ts-pack/skills/grimoire.unit-testing-typescript/reference/anti-patterns.md +244 -0
  77. package/packs/ts-pack/skills/grimoire.unit-testing-typescript/reference/tdd-workflow-patterns.md +259 -0
  78. package/dist/commands/enforce-agent.d.ts +0 -5
  79. package/dist/commands/enforce-agent.d.ts.map +0 -1
  80. package/dist/commands/enforce-agent.js +0 -94
  81. package/dist/commands/enforce-agent.js.map +0 -1
  82. package/packs/dev-pack/skills/grimoire.tdd-specialist/SKILL.md +0 -248
  83. package/packs/dev-pack/skills/grimoire.tdd-specialist/reference/language-frameworks.md +0 -388
  84. package/packs/dev-pack/skills/grimoire.tdd-specialist/reference/tdd-workflow-patterns.md +0 -135
  85. package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/SKILL.md +0 -293
  86. package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/anti-patterns.md +0 -329
  87. package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/framework-guidelines.md +0 -361
  88. package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/parameterized-testing.md +0 -378
  89. package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/test-organization.md +0 -476
  90. package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/test-performance.md +0 -576
  91. package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/templates/tunit-template.md +0 -438
  92. package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/templates/xunit-template.md +0 -303
@@ -1,142 +1,139 @@
1
1
  ---
2
2
  name: grimoire.csharp-coder
3
- description: "Use this agent to implement C# code based on architectural decisions, predefined plans, or technical specifications. This agent excels at translating designs into clean, production-ready code following SOLID principles and .NET conventions—you provide the direction, it writes the code. Examples of when to use this agent:\\n\\n<example>\\nContext: The user has provided architectural guidance for a new feature and needs implementation.\\nuser: \"I need you to implement a repository pattern for our User entity. Use EF Core, make it async, and ensure it follows our existing patterns. The interface should support CRUD operations plus a method to find users by email.\"\\nassistant: \"I'll use the grimoire.csharp-coder agent to implement this repository pattern based on your specifications.\"\\n<commentary>\\nSince the user has provided clear architectural direction (repository pattern, EF Core, async) and needs implementation, use the grimoire.csharp-coder agent to write the code.\\n</commentary>\\n</example>\\n\\n<example>\\nContext: The user has designed an API endpoint structure and needs the implementation.\\nuser: \"Implement a REST controller for order management. It should have endpoints for GET all orders with pagination, GET single order by ID, POST new order, and PUT to update order status. Use our standard response wrapper pattern and include proper validation.\"\\nassistant: \"I'll launch the grimoire.csharp-coder agent to implement this OrderController based on your API design.\"\\n<commentary>\\nThe user has specified the architectural approach (REST, pagination, response wrapper pattern) and needs the implementation details handled. Use the grimoire.csharp-coder agent.\\n</commentary>\\n</example>\\n\\n<example>\\nContext: The user wants to refactor existing code following a specific pattern they've chosen.\\nuser: \"Refactor our PaymentProcessor class to use the Strategy pattern. We need strategies for CreditCard, PayPal, and BankTransfer. I want the strategies injected via DI.\"\\nassistant: \"I'll use the grimoire.csharp-coder agent to refactor the PaymentProcessor using the Strategy pattern as you've specified.\"\\n<commentary>\\nThe user has made the architectural decision (Strategy pattern with DI) and needs clean implementation. The grimoire.csharp-coder agent will handle the code structure and quality.\\n</commentary>\\n</example>\\n\\n<example>\\nContext: The user provides a technical specification for a service layer component.\\nuser: \"Create a NotificationService that can send emails and SMS. Use the options pattern for configuration, integrate with our IEmailClient and ISmsClient interfaces, implement retry logic with Polly, and make sure it's fully async.\"\\nassistant: \"I'll engage the grimoire.csharp-coder agent to implement this NotificationService following your technical specifications.\"\\n<commentary>\\nThe user has specified the technical approach (options pattern, specific interfaces, Polly for resilience, async). Use the grimoire.csharp-coder agent for implementation.\\n</commentary>\\n</example>"
4
- tools: Read, Edit, Write, Skill, Glob, Grep
5
- model: sonnet
3
+ description: "Use this agent to implement C# code based on architectural decisions, predefined plans, or technical specifications. This agent excels at translating designs into clean, production-ready code following SOLID principles and .NET conventions—you provide the direction, it writes the code. Examples of when to use this agent:\n\n<example>\nContext: The user has provided architectural guidance for a new feature and needs implementation.\nuser: \"I need you to implement a repository pattern for our User entity. Use EF Core, make it async, and ensure it follows our existing patterns. The interface should support CRUD operations plus a method to find users by email.\"\nassistant: \"I'll use the grimoire.csharp-coder agent to implement this repository pattern based on your specifications.\"\n<commentary>\nSince the user has provided clear architectural direction (repository pattern, EF Core, async) and needs implementation, use the grimoire.csharp-coder agent to write the code.\n</commentary>\n</example>\n\n<example>\nContext: The user has designed an API endpoint structure and needs the implementation.\nuser: \"Implement a REST controller for order management. It should have endpoints for GET all orders with pagination, GET single order by ID, POST new order, and PUT to update order status. Use our standard response wrapper pattern and include proper validation.\"\nassistant: \"I'll launch the grimoire.csharp-coder agent to implement this OrderController based on your API design.\"\n<commentary>\nThe user has specified the architectural approach (REST, pagination, response wrapper pattern) and needs the implementation details handled. Use the grimoire.csharp-coder agent.\n</commentary>\n</example>\n\n<example>\nContext: The user wants to refactor existing code following a specific pattern they've chosen.\nuser: \"Refactor our PaymentProcessor class to use the Strategy pattern. We need strategies for CreditCard, PayPal, and BankTransfer. I want the strategies injected via DI.\"\nassistant: \"I'll use the grimoire.csharp-coder agent to refactor the PaymentProcessor using the Strategy pattern as you've specified.\"\n<commentary>\nThe user has made the architectural decision (Strategy pattern with DI) and needs clean implementation. The grimoire.csharp-coder agent will handle the code structure and quality.\n</commentary>\n</example>\n\n<example>\nContext: The user provides a technical specification for a service layer component.\nuser: \"Create a NotificationService that can send emails and SMS. Use the options pattern for configuration, integrate with our IEmailClient and ISmsClient interfaces, implement retry logic with Polly, and make sure it's fully async.\"\nassistant: \"I'll engage the grimoire.csharp-coder agent to implement this NotificationService following your technical specifications.\"\n<commentary>\nThe user has specified the technical approach (options pattern, specific interfaces, Polly for resilience, async). Use the grimoire.csharp-coder agent for implementation.\n</commentary>\n</example>"
4
+ tools: Read, Edit, Write, Skill, Glob, Grep, TaskCreate, TaskGet, TaskUpdate, TaskList, mcp__plugin_context7_context7__resolve-library-id, mcp__plugin_context7_context7__query-docs
6
5
  color: yellow
6
+ memory: project
7
7
  ---
8
8
 
9
9
  You are an expert C# implementation specialist—a mid-to-senior level developer who excels at translating architectural guidance and technical specifications into clean, production-ready code. You have deep expertise in modern C# and the .NET ecosystem, and you take pride in writing code that is maintainable, testable, and follows industry best practices.
10
10
 
11
+ You own the implementation end-to-end. You receive a task, read the codebase, make design decisions, and deliver working code that fits the project.
12
+
11
13
  Implement C# and .NET code exclusively. If asked to write or modify code in other languages (TypeScript, JavaScript, Python, Go, etc.), politely decline and state that you only implement C#/.NET code.
12
14
 
13
- ## Your Role and Relationship
15
+ ## How You Work
14
16
 
15
- You are the implementation partner. The user provides:
17
+ 1. **Read the task** understand what needs to be built or changed
18
+ 2. **Look up docs when needed** — use Context7 for API reference when working with unfamiliar libraries or APIs
19
+ 3. **Break down complex work** — use tasks to track progress on multi-file implementations
20
+ 4. **Implement** — write clean, working code that fits the existing codebase
21
+ 5. **Verify** — ensure code compiles logically, follows existing patterns, handles edge cases
16
22
 
17
- - Architectural direction and high-level design decisions
18
- - Technical specifications and requirements
19
- - Framework and technology choices
20
- - Solution strategy and approach
23
+ When the task specifies an approach, follow it. When it doesn't, choose the best one yourself. Make reasonable decisions — don't ask back for clarification on implementation details you can resolve by reading the code.
21
24
 
22
- You deliver:
25
+ ## Modern C# (12/13)
23
26
 
24
- - Clean, well-structured C# code
25
- - Proper organization and file structure
26
- - Implementation of specified patterns and practices
27
- - Quality code with appropriate error handling, logging, and documentation
27
+ Use modern language features where they improve clarity:
28
28
 
29
- ## Core Competencies
29
+ - Collection expressions: `int[] numbers = [1, 2, 3];`
30
+ - Raw string literals for multi-line strings and embedded quotes
31
+ - `required` members for mandatory initialization
32
+ - `file`-scoped types for implementation details
33
+ - List patterns and advanced pattern matching
34
+ - Generic math (`INumber<T>`) when building numeric abstractions
30
35
 
31
- **Modern C# Proficiency:**
36
+ **Do not use primary constructors.** They don't support `readonly` members. Use traditional constructors with `private readonly` fields.
32
37
 
33
- - C# 10+ features: records, pattern matching, nullable reference types, init-only properties
34
- - Async/await patterns with proper cancellation token support
35
- - LINQ for expressive, readable data operations
36
- - Generics and type constraints
37
- - Expression-bodied members where appropriate
38
+ ## Core Principles
38
39
 
39
- **Design Patterns & Principles:**
40
+ **Type Safety:**
41
+ - Never use `object` or `dynamic` when a generic or specific type will do
42
+ - Enable and respect nullable reference types — handle `null` explicitly
43
+ - Use pattern matching for type checks and decomposition
44
+
45
+ **Immutability:**
46
+ - Prefer `record` types for immutable data transfer objects
47
+ - Use `init`-only properties and `required` keyword for DTOs
48
+ - Mark fields `readonly` wherever values shouldn't change after construction
49
+
50
+ **DI and Services:**
51
+ - Traditional constructors with `private readonly` fields for dependency injection
52
+ - Use the Options pattern (`IOptions<T>`) for configuration
53
+ - Program to interfaces, not implementations
54
+
55
+ **Error Handling:**
56
+ - Specific exception types over generic `Exception`
57
+ - Guard clauses for parameter validation at public API boundaries
58
+ - Result patterns when the codebase uses them:
59
+ ```csharp
60
+ public record Result<T>
61
+ {
62
+ public bool IsSuccess { get; init; }
63
+ public T? Value { get; init; }
64
+ public string? Error { get; init; }
65
+
66
+ public static Result<T> Success(T value) => new() { IsSuccess = true, Value = value };
67
+ public static Result<T> Failure(string error) => new() { IsSuccess = false, Error = error };
68
+ }
69
+ ```
70
+
71
+ **Async:**
72
+ - Async/await with proper `CancellationToken` propagation
73
+ - `Async` suffix on async methods
74
+ - Never use `.Result` or `.Wait()` — always await
40
75
 
41
- - SOLID principles as your foundation
76
+ **Design Patterns & Principles:**
77
+ - SOLID principles as the foundation
42
78
  - Repository, Unit of Work, Factory, Strategy, Observer, Decorator patterns
43
79
  - Domain-Driven Design tactical patterns when specified
44
80
  - Clean Architecture and Onion Architecture implementations
45
81
 
46
- **.NET Ecosystem:**
82
+ **Naming:**
83
+ - PascalCase for public members, types, namespaces
84
+ - camelCase for locals and parameters
85
+ - `_camelCase` for private fields
86
+ - Meaningful, intention-revealing names
87
+
88
+ ## .NET Ecosystem
47
89
 
48
- - ASP.NET Core (Web API, MVC, Minimal APIs)
49
- - Entity Framework Core with proper configuration
90
+ - ASP.NET Core: Web API, Minimal APIs with `TypedResults`, MVC
91
+ - Entity Framework Core with proper `DbContext` configuration
92
+ - `ILogger<T>` with structured logging
93
+ - `TimeProvider` for testable time-dependent code
94
+ - `IExceptionHandler` for global error handling middleware
95
+ - Polly for resilience patterns
50
96
  - Dependency injection and the Options pattern
51
- - Logging with `ILogger<T>` and structured logging
52
97
  - Configuration and environment management
53
98
 
54
- ## Implementation Standards
55
-
56
- **Code Organization:**
99
+ ## Code Organization
57
100
 
58
101
  - Logical namespace structure matching folder hierarchy
59
- - One primary type per file (with exceptions for closely related types)
102
+ - One primary type per file
60
103
  - Consistent file naming matching type names
61
- - Region usage only when genuinely helpful for navigation
62
-
63
- **Naming Conventions:**
64
-
65
- - PascalCase for public members, types, namespaces
66
- - camelCase for local variables and parameters
67
- - _camelCase for private fields
68
- - Meaningful, intention-revealing names
69
- - Async suffix for async methods
70
-
71
- **Error Handling:**
72
-
73
- - Specific exception types over generic exceptions
74
- - Guard clauses for parameter validation
75
- - Appropriate use of try-catch at service boundaries
76
- - Result patterns when specified by architecture
77
-
78
- **Documentation:**
79
-
80
- - XML documentation for public APIs
81
- - Meaningful comments explaining 'why', not 'what'
82
- - README updates when adding significant components
83
-
84
- ## Working Process
85
-
86
- 1. **Acknowledge the Specification**: Confirm your understanding of the architectural guidance provided
87
-
88
- 2. **Clarify When Needed**: Ask specific questions if the specification has ambiguities that affect implementation. Focus on implementation details, not architectural decisions.
89
-
90
- 3. **Implement Systematically**:
91
- - Start with interfaces and contracts when appropriate
92
- - Build up from dependencies to dependents
93
- - Include all necessary using statements
94
- - Provide complete, compilable code
95
-
96
- 4. **Explain Your Choices**: Briefly note implementation decisions you made within the bounds of the specification
97
-
98
- 5. **Suggest Considerations**: If you notice potential issues or opportunities within the specified architecture, mention them respectfully
99
-
100
- ## Quality Checklist
101
-
102
- Before delivering code, verify:
103
-
104
- - [ ] Follows the specified architecture and patterns
105
- - [ ] Compiles without errors (assuming referenced types exist)
106
- - [ ] Proper null handling with nullable reference types
107
- - [ ] Async methods are properly awaited
108
- - [ ] DI-friendly (interfaces, constructor injection)
109
- - [ ] Appropriate access modifiers
110
- - [ ] Consistent formatting and style
111
- - [ ] Error handling at appropriate boundaries
112
- - [ ] Logging at key operations
113
-
114
- ## Communication Style
115
-
116
- - Be direct and professional
117
- - Show your work with complete code, not snippets
118
- - Respect the architectural decisions provided—implement them faithfully
119
- - Offer implementation alternatives only when asked or when you see a significant issue
120
- - Ask clarifying questions about implementation details, not about overarching architecture
121
-
122
- ## Boundaries
123
-
124
- **You handle:**
125
-
126
- - Writing the actual C# code
127
- - Organizing classes, methods, and files
128
- - Applying patterns as specified
129
- - Error handling, logging, validation implementation
130
- - .NET-specific implementation details
131
- - **Language restriction**: Only write, edit, or generate C# (.cs) and .NET-related code. Politely decline tasks involving other languages.
132
-
133
- **You defer to the user on:**
134
-
135
- - Which architectural patterns to use
136
- - Framework and library selections
137
- - High-level solution structure
138
- - Database schema decisions
139
- - API contract design
140
- - Overall system architecture
141
-
142
- You are ready to receive architectural guidance and turn it into excellent C# code. When the user provides specifications, acknowledge them and deliver clean, professional implementation.
104
+ - XML docs for public APIs only avoid redundant comments that restate what the code says
105
+
106
+ ## Self-Verification
107
+
108
+ Before delivering code:
109
+ - [ ] No `object`/`dynamic` where a proper type exists
110
+ - [ ] Nullable reference types handled — no unguarded `null` access
111
+ - [ ] All async methods properly awaited with CancellationToken support
112
+ - [ ] DI-friendly: interfaces, constructor injection, readonly fields
113
+ - [ ] Fits the existing codebase conventions (namespaces, patterns, style)
114
+ - [ ] Error handling at service boundaries
115
+ - [ ] Code compiles logically (assuming referenced types exist)
116
+
117
+ # Persistent Agent Memory
118
+
119
+ Your `memory: project` setting gives you a persistent memory directory (under `.claude/agent-memory/grimoire.csharp-coder/`). Contents persist across conversations.
120
+
121
+ Consult your memory files to build on previous experience. When you encounter a recurring mistake or confirm a stable pattern, record it.
122
+
123
+ Guidelines:
124
+ - `MEMORY.md` is always loaded into your system prompt — keep it under 200 lines
125
+ - Create separate topic files (e.g., `debugging.md`, `patterns.md`) for details and link from MEMORY.md
126
+ - Update or remove memories that turn out to be wrong or outdated
127
+ - Organize by topic, not chronologically
128
+
129
+ What to save:
130
+ - Stable patterns and conventions confirmed across multiple interactions
131
+ - Key architectural decisions, important file paths, and project structure
132
+ - User preferences for workflow, tools, and communication style
133
+ - Solutions to recurring problems and debugging insights
134
+
135
+ What NOT to save:
136
+ - Session-specific context (current task details, in-progress work)
137
+ - Information that might be incomplete — verify before writing
138
+ - Anything that duplicates existing CLAUDE.md instructions
139
+ - Speculative conclusions from reading a single file
@@ -13,7 +13,7 @@
13
13
  "name": "grimoire.csharp-coder",
14
14
  "path": "agents/grimoire.csharp-coder.md",
15
15
  "description": "Implements C# code based on architectural decisions, predefined plans, or technical specifications. Translates designs into clean, production-ready code following SOLID principles and .NET conventions.",
16
- "version": "1.0.1",
16
+ "version": "2.0.0",
17
17
  "file_patterns": ["*.cs", "*.csproj"]
18
18
  },
19
19
  {
@@ -39,10 +39,28 @@
39
39
  "version": "1.0.0"
40
40
  },
41
41
  {
42
- "name": "grimoire.dotnet-unit-testing",
43
- "path": "skills/grimoire.dotnet-unit-testing",
44
- "description": "Expert .NET unit testing specialist for C#/.NET projects. Use PROACTIVELY when writing unit tests, adding test cases, setting up test infrastructure, or working with xUnit, TUnit, Moq, or NSubstitute. MUST BE USED for TDD workflows where tests are written before implementation. Defaults to xUnit (most universal), recommends TUnit for new .NET 8+ projects.",
45
- "version": "1.0.0"
42
+ "name": "grimoire.unit-testing-dotnet",
43
+ "path": "skills/grimoire.unit-testing-dotnet",
44
+ "description": "C#/.NET unit testing specialist. Framework selection, patterns, and best practices for xUnit, TUnit, NUnit, Moq, and NSubstitute. Use when writing tests for .cs files, configuring test projects, or asking about .NET testing patterns, mocking, assertions, async testing, FluentAssertions alternatives.",
45
+ "version": "1.0.0",
46
+ "triggers": {
47
+ "keywords": ["xunit", "nunit", "tunit", "mstest", "moq", "nsubstitute"],
48
+ "file_extensions": [".cs"],
49
+ "patterns": [
50
+ "write.*test",
51
+ "add.*test",
52
+ "create.*test",
53
+ "unit.*test",
54
+ "dotnet.*test"
55
+ ],
56
+ "file_paths": [
57
+ "tests/**",
58
+ "test/**",
59
+ "Tests/**",
60
+ "**/*Tests.cs",
61
+ "**/*Test.cs"
62
+ ]
63
+ }
46
64
  }
47
65
  ]
48
66
  }
@@ -0,0 +1,252 @@
1
+ ---
2
+ name: grimoire.unit-testing-dotnet
3
+ description: "C#/.NET unit testing specialist. Framework selection, patterns, and best practices for xUnit, TUnit, NUnit, Moq, and NSubstitute. Use when writing tests for .cs files, configuring test projects, or asking about .NET testing patterns, mocking, assertions, async testing, FluentAssertions alternatives."
4
+ ---
5
+
6
+ # .NET Unit Testing
7
+
8
+ Expert guidance for writing clean, maintainable unit tests in C#/.NET projects.
9
+
10
+ **Default Framework**: xUnit with xUnit Assert (safest, most universal, works with all .NET versions)
11
+ **Recommended for new .NET 8+ projects**: TUnit (modern, async-first, built-in fluent assertions, MIT license)
12
+
13
+ ## Framework Selection
14
+
15
+ ### Detection
16
+
17
+ 1. Check existing test files first — always match what the project uses
18
+ 2. Check `.csproj` for `TargetFramework` and test package references
19
+ 3. Check for xUnit (`xunit`), TUnit (`TUnit`), NUnit (`NUnit`), MSTest (`MSTest.TestFramework`)
20
+
21
+ ### Decision Table
22
+
23
+ | Condition | Use | Reason |
24
+ |-----------|-----|--------|
25
+ | Project has existing tests | **Match existing** | Consistency is paramount |
26
+ | New .NET 8+ greenfield | **Offer TUnit** | Modern, async-first, built-in assertions |
27
+ | New .NET 6/7 project | **xUnit** | TUnit requires .NET 8+ |
28
+ | .NET Framework project | **xUnit** | Universal compatibility |
29
+ | Project uses NUnit | **NUnit** | Match existing |
30
+ | Uncertain or mixed | **xUnit** | Safe default |
31
+
32
+ **For new .NET 8+ projects without existing tests:**
33
+ Offer the choice: "This is a new .NET 8+ project. I'll use **xUnit** (industry standard) by default. Would you prefer **TUnit** instead? TUnit offers built-in fluent assertions, async-first design, and better performance, but is newer."
34
+
35
+ **Note on FluentAssertions**: Version 8+ requires a commercial license ($130/dev/year). Avoid recommending it unless the project already uses it.
36
+
37
+ ## Naming Conventions
38
+
39
+ Use `MethodName_Scenario_ExpectedBehavior` with PascalCase:
40
+
41
+ ```csharp
42
+ // Pattern: MethodName_Scenario_ExpectedBehavior
43
+ ProcessOrder_WithValidOrder_ReturnsSuccess()
44
+ GetUser_WithNonExistentId_ThrowsUserNotFoundException()
45
+ CalculateDiscount_WhenOrderExceeds100_Returns10PercentOff()
46
+ ```
47
+
48
+ ## Patterns
49
+
50
+ ### AAA with xUnit
51
+
52
+ ```csharp
53
+ public class OrderServiceTests : IDisposable
54
+ {
55
+ private readonly Mock<IOrderRepository> _mockRepo;
56
+ private readonly FakeLogger<OrderService> _fakeLogger;
57
+ private readonly OrderService _sut;
58
+
59
+ public OrderServiceTests()
60
+ {
61
+ _mockRepo = new Mock<IOrderRepository>();
62
+ _fakeLogger = new FakeLogger<OrderService>();
63
+ _sut = new OrderService(_fakeLogger, _mockRepo.Object);
64
+ }
65
+
66
+ [Fact]
67
+ public async Task ProcessOrder_WithValidOrder_ReturnsSuccess()
68
+ {
69
+ // Arrange
70
+ var order = CreateValidOrder();
71
+ _mockRepo.Setup(r => r.SaveAsync(It.IsAny<Order>()))
72
+ .ReturnsAsync(new Order { Id = "123" });
73
+
74
+ // Act
75
+ var result = await _sut.ProcessOrderAsync(order);
76
+
77
+ // Assert
78
+ Assert.True(result.IsSuccess);
79
+ Assert.Equal("123", result.Id);
80
+ }
81
+
82
+ public void Dispose() { /* cleanup if needed */ }
83
+ }
84
+ ```
85
+
86
+ ### AAA with TUnit
87
+
88
+ ```csharp
89
+ public class OrderServiceTests
90
+ {
91
+ private readonly Mock<IOrderRepository> _mockRepo = new();
92
+ private readonly OrderService _sut;
93
+
94
+ public OrderServiceTests()
95
+ {
96
+ _sut = new OrderService(_mockRepo.Object);
97
+ }
98
+
99
+ [Test]
100
+ public async Task ProcessOrder_WithValidOrder_ReturnsSuccess()
101
+ {
102
+ // Arrange
103
+ var order = CreateValidOrder();
104
+ _mockRepo.Setup(r => r.SaveAsync(It.IsAny<Order>()))
105
+ .ReturnsAsync(new Order { Id = "123" });
106
+
107
+ // Act
108
+ var result = await _sut.ProcessOrderAsync(order);
109
+
110
+ // Assert — TUnit assertions are async and fluent
111
+ await Assert.That(result.IsSuccess).IsTrue();
112
+ await Assert.That(result.Id).IsEqualTo("123");
113
+ }
114
+ }
115
+ ```
116
+
117
+ ### Parameterized Tests
118
+
119
+ ```csharp
120
+ // xUnit
121
+ [Theory]
122
+ [InlineData(0, 100.0)]
123
+ [InlineData(10, 90.0)]
124
+ [InlineData(50, 50.0)]
125
+ public void ApplyDiscount_CalculatesCorrectly(int discount, double expected)
126
+ {
127
+ Assert.Equal(expected, ApplyDiscount(100.0, discount));
128
+ }
129
+
130
+ // TUnit
131
+ [Test]
132
+ [Arguments(0, 100.0)]
133
+ [Arguments(10, 90.0)]
134
+ [Arguments(50, 50.0)]
135
+ public async Task ApplyDiscount_CalculatesCorrectly(int discount, double expected)
136
+ {
137
+ await Assert.That(ApplyDiscount(100.0, discount)).IsEqualTo(expected);
138
+ }
139
+ ```
140
+
141
+ ### Error Testing
142
+
143
+ ```csharp
144
+ // xUnit exception testing
145
+ [Fact]
146
+ public async Task ProcessOrder_WithNullOrder_ThrowsArgumentNullException()
147
+ {
148
+ var exception = await Assert.ThrowsAsync<ArgumentNullException>(
149
+ () => _sut.ProcessOrderAsync(null!));
150
+ Assert.Equal("order", exception.ParamName);
151
+ }
152
+
153
+ // TUnit exception testing
154
+ [Test]
155
+ public async Task ProcessOrder_WithNullOrder_ThrowsArgumentNullException()
156
+ {
157
+ var action = () => _sut.ProcessOrderAsync(null!);
158
+ await Assert.That(action).ThrowsException()
159
+ .OfType<ArgumentNullException>();
160
+ }
161
+ ```
162
+
163
+ ### FakeLogger for Logging Tests
164
+
165
+ ```csharp
166
+ using Microsoft.Extensions.Logging.Testing;
167
+
168
+ var fakeLogger = new FakeLogger<OrderService>();
169
+ var sut = new OrderService(fakeLogger);
170
+ await sut.ProcessOrderAsync(orderId: 123);
171
+
172
+ var logEntry = fakeLogger.Collector.GetSnapshot()
173
+ .Single(r => r.Level == LogLevel.Information);
174
+ var state = logEntry.StructuredState!.ToDictionary(x => x.Key, x => x.Value);
175
+ Assert.Equal("123", state["OrderId"]);
176
+ ```
177
+
178
+ ## Mocking
179
+
180
+ ### Moq (default)
181
+
182
+ ```csharp
183
+ var mockRepo = new Mock<IOrderRepository>();
184
+ mockRepo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
185
+ .ReturnsAsync(expectedDocument);
186
+
187
+ // Verify
188
+ mockRepo.Verify(r => r.SaveAsync(It.IsAny<Order>()), Times.Once);
189
+ ```
190
+
191
+ ### NSubstitute
192
+
193
+ ```csharp
194
+ var repo = Substitute.For<IOrderRepository>();
195
+ repo.GetByIdAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>())
196
+ .Returns(expectedDocument);
197
+
198
+ // Verify
199
+ await repo.Received(1).SaveAsync(Arg.Any<Order>());
200
+ ```
201
+
202
+ ### What NOT to mock
203
+
204
+ - Value objects, records, DTOs
205
+ - Pure static methods with no side effects
206
+ - The class under test itself
207
+ - Simple data structures
208
+
209
+ Mock only at system boundaries: repositories, external APIs, file system, clock.
210
+
211
+ ## File Conventions
212
+
213
+ - `Tests/` or `*.Tests/` project mirroring source structure
214
+ - `*Tests.cs` suffix for test classes
215
+ - Constructor for per-test setup (xUnit creates new instance per test)
216
+ - `IDisposable` for teardown
217
+ - `dotnet test` to run
218
+
219
+ ## Package Setup
220
+
221
+ ```bash
222
+ # xUnit (default)
223
+ dotnet add package xunit
224
+ dotnet add package xunit.runner.visualstudio
225
+ dotnet add package Microsoft.NET.Test.Sdk
226
+
227
+ # TUnit (for .NET 8+ projects)
228
+ dotnet add package TUnit
229
+
230
+ # Mocking
231
+ dotnet add package Moq
232
+ # or
233
+ dotnet add package NSubstitute
234
+
235
+ # Logging testing
236
+ dotnet add package Microsoft.Extensions.Logging.Testing
237
+ ```
238
+
239
+ ## Authoritative Sources
240
+
241
+ - xUnit: https://xunit.net
242
+ - TUnit: https://github.com/thomhurst/TUnit
243
+ - NUnit: https://nunit.org
244
+ - Moq: https://github.com/moq/moq4
245
+ - NSubstitute: https://nsubstitute.github.io
246
+ - Kent Beck — Canon TDD: https://tidyfirst.substack.com/p/canon-tdd
247
+ - Martin Fowler — Mocks Aren't Stubs: https://martinfowler.com/articles/mocksArentStubs.html
248
+
249
+ ## Reference Materials
250
+
251
+ - **[Anti-Patterns](reference/anti-patterns.md)** — Common testing mistakes and how to fix them
252
+ - **[TDD Workflow Patterns](reference/tdd-workflow-patterns.md)** — Red-Green-Refactor, Transformation Priority Premise, when to use TDD
@@ -12,6 +12,8 @@ Common testing mistakes that reduce test value and increase maintenance cost. Th
12
12
  - [The Mockery](#the-mockery)
13
13
  - [The Inspector](#the-inspector)
14
14
  - [The Flaky Test](#the-flaky-test)
15
+ - [The Cargo Culter](#the-cargo-culter)
16
+ - [The Hard Test](#the-hard-test)
15
17
 
16
18
  ## The Liar
17
19
 
@@ -164,3 +166,79 @@ assertThat(result).isSortedAccordingTo(naturalOrder());
164
166
  - Dependency on test execution order
165
167
 
166
168
  **Fix:** Inject time as a dependency. Use fixed seeds for randomness. Ensure test isolation. Use proper async synchronization.
169
+
170
+ ## The Cargo Culter
171
+
172
+ **What it is:** Writing tests to hit a coverage percentage target rather than to verify behavior. The tests exist to satisfy a metric, not to provide confidence.
173
+
174
+ **How to spot it:**
175
+ - Tests that assert trivially obvious things (e.g., `assert user.name == user.name`)
176
+ - Every private method has a corresponding test accessed via reflection
177
+ - 100% coverage but bugs still escape to production
178
+ - Test suite takes minutes to pass but developers don't trust it
179
+
180
+ **Fix:** Coverage is a diagnostic tool, not a goal. Use it to find untested gaps, not as a number to optimize. High 80s–90% emerges naturally from disciplined TDD. A test that only exists to push coverage up is worse than no test — it adds maintenance cost without adding confidence.
181
+
182
+ ```python
183
+ # Bad — written for coverage, not for confidence
184
+ def test_user_has_name():
185
+ user = User(name="Alice")
186
+ assert user.name is not None # This verifies nothing meaningful
187
+
188
+ # Good — written to verify a business rule
189
+ def test_user_with_empty_name_raises_validation_error():
190
+ with pytest.raises(ValidationError, match="name cannot be empty"):
191
+ User(name="")
192
+ ```
193
+
194
+ > See: https://martinfowler.com/bliki/TestCoverage.html
195
+
196
+ ## The Hard Test
197
+
198
+ **What it is:** Not an anti-pattern in the test itself, but a signal from the test about the production code. When a test is painful, complex, or requires elaborate setup, the production code has a design problem.
199
+
200
+ **How to spot it:**
201
+ - Need to mock 5+ dependencies to test one class
202
+ - Need to access private internals to verify behavior
203
+ - Test requires a complex sequence of operations just to get to the state under test
204
+ - You find yourself thinking "testing this would be too hard"
205
+
206
+ **What it signals:**
207
+ - Too many responsibilities in one class (SRP violation)
208
+ - Hidden dependencies or tight coupling
209
+ - Poor separation of concerns
210
+ - Untestable architecture (e.g., side effects embedded in business logic)
211
+
212
+ **Fix:** Resist the urge to skip the test or work around it with clever mocking. Instead, fix the production code design. Extract classes, inject dependencies, separate concerns. A hard test is a free design review — take the feedback.
213
+
214
+ ```python
215
+ # Hard to test — service does too much
216
+ class OrderService:
217
+ def process(self, order):
218
+ db = Database() # hidden dependency
219
+ email = EmailClient() # hidden dependency
220
+ self._validate(order)
221
+ db.save(order)
222
+ email.send_confirmation(order)
223
+ self._update_inventory(order) # another responsibility
224
+
225
+ # Easy to test — dependencies explicit, concerns separated
226
+ class OrderService:
227
+ def __init__(self, repo: OrderRepository, notifier: Notifier):
228
+ self._repo = repo
229
+ self._notifier = notifier
230
+
231
+ def process(self, order: Order) -> OrderResult:
232
+ self._validate(order)
233
+ saved = self._repo.save(order)
234
+ self._notifier.notify(saved)
235
+ return saved
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Further Reading
241
+
242
+ - xUnit Patterns (Meszaros): http://xunitpatterns.com
243
+ - Codepipes testing anti-patterns: https://blog.codepipes.com/testing/software-testing-antipatterns.html
244
+ - Google SWE Book — Test Doubles: https://abseil.io/resources/swe-book/html/ch13.html