@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,330 @@
1
+ ---
2
+ paths:
3
+ - "**/exceptions/**/*.py"
4
+ - "**/errors/**/*.py"
5
+ - "**/handlers/**/*.py"
6
+ - "**/api/**/*.py"
7
+ - "**/routers/**/*.py"
8
+ ---
9
+
10
+ # Python Error Handling
11
+
12
+ ## Custom Exception Hierarchy
13
+
14
+ ```python
15
+ # exceptions/base.py
16
+ from typing import Any
17
+
18
+
19
+ class AppError(Exception):
20
+ """Base exception for application errors."""
21
+
22
+ def __init__(
23
+ self,
24
+ message: str,
25
+ code: str = "APP_ERROR",
26
+ details: dict[str, Any] | None = None,
27
+ ):
28
+ self.message = message
29
+ self.code = code
30
+ self.details = details or {}
31
+ super().__init__(message)
32
+
33
+
34
+ class ValidationError(AppError):
35
+ """Input validation failed."""
36
+
37
+ def __init__(self, message: str, field: str | None = None):
38
+ super().__init__(
39
+ message=message,
40
+ code="VALIDATION_ERROR",
41
+ details={"field": field} if field else {},
42
+ )
43
+
44
+
45
+ class NotFoundError(AppError):
46
+ """Resource not found."""
47
+
48
+ def __init__(self, resource: str, id: str | int):
49
+ super().__init__(
50
+ message=f"{resource} with id {id} not found",
51
+ code="NOT_FOUND",
52
+ details={"resource": resource, "id": str(id)},
53
+ )
54
+
55
+
56
+ class ConflictError(AppError):
57
+ """Resource already exists."""
58
+
59
+ def __init__(self, resource: str, field: str, value: str):
60
+ super().__init__(
61
+ message=f"{resource} with {field}={value} already exists",
62
+ code="CONFLICT",
63
+ details={"resource": resource, "field": field, "value": value},
64
+ )
65
+
66
+
67
+ class UnauthorizedError(AppError):
68
+ """Authentication required."""
69
+
70
+ def __init__(self, message: str = "Authentication required"):
71
+ super().__init__(message=message, code="UNAUTHORIZED")
72
+
73
+
74
+ class ForbiddenError(AppError):
75
+ """Permission denied."""
76
+
77
+ def __init__(self, message: str = "Permission denied"):
78
+ super().__init__(message=message, code="FORBIDDEN")
79
+
80
+
81
+ class ExternalServiceError(AppError):
82
+ """External service call failed."""
83
+
84
+ def __init__(self, service: str, message: str):
85
+ super().__init__(
86
+ message=f"{service}: {message}",
87
+ code="EXTERNAL_SERVICE_ERROR",
88
+ details={"service": service},
89
+ )
90
+ ```
91
+
92
+ ## FastAPI Exception Handlers
93
+
94
+ ```python
95
+ # exceptions/handlers.py
96
+ from fastapi import FastAPI, Request
97
+ from fastapi.responses import JSONResponse
98
+ from pydantic import ValidationError as PydanticValidationError
99
+ import logging
100
+
101
+ from .base import (
102
+ AppError,
103
+ NotFoundError,
104
+ ValidationError,
105
+ UnauthorizedError,
106
+ ForbiddenError,
107
+ )
108
+
109
+ logger = logging.getLogger(__name__)
110
+
111
+
112
+ def register_exception_handlers(app: FastAPI) -> None:
113
+ """Register all exception handlers."""
114
+
115
+ @app.exception_handler(NotFoundError)
116
+ async def not_found_handler(request: Request, exc: NotFoundError):
117
+ return JSONResponse(
118
+ status_code=404,
119
+ content={
120
+ "error": exc.code,
121
+ "message": exc.message,
122
+ "details": exc.details,
123
+ },
124
+ )
125
+
126
+ @app.exception_handler(ValidationError)
127
+ async def validation_handler(request: Request, exc: ValidationError):
128
+ return JSONResponse(
129
+ status_code=400,
130
+ content={
131
+ "error": exc.code,
132
+ "message": exc.message,
133
+ "details": exc.details,
134
+ },
135
+ )
136
+
137
+ @app.exception_handler(UnauthorizedError)
138
+ async def unauthorized_handler(request: Request, exc: UnauthorizedError):
139
+ return JSONResponse(
140
+ status_code=401,
141
+ content={"error": exc.code, "message": exc.message},
142
+ headers={"WWW-Authenticate": "Bearer"},
143
+ )
144
+
145
+ @app.exception_handler(ForbiddenError)
146
+ async def forbidden_handler(request: Request, exc: ForbiddenError):
147
+ return JSONResponse(
148
+ status_code=403,
149
+ content={"error": exc.code, "message": exc.message},
150
+ )
151
+
152
+ @app.exception_handler(AppError)
153
+ async def app_error_handler(request: Request, exc: AppError):
154
+ logger.error(f"Application error: {exc.code} - {exc.message}")
155
+ return JSONResponse(
156
+ status_code=422,
157
+ content={
158
+ "error": exc.code,
159
+ "message": exc.message,
160
+ "details": exc.details,
161
+ },
162
+ )
163
+
164
+ @app.exception_handler(PydanticValidationError)
165
+ async def pydantic_validation_handler(
166
+ request: Request, exc: PydanticValidationError
167
+ ):
168
+ return JSONResponse(
169
+ status_code=422,
170
+ content={
171
+ "error": "VALIDATION_ERROR",
172
+ "message": "Request validation failed",
173
+ "details": exc.errors(),
174
+ },
175
+ )
176
+
177
+ @app.exception_handler(Exception)
178
+ async def unhandled_exception_handler(request: Request, exc: Exception):
179
+ logger.exception("Unhandled exception")
180
+ return JSONResponse(
181
+ status_code=500,
182
+ content={
183
+ "error": "INTERNAL_ERROR",
184
+ "message": "An unexpected error occurred",
185
+ },
186
+ )
187
+ ```
188
+
189
+ ## Error Response Schema
190
+
191
+ ```python
192
+ # schemas/error.py
193
+ from pydantic import BaseModel
194
+ from typing import Any
195
+
196
+
197
+ class ErrorResponse(BaseModel):
198
+ """Standard error response format."""
199
+
200
+ error: str
201
+ message: str
202
+ details: dict[str, Any] = {}
203
+
204
+ model_config = {
205
+ "json_schema_extra": {
206
+ "examples": [
207
+ {
208
+ "error": "NOT_FOUND",
209
+ "message": "User with id 123 not found",
210
+ "details": {"resource": "User", "id": "123"},
211
+ }
212
+ ]
213
+ }
214
+ }
215
+ ```
216
+
217
+ ## Usage in Services
218
+
219
+ ```python
220
+ # services/user.py
221
+ from exceptions import NotFoundError, ConflictError
222
+
223
+
224
+ class UserService:
225
+ async def get_by_id(self, user_id: int) -> User:
226
+ user = await self.repository.get(user_id)
227
+ if not user:
228
+ raise NotFoundError("User", user_id)
229
+ return user
230
+
231
+ async def create(self, data: UserCreate) -> User:
232
+ existing = await self.repository.get_by_email(data.email)
233
+ if existing:
234
+ raise ConflictError("User", "email", data.email)
235
+ return await self.repository.create(data)
236
+ ```
237
+
238
+ ## Result Pattern (Alternative)
239
+
240
+ ```python
241
+ # core/result.py
242
+ from dataclasses import dataclass
243
+ from typing import Generic, TypeVar
244
+
245
+ T = TypeVar("T")
246
+ E = TypeVar("E", bound=Exception)
247
+
248
+
249
+ @dataclass
250
+ class Ok(Generic[T]):
251
+ value: T
252
+
253
+ def is_ok(self) -> bool:
254
+ return True
255
+
256
+ def is_err(self) -> bool:
257
+ return False
258
+
259
+
260
+ @dataclass
261
+ class Err(Generic[E]):
262
+ error: E
263
+
264
+ def is_ok(self) -> bool:
265
+ return False
266
+
267
+ def is_err(self) -> bool:
268
+ return True
269
+
270
+
271
+ Result = Ok[T] | Err[E]
272
+
273
+
274
+ # Usage
275
+ async def get_user(user_id: int) -> Result[User, NotFoundError]:
276
+ user = await repository.get(user_id)
277
+ if not user:
278
+ return Err(NotFoundError("User", user_id))
279
+ return Ok(user)
280
+
281
+
282
+ # Handling
283
+ result = await get_user(123)
284
+ match result:
285
+ case Ok(user):
286
+ return user
287
+ case Err(error):
288
+ raise error
289
+ ```
290
+
291
+ ## Anti-Patterns
292
+
293
+ ```python
294
+ # BAD: Catching all exceptions
295
+ try:
296
+ user = await get_user(user_id)
297
+ except Exception:
298
+ return None # Swallows all errors!
299
+
300
+
301
+ # GOOD: Catch specific exceptions
302
+ try:
303
+ user = await get_user(user_id)
304
+ except NotFoundError:
305
+ return None
306
+ except Exception:
307
+ logger.exception("Unexpected error getting user")
308
+ raise
309
+
310
+
311
+ # BAD: Raising generic exceptions
312
+ if not user:
313
+ raise Exception("User not found")
314
+
315
+
316
+ # GOOD: Raise typed exceptions
317
+ if not user:
318
+ raise NotFoundError("User", user_id)
319
+
320
+
321
+ # BAD: Exposing internal errors to clients
322
+ except Exception as e:
323
+ return {"error": str(e)} # Leaks implementation details!
324
+
325
+
326
+ # GOOD: Map to safe error responses
327
+ except Exception as e:
328
+ logger.exception("Internal error")
329
+ return {"error": "INTERNAL_ERROR", "message": "An unexpected error occurred"}
330
+ ```