@malamute/ai-rules 1.0.0 → 1.2.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 (133) hide show
  1. package/README.md +270 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/.claude/rules/conventions/documentation.md +324 -0
  4. package/configs/_shared/.claude/rules/conventions/git.md +265 -0
  5. package/configs/_shared/.claude/rules/{performance.md → conventions/performance.md} +1 -1
  6. package/configs/_shared/.claude/rules/conventions/principles.md +334 -0
  7. package/configs/_shared/.claude/rules/devops/ci-cd.md +262 -0
  8. package/configs/_shared/.claude/rules/devops/docker.md +275 -0
  9. package/configs/_shared/.claude/rules/devops/nx.md +194 -0
  10. package/configs/_shared/.claude/rules/domain/backend/api-design.md +203 -0
  11. package/configs/_shared/.claude/rules/lang/csharp/async.md +220 -0
  12. package/configs/_shared/.claude/rules/lang/csharp/csharp.md +314 -0
  13. package/configs/_shared/.claude/rules/lang/csharp/linq.md +210 -0
  14. package/configs/_shared/.claude/rules/lang/python/async.md +337 -0
  15. package/configs/_shared/.claude/rules/lang/python/celery.md +476 -0
  16. package/configs/_shared/.claude/rules/lang/python/config.md +339 -0
  17. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/database/sqlalchemy.md +6 -1
  18. package/configs/_shared/.claude/rules/lang/python/deployment.md +523 -0
  19. package/configs/_shared/.claude/rules/lang/python/error-handling.md +330 -0
  20. package/configs/_shared/.claude/rules/lang/python/migrations.md +421 -0
  21. package/configs/_shared/.claude/rules/lang/python/python.md +172 -0
  22. package/configs/_shared/.claude/rules/lang/python/repository.md +383 -0
  23. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/testing.md +2 -69
  24. package/configs/_shared/.claude/rules/lang/typescript/async.md +447 -0
  25. package/configs/_shared/.claude/rules/lang/typescript/generics.md +356 -0
  26. package/configs/_shared/.claude/rules/lang/typescript/typescript.md +212 -0
  27. package/configs/_shared/.claude/rules/quality/error-handling.md +48 -0
  28. package/configs/_shared/.claude/rules/quality/logging.md +45 -0
  29. package/configs/_shared/.claude/rules/quality/observability.md +240 -0
  30. package/configs/_shared/.claude/rules/quality/testing-patterns.md +65 -0
  31. package/configs/_shared/.claude/rules/security/secrets-management.md +222 -0
  32. package/configs/_shared/.claude/skills/analysis/explore/SKILL.md +257 -0
  33. package/configs/_shared/.claude/skills/analysis/security-audit/SKILL.md +184 -0
  34. package/configs/_shared/.claude/skills/dev/api-endpoint/SKILL.md +126 -0
  35. package/configs/_shared/.claude/{commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  36. package/configs/_shared/.claude/{commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  37. package/configs/_shared/.claude/{commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  38. package/configs/_shared/.claude/skills/infra/deploy/SKILL.md +139 -0
  39. package/configs/_shared/.claude/skills/infra/docker/SKILL.md +95 -0
  40. package/configs/_shared/.claude/skills/infra/migration/SKILL.md +158 -0
  41. package/configs/_shared/.claude/skills/nx/nx-affected/SKILL.md +72 -0
  42. package/configs/_shared/.claude/skills/nx/nx-lib/SKILL.md +375 -0
  43. package/configs/_shared/CLAUDE.md +52 -149
  44. package/configs/angular/.claude/rules/{components.md → core/components.md} +69 -15
  45. package/configs/angular/.claude/rules/core/resource.md +285 -0
  46. package/configs/angular/.claude/rules/core/signals.md +323 -0
  47. package/configs/angular/.claude/rules/http.md +338 -0
  48. package/configs/angular/.claude/rules/routing.md +291 -0
  49. package/configs/angular/.claude/rules/ssr.md +312 -0
  50. package/configs/angular/.claude/rules/state/signal-store.md +408 -0
  51. package/configs/angular/.claude/rules/{state.md → state/state.md} +2 -2
  52. package/configs/angular/.claude/rules/testing.md +7 -7
  53. package/configs/angular/.claude/rules/ui/aria.md +422 -0
  54. package/configs/angular/.claude/rules/ui/forms.md +424 -0
  55. package/configs/angular/.claude/rules/ui/pipes-directives.md +335 -0
  56. package/configs/angular/.claude/settings.json +1 -0
  57. package/configs/angular/.claude/skills/ngrx-slice/SKILL.md +362 -0
  58. package/configs/angular/.claude/skills/signal-store/SKILL.md +445 -0
  59. package/configs/angular/CLAUDE.md +24 -216
  60. package/configs/dotnet/.claude/rules/background-services.md +552 -0
  61. package/configs/dotnet/.claude/rules/configuration.md +426 -0
  62. package/configs/dotnet/.claude/rules/ddd.md +447 -0
  63. package/configs/dotnet/.claude/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/.claude/rules/mediatr.md +320 -0
  65. package/configs/dotnet/.claude/rules/middleware.md +489 -0
  66. package/configs/dotnet/.claude/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/.claude/rules/validation.md +388 -0
  68. package/configs/dotnet/.claude/settings.json +21 -3
  69. package/configs/dotnet/CLAUDE.md +53 -286
  70. package/configs/fastapi/.claude/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/.claude/rules/dependencies.md +170 -0
  72. package/configs/{python → fastapi}/.claude/rules/fastapi.md +61 -1
  73. package/configs/fastapi/.claude/rules/lifespan.md +274 -0
  74. package/configs/fastapi/.claude/rules/middleware.md +229 -0
  75. package/configs/fastapi/.claude/rules/pydantic.md +433 -0
  76. package/configs/fastapi/.claude/rules/responses.md +251 -0
  77. package/configs/fastapi/.claude/rules/routers.md +202 -0
  78. package/configs/fastapi/.claude/rules/security.md +222 -0
  79. package/configs/fastapi/.claude/rules/testing.md +251 -0
  80. package/configs/fastapi/.claude/rules/websockets.md +298 -0
  81. package/configs/fastapi/.claude/settings.json +33 -0
  82. package/configs/fastapi/CLAUDE.md +144 -0
  83. package/configs/flask/.claude/rules/blueprints.md +208 -0
  84. package/configs/flask/.claude/rules/cli.md +285 -0
  85. package/configs/flask/.claude/rules/configuration.md +281 -0
  86. package/configs/flask/.claude/rules/context.md +238 -0
  87. package/configs/flask/.claude/rules/error-handlers.md +278 -0
  88. package/configs/flask/.claude/rules/extensions.md +278 -0
  89. package/configs/flask/.claude/rules/flask.md +171 -0
  90. package/configs/flask/.claude/rules/marshmallow.md +206 -0
  91. package/configs/flask/.claude/rules/security.md +267 -0
  92. package/configs/flask/.claude/rules/testing.md +284 -0
  93. package/configs/flask/.claude/settings.json +33 -0
  94. package/configs/flask/CLAUDE.md +166 -0
  95. package/configs/nestjs/.claude/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/.claude/rules/filters.md +376 -0
  97. package/configs/nestjs/.claude/rules/interceptors.md +317 -0
  98. package/configs/nestjs/.claude/rules/middleware.md +321 -0
  99. package/configs/nestjs/.claude/rules/modules.md +26 -0
  100. package/configs/nestjs/.claude/rules/pipes.md +351 -0
  101. package/configs/nestjs/.claude/rules/websockets.md +451 -0
  102. package/configs/nestjs/.claude/settings.json +16 -2
  103. package/configs/nestjs/CLAUDE.md +57 -215
  104. package/configs/nextjs/.claude/rules/api-routes.md +358 -0
  105. package/configs/nextjs/.claude/rules/authentication.md +355 -0
  106. package/configs/nextjs/.claude/rules/components.md +52 -0
  107. package/configs/nextjs/.claude/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/.claude/rules/database.md +400 -0
  109. package/configs/nextjs/.claude/rules/middleware.md +303 -0
  110. package/configs/nextjs/.claude/rules/routing.md +324 -0
  111. package/configs/nextjs/.claude/rules/seo.md +350 -0
  112. package/configs/nextjs/.claude/rules/server-actions.md +353 -0
  113. package/configs/nextjs/.claude/rules/state/zustand.md +6 -6
  114. package/configs/nextjs/.claude/settings.json +5 -0
  115. package/configs/nextjs/CLAUDE.md +69 -331
  116. package/package.json +23 -9
  117. package/src/cli.js +220 -0
  118. package/src/config.js +29 -0
  119. package/src/index.js +13 -0
  120. package/src/installer.js +361 -0
  121. package/src/merge.js +116 -0
  122. package/src/tech-config.json +29 -0
  123. package/src/utils.js +96 -0
  124. package/configs/python/.claude/rules/flask.md +0 -332
  125. package/configs/python/.claude/settings.json +0 -18
  126. package/configs/python/CLAUDE.md +0 -273
  127. package/src/install.js +0 -315
  128. /package/configs/_shared/.claude/rules/{accessibility.md → domain/frontend/accessibility.md} +0 -0
  129. /package/configs/_shared/.claude/rules/{security.md → security/security.md} +0 -0
  130. /package/configs/_shared/.claude/skills/{debug → dev/debug}/SKILL.md +0 -0
  131. /package/configs/_shared/.claude/skills/{learning → dev/learning}/SKILL.md +0 -0
  132. /package/configs/_shared/.claude/skills/{spec → dev/spec}/SKILL.md +0 -0
  133. /package/configs/_shared/.claude/skills/{review → git/review}/SKILL.md +0 -0
@@ -0,0 +1,363 @@
1
+ ---
2
+ paths:
3
+ - "src/**/*.cs"
4
+ - "src/Application/**/*.cs"
5
+ - "src/Domain/**/*.cs"
6
+ ---
7
+
8
+ # Result Pattern
9
+
10
+ ## Result Type
11
+
12
+ ```csharp
13
+ // Domain/Common/Result.cs
14
+ public class Result
15
+ {
16
+ protected Result(bool isSuccess, Error error)
17
+ {
18
+ if (isSuccess && error != Error.None ||
19
+ !isSuccess && error == Error.None)
20
+ {
21
+ throw new ArgumentException("Invalid error state", nameof(error));
22
+ }
23
+
24
+ IsSuccess = isSuccess;
25
+ Error = error;
26
+ }
27
+
28
+ public bool IsSuccess { get; }
29
+ public bool IsFailure => !IsSuccess;
30
+ public Error Error { get; }
31
+
32
+ public static Result Success() => new(true, Error.None);
33
+ public static Result Failure(Error error) => new(false, error);
34
+
35
+ public static Result<T> Success<T>(T value) => new(value, true, Error.None);
36
+ public static Result<T> Failure<T>(Error error) => new(default!, false, error);
37
+ }
38
+
39
+ public class Result<T> : Result
40
+ {
41
+ private readonly T _value;
42
+
43
+ protected internal Result(T value, bool isSuccess, Error error)
44
+ : base(isSuccess, error)
45
+ {
46
+ _value = value;
47
+ }
48
+
49
+ public T Value => IsSuccess
50
+ ? _value
51
+ : throw new InvalidOperationException("Cannot access value of failed result");
52
+
53
+ public static implicit operator Result<T>(T value) => Success(value);
54
+ }
55
+ ```
56
+
57
+ ## Error Type
58
+
59
+ ```csharp
60
+ // Domain/Common/Error.cs
61
+ public sealed record Error(string Code, string Description)
62
+ {
63
+ public static readonly Error None = new(string.Empty, string.Empty);
64
+ public static readonly Error NullValue = new("Error.NullValue", "A null value was provided");
65
+
66
+ public static Error NotFound(string entityName, object id) =>
67
+ new($"{entityName}.NotFound", $"{entityName} with id {id} was not found");
68
+
69
+ public static Error Validation(string propertyName, string message) =>
70
+ new($"Validation.{propertyName}", message);
71
+
72
+ public static Error Conflict(string entityName, string details) =>
73
+ new($"{entityName}.Conflict", details);
74
+
75
+ public static Error Unauthorized(string message = "Unauthorized access") =>
76
+ new("Auth.Unauthorized", message);
77
+
78
+ public static Error Forbidden(string message = "Access denied") =>
79
+ new("Auth.Forbidden", message);
80
+ }
81
+
82
+ // Domain-specific errors
83
+ public static class UserErrors
84
+ {
85
+ public static Error NotFound(Guid id) =>
86
+ Error.NotFound("User", id);
87
+
88
+ public static Error EmailAlreadyExists(string email) =>
89
+ Error.Conflict("User", $"Email {email} is already registered");
90
+
91
+ public static Error InvalidPassword =>
92
+ Error.Validation("Password", "Password does not meet requirements");
93
+
94
+ public static Error AccountLocked =>
95
+ new("User.AccountLocked", "Account has been locked due to too many failed attempts");
96
+ }
97
+ ```
98
+
99
+ ## Usage in Handlers
100
+
101
+ ```csharp
102
+ // Application/Users/Commands/CreateUserCommandHandler.cs
103
+ public class CreateUserCommandHandler(
104
+ IUserRepository userRepository,
105
+ IUnitOfWork unitOfWork
106
+ ) : IRequestHandler<CreateUserCommand, Result<Guid>>
107
+ {
108
+ public async Task<Result<Guid>> Handle(
109
+ CreateUserCommand request,
110
+ CancellationToken cancellationToken)
111
+ {
112
+ // Check for existing user
113
+ if (await userRepository.ExistsByEmailAsync(request.Email, cancellationToken))
114
+ {
115
+ return Result.Failure<Guid>(UserErrors.EmailAlreadyExists(request.Email));
116
+ }
117
+
118
+ // Create user (factory method returns Result)
119
+ var userResult = User.Create(request.Email, request.Name, request.Password);
120
+ if (userResult.IsFailure)
121
+ {
122
+ return Result.Failure<Guid>(userResult.Error);
123
+ }
124
+
125
+ await userRepository.AddAsync(userResult.Value, cancellationToken);
126
+ await unitOfWork.SaveChangesAsync(cancellationToken);
127
+
128
+ return userResult.Value.Id;
129
+ }
130
+ }
131
+ ```
132
+
133
+ ## Domain Entity with Result
134
+
135
+ ```csharp
136
+ // Domain/Users/User.cs
137
+ public class User : Entity
138
+ {
139
+ private User(Guid id, Email email, string name, PasswordHash passwordHash)
140
+ {
141
+ Id = id;
142
+ Email = email;
143
+ Name = name;
144
+ PasswordHash = passwordHash;
145
+ CreatedAt = DateTime.UtcNow;
146
+ }
147
+
148
+ public Email Email { get; private set; }
149
+ public string Name { get; private set; }
150
+ public PasswordHash PasswordHash { get; private set; }
151
+ public DateTime CreatedAt { get; }
152
+ public bool IsActive { get; private set; } = true;
153
+
154
+ public static Result<User> Create(string email, string name, string password)
155
+ {
156
+ // Validate email
157
+ var emailResult = Email.Create(email);
158
+ if (emailResult.IsFailure)
159
+ {
160
+ return Result.Failure<User>(emailResult.Error);
161
+ }
162
+
163
+ // Validate name
164
+ if (string.IsNullOrWhiteSpace(name) || name.Length > 100)
165
+ {
166
+ return Result.Failure<User>(Error.Validation("Name", "Name is required and max 100 chars"));
167
+ }
168
+
169
+ // Validate and hash password
170
+ var passwordResult = PasswordHash.Create(password);
171
+ if (passwordResult.IsFailure)
172
+ {
173
+ return Result.Failure<User>(passwordResult.Error);
174
+ }
175
+
176
+ return new User(Guid.NewGuid(), emailResult.Value, name, passwordResult.Value);
177
+ }
178
+
179
+ public Result UpdateName(string newName)
180
+ {
181
+ if (string.IsNullOrWhiteSpace(newName) || newName.Length > 100)
182
+ {
183
+ return Result.Failure(Error.Validation("Name", "Name is required and max 100 chars"));
184
+ }
185
+
186
+ Name = newName;
187
+ return Result.Success();
188
+ }
189
+
190
+ public Result Deactivate()
191
+ {
192
+ if (!IsActive)
193
+ {
194
+ return Result.Failure(new Error("User.AlreadyDeactivated", "User is already deactivated"));
195
+ }
196
+
197
+ IsActive = false;
198
+ return Result.Success();
199
+ }
200
+ }
201
+ ```
202
+
203
+ ## Value Objects with Result
204
+
205
+ ```csharp
206
+ // Domain/Users/ValueObjects/Email.cs
207
+ public sealed class Email : ValueObject
208
+ {
209
+ private Email(string value) => Value = value;
210
+
211
+ public string Value { get; }
212
+
213
+ public static Result<Email> Create(string email)
214
+ {
215
+ if (string.IsNullOrWhiteSpace(email))
216
+ {
217
+ return Result.Failure<Email>(Error.Validation("Email", "Email is required"));
218
+ }
219
+
220
+ email = email.Trim().ToLowerInvariant();
221
+
222
+ if (email.Length > 256)
223
+ {
224
+ return Result.Failure<Email>(Error.Validation("Email", "Email is too long"));
225
+ }
226
+
227
+ if (!IsValidEmail(email))
228
+ {
229
+ return Result.Failure<Email>(Error.Validation("Email", "Email format is invalid"));
230
+ }
231
+
232
+ return new Email(email);
233
+ }
234
+
235
+ private static bool IsValidEmail(string email) =>
236
+ Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");
237
+
238
+ protected override IEnumerable<object> GetAtomicValues()
239
+ {
240
+ yield return Value;
241
+ }
242
+ }
243
+ ```
244
+
245
+ ## API Response Mapping
246
+
247
+ ```csharp
248
+ // WebApi/Extensions/ResultExtensions.cs
249
+ public static class ResultExtensions
250
+ {
251
+ public static IResult ToApiResult(this Result result) =>
252
+ result.IsSuccess
253
+ ? Results.Ok()
254
+ : result.ToProblemDetails();
255
+
256
+ public static IResult ToApiResult<T>(this Result<T> result) =>
257
+ result.IsSuccess
258
+ ? Results.Ok(result.Value)
259
+ : result.ToProblemDetails();
260
+
261
+ public static IResult ToCreatedResult<T>(
262
+ this Result<T> result,
263
+ string routeName,
264
+ Func<T, object> routeValues)
265
+ {
266
+ return result.IsSuccess
267
+ ? Results.CreatedAtRoute(routeName, routeValues(result.Value), result.Value)
268
+ : result.ToProblemDetails();
269
+ }
270
+
271
+ private static IResult ToProblemDetails(this Result result)
272
+ {
273
+ var statusCode = result.Error.Code switch
274
+ {
275
+ var code when code.Contains("NotFound") => StatusCodes.Status404NotFound,
276
+ var code when code.Contains("Validation") => StatusCodes.Status400BadRequest,
277
+ var code when code.Contains("Conflict") => StatusCodes.Status409Conflict,
278
+ var code when code.Contains("Unauthorized") => StatusCodes.Status401Unauthorized,
279
+ var code when code.Contains("Forbidden") => StatusCodes.Status403Forbidden,
280
+ _ => StatusCodes.Status400BadRequest
281
+ };
282
+
283
+ return Results.Problem(
284
+ title: result.Error.Code,
285
+ detail: result.Error.Description,
286
+ statusCode: statusCode);
287
+ }
288
+ }
289
+
290
+ // Usage in Minimal API
291
+ app.MapPost("/api/users", async (CreateUserCommand command, ISender sender) =>
292
+ {
293
+ var result = await sender.Send(command);
294
+ return result.ToCreatedResult("GetUser", id => new { id });
295
+ });
296
+
297
+ app.MapGet("/api/users/{id:guid}", async (Guid id, ISender sender) =>
298
+ {
299
+ var result = await sender.Send(new GetUserQuery(id));
300
+ return result.ToApiResult();
301
+ });
302
+ ```
303
+
304
+ ## Match Pattern (Functional)
305
+
306
+ ```csharp
307
+ // Extensions for functional-style handling
308
+ public static class ResultMatchExtensions
309
+ {
310
+ public static T Match<T>(
311
+ this Result result,
312
+ Func<T> onSuccess,
313
+ Func<Error, T> onFailure) =>
314
+ result.IsSuccess ? onSuccess() : onFailure(result.Error);
315
+
316
+ public static TOut Match<TIn, TOut>(
317
+ this Result<TIn> result,
318
+ Func<TIn, TOut> onSuccess,
319
+ Func<Error, TOut> onFailure) =>
320
+ result.IsSuccess ? onSuccess(result.Value) : onFailure(result.Error);
321
+ }
322
+
323
+ // Usage
324
+ var response = result.Match(
325
+ onSuccess: user => Results.Ok(user),
326
+ onFailure: error => Results.Problem(error.Description)
327
+ );
328
+ ```
329
+
330
+ ## Anti-Patterns
331
+
332
+ ```csharp
333
+ // BAD: Throwing exceptions for business logic
334
+ public User GetUser(Guid id)
335
+ {
336
+ var user = _repository.GetById(id);
337
+ if (user is null)
338
+ throw new NotFoundException($"User {id} not found"); // Exception for flow control!
339
+ return user;
340
+ }
341
+
342
+ // GOOD: Return Result
343
+ public Result<User> GetUser(Guid id)
344
+ {
345
+ var user = _repository.GetById(id);
346
+ return user is null
347
+ ? Result.Failure<User>(UserErrors.NotFound(id))
348
+ : Result.Success(user);
349
+ }
350
+
351
+
352
+ // BAD: Ignoring Result
353
+ var result = await CreateUserAsync(command);
354
+ return result.Value; // Throws if failure!
355
+
356
+ // GOOD: Handle Result properly
357
+ var result = await CreateUserAsync(command);
358
+ if (result.IsFailure)
359
+ {
360
+ return Results.Problem(result.Error.Description);
361
+ }
362
+ return Results.Ok(result.Value);
363
+ ```