@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,356 @@
1
+ ---
2
+ paths:
3
+ - "**/*.ts"
4
+ - "**/*.tsx"
5
+ ---
6
+
7
+ # TypeScript Generics & Advanced Types
8
+
9
+ ## Generic Basics
10
+
11
+ ```typescript
12
+ // Generic function
13
+ function identity<T>(value: T): T {
14
+ return value;
15
+ }
16
+
17
+ // Generic with constraint
18
+ function getLength<T extends { length: number }>(item: T): number {
19
+ return item.length;
20
+ }
21
+
22
+ // Multiple type parameters
23
+ function pair<K, V>(key: K, value: V): [K, V] {
24
+ return [key, value];
25
+ }
26
+
27
+ // Generic interface
28
+ interface Repository<T> {
29
+ find(id: string): Promise<T | null>;
30
+ save(item: T): Promise<T>;
31
+ delete(id: string): Promise<void>;
32
+ }
33
+
34
+ // Generic class
35
+ class Cache<T> {
36
+ private items = new Map<string, T>();
37
+
38
+ get(key: string): T | undefined {
39
+ return this.items.get(key);
40
+ }
41
+
42
+ set(key: string, value: T): void {
43
+ this.items.set(key, value);
44
+ }
45
+ }
46
+ ```
47
+
48
+ ## Constraints
49
+
50
+ ```typescript
51
+ // Extends constraint
52
+ function merge<T extends object, U extends object>(a: T, b: U): T & U {
53
+ return { ...a, ...b };
54
+ }
55
+
56
+ // keyof constraint
57
+ function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
58
+ return obj[key];
59
+ }
60
+
61
+ // Constructor constraint
62
+ function createInstance<T>(ctor: new () => T): T {
63
+ return new ctor();
64
+ }
65
+
66
+ // Multiple constraints
67
+ function process<T extends Serializable & Validatable>(item: T): void {
68
+ item.validate();
69
+ item.serialize();
70
+ }
71
+ ```
72
+
73
+ ## Utility Types
74
+
75
+ ```typescript
76
+ interface User {
77
+ id: string;
78
+ name: string;
79
+ email: string;
80
+ password: string;
81
+ createdAt: Date;
82
+ }
83
+
84
+ // Partial - all properties optional
85
+ type UpdateUserDto = Partial<User>;
86
+ // { id?: string; name?: string; ... }
87
+
88
+ // Required - all properties required
89
+ type CompleteUser = Required<User>;
90
+
91
+ // Pick - select specific properties
92
+ type UserCredentials = Pick<User, 'email' | 'password'>;
93
+ // { email: string; password: string }
94
+
95
+ // Omit - exclude properties
96
+ type PublicUser = Omit<User, 'password'>;
97
+ // { id: string; name: string; email: string; createdAt: Date }
98
+
99
+ // Record - key-value mapping
100
+ type UserRoles = Record<string, 'admin' | 'user' | 'guest'>;
101
+ // { [key: string]: 'admin' | 'user' | 'guest' }
102
+
103
+ // Readonly - immutable
104
+ type ImmutableUser = Readonly<User>;
105
+
106
+ // Extract - extract union members
107
+ type StringOrNumber = string | number | boolean;
108
+ type OnlyStrings = Extract<StringOrNumber, string>; // string
109
+
110
+ // Exclude - remove union members
111
+ type NoStrings = Exclude<StringOrNumber, string>; // number | boolean
112
+
113
+ // NonNullable - remove null/undefined
114
+ type MaybeString = string | null | undefined;
115
+ type DefiniteString = NonNullable<MaybeString>; // string
116
+
117
+ // ReturnType - get function return type
118
+ function getUser() { return { id: '1', name: 'John' }; }
119
+ type UserReturn = ReturnType<typeof getUser>;
120
+ // { id: string; name: string }
121
+
122
+ // Parameters - get function parameters
123
+ type GetUserParams = Parameters<typeof getUser>; // []
124
+
125
+ // Awaited - unwrap Promise
126
+ type ResolvedUser = Awaited<Promise<User>>; // User
127
+ ```
128
+
129
+ ## Discriminated Unions
130
+
131
+ ```typescript
132
+ // GOOD - discriminated union with literal type
133
+ type Result<T> =
134
+ | { success: true; data: T }
135
+ | { success: false; error: string };
136
+
137
+ function handleResult<T>(result: Result<T>): T | null {
138
+ if (result.success) {
139
+ return result.data; // TypeScript knows data exists
140
+ }
141
+ console.error(result.error); // TypeScript knows error exists
142
+ return null;
143
+ }
144
+
145
+ // API response pattern
146
+ type ApiResponse<T> =
147
+ | { status: 'loading' }
148
+ | { status: 'success'; data: T }
149
+ | { status: 'error'; error: Error };
150
+
151
+ function renderResponse<T>(response: ApiResponse<T>) {
152
+ switch (response.status) {
153
+ case 'loading':
154
+ return <Spinner />;
155
+ case 'success':
156
+ return <Data data={response.data} />;
157
+ case 'error':
158
+ return <Error error={response.error} />;
159
+ }
160
+ }
161
+
162
+ // Event pattern
163
+ type AppEvent =
164
+ | { type: 'USER_LOGIN'; userId: string }
165
+ | { type: 'USER_LOGOUT' }
166
+ | { type: 'ITEM_ADDED'; itemId: string; quantity: number };
167
+
168
+ function handleEvent(event: AppEvent) {
169
+ switch (event.type) {
170
+ case 'USER_LOGIN':
171
+ return login(event.userId);
172
+ case 'USER_LOGOUT':
173
+ return logout();
174
+ case 'ITEM_ADDED':
175
+ return addItem(event.itemId, event.quantity);
176
+ }
177
+ }
178
+ ```
179
+
180
+ ## Type Guards
181
+
182
+ ```typescript
183
+ // Type predicate (is)
184
+ function isString(value: unknown): value is string {
185
+ return typeof value === 'string';
186
+ }
187
+
188
+ function isUser(value: unknown): value is User {
189
+ return (
190
+ typeof value === 'object' &&
191
+ value !== null &&
192
+ 'id' in value &&
193
+ 'email' in value
194
+ );
195
+ }
196
+
197
+ // Usage
198
+ function process(value: unknown) {
199
+ if (isString(value)) {
200
+ console.log(value.toUpperCase()); // value is string
201
+ }
202
+ if (isUser(value)) {
203
+ console.log(value.email); // value is User
204
+ }
205
+ }
206
+
207
+ // Assertion function (asserts)
208
+ function assertDefined<T>(value: T | null | undefined): asserts value is T {
209
+ if (value === null || value === undefined) {
210
+ throw new Error('Value is not defined');
211
+ }
212
+ }
213
+
214
+ // Usage
215
+ function getUser(id: string): User | null {
216
+ return db.find(id);
217
+ }
218
+
219
+ const user = getUser('123');
220
+ assertDefined(user); // Throws if null
221
+ console.log(user.name); // user is User, not User | null
222
+
223
+ // in operator narrowing
224
+ interface Cat { meow(): void }
225
+ interface Dog { bark(): void }
226
+
227
+ function speak(animal: Cat | Dog) {
228
+ if ('meow' in animal) {
229
+ animal.meow();
230
+ } else {
231
+ animal.bark();
232
+ }
233
+ }
234
+ ```
235
+
236
+ ## Const Assertions
237
+
238
+ ```typescript
239
+ // Without as const - types are widened
240
+ const config = {
241
+ endpoint: '/api',
242
+ methods: ['GET', 'POST'],
243
+ };
244
+ // type: { endpoint: string; methods: string[] }
245
+
246
+ // With as const - literal types preserved
247
+ const config = {
248
+ endpoint: '/api',
249
+ methods: ['GET', 'POST'],
250
+ } as const;
251
+ // type: { readonly endpoint: '/api'; readonly methods: readonly ['GET', 'POST'] }
252
+
253
+ // Useful for union types from arrays
254
+ const ROLES = ['admin', 'user', 'guest'] as const;
255
+ type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest'
256
+
257
+ // Object values as union
258
+ const STATUS = {
259
+ PENDING: 'pending',
260
+ ACTIVE: 'active',
261
+ INACTIVE: 'inactive',
262
+ } as const;
263
+ type Status = typeof STATUS[keyof typeof STATUS];
264
+ // 'pending' | 'active' | 'inactive'
265
+ ```
266
+
267
+ ## Conditional Types
268
+
269
+ ```typescript
270
+ // Basic conditional
271
+ type IsString<T> = T extends string ? true : false;
272
+
273
+ type A = IsString<string>; // true
274
+ type B = IsString<number>; // false
275
+
276
+ // Infer keyword - extract types
277
+ type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
278
+
279
+ type X = UnwrapPromise<Promise<string>>; // string
280
+ type Y = UnwrapPromise<number>; // number
281
+
282
+ // Extract array element type
283
+ type ArrayElement<T> = T extends (infer E)[] ? E : never;
284
+
285
+ type El = ArrayElement<string[]>; // string
286
+
287
+ // Function return type extraction
288
+ type GetReturn<T> = T extends (...args: any[]) => infer R ? R : never;
289
+
290
+ // Distributive conditional types
291
+ type ToArray<T> = T extends any ? T[] : never;
292
+
293
+ type Distributed = ToArray<string | number>;
294
+ // string[] | number[] (not (string | number)[])
295
+ ```
296
+
297
+ ## Mapped Types
298
+
299
+ ```typescript
300
+ // Make all properties optional
301
+ type Optional<T> = {
302
+ [K in keyof T]?: T[K];
303
+ };
304
+
305
+ // Make all properties readonly
306
+ type Immutable<T> = {
307
+ readonly [K in keyof T]: T[K];
308
+ };
309
+
310
+ // Transform property types
311
+ type Stringify<T> = {
312
+ [K in keyof T]: string;
313
+ };
314
+
315
+ // Key remapping (4.1+)
316
+ type Getters<T> = {
317
+ [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
318
+ };
319
+
320
+ interface Person {
321
+ name: string;
322
+ age: number;
323
+ }
324
+
325
+ type PersonGetters = Getters<Person>;
326
+ // { getName: () => string; getAge: () => number }
327
+
328
+ // Filter properties by type
329
+ type OnlyStrings<T> = {
330
+ [K in keyof T as T[K] extends string ? K : never]: T[K];
331
+ };
332
+ ```
333
+
334
+ ## Template Literal Types
335
+
336
+ ```typescript
337
+ // Basic template literal
338
+ type Greeting = `Hello, ${string}!`;
339
+
340
+ // Event names
341
+ type EventName = `on${Capitalize<'click' | 'focus' | 'blur'>}`;
342
+ // 'onClick' | 'onFocus' | 'onBlur'
343
+
344
+ // API routes
345
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
346
+ type Route = '/users' | '/posts';
347
+ type Endpoint = `${HttpMethod} ${Route}`;
348
+ // 'GET /users' | 'GET /posts' | 'POST /users' | ...
349
+
350
+ // CSS units
351
+ type CSSUnit = 'px' | 'em' | 'rem' | '%';
352
+ type CSSValue = `${number}${CSSUnit}`;
353
+
354
+ const width: CSSValue = '100px'; // OK
355
+ const height: CSSValue = '50%'; // OK
356
+ ```
@@ -0,0 +1,212 @@
1
+ ---
2
+ paths:
3
+ - "**/*.ts"
4
+ - "**/*.tsx"
5
+ ---
6
+
7
+ # TypeScript Code Style Rules
8
+
9
+ ## Strict Mode Required
10
+
11
+ ```json
12
+ {
13
+ "strict": true,
14
+ "noImplicitAny": true,
15
+ "strictNullChecks": true
16
+ }
17
+ ```
18
+
19
+ ## Type Guidelines
20
+
21
+ ### Interfaces vs Types
22
+
23
+ ```typescript
24
+ // Interface for object shapes
25
+ interface User {
26
+ id: string;
27
+ name: string;
28
+ email: string;
29
+ createdAt: Date;
30
+ }
31
+
32
+ // Type for unions/intersections
33
+ type Status = 'pending' | 'active' | 'inactive';
34
+ type Result<T> = { success: true; data: T } | { success: false; error: string };
35
+ ```
36
+
37
+ ### NEVER Use `any`
38
+
39
+ **`any` is forbidden.** It disables type checking and defeats the purpose of TypeScript.
40
+
41
+ | Instead of `any` | Use |
42
+ |------------------|-----|
43
+ | Unknown data | `unknown` + type guard |
44
+ | Object with unknown keys | `Record<string, unknown>` |
45
+ | Array of unknown | `unknown[]` |
46
+ | Function args | Generics `<T>` |
47
+ | JSON response | Define interface or use `unknown` |
48
+ | Third-party lib | `@types/*` or declare module |
49
+
50
+ ```typescript
51
+ // BAD - NEVER do this
52
+ function process(data: any) { }
53
+ const response = await fetch(url) as any;
54
+ const items: any[] = [];
55
+
56
+ // GOOD - unknown + type guard
57
+ function process(data: unknown) {
58
+ if (isValidData(data)) {
59
+ // data is now typed
60
+ }
61
+ }
62
+
63
+ // GOOD - explicit type
64
+ function process(data: UserInput) { }
65
+
66
+ // GOOD - generic
67
+ function transform<T>(data: T): T { }
68
+
69
+ // GOOD - Record for dynamic keys
70
+ const cache: Record<string, unknown> = {};
71
+ ```
72
+
73
+ **Exception**: Only when interfacing with untyped legacy code, with explicit justification:
74
+ ```typescript
75
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Legacy API v1, see TECH-123
76
+ const legacyResponse = await oldApi.fetch() as any;
77
+ ```
78
+
79
+ ### Explicit Return Types on Public Functions
80
+
81
+ ```typescript
82
+ // BAD - inferred return type
83
+ export function getUser(id: string) {
84
+ return db.users.findById(id);
85
+ }
86
+
87
+ // GOOD - explicit return type
88
+ export function getUser(id: string): Promise<User | null> {
89
+ return db.users.findById(id);
90
+ }
91
+ ```
92
+
93
+ ## Naming Conventions
94
+
95
+ ### Be Explicit - No Cryptic Names
96
+
97
+ ```typescript
98
+ // BAD - cryptic names
99
+ const c = getConfig();
100
+ users.filter(u => u.a);
101
+ const d = new Date();
102
+ const tmp = calculate();
103
+
104
+ // GOOD - explicit names
105
+ const appConfig = getConfig();
106
+ users.filter(user => user.isActive);
107
+ const createdAt = new Date();
108
+ const totalAmount = calculate();
109
+ ```
110
+
111
+ ### Named Constants Over Magic Numbers
112
+
113
+ ```typescript
114
+ // BAD - magic numbers
115
+ if (password.length < 8) { }
116
+ setTimeout(fn, 86400000);
117
+
118
+ // GOOD - named constants
119
+ const MIN_PASSWORD_LENGTH = 8;
120
+ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
121
+
122
+ if (password.length < MIN_PASSWORD_LENGTH) { }
123
+ setTimeout(fn, ONE_DAY_MS);
124
+ ```
125
+
126
+ ## Lint Disable Rules
127
+
128
+ ```typescript
129
+ // FORBIDDEN - no justification
130
+ // eslint-disable-next-line
131
+ const x = something;
132
+
133
+ // ALLOWED - with explicit reason + ticket
134
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Legacy API returns any, see TECH-456
135
+ const legacyData = await legacyApi.fetch();
136
+ ```
137
+
138
+ ## Function Guidelines
139
+
140
+ ### Small Functions (< 30 lines)
141
+
142
+ ```typescript
143
+ // BAD - too long, multiple responsibilities
144
+ async function processOrder(order: Order) {
145
+ // 50+ lines of validation, calculation, saving, emailing...
146
+ }
147
+
148
+ // GOOD - single responsibility
149
+ async function processOrder(order: Order): Promise<OrderResult> {
150
+ validateOrder(order);
151
+ const total = calculateTotal(order);
152
+ const savedOrder = await saveOrder(order, total);
153
+ await sendConfirmation(savedOrder);
154
+ return savedOrder;
155
+ }
156
+ ```
157
+
158
+ ### Max Nesting: 3 Levels - Use Early Returns
159
+
160
+ ```typescript
161
+ // BAD - deep nesting
162
+ function process(user: User | null) {
163
+ if (user) {
164
+ if (user.isActive) {
165
+ if (user.hasPermission) {
166
+ // do something
167
+ }
168
+ }
169
+ }
170
+ }
171
+
172
+ // GOOD - early returns
173
+ function process(user: User | null) {
174
+ if (!user) return;
175
+ if (!user.isActive) return;
176
+ if (!user.hasPermission) return;
177
+
178
+ // do something
179
+ }
180
+ ```
181
+
182
+ ## Error Handling
183
+
184
+ ### Never Swallow Errors Silently
185
+
186
+ ```typescript
187
+ // BAD - silent failure
188
+ try {
189
+ await saveUser(user);
190
+ } catch (error) {
191
+ // empty catch
192
+ }
193
+
194
+ // GOOD - log with context
195
+ try {
196
+ await saveUser(user);
197
+ } catch (error) {
198
+ logger.error('Failed to save user', { userId: user.id, error });
199
+ throw error;
200
+ }
201
+ ```
202
+
203
+ ### User-Facing vs Internal Errors
204
+
205
+ ```typescript
206
+ // User-facing: clear, actionable message
207
+ throw new BadRequestError('Email address is already registered');
208
+
209
+ // Internal: detailed logs, generic user message
210
+ logger.error('Database constraint violation', { error, userId });
211
+ throw new InternalError('Unable to complete registration');
212
+ ```
@@ -0,0 +1,48 @@
1
+ ---
2
+ paths:
3
+ - "**/*"
4
+ ---
5
+
6
+ # Error Handling Principles
7
+
8
+ ## Core Rules
9
+
10
+ - **Fail fast, fail loud** - Don't swallow errors silently
11
+ - **Use typed errors** with error codes
12
+ - **Separate user-facing from internal errors**
13
+ - **Always log with context** before throwing
14
+
15
+ ## Error Categories
16
+
17
+ | Type | HTTP Code | When to Use |
18
+ |------|-----------|-------------|
19
+ | Validation | 400 | Invalid input format |
20
+ | Authentication | 401 | Missing/invalid credentials |
21
+ | Authorization | 403 | Insufficient permissions |
22
+ | Not Found | 404 | Resource doesn't exist |
23
+ | Conflict | 409 | Duplicate resource |
24
+ | Internal | 500 | Unexpected server error |
25
+
26
+ ## Error Response Format
27
+
28
+ Consistent API error responses should include:
29
+ - Error code (machine-readable)
30
+ - Message (human-readable)
31
+ - Details/context (optional)
32
+ - Request ID (for correlation)
33
+
34
+ ## Anti-Patterns
35
+
36
+ - Empty catch blocks
37
+ - Logging without rethrowing
38
+ - Exposing stack traces to users
39
+ - Generic "Something went wrong" without logging details
40
+ - Swallowing errors in fire-and-forget operations
41
+
42
+ ## Best Practices
43
+
44
+ - Create error hierarchy with base error class
45
+ - Include enough context for debugging
46
+ - Use Result/Either pattern for expected failures
47
+ - Implement retry with exponential backoff for transient errors
48
+ - Centralize error handling (middleware/interceptor)
@@ -0,0 +1,45 @@
1
+ ---
2
+ paths:
3
+ - "**/*"
4
+ ---
5
+
6
+ # Logging Principles
7
+
8
+ ## Log Levels
9
+
10
+ | Level | When to Use |
11
+ |-------|-------------|
12
+ | `debug` | Development details, variable values |
13
+ | `info` | Business events, state changes |
14
+ | `warn` | Recoverable issues, deprecations |
15
+ | `error` | Failures requiring attention |
16
+
17
+ ## What to Log
18
+
19
+ - Application lifecycle (startup, shutdown)
20
+ - Business events (order placed, user registered)
21
+ - External calls with duration (HTTP, DB)
22
+ - Errors with full context
23
+
24
+ ## What NOT to Log
25
+
26
+ - Passwords
27
+ - Auth tokens
28
+ - Credit card numbers
29
+ - PII (SSN, personal data)
30
+ - Full request bodies (may contain secrets)
31
+
32
+ ## Best Practices
33
+
34
+ - **Structured logging** (JSON) in production
35
+ - **Include context**: userId, requestId, traceId
36
+ - **Redact sensitive fields** before logging
37
+ - **Correlation IDs** across async operations
38
+ - **Log at boundaries**: API entry/exit, external calls
39
+
40
+ ## Log Message Guidelines
41
+
42
+ - Use consistent format across the codebase
43
+ - Include actionable information
44
+ - Avoid logging the same event multiple times
45
+ - Don't log expected/handled errors at ERROR level