ma-agents 2.20.3 → 2.22.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 (149) hide show
  1. package/.opencode/skills/.ma-agents.json +241 -0
  2. package/.opencode/skills/MANIFEST.yaml +254 -0
  3. package/.opencode/skills/ai-audit-trail/SKILL.md +23 -0
  4. package/.opencode/skills/auto-bug-detection/SKILL.md +169 -0
  5. package/.opencode/skills/cmake-best-practices/SKILL.md +64 -0
  6. package/.opencode/skills/cmake-best-practices/examples/cmake.md +59 -0
  7. package/.opencode/skills/code-documentation/SKILL.md +57 -0
  8. package/.opencode/skills/code-documentation/examples/cpp.md +29 -0
  9. package/.opencode/skills/code-documentation/examples/csharp.md +28 -0
  10. package/.opencode/skills/code-documentation/examples/javascript_typescript.md +28 -0
  11. package/.opencode/skills/code-documentation/examples/python.md +57 -0
  12. package/.opencode/skills/code-review/SKILL.md +43 -0
  13. package/.opencode/skills/commit-message/SKILL.md +79 -0
  14. package/.opencode/skills/cpp-best-practices/SKILL.md +234 -0
  15. package/.opencode/skills/cpp-best-practices/examples/modern-idioms.md +189 -0
  16. package/.opencode/skills/cpp-best-practices/examples/naming-and-organization.md +102 -0
  17. package/.opencode/skills/cpp-concurrency-safety/SKILL.md +60 -0
  18. package/.opencode/skills/cpp-concurrency-safety/examples/concurrency.md +73 -0
  19. package/.opencode/skills/cpp-const-correctness/SKILL.md +63 -0
  20. package/.opencode/skills/cpp-const-correctness/examples/const_correctness.md +54 -0
  21. package/.opencode/skills/cpp-memory-handling/SKILL.md +42 -0
  22. package/.opencode/skills/cpp-memory-handling/examples/modern-cpp.md +49 -0
  23. package/.opencode/skills/cpp-memory-handling/examples/smart-pointers.md +46 -0
  24. package/.opencode/skills/cpp-modern-composition/SKILL.md +64 -0
  25. package/.opencode/skills/cpp-modern-composition/examples/composition.md +51 -0
  26. package/.opencode/skills/cpp-robust-interfaces/SKILL.md +55 -0
  27. package/.opencode/skills/cpp-robust-interfaces/examples/interfaces.md +56 -0
  28. package/.opencode/skills/create-hardened-docker-skill/SKILL.md +637 -0
  29. package/.opencode/skills/create-hardened-docker-skill/scripts/create-all.sh +489 -0
  30. package/.opencode/skills/csharp-best-practices/SKILL.md +278 -0
  31. package/.opencode/skills/docker-hardening-verification/SKILL.md +28 -0
  32. package/.opencode/skills/docker-hardening-verification/scripts/verify-hardening.sh +39 -0
  33. package/.opencode/skills/docker-image-signing/SKILL.md +28 -0
  34. package/.opencode/skills/docker-image-signing/scripts/sign-image.sh +33 -0
  35. package/.opencode/skills/document-revision-history/SKILL.md +104 -0
  36. package/.opencode/skills/git-workflow-skill/SKILL.md +194 -0
  37. package/.opencode/skills/git-workflow-skill/hooks/commit-msg +61 -0
  38. package/.opencode/skills/git-workflow-skill/hooks/pre-commit +38 -0
  39. package/.opencode/skills/git-workflow-skill/hooks/prepare-commit-msg +56 -0
  40. package/.opencode/skills/git-workflow-skill/scripts/finish-feature.sh +192 -0
  41. package/.opencode/skills/git-workflow-skill/scripts/install-hooks.sh +55 -0
  42. package/.opencode/skills/git-workflow-skill/scripts/start-feature.sh +110 -0
  43. package/.opencode/skills/git-workflow-skill/scripts/validate-workflow.sh +229 -0
  44. package/.opencode/skills/js-ts-dependency-mgmt/SKILL.md +49 -0
  45. package/.opencode/skills/js-ts-dependency-mgmt/examples/dependency_mgmt.md +60 -0
  46. package/.opencode/skills/js-ts-security-skill/SKILL.md +64 -0
  47. package/.opencode/skills/js-ts-security-skill/scripts/verify-security.sh +136 -0
  48. package/.opencode/skills/logging-best-practices/SKILL.md +50 -0
  49. package/.opencode/skills/logging-best-practices/examples/cpp.md +36 -0
  50. package/.opencode/skills/logging-best-practices/examples/csharp.md +49 -0
  51. package/.opencode/skills/logging-best-practices/examples/javascript.md +77 -0
  52. package/.opencode/skills/logging-best-practices/examples/python.md +57 -0
  53. package/.opencode/skills/logging-best-practices/references/logging-standards.md +29 -0
  54. package/.opencode/skills/open-presentation/SKILL.md +35 -0
  55. package/.opencode/skills/opentelemetry-best-practices/SKILL.md +34 -0
  56. package/.opencode/skills/opentelemetry-best-practices/examples/go.md +32 -0
  57. package/.opencode/skills/opentelemetry-best-practices/examples/javascript.md +58 -0
  58. package/.opencode/skills/opentelemetry-best-practices/examples/python.md +37 -0
  59. package/.opencode/skills/opentelemetry-best-practices/references/otel-standards.md +37 -0
  60. package/.opencode/skills/python-best-practices/SKILL.md +385 -0
  61. package/.opencode/skills/python-dependency-mgmt/SKILL.md +42 -0
  62. package/.opencode/skills/python-dependency-mgmt/examples/dependency_mgmt.md +67 -0
  63. package/.opencode/skills/python-security-skill/SKILL.md +56 -0
  64. package/.opencode/skills/python-security-skill/examples/security.md +56 -0
  65. package/.opencode/skills/self-signed-cert/SKILL.md +42 -0
  66. package/.opencode/skills/self-signed-cert/scripts/generate-cert.ps1 +45 -0
  67. package/.opencode/skills/self-signed-cert/scripts/generate-cert.sh +43 -0
  68. package/.opencode/skills/skill-creator/SKILL.md +196 -0
  69. package/.opencode/skills/skill-creator/references/output-patterns.md +82 -0
  70. package/.opencode/skills/skill-creator/references/workflows.md +28 -0
  71. package/.opencode/skills/skill-creator/scripts/init_skill.py +208 -0
  72. package/.opencode/skills/skill-creator/scripts/package_skill.py +99 -0
  73. package/.opencode/skills/skill-creator/scripts/quick_validate.py +113 -0
  74. package/.opencode/skills/story-status-lookup/SKILL.md +78 -0
  75. package/.opencode/skills/test-accompanied-development/SKILL.md +50 -0
  76. package/.opencode/skills/test-generator/SKILL.md +65 -0
  77. package/.opencode/skills/vercel-react-best-practices/SKILL.md +109 -0
  78. package/.opencode/skills/verify-hardened-docker-skill/SKILL.md +442 -0
  79. package/.opencode/skills/verify-hardened-docker-skill/scripts/verify-docker-hardening.sh +439 -0
  80. package/AiAudit.md +5 -0
  81. package/QUICK_START.md +11 -5
  82. package/README.md +52 -1
  83. package/bin/cli.js +31 -4
  84. package/docs/BMAD_AI_Development_Training.pptx +0 -0
  85. package/docs/technical-notes/context-persistence-research.md +434 -0
  86. package/docs/technical-notes/enforcement-hooks-research.md +415 -0
  87. package/lib/agents.js +34 -0
  88. package/lib/bmad-extension/agents/bmm-architect.customize.yaml +5 -0
  89. package/lib/bmad-extension/agents/bmm-bmad-master.customize.yaml +5 -0
  90. package/lib/bmad-extension/agents/bmm-cyber.customize.yaml +30 -0
  91. package/lib/bmad-extension/agents/bmm-dev.customize.yaml +5 -0
  92. package/lib/bmad-extension/agents/bmm-devops.customize.yaml +30 -0
  93. package/lib/bmad-extension/agents/bmm-mil498.customize.yaml +42 -0
  94. package/lib/bmad-extension/agents/bmm-pm.customize.yaml +5 -0
  95. package/lib/bmad-extension/agents/bmm-qa.customize.yaml +5 -0
  96. package/lib/bmad-extension/agents/bmm-sm.customize.yaml +5 -0
  97. package/lib/bmad-extension/agents/bmm-sre.customize.yaml +30 -0
  98. package/lib/bmad-extension/agents/bmm-tech-writer.customize.yaml +5 -0
  99. package/lib/bmad-extension/agents/bmm-ux-designer.customize.yaml +5 -0
  100. package/lib/bmad-extension/module-help.csv +7 -0
  101. package/lib/bmad-extension/module.yaml +3 -0
  102. package/lib/bmad-extension/workflows/add-sprint/workflow.md +112 -0
  103. package/lib/bmad-extension/workflows/add-to-sprint/workflow.md +206 -0
  104. package/lib/bmad-extension/workflows/create-bug-story/workflow.md +186 -0
  105. package/lib/bmad-extension/workflows/modify-sprint/workflow.md +250 -0
  106. package/lib/bmad-extension/workflows/project-context-expansion/workflow.md +229 -0
  107. package/lib/bmad-extension/workflows/sprint-status-view/workflow.md +193 -0
  108. package/lib/bmad.js +168 -36
  109. package/lib/hooks/claude-code/verify-manifest.js +56 -0
  110. package/lib/installer.js +282 -1
  111. package/lib/methodology/BMAD_AI_Development_Training.pptx +0 -0
  112. package/lib/methodology/version.json +7 -0
  113. package/lib/skill-authoring.js +732 -0
  114. package/lib/templates/project-context.template.md +47 -0
  115. package/opencode.json +8 -0
  116. package/package.json +2 -2
  117. package/skills/auto-bug-detection/SKILL.md +165 -0
  118. package/skills/auto-bug-detection/skill.json +8 -0
  119. package/skills/code-review/SKILL.md +40 -0
  120. package/skills/cpp-best-practices/SKILL.md +230 -0
  121. package/skills/cpp-best-practices/examples/modern-idioms.md +189 -0
  122. package/skills/cpp-best-practices/examples/naming-and-organization.md +102 -0
  123. package/skills/cpp-best-practices/skill.json +25 -0
  124. package/skills/csharp-best-practices/SKILL.md +274 -0
  125. package/skills/csharp-best-practices/skill.json +23 -0
  126. package/skills/git-workflow-skill/skill.json +1 -1
  127. package/skills/open-presentation/SKILL.md +31 -0
  128. package/skills/open-presentation/skill.json +11 -0
  129. package/skills/python-best-practices/SKILL.md +381 -0
  130. package/skills/python-best-practices/skill.json +26 -0
  131. package/skills/story-status-lookup/SKILL.md +74 -0
  132. package/skills/story-status-lookup/skill.json +8 -0
  133. package/test/agent-injection-strategy.test.js +13 -7
  134. package/test/bmad-extension.test.js +237 -0
  135. package/test/bmad-output-policy.test.js +119 -0
  136. package/test/build-bmad-args.test.js +361 -0
  137. package/test/create-agent.test.js +232 -0
  138. package/test/enforcement-hooks.test.js +324 -0
  139. package/test/generate-project-context.test.js +337 -0
  140. package/test/integration-verification.test.js +402 -0
  141. package/test/opencode-agent.test.js +150 -0
  142. package/test/opencode-json-error.test.js +260 -0
  143. package/test/opencode-json-injection.test.js +256 -0
  144. package/test/opencode-json-merge.test.js +299 -0
  145. package/test/skill-authoring.test.js +272 -0
  146. package/test/skill-customize-agent.test.js +253 -0
  147. package/test/skill-mandatory.test.js +235 -0
  148. package/test/skill-validation.test.js +378 -0
  149. package/test/yes-flag.test.js +1 -1
@@ -0,0 +1,189 @@
1
+ # Modern C++ Idioms Examples
2
+
3
+ ## C++17: Structured Bindings and std::optional
4
+
5
+ ```cpp
6
+ #include <map>
7
+ #include <optional>
8
+ #include <string>
9
+
10
+ // Structured bindings — prefer over .first/.second
11
+ std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};
12
+ for (const auto& [name, score] : scores) {
13
+ // name and score are directly named — no .first/.second
14
+ }
15
+
16
+ // std::optional — no more nullptr sentinels or bool out-parameters
17
+ std::optional<std::string> findUser(int id) {
18
+ if (id == 42) return "Alice";
19
+ return std::nullopt; // Not found — explicit, type-safe
20
+ }
21
+
22
+ void example() {
23
+ if (const auto user = findUser(42); user.has_value()) {
24
+ // C++17 if-with-initializer keeps 'user' scoped tightly
25
+ doSomethingWith(*user);
26
+ }
27
+ }
28
+ ```
29
+
30
+ ## C++17: std::variant for Sum Types
31
+
32
+ ```cpp
33
+ #include <variant>
34
+ #include <string>
35
+
36
+ struct Success { std::string message; };
37
+ struct NotFound { int id; };
38
+ struct PermissionDenied { std::string reason; };
39
+
40
+ using LookupResult = std::variant<Success, NotFound, PermissionDenied>;
41
+
42
+ LookupResult lookup(int id) {
43
+ if (id == 0) return NotFound{id};
44
+ if (id < 0) return PermissionDenied{"negative ids reserved"};
45
+ return Success{"found item " + std::to_string(id)};
46
+ }
47
+
48
+ void handle(const LookupResult& result) {
49
+ std::visit([](const auto& v) {
50
+ using T = std::decay_t<decltype(v)>;
51
+ if constexpr (std::is_same_v<T, Success>) {
52
+ log(v.message);
53
+ } else if constexpr (std::is_same_v<T, NotFound>) {
54
+ log("not found: " + std::to_string(v.id));
55
+ } else {
56
+ log("denied: " + v.reason);
57
+ }
58
+ }, result);
59
+ }
60
+ ```
61
+
62
+ ## C++20: Concepts
63
+
64
+ ```cpp
65
+ #include <concepts>
66
+ #include <iostream>
67
+ #include <vector>
68
+
69
+ // Before concepts: SFINAE was required — hard to read and debug
70
+ // After concepts: clear, readable constraints
71
+
72
+ template<std::integral T>
73
+ T clamp(T value, T lo, T hi) {
74
+ return std::max(lo, std::min(value, hi));
75
+ }
76
+
77
+ // Custom concept
78
+ template<typename T>
79
+ concept Printable = requires(T t) {
80
+ { t.toString() } -> std::convertible_to<std::string>;
81
+ };
82
+
83
+ template<Printable T>
84
+ void printAll(const std::vector<T>& items) {
85
+ for (const auto& item : items) {
86
+ std::cout << item.toString() << '\n';
87
+ }
88
+ }
89
+ ```
90
+
91
+ ## C++20: Ranges
92
+
93
+ ```cpp
94
+ #include <algorithm>
95
+ #include <iostream>
96
+ #include <ranges>
97
+ #include <vector>
98
+ #include <string>
99
+
100
+ void modernRanges() {
101
+ std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
102
+
103
+ // Chain range adaptors — lazy, composable, readable
104
+ auto evenSquares = numbers
105
+ | std::views::filter([](int n) { return n % 2 == 0; })
106
+ | std::views::transform([](int n) { return n * n; });
107
+
108
+ // Iterate result without materializing an intermediate container
109
+ for (int v : evenSquares) {
110
+ std::cout << v << '\n'; // 4 16 36 64 100
111
+ }
112
+
113
+ // Sorting with ranges — no begin/end iterator boilerplate
114
+ std::vector<std::string> names = {"Charlie", "Alice", "Bob"};
115
+ std::ranges::sort(names);
116
+ }
117
+ ```
118
+
119
+ ## C++23: std::expected for Error Handling
120
+
121
+ ```cpp
122
+ #include <expected>
123
+ #include <string>
124
+ #include <fstream>
125
+
126
+ enum class ParseError { InvalidFormat, EmptyInput, Overflow };
127
+
128
+ // Return expected<T, E> instead of throwing for anticipated failures
129
+ std::expected<int, ParseError> parsePositiveInt(std::string_view input) {
130
+ if (input.empty()) return std::unexpected(ParseError::EmptyInput);
131
+ int result = 0;
132
+ for (char c : input) {
133
+ if (c < '0' || c > '9') return std::unexpected(ParseError::InvalidFormat);
134
+ result = result * 10 + (c - '0');
135
+ if (result < 0) return std::unexpected(ParseError::Overflow);
136
+ }
137
+ return result;
138
+ }
139
+
140
+ void usage() {
141
+ const auto result = parsePositiveInt("123");
142
+ if (result) {
143
+ std::println("Parsed: {}", *result);
144
+ } else {
145
+ // Handle error without try/catch — expected path, not exceptional
146
+ switch (result.error()) {
147
+ case ParseError::InvalidFormat: std::println("invalid format"); break;
148
+ case ParseError::EmptyInput: std::println("empty input"); break;
149
+ case ParseError::Overflow: std::println("overflow"); break;
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ ## Class Design: Rule of Zero
156
+
157
+ ```cpp
158
+ #include <memory>
159
+ #include <string>
160
+ #include <vector>
161
+
162
+ // Good: Rule of Zero — all members manage themselves
163
+ class Document {
164
+ public:
165
+ explicit Document(std::string title) : title_(std::move(title)) {}
166
+
167
+ void addSection(std::string content) {
168
+ sections_.emplace_back(std::move(content));
169
+ }
170
+
171
+ private:
172
+ std::string title_; // manages its own lifetime
173
+ std::vector<std::string> sections_; // manages its own lifetime
174
+ // No destructor, no copy/move definitions needed — compiler generates correct ones
175
+ };
176
+
177
+ // Bad: unnecessary manual resource management
178
+ class BadDocument {
179
+ public:
180
+ BadDocument(const char* title) {
181
+ title_ = new char[strlen(title) + 1]; // raw new — avoid this
182
+ strcpy(title_, title);
183
+ }
184
+ ~BadDocument() { delete[] title_; } // manual cleanup — fragile
185
+ // Must also define copy ctor, copy assign, move ctor, move assign correctly...
186
+ private:
187
+ char* title_;
188
+ };
189
+ ```
@@ -0,0 +1,102 @@
1
+ # Naming and Organization Examples
2
+
3
+ ## Naming Conventions
4
+
5
+ ```cpp
6
+ // File: user_account.h
7
+ #pragma once
8
+
9
+ #include <string>
10
+ #include <string_view>
11
+ #include <optional>
12
+
13
+ namespace myapp::domain {
14
+
15
+ // Types use PascalCase
16
+ class UserAccount {
17
+ public:
18
+ // Constants use UPPER_SNAKE_CASE
19
+ static constexpr int MAX_NAME_LENGTH = 128;
20
+
21
+ // Constructors and methods use camelCase
22
+ explicit UserAccount(std::string_view userName, int accountId);
23
+
24
+ // Const member function — does not modify the object
25
+ [[nodiscard]] std::string_view getUserName() const;
26
+ [[nodiscard]] int getAccountId() const;
27
+
28
+ // Mutating method
29
+ void setUserName(std::string_view newName);
30
+
31
+ private:
32
+ // Private members use camelCase with trailing underscore
33
+ std::string userName_;
34
+ int accountId_;
35
+ };
36
+
37
+ } // namespace myapp::domain
38
+ ```
39
+
40
+ ## Header Organization
41
+
42
+ ```cpp
43
+ // File: connection_pool.cpp
44
+
45
+ // 1. Project headers first
46
+ #include "connection_pool.h"
47
+ #include "network/socket.h"
48
+
49
+ // 2. Third-party headers
50
+ #include <boost/asio.hpp>
51
+
52
+ // 3. Standard library
53
+ #include <algorithm>
54
+ #include <chrono>
55
+ #include <memory>
56
+ #include <vector>
57
+
58
+ namespace myapp::network {
59
+ // ...
60
+ } // namespace myapp::network
61
+ ```
62
+
63
+ ## Forward Declarations to Reduce Coupling
64
+
65
+ ```cpp
66
+ // File: request_handler.h
67
+ #pragma once
68
+
69
+ // Forward declare instead of including the full header
70
+ namespace myapp::domain {
71
+ class UserAccount;
72
+ }
73
+
74
+ namespace myapp::network {
75
+
76
+ class RequestHandler {
77
+ public:
78
+ // Only a reference/pointer is needed here — forward declaration is sufficient
79
+ void handle(const myapp::domain::UserAccount& user);
80
+ };
81
+
82
+ } // namespace myapp::network
83
+ ```
84
+
85
+ ## Namespace Usage
86
+
87
+ ```cpp
88
+ // Good: deeply nested namespace matching directory structure
89
+ namespace myapp::network::http {
90
+
91
+ class Client {
92
+ // ...
93
+ };
94
+
95
+ } // namespace myapp::network::http
96
+
97
+ // Avoid in headers — pollutes every translation unit that includes this header:
98
+ // using namespace std; // BAD in headers
99
+
100
+ // OK in .cpp files, restricted to file scope:
101
+ // using namespace myapp::network::http;
102
+ ```
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "C++ Best Practices",
3
+ "description": "Comprehensive C++ coding standards covering naming conventions, modern C++ idioms (C++17/20/23), error handling, and build guidelines. Cross-references domain-specific C++ skills for deep-dives.",
4
+ "version": "1.0.0",
5
+ "author": "Antigravity",
6
+ "tags": [
7
+ "c++",
8
+ "cpp",
9
+ "coding-standards",
10
+ "best-practices",
11
+ "modern-cpp",
12
+ "c++17",
13
+ "c++20",
14
+ "c++23"
15
+ ],
16
+ "applies_when": [
17
+ "writing c++ code",
18
+ "modifying c++ code",
19
+ "creating new c++ files or classes",
20
+ "reviewing c++ code style or conventions",
21
+ "refactoring c++ code",
22
+ "setting up a c++ project"
23
+ ],
24
+ "always_load": true
25
+ }
@@ -0,0 +1,274 @@
1
+ # C# Best Practices
2
+
3
+ Coding standards for modern C# (C# 10+) across .NET 6 through .NET 8+. This skill covers language-level patterns. Framework-specific patterns (ASP.NET Core middleware, EF Core, Blazor) belong in future skills.
4
+
5
+ ## 1. Naming Conventions
6
+
7
+ **Rule:** Use consistent naming to maximize code readability and tooling support.
8
+
9
+ | Element | Convention | Example |
10
+ |---------|-----------|---------|
11
+ | Namespace | PascalCase, match folder | `MyApp.Services` |
12
+ | Class / Struct / Record | PascalCase | `CustomerService` |
13
+ | Interface | `I` + PascalCase | `ICustomerService` |
14
+ | Method | PascalCase | `GetCustomerAsync` |
15
+ | Property | PascalCase | `FirstName` |
16
+ | Local variable | camelCase | `customerCount` |
17
+ | Parameter | camelCase | `customerId` |
18
+ | Private field | `_` + camelCase | `_logger` |
19
+ | Constant | PascalCase | `MaxRetryCount` |
20
+ | Enum type | PascalCase singular | `OrderStatus` |
21
+ | Enum member | PascalCase | `OrderStatus.Pending` |
22
+ | Async method | suffix `Async` | `SaveAsync` |
23
+
24
+ ## 2. Code Organization
25
+
26
+ **Rule:** One type per file; namespace matches folder path.
27
+
28
+ - File-scoped namespace declaration (C# 10+): `namespace MyApp.Services;` — no extra indentation level.
29
+ - Organize types in the order: constants, fields, constructors, properties, methods (public first, then private).
30
+ - Use `global using` directives in a dedicated `GlobalUsings.cs` file to eliminate repetitive `using` statements project-wide.
31
+ - Prefer `record` types for immutable data containers; use `record struct` for small value-type data.
32
+
33
+ ## 3. Nullable Reference Types
34
+
35
+ **Rule:** Enable NRT project-wide and treat warnings as errors.
36
+
37
+ ```xml
38
+ <Nullable>enable</Nullable>
39
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
40
+ ```
41
+
42
+ - Never use `!` (null-forgiving) to silence warnings without a documented reason.
43
+ - Use `string?` for intentionally nullable references; non-nullable types must be initialized before use.
44
+ - Initialize fields in the constructor or use `required` members (C# 11+) to enforce initialization at the call site.
45
+
46
+ ```csharp
47
+ public class Order
48
+ {
49
+ public required string CustomerId { get; init; }
50
+ public string? Notes { get; init; }
51
+ }
52
+ ```
53
+
54
+ ## 4. Modern C# Language Features
55
+
56
+ ### 4.1 Primary Constructors (C# 12)
57
+
58
+ Use primary constructors for classes and structs that primarily capture injected dependencies.
59
+
60
+ ```csharp
61
+ public class OrderService(IOrderRepository repository, ILogger<OrderService> logger)
62
+ {
63
+ public async Task<Order?> GetAsync(int id, CancellationToken ct)
64
+ {
65
+ logger.LogInformation("Getting order {Id}", id);
66
+ return await repository.FindAsync(id, ct);
67
+ }
68
+ }
69
+ ```
70
+
71
+ ### 4.2 Collection Expressions (C# 12)
72
+
73
+ Prefer collection expressions over explicit constructors for collection initialization.
74
+
75
+ ```csharp
76
+ // Preferred
77
+ int[] ids = [1, 2, 3];
78
+ List<string> names = ["Alice", "Bob"];
79
+
80
+ // Also valid for spread
81
+ int[] combined = [..ids, 4, 5];
82
+ ```
83
+
84
+ ### 4.3 Raw String Literals (C# 11)
85
+
86
+ Use raw string literals for JSON, SQL, or multi-line strings to avoid escaping.
87
+
88
+ ```csharp
89
+ var json = """
90
+ {
91
+ "name": "Alice",
92
+ "age": 30
93
+ }
94
+ """;
95
+ ```
96
+
97
+ ### 4.4 Pattern Matching
98
+
99
+ Use pattern matching to simplify type checks and conditional logic.
100
+
101
+ ```csharp
102
+ // Type patterns
103
+ string Describe(object obj) => obj switch
104
+ {
105
+ int n when n > 0 => "positive integer",
106
+ int n => "non-positive integer",
107
+ string s => $"string: {s}",
108
+ null => "null",
109
+ _ => "other"
110
+ };
111
+
112
+ // List patterns (C# 11)
113
+ bool IsFirstTwo(int[] arr) => arr is [var first, var second, ..];
114
+ ```
115
+
116
+ ### 4.5 Records and Record Structs
117
+
118
+ Use `record` for immutable reference-type DTOs and value objects. Use `readonly record struct` for small immutable value types that benefit from stack allocation.
119
+
120
+ ```csharp
121
+ // Reference-type record (class semantics, heap allocated)
122
+ public record Address(string Street, string City, string PostalCode);
123
+
124
+ // with-expressions for non-destructive mutation
125
+ var updated = original with { City = "New York" };
126
+
127
+ // Value-type record (struct semantics, stack allocated)
128
+ public readonly record struct Point(double X, double Y);
129
+
130
+ // Mutable record struct — only when mutation is explicitly needed
131
+ public record struct Range(int Start, int End);
132
+ ```
133
+
134
+ Choose `readonly record struct` over `record` when: the data is small (2-4 fields), frequently allocated in hot paths, and value semantics (equality by value, copied on assignment) are correct.
135
+
136
+ ### 4.6 File-Scoped Types (C# 11)
137
+
138
+ Use the `file` access modifier to restrict a type's visibility to the single file where it is declared. This is useful for implementation detail types that should not leak into other files.
139
+
140
+ ```csharp
141
+ // Only visible within this .cs file — cannot be used from any other file
142
+ file class OrderValidator
143
+ {
144
+ public bool IsValid(Order order) => order.CustomerId is not null;
145
+ }
146
+
147
+ // Also applies to records, structs, interfaces, delegates
148
+ file record struct ValidationResult(bool IsValid, string? Error);
149
+ ```
150
+
151
+ Use `file`-scoped types for: helper classes private to a single implementation file, source generators' internal scaffolding, and avoiding name collisions across files without polluting `internal` scope.
152
+
153
+ ## 5. Async/Await
154
+
155
+ **Rule:** Use async all the way up the call stack; never block on async code.
156
+
157
+ - Prefer `async Task` / `async Task<T>` over returning naked `Task`.
158
+ - Never use `async void` except for event handlers; use `async Task` instead.
159
+ - Always propagate `CancellationToken` from public API boundaries down to I/O calls.
160
+ - Prefer `ConfigureAwait(false)` in library code where the calling context does not matter.
161
+ - Use `ValueTask<T>` when a method frequently returns a synchronous result (hot-path optimization); default to `Task<T>` for simplicity.
162
+
163
+ ```csharp
164
+ // Correct — async all the way, CancellationToken propagated, ConfigureAwait in library code
165
+ public async Task<Order?> FindOrderAsync(int id, CancellationToken ct = default)
166
+ {
167
+ return await _repository.GetByIdAsync(id, ct).ConfigureAwait(false);
168
+ }
169
+
170
+ // Correct — multiple I/O operations with cancellation
171
+ public async Task<IReadOnlyList<Order>> GetActiveOrdersAsync(CancellationToken ct = default)
172
+ {
173
+ var orders = await _repository.ListAsync(ct).ConfigureAwait(false);
174
+ return orders.Where(o => o.IsActive).ToList();
175
+ }
176
+
177
+ // Wrong — blocks the thread pool, risks deadlock
178
+ public Order? FindOrder(int id) => FindOrderAsync(id).Result;
179
+ ```
180
+
181
+ ## 6. LINQ
182
+
183
+ **Rule:** Prefer method syntax; be aware of deferred execution.
184
+
185
+ - Use method syntax (`Where`, `Select`, `FirstOrDefault`) over query syntax for consistency with the rest of the codebase.
186
+ - LINQ queries execute lazily — materialize with `.ToList()`, `.ToArray()`, or `.ToDictionary()` when the result will be enumerated more than once or when you need a stable snapshot.
187
+ - Avoid LINQ inside tight loops on large collections; profile before assuming LINQ is a bottleneck.
188
+ - For in-memory filtering on large datasets, consider `AsSpan()` or `ArrayPool<T>` instead of repeated LINQ queries.
189
+
190
+ ```csharp
191
+ // Materialize when enumerating multiple times
192
+ var activeOrders = orders.Where(o => o.IsActive).ToList();
193
+
194
+ // Avoid multiple enumeration of IEnumerable
195
+ foreach (var order in activeOrders) { /* ... */ }
196
+ int count = activeOrders.Count;
197
+ ```
198
+
199
+ ## 7. Error Handling
200
+
201
+ **Rule:** Use structured exception handling; reserve exceptions for truly exceptional conditions.
202
+
203
+ - Define a domain exception hierarchy derived from a base exception: `AppException : Exception`.
204
+ - Use the Result pattern for expected failure cases (validation, not-found) where exceptions are expensive overhead.
205
+ - For HTTP APIs, structure error responses using ProblemDetails (RFC 9457) — the language-level concern is ensuring your exceptions carry enough data to produce one; the middleware that converts them belongs in a future `csharp-aspnet-patterns` skill.
206
+ - Never swallow exceptions silently; always log or rethrow.
207
+
208
+ ```csharp
209
+ // Result pattern for expected failures
210
+ public record Result<T>(T? Value, string? Error)
211
+ {
212
+ public bool IsSuccess => Error is null;
213
+ public static Result<T> Ok(T value) => new(value, null);
214
+ public static Result<T> Fail(string error) => new(default, error);
215
+ }
216
+
217
+ // Exception hierarchy
218
+ public class AppException(string message) : Exception(message);
219
+ public class NotFoundException(string entity, object id)
220
+ : AppException($"{entity} with id '{id}' was not found.");
221
+ ```
222
+
223
+ ## 8. Dependency Injection
224
+
225
+ **Rule:** Favor constructor injection; choose service lifetimes deliberately.
226
+
227
+ - Constructor injection is the standard pattern — inject only what the type needs.
228
+ - **Singleton:** One instance for the application lifetime. Use for stateless services and configuration.
229
+ - **Scoped:** One instance per request (web) or operation scope. Use for database contexts and unit-of-work patterns.
230
+ - **Transient:** New instance per resolution. Use for lightweight, stateless utilities.
231
+ - Never inject a Scoped or Transient service into a Singleton — this causes captive dependency bugs.
232
+
233
+ ```csharp
234
+ // Registration via IServiceCollection (Microsoft.Extensions.DependencyInjection)
235
+ // Works in console apps, workers, ASP.NET Core, and any .NET host
236
+ IServiceCollection services = new ServiceCollection();
237
+ services.AddSingleton<IConfigService, ConfigService>();
238
+ services.AddScoped<IOrderService, OrderService>();
239
+ services.AddTransient<IEmailFormatter, EmailFormatter>();
240
+ ```
241
+
242
+ ## 9. Testing
243
+
244
+ **Rule:** Follow Arrange-Act-Assert; use framework-agnostic patterns.
245
+
246
+ - Name tests: `MethodName_StateUnderTest_ExpectedBehavior`.
247
+ - Arrange all preconditions, act on the system under test, assert the single observable outcome.
248
+ - Major test frameworks: xUnit (most popular for .NET), NUnit, MSTest — choose based on team preference.
249
+ - Popular assertion libraries: FluentAssertions, Shouldly. Popular mock libraries: NSubstitute, Moq.
250
+ - Avoid test logic that depends on execution order; each test must be independent.
251
+
252
+ ```csharp
253
+ // xUnit example (patterns apply to NUnit/MSTest)
254
+ public class OrderServiceTests
255
+ {
256
+ [Fact]
257
+ public async Task GetAsync_ExistingId_ReturnsOrder()
258
+ {
259
+ // Arrange
260
+ var repository = Substitute.For<IOrderRepository>();
261
+ var logger = Substitute.For<ILogger<OrderService>>();
262
+ repository.FindAsync(1, Arg.Any<CancellationToken>())
263
+ .Returns(new Order { Id = 1 });
264
+ var sut = new OrderService(repository, logger);
265
+
266
+ // Act
267
+ var result = await sut.GetAsync(1, CancellationToken.None);
268
+
269
+ // Assert
270
+ result.Should().NotBeNull();
271
+ result!.Id.Should().Be(1);
272
+ }
273
+ }
274
+ ```
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "C# Best Practices",
3
+ "description": "Comprehensive C# coding standards covering modern C# (C# 10-12), async/await, LINQ, dependency injection basics, nullable reference types, and testing conventions.",
4
+ "version": "1.0.0",
5
+ "author": "ma-agents",
6
+ "tags": [
7
+ "csharp",
8
+ "c#",
9
+ "dotnet",
10
+ ".net",
11
+ "coding-standards",
12
+ "best-practices",
13
+ "net8"
14
+ ],
15
+ "applies_when": [
16
+ "writing c# code",
17
+ "creating new c# files or classes",
18
+ "reviewing c# code style or conventions",
19
+ "working with .net projects",
20
+ "setting up a c# project"
21
+ ],
22
+ "always_load": true
23
+ }
@@ -18,4 +18,4 @@
18
18
  "any code writing or modification task"
19
19
  ],
20
20
  "always_load": true
21
- }
21
+ }
@@ -0,0 +1,31 @@
1
+ # Open Presentation Skill
2
+
3
+ ## Description
4
+ Opens the BMAD-METHOD AI Development Training presentation installed at `_bmad-output/methodology/`.
5
+
6
+ ## Usage
7
+ Invoke this skill by typing `/open-presentation`.
8
+
9
+ ## Instructions
10
+
11
+ When the user invokes `/open-presentation`:
12
+
13
+ 1. **Locate the presentation** at `_bmad-output/methodology/BMAD_AI_Development_Training.pptx` relative to the project root.
14
+
15
+ 2. **Open it** using the OS default viewer via the Bash tool:
16
+ - **Windows**: `start "" "_bmad-output/methodology/BMAD_AI_Development_Training.pptx"`
17
+ - **macOS**: `open "_bmad-output/methodology/BMAD_AI_Development_Training.pptx"`
18
+ - **Linux**: `xdg-open "_bmad-output/methodology/BMAD_AI_Development_Training.pptx"`
19
+
20
+ 3. **If the file does not exist**, inform the user:
21
+ > "The methodology presentation has not been installed yet. Run `npx ma-agents install` with BMAD to deploy it to `_bmad-output/methodology/`."
22
+
23
+ 4. **Detect the OS** using the Node.js `process.platform` convention or by checking which command is available, then run the appropriate open command.
24
+
25
+ ## Examples
26
+
27
+ **User**: `/open-presentation`
28
+ **Assistant**: Opens `_bmad-output/methodology/BMAD_AI_Development_Training.pptx` in the system default PowerPoint viewer.
29
+
30
+ **User**: "open the methodology slides"
31
+ **Assistant**: [Treats this as /open-presentation and follows the instructions above]
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "Open Presentation",
3
+ "description": "Opens the BMAD-METHOD AI Development Training presentation from _bmad-output/methodology/",
4
+ "version": "1.0.0",
5
+ "author": "AI Agent Skills",
6
+ "tags": ["methodology", "presentation", "bmad", "training"],
7
+ "applies_when": [
8
+ "user types /open-presentation",
9
+ "user asks to open the methodology presentation or training slides"
10
+ ]
11
+ }