@malamute/ai-rules 1.4.0 → 1.5.1

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.
package/README.md CHANGED
@@ -41,14 +41,15 @@ npx @malamute/ai-rules <command>
41
41
 
42
42
  ## Supported Technologies
43
43
 
44
- | Technology | Stack | Version |
45
- | ----------- | ----------------------------------------- | ------- |
46
- | **Angular** | Nx + NgRx + Signals + Vitest | 21+ |
47
- | **Next.js** | App Router + React 19 + Server Components | 15+ |
48
- | **NestJS** | Prisma/TypeORM + Passport + Vitest | 11+ |
49
- | **.NET** | Clean Architecture + MediatR + EF Core | 9+ |
50
- | **FastAPI** | Pydantic v2 + SQLAlchemy 2.0 + pytest | 0.115+ |
51
- | **Flask** | Marshmallow + SQLAlchemy 2.0 + pytest | 3.0+ |
44
+ | Technology | Stack | Version |
45
+ | ------------ | ----------------------------------------- | ------- |
46
+ | **Angular** | Nx + NgRx + Signals + Vitest | 21+ |
47
+ | **Next.js** | App Router + React 19 + Server Components | 15+ |
48
+ | **NestJS** | Prisma/TypeORM + Passport + Vitest | 11+ |
49
+ | **AdonisJS** | Lucid ORM + VineJS + Japa | 6+ |
50
+ | **.NET** | Clean Architecture + MediatR + EF Core | 9+ |
51
+ | **FastAPI** | Pydantic v2 + SQLAlchemy 2.0 + pytest | 0.115+ |
52
+ | **Flask** | Marshmallow + SQLAlchemy 2.0 + pytest | 3.0+ |
52
53
 
53
54
  ## Commands
54
55
 
@@ -121,17 +122,19 @@ Interactive workflows invoked with `/skill-name`:
121
122
  | `/review` | Code review with security/perf checklist |
122
123
  | `/debug` | Structured debugging workflow |
123
124
  | `/spec` | Write technical spec before implementing |
125
+ | `/sudden-death` | Kill indecision with rapid-fire questions |
124
126
  | `/fix-issue` | Analyze GitHub issue and implement fix |
125
127
  | `/generate-tests` | Generate comprehensive tests |
126
128
 
127
129
  <details>
128
- <summary><strong>See all 13 skills</strong></summary>
130
+ <summary><strong>See all 14 skills</strong></summary>
129
131
 
130
132
  | Skill | Usage | Description |
131
133
  | ----------------- | ----------------------------- | ------------------------------------- |
132
134
  | `/learning` | `/learning nextjs` | Explains concepts before implementing |
133
135
  | `/review` | `/review src/users/` | Code review with checklist |
134
136
  | `/spec` | `/spec add auth` | Technical specification |
137
+ | `/sudden-death` | `/sudden-death backend` | Kill indecision, get a verdict |
135
138
  | `/debug` | `/debug TypeError...` | Systematic debugging |
136
139
  | `/fix-issue` | `/fix-issue 123` | Fix GitHub issue |
137
140
  | `/review-pr` | `/review-pr 456` | Review pull request |
@@ -253,6 +256,19 @@ ai-rules update
253
256
 
254
257
  </details>
255
258
 
259
+ <details>
260
+ <summary><strong>AdonisJS</strong></summary>
261
+
262
+ | Aspect | Convention |
263
+ | ------------ | ----------------------------------- |
264
+ | Architecture | MVC with Services layer |
265
+ | Validation | VineJS |
266
+ | ORM | Lucid (Active Record) |
267
+ | Auth | Access Tokens / Session-based |
268
+ | Tests | Japa |
269
+
270
+ </details>
271
+
256
272
  <details>
257
273
  <summary><strong>.NET</strong></summary>
258
274
 
@@ -0,0 +1,38 @@
1
+ ---
2
+ paths:
3
+ - "**/*"
4
+ ---
5
+
6
+ # Interaction Rules
7
+
8
+ ## Rules Are Absolute
9
+
10
+ 1. **Rules can NEVER be violated. Tasks can fail.**
11
+ 2. If a task requires violating a rule, the task fails - not the rule.
12
+ 3. If a task is blocked, explain the problem and ask how to proceed.
13
+
14
+ ## Protected Changes
15
+
16
+ Never modify without explaining WHY and asking permission:
17
+ - Package manager config (yarn, npm, pnpm)
18
+ - Infrastructure (docker, CI/CD, deployment)
19
+ - Project structure
20
+ - Build config
21
+
22
+ ## Questions vs Actions
23
+
24
+ - **Question** ("what is...", "why...", "how does...") → Answer only, no code
25
+ - **Explicit request** ("create", "implement", "fix", "add") → Action with code
26
+
27
+ When the user asks a question, answer it. Do not start coding or running commands.
28
+
29
+ ## Confirmation Before Action
30
+
31
+ For non-trivial changes, confirm approach before implementing:
32
+ 1. Explain what will be done
33
+ 2. Wait for user approval
34
+ 3. Then execute
35
+
36
+ ## Language
37
+
38
+ Match the user's language. If they write in French, respond in French.
@@ -151,21 +151,77 @@ GET /api/v1/users?sort=lastName:asc,firstName:asc
151
151
  GET /api/v1/users?fields=id,name,email
152
152
  ```
153
153
 
154
- ## Versioning
154
+ ## API Versioning
155
155
 
156
- ### URL Path (recommended)
156
+ ### When to Version (Breaking Changes)
157
+
158
+ - Removing or renaming a field
159
+ - Changing field type (string → number)
160
+ - Removing an endpoint
161
+ - Changing authentication method
162
+ - Modifying error response structure
163
+
164
+ ### NOT Breaking (no version bump)
165
+
166
+ - Adding new optional fields
167
+ - Adding new endpoints
168
+ - Adding new query parameters
169
+ - Performance improvements
170
+
171
+ ### Strategy: URL Path (recommended)
157
172
 
158
173
  ```
159
174
  /api/v1/users
160
175
  /api/v2/users
161
176
  ```
162
177
 
178
+ - Simple, explicit, cacheable
179
+ - Easy to route at load balancer level
180
+ - Version visible in logs
181
+
163
182
  ### Header-based (alternative)
164
183
 
165
184
  ```
166
185
  Accept: application/vnd.api+json; version=1
167
186
  ```
168
187
 
188
+ ### Deprecation Policy
189
+
190
+ 1. Announce deprecation (minimum 6 months before sunset)
191
+ 2. Add `Deprecation` header to responses
192
+ 3. Document migration path
193
+ 4. Monitor usage, notify active consumers
194
+ 5. Sunset old version
195
+
196
+ ```
197
+ Deprecation: true
198
+ Sunset: Sat, 01 Jun 2025 00:00:00 GMT
199
+ Link: <https://api.example.com/docs/migration-v2>; rel="deprecation"
200
+ ```
201
+
202
+ ### Version Lifecycle
203
+
204
+ | Status | Description |
205
+ |--------|-------------|
206
+ | **Current** | Latest stable, recommended |
207
+ | **Supported** | Still maintained, receives security fixes |
208
+ | **Deprecated** | Works but scheduled for removal |
209
+ | **Sunset** | No longer available |
210
+
211
+ ### Code Organization
212
+
213
+ ```
214
+ project/
215
+ ├── src/
216
+ │ ├── v1/
217
+ │ │ ├── controllers/
218
+ │ │ └── dto/
219
+ │ ├── v2/
220
+ │ │ ├── controllers/
221
+ │ │ └── dto/
222
+ │ └── shared/ # Version-agnostic (services, repositories)
223
+ ```
224
+
169
225
  ## Rate Limiting
170
226
 
171
227
  Include headers in response:
@@ -0,0 +1,148 @@
1
+ ---
2
+ name: sudden-death
3
+ description: Kill indecision with rapid-fire questionnaires
4
+ argument-hint: [decision-topic]
5
+ ---
6
+
7
+ # Sudden Death Mode
8
+
9
+ You are now in **sudden death mode**. No more "it depends" - guide the user through a tournament-style elimination and deliver a decisive verdict.
10
+
11
+ **IMPORTANT: Always respond in the user's language.** If they write in French, respond in French. Polish? Polish. The examples below are in French for flavor, but adapt to the user.
12
+
13
+ ## Input
14
+
15
+ Decision topic: `$ARGUMENTS`
16
+
17
+ If no argument provided, ask: "What's on the chopping block? (e.g., backend stack, database, UI library, hosting)"
18
+
19
+ ## The Game
20
+
21
+ ### Phase 1: Candidates
22
+
23
+ List all reasonable options for the domain. Example for backend:
24
+
25
+ ```
26
+ Candidats: NestJS, Hono, Fastify, Elysia, AdonisJS, .NET, FastAPI, Go
27
+
28
+ En garde. Première question...
29
+ ```
30
+
31
+ ### Phase 2: Elimination Tournament
32
+
33
+ Ask **5-8 killer questions**. Each question should potentially eliminate candidates.
34
+
35
+ Format:
36
+ ```
37
+ ### Q1: [Short punchy question]
38
+ [Context if needed]
39
+
40
+ → User answers
41
+ → **Eliminated: [X, Y]** or **Advantage: [Z]** or **Point: [Z]**
42
+ ```
43
+
44
+ Example questions (backend stack):
45
+ - "Full TypeScript (front + back) or ok to switch languages?"
46
+ - "Structured framework (modules, DI, conventions) or minimal?"
47
+ - "Decorators (@Controller, @Get) or simple functions?"
48
+ - "Batteries included or pick your own libs?"
49
+ - "Big community or cutting-edge?"
50
+
51
+ **Elimination rules:**
52
+ - Strong preference → Eliminate mismatches immediately
53
+ - Slight preference → Note advantage, keep in race
54
+ - "Tie" / "Both good" → No elimination, move on
55
+
56
+ ### Phase 3: Final Showdown
57
+
58
+ When down to 2-3 candidates:
59
+
60
+ ```
61
+ ### Finale: [A] vs [B]
62
+
63
+ [A]: [2-3 key traits]
64
+ [B]: [2-3 key traits]
65
+ ```
66
+
67
+ If clear winner → Declare it
68
+ If tie → Go to tiebreaker
69
+
70
+ ### Phase 4: Tiebreaker (when needed)
71
+
72
+ Frame it as a **character choice**, not just technical:
73
+
74
+ ```
75
+ Score: [A] 2 - [B] 2
76
+
77
+ Tu as choisi [previous bold choice] pour sortir de ta zone.
78
+ [A] = full send, nouvelle expérience
79
+ [B] = un pied dans le connu
80
+
81
+ On est des fous ou pas ?
82
+ ```
83
+
84
+ ### Phase 5: Verdict
85
+
86
+ ```
87
+ ### Winner: **[Option]**
88
+
89
+ [One-liner why it fits THEIR specific answers]
90
+ ```
91
+
92
+ ## Between Decisions
93
+
94
+ After each major decision, recap and offer options:
95
+
96
+ ```
97
+ Stack actuelle:
98
+ - Frontend: Next.js 15
99
+ - Backend: AdonisJS
100
+ - ORM: Lucid
101
+
102
+ On continue ? Il reste:
103
+ - UI lib (shadcn, autre ?)
104
+ - State management
105
+ - Hosting
106
+
107
+ **Sudden death** ou **tu tranches direct** ?
108
+ ```
109
+
110
+ - **Sudden death** = Full questionnaire
111
+ - **Tu tranches direct** = User is confident, give quick recommendation
112
+
113
+ ## Tone
114
+
115
+ - **Playful combat** - "En garde", "Eliminated", "Survivor"
116
+ - **Call out bold choices** - "On est des fous !", "Allez on y va !"
117
+ - **No corporate speak** - Skip the "it depends on your requirements"
118
+ - **Quick and punchy** - Short questions, fast eliminations
119
+ - **Celebrate decisions** - Each choice is a win, not a compromise
120
+
121
+ ## Quick Verdict Mode
122
+
123
+ If user says "tu tranches" or wants fast advice:
124
+
125
+ ```
126
+ Pour [context], je dirais **[Option]**.
127
+
128
+ [One sentence why]
129
+
130
+ Sold ? Ou on fait un sudden death pour être sûr ?
131
+ ```
132
+
133
+ ## Adapt to Domain
134
+
135
+ Common sudden death topics:
136
+
137
+ | Domain | Typical Candidates |
138
+ |--------|-------------------|
139
+ | Backend | NestJS, Fastify, Hono, AdonisJS, .NET, FastAPI, Go |
140
+ | Frontend | Next.js, Nuxt, SvelteKit, Remix, Angular |
141
+ | Database | PostgreSQL, MySQL, MongoDB, SQLite, Supabase, PlanetScale |
142
+ | ORM | Prisma, Drizzle, TypeORM, Lucid, SQLAlchemy |
143
+ | UI | shadcn/ui, Radix, Chakra, MUI, Mantine |
144
+ | Hosting | Vercel, Railway, Render, Fly.io, AWS, Coolify |
145
+ | State | Zustand, Jotai, Redux Toolkit, Signals, TanStack Query |
146
+ | Auth | Auth.js, Lucia, Clerk, Supabase Auth, custom JWT |
147
+
148
+ For unknown domains, identify the key trade-offs and build questions on the fly.
@@ -0,0 +1,192 @@
1
+ ---
2
+ paths:
3
+ - "app/controllers/auth/**/*.ts"
4
+ - "app/middleware/**/*.ts"
5
+ - "config/auth.ts"
6
+ ---
7
+
8
+ # AdonisJS Authentication
9
+
10
+ ## Access Tokens (API)
11
+
12
+ ### Configuration
13
+
14
+ ```typescript
15
+ // config/auth.ts
16
+ import { defineConfig } from '@adonisjs/auth'
17
+ import { tokensGuard, tokensUserProvider } from '@adonisjs/auth/access_tokens'
18
+
19
+ export default defineConfig({
20
+ default: 'api',
21
+ guards: {
22
+ api: tokensGuard({
23
+ provider: tokensUserProvider({
24
+ tokens: 'accessTokens',
25
+ model: () => import('#models/user'),
26
+ }),
27
+ }),
28
+ },
29
+ })
30
+ ```
31
+
32
+ ### User Model Setup
33
+
34
+ ```typescript
35
+ import { DbAccessTokensProvider } from '@adonisjs/auth/access_tokens'
36
+
37
+ export default class User extends BaseModel {
38
+ // ... other columns
39
+
40
+ static accessTokens = DbAccessTokensProvider.forModel(User)
41
+ }
42
+ ```
43
+
44
+ ### Auth Controller
45
+
46
+ ```typescript
47
+ import type { HttpContext } from '@adonisjs/core/http'
48
+ import User from '#models/user'
49
+ import hash from '@adonisjs/core/services/hash'
50
+ import { loginValidator, registerValidator } from '#validators/auth'
51
+
52
+ export default class AuthController {
53
+ async register({ request, response }: HttpContext) {
54
+ const payload = await request.validateUsing(registerValidator)
55
+ const user = await User.create(payload)
56
+ const token = await User.accessTokens.create(user)
57
+
58
+ return response.created({
59
+ user,
60
+ token: token.value!.release(),
61
+ })
62
+ }
63
+
64
+ async login({ request, response }: HttpContext) {
65
+ const { email, password } = await request.validateUsing(loginValidator)
66
+
67
+ const user = await User.findBy('email', email)
68
+ if (!user) {
69
+ return response.unauthorized({ message: 'Invalid credentials' })
70
+ }
71
+
72
+ const isValid = await hash.verify(user.password, password)
73
+ if (!isValid) {
74
+ return response.unauthorized({ message: 'Invalid credentials' })
75
+ }
76
+
77
+ const token = await User.accessTokens.create(user)
78
+
79
+ return response.ok({
80
+ user,
81
+ token: token.value!.release(),
82
+ })
83
+ }
84
+
85
+ async logout({ auth, response }: HttpContext) {
86
+ const user = auth.user!
87
+ await User.accessTokens.delete(user, user.currentAccessToken.identifier)
88
+ return response.noContent()
89
+ }
90
+
91
+ async me({ auth, response }: HttpContext) {
92
+ return response.ok(auth.user)
93
+ }
94
+ }
95
+ ```
96
+
97
+ ### Routes
98
+
99
+ ```typescript
100
+ // start/routes.ts
101
+ import router from '@adonisjs/core/services/router'
102
+ import { middleware } from '#start/kernel'
103
+
104
+ const AuthController = () => import('#controllers/auth_controller')
105
+
106
+ router.group(() => {
107
+ router.post('register', [AuthController, 'register'])
108
+ router.post('login', [AuthController, 'login'])
109
+
110
+ router.group(() => {
111
+ router.delete('logout', [AuthController, 'logout'])
112
+ router.get('me', [AuthController, 'me'])
113
+ }).use(middleware.auth())
114
+ }).prefix('auth')
115
+ ```
116
+
117
+ ## Middleware
118
+
119
+ ### Auth Middleware
120
+
121
+ ```typescript
122
+ // Protect routes
123
+ router.get('profile', [ProfileController, 'show']).use(middleware.auth())
124
+
125
+ // In controller, access user
126
+ async show({ auth }: HttpContext) {
127
+ const user = auth.user! // Typed as User
128
+ }
129
+ ```
130
+
131
+ ### Custom Middleware
132
+
133
+ ```typescript
134
+ // app/middleware/admin_middleware.ts
135
+ import type { HttpContext } from '@adonisjs/core/http'
136
+ import type { NextFn } from '@adonisjs/core/types/http'
137
+
138
+ export default class AdminMiddleware {
139
+ async handle({ auth, response }: HttpContext, next: NextFn) {
140
+ if (auth.user?.role !== 'admin') {
141
+ return response.forbidden({ message: 'Admin access required' })
142
+ }
143
+ await next()
144
+ }
145
+ }
146
+ ```
147
+
148
+ ### Register Middleware
149
+
150
+ ```typescript
151
+ // start/kernel.ts
152
+ import router from '@adonisjs/core/services/router'
153
+
154
+ router.named({
155
+ auth: () => import('#middleware/auth_middleware'),
156
+ admin: () => import('#middleware/admin_middleware'),
157
+ })
158
+ ```
159
+
160
+ ## Password Hashing
161
+
162
+ ```typescript
163
+ import hash from '@adonisjs/core/services/hash'
164
+
165
+ // Hash password
166
+ const hashed = await hash.make('password')
167
+
168
+ // Verify password
169
+ const isValid = await hash.verify(hashed, 'password')
170
+ ```
171
+
172
+ ## Auth Validators
173
+
174
+ ```typescript
175
+ // app/validators/auth.ts
176
+ import vine from '@vinejs/vine'
177
+
178
+ export const registerValidator = vine.compile(
179
+ vine.object({
180
+ email: vine.string().email().normalizeEmail(),
181
+ password: vine.string().minLength(8).confirmed(),
182
+ name: vine.string().minLength(2),
183
+ })
184
+ )
185
+
186
+ export const loginValidator = vine.compile(
187
+ vine.object({
188
+ email: vine.string().email(),
189
+ password: vine.string(),
190
+ })
191
+ )
192
+ ```
@@ -0,0 +1,111 @@
1
+ ---
2
+ paths:
3
+ - "app/controllers/**/*.ts"
4
+ ---
5
+
6
+ # AdonisJS Controllers
7
+
8
+ ## Structure
9
+
10
+ Controllers handle HTTP concerns only. Delegate business logic to services.
11
+
12
+ ```typescript
13
+ import type { HttpContext } from '@adonisjs/core/http'
14
+ import { inject } from '@adonisjs/core'
15
+ import UserService from '#services/user_service'
16
+ import { createUserValidator, updateUserValidator } from '#validators/user'
17
+
18
+ @inject()
19
+ export default class UsersController {
20
+ constructor(private userService: UserService) {}
21
+
22
+ async index({ response }: HttpContext) {
23
+ const users = await this.userService.getAll()
24
+ return response.ok(users)
25
+ }
26
+
27
+ async store({ request, response }: HttpContext) {
28
+ const payload = await request.validateUsing(createUserValidator)
29
+ const user = await this.userService.create(payload)
30
+ return response.created(user)
31
+ }
32
+
33
+ async show({ params, response }: HttpContext) {
34
+ const user = await this.userService.findOrFail(params.id)
35
+ return response.ok(user)
36
+ }
37
+
38
+ async update({ params, request, response }: HttpContext) {
39
+ const payload = await request.validateUsing(updateUserValidator)
40
+ const user = await this.userService.update(params.id, payload)
41
+ return response.ok(user)
42
+ }
43
+
44
+ async destroy({ params, response }: HttpContext) {
45
+ await this.userService.delete(params.id)
46
+ return response.noContent()
47
+ }
48
+ }
49
+ ```
50
+
51
+ ## Best Practices
52
+
53
+ ### Use Dependency Injection
54
+
55
+ ```typescript
56
+ // Good
57
+ @inject()
58
+ export default class OrdersController {
59
+ constructor(
60
+ private orderService: OrderService,
61
+ private notificationService: NotificationService
62
+ ) {}
63
+ }
64
+
65
+ // Avoid: instantiating services manually
66
+ export default class OrdersController {
67
+ private orderService = new OrderService() // Hard to test
68
+ }
69
+ ```
70
+
71
+ ### Validate Input
72
+
73
+ Always validate using VineJS validators:
74
+
75
+ ```typescript
76
+ async store({ request }: HttpContext) {
77
+ // Validates and returns typed payload
78
+ const payload = await request.validateUsing(createOrderValidator)
79
+ // payload is now typed and validated
80
+ }
81
+ ```
82
+
83
+ ### Use Response Helpers
84
+
85
+ ```typescript
86
+ response.ok(data) // 200
87
+ response.created(data) // 201
88
+ response.noContent() // 204
89
+ response.badRequest(error) // 400
90
+ response.unauthorized() // 401
91
+ response.forbidden() // 403
92
+ response.notFound() // 404
93
+ ```
94
+
95
+ ### Resource Routes
96
+
97
+ ```typescript
98
+ // start/routes.ts
99
+ import router from '@adonisjs/core/services/router'
100
+
101
+ const UsersController = () => import('#controllers/users_controller')
102
+
103
+ router.resource('users', UsersController).apiOnly()
104
+
105
+ // Generates:
106
+ // GET /users → index
107
+ // POST /users → store
108
+ // GET /users/:id → show
109
+ // PUT /users/:id → update
110
+ // DELETE /users/:id → destroy
111
+ ```