@malamute/ai-rules 1.0.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 (46) hide show
  1. package/README.md +174 -0
  2. package/bin/cli.js +5 -0
  3. package/configs/_shared/.claude/commands/fix-issue.md +38 -0
  4. package/configs/_shared/.claude/commands/generate-tests.md +49 -0
  5. package/configs/_shared/.claude/commands/review-pr.md +77 -0
  6. package/configs/_shared/.claude/rules/accessibility.md +270 -0
  7. package/configs/_shared/.claude/rules/performance.md +226 -0
  8. package/configs/_shared/.claude/rules/security.md +188 -0
  9. package/configs/_shared/.claude/skills/debug/SKILL.md +118 -0
  10. package/configs/_shared/.claude/skills/learning/SKILL.md +224 -0
  11. package/configs/_shared/.claude/skills/review/SKILL.md +86 -0
  12. package/configs/_shared/.claude/skills/spec/SKILL.md +112 -0
  13. package/configs/_shared/CLAUDE.md +174 -0
  14. package/configs/angular/.claude/rules/components.md +257 -0
  15. package/configs/angular/.claude/rules/state.md +250 -0
  16. package/configs/angular/.claude/rules/testing.md +422 -0
  17. package/configs/angular/.claude/settings.json +31 -0
  18. package/configs/angular/CLAUDE.md +251 -0
  19. package/configs/dotnet/.claude/rules/api.md +370 -0
  20. package/configs/dotnet/.claude/rules/architecture.md +199 -0
  21. package/configs/dotnet/.claude/rules/database/efcore.md +408 -0
  22. package/configs/dotnet/.claude/rules/testing.md +389 -0
  23. package/configs/dotnet/.claude/settings.json +9 -0
  24. package/configs/dotnet/CLAUDE.md +319 -0
  25. package/configs/nestjs/.claude/rules/auth.md +321 -0
  26. package/configs/nestjs/.claude/rules/database/prisma.md +305 -0
  27. package/configs/nestjs/.claude/rules/database/typeorm.md +379 -0
  28. package/configs/nestjs/.claude/rules/modules.md +215 -0
  29. package/configs/nestjs/.claude/rules/testing.md +315 -0
  30. package/configs/nestjs/.claude/rules/validation.md +279 -0
  31. package/configs/nestjs/.claude/settings.json +15 -0
  32. package/configs/nestjs/CLAUDE.md +263 -0
  33. package/configs/nextjs/.claude/rules/components.md +211 -0
  34. package/configs/nextjs/.claude/rules/state/redux-toolkit.md +429 -0
  35. package/configs/nextjs/.claude/rules/state/zustand.md +299 -0
  36. package/configs/nextjs/.claude/rules/testing.md +315 -0
  37. package/configs/nextjs/.claude/settings.json +29 -0
  38. package/configs/nextjs/CLAUDE.md +376 -0
  39. package/configs/python/.claude/rules/database/sqlalchemy.md +355 -0
  40. package/configs/python/.claude/rules/fastapi.md +272 -0
  41. package/configs/python/.claude/rules/flask.md +332 -0
  42. package/configs/python/.claude/rules/testing.md +374 -0
  43. package/configs/python/.claude/settings.json +18 -0
  44. package/configs/python/CLAUDE.md +273 -0
  45. package/package.json +41 -0
  46. package/src/install.js +315 -0
@@ -0,0 +1,257 @@
1
+ ---
2
+ paths:
3
+ - "libs/**/feature/**/*.ts"
4
+ - "libs/**/ui/**/*.ts"
5
+ - "apps/**/*.component.ts"
6
+ ---
7
+
8
+ # Component Rules (Angular 21)
9
+
10
+ ## Code Quality
11
+
12
+ ### No Useless Comments
13
+
14
+ Code must be self-documenting. Variable and function names must be explicit.
15
+
16
+ ```typescript
17
+ // BAD
18
+ const c = getConfig(); // get the config
19
+ items.filter(x => x.active);
20
+
21
+ // GOOD
22
+ const applicationConfig = getConfig();
23
+ items.filter(item => item.active);
24
+ users.map(user => user.email);
25
+ ```
26
+
27
+ ### Never Disable Lint Rules Without Justification
28
+
29
+ ```typescript
30
+ // FORBIDDEN - no explanation
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+
33
+ // ALLOWED - only with explicit justification
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Third-party API returns untyped response, ticket ABC-123 to add types
35
+ ```
36
+
37
+ ### ChangeDetectionStrategy.OnPush
38
+
39
+ Use `OnPush` for performance optimization. Required for:
40
+ - All UI components (dumb components)
41
+ - Feature components with signal-based state
42
+ - Any component where you control the inputs
43
+
44
+ ```typescript
45
+ @Component({
46
+ changeDetection: ChangeDetectionStrategy.OnPush, // Always add this
47
+ })
48
+ ```
49
+
50
+ ## File Structure
51
+
52
+ Always use separate files for template and styles:
53
+
54
+ ```
55
+ user-list/
56
+ user-list.component.ts
57
+ user-list.component.html
58
+ user-list.component.scss
59
+ user-list.component.spec.ts
60
+ ```
61
+
62
+ ## @Component Decorator
63
+
64
+ ```typescript
65
+ @Component({
66
+ selector: 'app-user-list',
67
+ imports: [SomeComponent, SomePipe], // Direct imports
68
+ templateUrl: './user-list.component.html', // Always external
69
+ styleUrl: './user-list.component.scss', // Always external
70
+ changeDetection: ChangeDetectionStrategy.OnPush,
71
+ })
72
+ ```
73
+
74
+ **Do NOT include:**
75
+ - `standalone: true` (it's the default)
76
+ - `template:` inline templates
77
+ - `styles:` inline styles
78
+
79
+ ## Smart Components (feature/)
80
+
81
+ Smart components are located in `feature/` libraries.
82
+
83
+ ### Allowed
84
+
85
+ - Inject `Store` from NgRx
86
+ - Dispatch actions
87
+ - Use `selectSignal()` for state
88
+ - Handle routing and navigation
89
+ - Contain page-level logic
90
+
91
+ ### Required
92
+
93
+ - Pass data to UI components via `input()` signals
94
+ - Handle events from UI components via `output()`
95
+ - Use `ChangeDetectionStrategy.OnPush`
96
+ - Use `templateUrl` (external template file)
97
+
98
+ ### Example
99
+
100
+ ```typescript
101
+ // product-page.component.ts
102
+ @Component({
103
+ selector: 'app-product-page',
104
+ imports: [ProductListComponent, ProductFiltersComponent],
105
+ templateUrl: './product-page.component.html',
106
+ changeDetection: ChangeDetectionStrategy.OnPush,
107
+ })
108
+ export class ProductPageComponent {
109
+ private readonly store = inject(Store);
110
+
111
+ categories = this.store.selectSignal(selectCategories);
112
+ filteredProducts = this.store.selectSignal(selectFilteredProducts);
113
+ loading = this.store.selectSignal(selectProductsLoading);
114
+
115
+ onFilterChange(filters: Filters): void {
116
+ this.store.dispatch(ProductActions.setFilters({ filters }));
117
+ }
118
+
119
+ onAddToCart(product: Product): void {
120
+ this.store.dispatch(CartActions.addItem({ product }));
121
+ }
122
+ }
123
+ ```
124
+
125
+ ```html
126
+ <!-- product-page.component.html -->
127
+ <app-product-filters
128
+ [categories]="categories()"
129
+ (filterChange)="onFilterChange($event)"
130
+ />
131
+
132
+ <app-product-list
133
+ [products]="filteredProducts()"
134
+ [loading]="loading()"
135
+ (addToCart)="onAddToCart($event)"
136
+ />
137
+ ```
138
+
139
+ ## UI Components (ui/)
140
+
141
+ UI components are located in `ui/` libraries. They are purely presentational.
142
+
143
+ ### Forbidden
144
+
145
+ - NO `Store` injection
146
+ - NO service injection (except pure utility services)
147
+ - NO direct HTTP calls
148
+ - NO router navigation
149
+ - NO business logic
150
+
151
+ ### Required
152
+
153
+ - Use `input()` and `input.required()` for data
154
+ - Use `output()` for events
155
+ - Use `model()` for two-way binding
156
+ - Use `ChangeDetectionStrategy.OnPush`
157
+ - Must be fully testable in isolation
158
+
159
+ ### Example
160
+
161
+ ```typescript
162
+ // product-card.component.ts
163
+ @Component({
164
+ selector: 'app-product-card',
165
+ imports: [CurrencyPipe],
166
+ templateUrl: './product-card.component.html',
167
+ styleUrl: './product-card.component.scss',
168
+ changeDetection: ChangeDetectionStrategy.OnPush,
169
+ })
170
+ export class ProductCardComponent {
171
+ product = input.required<Product>();
172
+ addToCart = output<Product>();
173
+ }
174
+ ```
175
+
176
+ ```html
177
+ <!-- product-card.component.html -->
178
+ <article class="product-card">
179
+ <img [src]="product().image" [alt]="product().name" />
180
+ <h3>{{ product().name }}</h3>
181
+ <p class="price">{{ product().price | currency }}</p>
182
+ <button type="button" (click)="addToCart.emit(product())">
183
+ Add to Cart
184
+ </button>
185
+ </article>
186
+ ```
187
+
188
+ ## Signal Inputs/Outputs/Model
189
+
190
+ Always use signal-based functions, NOT decorators:
191
+
192
+ ```typescript
193
+ // Inputs - use input(), NOT @Input()
194
+ name = input<string>(); // Optional
195
+ name = input('default'); // With default
196
+ name = input.required<string>(); // Required
197
+
198
+ // Outputs - use output(), NOT @Output()
199
+ clicked = output<void>();
200
+ selected = output<Item>();
201
+
202
+ // Two-way binding - use model()
203
+ value = model<string>(''); // Optional with default
204
+ value = model.required<string>(); // Required
205
+
206
+ // Emit
207
+ this.clicked.emit();
208
+ this.selected.emit(item);
209
+ this.value.set('new value'); // model is writable
210
+ ```
211
+
212
+ ### Two-way Binding with model()
213
+
214
+ ```typescript
215
+ // Component
216
+ @Component({
217
+ selector: 'app-search-input',
218
+ templateUrl: './search-input.component.html',
219
+ })
220
+ export class SearchInputComponent {
221
+ query = model(''); // Creates [(query)] binding
222
+ }
223
+ ```
224
+
225
+ ```html
226
+ <!-- Parent usage -->
227
+ <app-search-input [(query)]="searchQuery" />
228
+ ```
229
+
230
+ ## Control Flow (in templates)
231
+
232
+ Use the built-in control flow syntax:
233
+
234
+ ```html
235
+ <!-- Conditionals -->
236
+ @if (loading()) {
237
+ <app-spinner />
238
+ } @else if (error()) {
239
+ <app-error [message]="error()" />
240
+ } @else {
241
+ <app-content [data]="data()" />
242
+ }
243
+
244
+ <!-- Loops - always use track! -->
245
+ @for (item of items(); track item.id) {
246
+ <app-item [item]="item" />
247
+ } @empty {
248
+ <p>No items found</p>
249
+ }
250
+
251
+ <!-- Switch -->
252
+ @switch (status()) {
253
+ @case ('loading') { <app-spinner /> }
254
+ @case ('error') { <app-error /> }
255
+ @default { <app-content /> }
256
+ }
257
+ ```
@@ -0,0 +1,250 @@
1
+ ---
2
+ paths:
3
+ - "libs/**/data-access/**/*.ts"
4
+ - "libs/**/+state/**/*.ts"
5
+ ---
6
+
7
+ # NgRx State Management Rules
8
+
9
+ ## File Structure
10
+
11
+ ```
12
+ libs/[domain]/data-access/
13
+ src/lib/
14
+ +state/
15
+ [domain].actions.ts
16
+ [domain].reducer.ts
17
+ [domain].effects.ts
18
+ [domain].selectors.ts
19
+ [domain].adapter.ts # If using @ngrx/entity
20
+ [domain].state.ts # State interface
21
+ services/
22
+ [domain].service.ts # API calls
23
+ index.ts # Public API
24
+ ```
25
+
26
+ ## Actions
27
+
28
+ Use `createActionGroup` for related actions:
29
+
30
+ ```typescript
31
+ import { createActionGroup, emptyProps, props } from '@ngrx/store';
32
+
33
+ export const UserActions = createActionGroup({
34
+ source: 'Users',
35
+ events: {
36
+ // Load
37
+ 'Load Users': emptyProps(),
38
+ 'Load Users Success': props<{ users: User[] }>(),
39
+ 'Load Users Failure': props<{ error: string }>(),
40
+
41
+ // CRUD
42
+ 'Add User': props<{ user: User }>(),
43
+ 'Update User': props<{ update: Update<User> }>(),
44
+ 'Delete User': props<{ id: string }>(),
45
+
46
+ // Selection
47
+ 'Select User': props<{ id: string }>(),
48
+ 'Clear Selection': emptyProps(),
49
+ },
50
+ });
51
+ ```
52
+
53
+ ## Reducer with Entity Adapter
54
+
55
+ Always use `@ngrx/entity` for collections:
56
+
57
+ ```typescript
58
+ // [domain].adapter.ts
59
+ import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';
60
+
61
+ export interface UserState extends EntityState<User> {
62
+ selectedId: string | null;
63
+ loading: boolean;
64
+ error: string | null;
65
+ }
66
+
67
+ export const userAdapter: EntityAdapter<User> = createEntityAdapter<User>({
68
+ selectId: (user) => user.id,
69
+ sortComparer: (a, b) => a.name.localeCompare(b.name),
70
+ });
71
+
72
+ export const initialUserState: UserState = userAdapter.getInitialState({
73
+ selectedId: null,
74
+ loading: false,
75
+ error: null,
76
+ });
77
+ ```
78
+
79
+ ```typescript
80
+ // [domain].reducer.ts
81
+ import { createReducer, on } from '@ngrx/store';
82
+
83
+ export const userReducer = createReducer(
84
+ initialUserState,
85
+
86
+ on(UserActions.loadUsers, (state) => ({
87
+ ...state,
88
+ loading: true,
89
+ error: null,
90
+ })),
91
+
92
+ on(UserActions.loadUsersSuccess, (state, { users }) =>
93
+ userAdapter.setAll(users, { ...state, loading: false })
94
+ ),
95
+
96
+ on(UserActions.loadUsersFailure, (state, { error }) => ({
97
+ ...state,
98
+ loading: false,
99
+ error,
100
+ })),
101
+
102
+ on(UserActions.addUser, (state, { user }) =>
103
+ userAdapter.addOne(user, state)
104
+ ),
105
+
106
+ on(UserActions.updateUser, (state, { update }) =>
107
+ userAdapter.updateOne(update, state)
108
+ ),
109
+
110
+ on(UserActions.deleteUser, (state, { id }) =>
111
+ userAdapter.removeOne(id, state)
112
+ ),
113
+
114
+ on(UserActions.selectUser, (state, { id }) => ({
115
+ ...state,
116
+ selectedId: id,
117
+ })),
118
+
119
+ on(UserActions.clearSelection, (state) => ({
120
+ ...state,
121
+ selectedId: null,
122
+ }))
123
+ );
124
+ ```
125
+
126
+ ## Selectors
127
+
128
+ Use adapter selectors and compose them:
129
+
130
+ ```typescript
131
+ // [domain].selectors.ts
132
+ import { createFeatureSelector, createSelector } from '@ngrx/store';
133
+
134
+ // Feature selector
135
+ export const selectUserState = createFeatureSelector<UserState>('users');
136
+
137
+ // Adapter selectors
138
+ const { selectAll, selectEntities, selectIds, selectTotal } =
139
+ userAdapter.getSelectors();
140
+
141
+ // Exported selectors
142
+ export const selectAllUsers = createSelector(selectUserState, selectAll);
143
+
144
+ export const selectUserEntities = createSelector(selectUserState, selectEntities);
145
+
146
+ export const selectUsersLoading = createSelector(
147
+ selectUserState,
148
+ (state) => state.loading
149
+ );
150
+
151
+ export const selectUsersError = createSelector(
152
+ selectUserState,
153
+ (state) => state.error
154
+ );
155
+
156
+ export const selectSelectedUserId = createSelector(
157
+ selectUserState,
158
+ (state) => state.selectedId
159
+ );
160
+
161
+ export const selectSelectedUser = createSelector(
162
+ selectUserEntities,
163
+ selectSelectedUserId,
164
+ (entities, selectedId) => (selectedId ? entities[selectedId] : null)
165
+ );
166
+
167
+ // Derived/computed selectors
168
+ export const selectActiveUsers = createSelector(
169
+ selectAllUsers,
170
+ (users) => users.filter((u) => u.active)
171
+ );
172
+
173
+ export const selectUserCount = createSelector(
174
+ selectUserState,
175
+ selectTotal
176
+ );
177
+ ```
178
+
179
+ ## Effects
180
+
181
+ Keep effects clean and focused:
182
+
183
+ ```typescript
184
+ // [domain].effects.ts
185
+ import { inject } from '@angular/core';
186
+ import { Actions, createEffect, ofType } from '@ngrx/effects';
187
+ import { catchError, exhaustMap, map, of } from 'rxjs';
188
+
189
+ export const loadUsers$ = createEffect(
190
+ (actions$ = inject(Actions), userService = inject(UserService)) =>
191
+ actions$.pipe(
192
+ ofType(UserActions.loadUsers),
193
+ exhaustMap(() =>
194
+ userService.getAll().pipe(
195
+ map((users) => UserActions.loadUsersSuccess({ users })),
196
+ catchError((error) =>
197
+ of(UserActions.loadUsersFailure({ error: error.message }))
198
+ )
199
+ )
200
+ )
201
+ ),
202
+ { functional: true }
203
+ );
204
+
205
+ export const addUser$ = createEffect(
206
+ (actions$ = inject(Actions), userService = inject(UserService)) =>
207
+ actions$.pipe(
208
+ ofType(UserActions.addUser),
209
+ exhaustMap(({ user }) =>
210
+ userService.create(user).pipe(
211
+ map((created) => UserActions.addUserSuccess({ user: created })),
212
+ catchError((error) =>
213
+ of(UserActions.addUserFailure({ error: error.message }))
214
+ )
215
+ )
216
+ )
217
+ ),
218
+ { functional: true }
219
+ );
220
+ ```
221
+
222
+ ## RxJS Operator Guidelines
223
+
224
+ | Scenario | Operator |
225
+ |----------|----------|
226
+ | Single request, cancel previous | `switchMap` |
227
+ | Single request, ignore while pending | `exhaustMap` |
228
+ | Queue all requests | `concatMap` |
229
+ | Parallel requests | `mergeMap` |
230
+
231
+ ## Store Usage in Components
232
+
233
+ Only in smart components (feature/):
234
+
235
+ ```typescript
236
+ // Use selectSignal for signal-based selection
237
+ users = this.store.selectSignal(selectAllUsers);
238
+ loading = this.store.selectSignal(selectUsersLoading);
239
+
240
+ // Dispatch actions
241
+ this.store.dispatch(UserActions.loadUsers());
242
+ ```
243
+
244
+ ## Anti-patterns to Avoid
245
+
246
+ - Never store derived state in the store (use selectors)
247
+ - Never dispatch actions from effects that trigger the same effect
248
+ - Never use `store.select()` in UI components
249
+ - Never mutate state directly
250
+ - Avoid fat effects - keep business logic in services