@malamute/ai-rules 1.3.2 → 1.4.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 +112 -100
- package/configs/_shared/rules/conventions/git.md +2 -0
- package/configs/_shared/rules/lang/python/python.md +4 -6
- package/configs/_shared/skills/analysis/sudden-death/SKILL.md +148 -0
- package/configs/flask/rules/flask.md +4 -6
- package/configs/flask/rules/marshmallow.md +4 -6
- package/package.json +7 -1
- package/src/cli.js +93 -59
- package/src/installer.js +39 -25
- package/src/tech-config.json +1 -6
package/README.md
CHANGED
|
@@ -10,12 +10,12 @@ AI Rules installs curated configuration boilerplates that teach Claude Code your
|
|
|
10
10
|
|
|
11
11
|
## Why Use This?
|
|
12
12
|
|
|
13
|
-
| Without AI Rules
|
|
14
|
-
|
|
15
|
-
| Claude uses generic patterns
|
|
16
|
-
| You repeat "use signals, not decorators" | Angular 21 patterns are built-in
|
|
17
|
-
| Security issues slip through
|
|
18
|
-
| Inconsistent code style
|
|
13
|
+
| Without AI Rules | With AI Rules |
|
|
14
|
+
| ---------------------------------------- | ---------------------------------------- |
|
|
15
|
+
| Claude uses generic patterns | Claude follows your framework's idioms |
|
|
16
|
+
| You repeat "use signals, not decorators" | Angular 21 patterns are built-in |
|
|
17
|
+
| Security issues slip through | OWASP Top 10 rules catch vulnerabilities |
|
|
18
|
+
| Inconsistent code style | Consistent conventions across the team |
|
|
19
19
|
|
|
20
20
|
## Quick Start
|
|
21
21
|
|
|
@@ -24,7 +24,7 @@ AI Rules installs curated configuration boilerplates that teach Claude Code your
|
|
|
24
24
|
npx @malamute/ai-rules init
|
|
25
25
|
|
|
26
26
|
# Or specify your stack directly
|
|
27
|
-
npx @malamute/ai-rules init angular nestjs
|
|
27
|
+
npx @malamute/ai-rules init angular nestjs
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
That's it. Claude Code now understands your stack.
|
|
@@ -41,19 +41,20 @@ npx @malamute/ai-rules <command>
|
|
|
41
41
|
|
|
42
42
|
## Supported Technologies
|
|
43
43
|
|
|
44
|
-
| Technology
|
|
45
|
-
|
|
46
|
-
| **Angular** | Nx + NgRx + Signals + Vitest
|
|
47
|
-
| **Next.js** | App Router + React 19 + Server Components | 15+
|
|
48
|
-
| **NestJS**
|
|
49
|
-
| **.NET**
|
|
50
|
-
| **FastAPI** | Pydantic v2 + SQLAlchemy 2.0 + pytest
|
|
51
|
-
| **Flask**
|
|
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+ |
|
|
52
52
|
|
|
53
53
|
## Commands
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
56
|
ai-rules init [tech...] # Install configs (interactive if no tech)
|
|
57
|
+
ai-rules add <tech> # Add technology to existing installation
|
|
57
58
|
ai-rules update # Update to latest rules
|
|
58
59
|
ai-rules status # Show installation info
|
|
59
60
|
ai-rules list # List available technologies
|
|
@@ -61,14 +62,14 @@ ai-rules list # List available technologies
|
|
|
61
62
|
|
|
62
63
|
### Options
|
|
63
64
|
|
|
64
|
-
| Option
|
|
65
|
-
|
|
66
|
-
| `--
|
|
67
|
-
| `--
|
|
68
|
-
| `--
|
|
69
|
-
| `--
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
| Option | Description |
|
|
66
|
+
| ---------------- | --------------------------------------------------------- |
|
|
67
|
+
| `--minimal` | Skip skills and shared rules (only tech rules + settings) |
|
|
68
|
+
| `--dry-run` | Preview changes without writing files |
|
|
69
|
+
| `--target <dir>` | Install to a specific directory |
|
|
70
|
+
| `--force` | Overwrite without creating backups |
|
|
71
|
+
|
|
72
|
+
By default, `init` installs everything (skills + shared rules). Use `--minimal` to skip extras.
|
|
72
73
|
|
|
73
74
|
## What Gets Installed
|
|
74
75
|
|
|
@@ -100,9 +101,11 @@ Context-aware rules that activate based on file paths:
|
|
|
100
101
|
```markdown
|
|
101
102
|
---
|
|
102
103
|
paths:
|
|
103
|
-
-
|
|
104
|
+
- '**/*.component.ts'
|
|
104
105
|
---
|
|
106
|
+
|
|
105
107
|
# Angular Component Rules
|
|
108
|
+
|
|
106
109
|
- Use `ChangeDetectionStrategy.OnPush`
|
|
107
110
|
- Use `input()`, `output()`, not decorators
|
|
108
111
|
- Template in separate `.html` file
|
|
@@ -112,33 +115,35 @@ paths:
|
|
|
112
115
|
|
|
113
116
|
Interactive workflows invoked with `/skill-name`:
|
|
114
117
|
|
|
115
|
-
| Skill
|
|
116
|
-
|
|
117
|
-
| `/learning`
|
|
118
|
-
| `/review`
|
|
119
|
-
| `/debug`
|
|
120
|
-
| `/spec`
|
|
121
|
-
| `/
|
|
122
|
-
| `/
|
|
118
|
+
| Skill | Description |
|
|
119
|
+
| ----------------- | ----------------------------------------- |
|
|
120
|
+
| `/learning` | Pedagogical mode — explains before coding |
|
|
121
|
+
| `/review` | Code review with security/perf checklist |
|
|
122
|
+
| `/debug` | Structured debugging workflow |
|
|
123
|
+
| `/spec` | Write technical spec before implementing |
|
|
124
|
+
| `/sudden-death` | Kill indecision with rapid-fire questions |
|
|
125
|
+
| `/fix-issue` | Analyze GitHub issue and implement fix |
|
|
126
|
+
| `/generate-tests` | Generate comprehensive tests |
|
|
123
127
|
|
|
124
128
|
<details>
|
|
125
|
-
<summary><strong>See all
|
|
126
|
-
|
|
127
|
-
| Skill
|
|
128
|
-
|
|
129
|
-
| `/learning`
|
|
130
|
-
| `/review`
|
|
131
|
-
| `/spec`
|
|
132
|
-
| `/
|
|
133
|
-
| `/
|
|
134
|
-
| `/
|
|
135
|
-
| `/
|
|
136
|
-
| `/
|
|
137
|
-
| `/
|
|
138
|
-
| `/
|
|
139
|
-
| `/
|
|
140
|
-
| `/
|
|
141
|
-
| `/
|
|
129
|
+
<summary><strong>See all 14 skills</strong></summary>
|
|
130
|
+
|
|
131
|
+
| Skill | Usage | Description |
|
|
132
|
+
| ----------------- | ----------------------------- | ------------------------------------- |
|
|
133
|
+
| `/learning` | `/learning nextjs` | Explains concepts before implementing |
|
|
134
|
+
| `/review` | `/review src/users/` | Code review with checklist |
|
|
135
|
+
| `/spec` | `/spec add auth` | Technical specification |
|
|
136
|
+
| `/sudden-death` | `/sudden-death backend` | Kill indecision, get a verdict |
|
|
137
|
+
| `/debug` | `/debug TypeError...` | Systematic debugging |
|
|
138
|
+
| `/fix-issue` | `/fix-issue 123` | Fix GitHub issue |
|
|
139
|
+
| `/review-pr` | `/review-pr 456` | Review pull request |
|
|
140
|
+
| `/generate-tests` | `/generate-tests src/user.ts` | Generate tests |
|
|
141
|
+
| `/api-endpoint` | `/api-endpoint POST /users` | Generate API endpoint |
|
|
142
|
+
| `/migration` | `/migration add users` | Database migration |
|
|
143
|
+
| `/security-audit` | `/security-audit` | Security analysis |
|
|
144
|
+
| `/docker` | `/docker` | Dockerfile generation |
|
|
145
|
+
| `/deploy` | `/deploy` | Deployment config |
|
|
146
|
+
| `/explore` | `/explore` | Repository analysis |
|
|
142
147
|
|
|
143
148
|
</details>
|
|
144
149
|
|
|
@@ -146,15 +151,15 @@ Interactive workflows invoked with `/skill-name`:
|
|
|
146
151
|
|
|
147
152
|
Cross-framework rules included with `--with-rules`:
|
|
148
153
|
|
|
149
|
-
| Rule
|
|
150
|
-
|
|
151
|
-
| **security.md**
|
|
152
|
-
| **performance.md**
|
|
153
|
-
| **accessibility.md**
|
|
154
|
-
| **testing-patterns.md** | AAA pattern, mocking, coverage
|
|
155
|
-
| **error-handling.md**
|
|
156
|
-
| **git.md**
|
|
157
|
-
| **observability.md**
|
|
154
|
+
| Rule | What It Covers |
|
|
155
|
+
| ----------------------- | ------------------------------------------- |
|
|
156
|
+
| **security.md** | OWASP Top 10: injection, XSS, CSRF, secrets |
|
|
157
|
+
| **performance.md** | N+1 queries, caching, lazy loading |
|
|
158
|
+
| **accessibility.md** | WCAG 2.1, semantic HTML, ARIA |
|
|
159
|
+
| **testing-patterns.md** | AAA pattern, mocking, coverage |
|
|
160
|
+
| **error-handling.md** | Error categories, response formats |
|
|
161
|
+
| **git.md** | Conventional commits, branching, PRs |
|
|
162
|
+
| **observability.md** | Logging, metrics, tracing |
|
|
158
163
|
|
|
159
164
|
## Examples
|
|
160
165
|
|
|
@@ -162,10 +167,16 @@ Cross-framework rules included with `--with-rules`:
|
|
|
162
167
|
|
|
163
168
|
```bash
|
|
164
169
|
# Angular frontend + NestJS backend
|
|
165
|
-
ai-rules init angular nestjs
|
|
170
|
+
ai-rules init angular nestjs
|
|
166
171
|
|
|
167
172
|
# Next.js frontend + FastAPI backend
|
|
168
|
-
ai-rules init nextjs fastapi
|
|
173
|
+
ai-rules init nextjs fastapi
|
|
174
|
+
|
|
175
|
+
# Add a technology to existing installation
|
|
176
|
+
ai-rules add nestjs
|
|
177
|
+
|
|
178
|
+
# Minimal install (no skills/shared rules)
|
|
179
|
+
ai-rules init angular --minimal
|
|
169
180
|
```
|
|
170
181
|
|
|
171
182
|
### Preview Before Installing
|
|
@@ -175,6 +186,7 @@ ai-rules init angular --dry-run
|
|
|
175
186
|
```
|
|
176
187
|
|
|
177
188
|
Output:
|
|
189
|
+
|
|
178
190
|
```
|
|
179
191
|
DRY RUN - No files will be modified
|
|
180
192
|
|
|
@@ -207,77 +219,77 @@ ai-rules update
|
|
|
207
219
|
<details>
|
|
208
220
|
<summary><strong>Angular</strong></summary>
|
|
209
221
|
|
|
210
|
-
| Aspect
|
|
211
|
-
|
|
212
|
-
| Components | Standalone, OnPush change detection
|
|
213
|
-
| Signals
|
|
214
|
-
| State
|
|
215
|
-
| Structure
|
|
216
|
-
| Tests
|
|
222
|
+
| Aspect | Convention |
|
|
223
|
+
| ---------- | --------------------------------------------- |
|
|
224
|
+
| Components | Standalone, OnPush change detection |
|
|
225
|
+
| Signals | `input()`, `output()`, `model()` functions |
|
|
226
|
+
| State | NgRx with Entity Adapter + Functional Effects |
|
|
227
|
+
| Structure | Nx monorepo with feature/ui/data-access libs |
|
|
228
|
+
| Tests | Vitest + Marble testing |
|
|
217
229
|
|
|
218
230
|
</details>
|
|
219
231
|
|
|
220
232
|
<details>
|
|
221
233
|
<summary><strong>Next.js</strong></summary>
|
|
222
234
|
|
|
223
|
-
| Aspect
|
|
224
|
-
|
|
225
|
-
| Components | Server Components by default
|
|
226
|
-
| Client
|
|
227
|
-
| Data
|
|
228
|
-
| State
|
|
229
|
-
| Structure
|
|
235
|
+
| Aspect | Convention |
|
|
236
|
+
| ---------- | ------------------------------------------ |
|
|
237
|
+
| Components | Server Components by default |
|
|
238
|
+
| Client | `'use client'` directive for interactivity |
|
|
239
|
+
| Data | Server Components + fetch, Server Actions |
|
|
240
|
+
| State | Zustand (simple) / Redux Toolkit (complex) |
|
|
241
|
+
| Structure | App Router with route groups |
|
|
230
242
|
|
|
231
243
|
</details>
|
|
232
244
|
|
|
233
245
|
<details>
|
|
234
246
|
<summary><strong>NestJS</strong></summary>
|
|
235
247
|
|
|
236
|
-
| Aspect
|
|
237
|
-
|
|
238
|
-
| Architecture | Modular Monolith
|
|
239
|
-
| Validation
|
|
240
|
-
| Database
|
|
241
|
-
| Auth
|
|
242
|
-
| Tests
|
|
248
|
+
| Aspect | Convention |
|
|
249
|
+
| ------------ | -------------------------------------- |
|
|
250
|
+
| Architecture | Modular Monolith |
|
|
251
|
+
| Validation | class-validator + class-transformer |
|
|
252
|
+
| Database | Prisma (modern) / TypeORM (decorators) |
|
|
253
|
+
| Auth | Passport + JWT |
|
|
254
|
+
| Tests | Vitest + Supertest |
|
|
243
255
|
|
|
244
256
|
</details>
|
|
245
257
|
|
|
246
258
|
<details>
|
|
247
259
|
<summary><strong>.NET</strong></summary>
|
|
248
260
|
|
|
249
|
-
| Aspect
|
|
250
|
-
|
|
261
|
+
| Aspect | Convention |
|
|
262
|
+
| ------------ | ----------------------------------------- |
|
|
251
263
|
| Architecture | Clean Architecture (Domain → App → Infra) |
|
|
252
|
-
| API
|
|
253
|
-
| CQRS
|
|
254
|
-
| ORM
|
|
255
|
-
| Tests
|
|
264
|
+
| API | Minimal APIs (preferred) or Controllers |
|
|
265
|
+
| CQRS | MediatR for Commands/Queries |
|
|
266
|
+
| ORM | Entity Framework Core |
|
|
267
|
+
| Tests | xUnit + NSubstitute + FluentAssertions |
|
|
256
268
|
|
|
257
269
|
</details>
|
|
258
270
|
|
|
259
271
|
<details>
|
|
260
272
|
<summary><strong>FastAPI</strong></summary>
|
|
261
273
|
|
|
262
|
-
| Aspect
|
|
263
|
-
|
|
264
|
-
| Framework
|
|
265
|
-
| Validation | Pydantic v2
|
|
266
|
-
| ORM
|
|
267
|
-
| Tests
|
|
268
|
-
| Migrations | Alembic
|
|
274
|
+
| Aspect | Convention |
|
|
275
|
+
| ---------- | --------------------------------- |
|
|
276
|
+
| Framework | FastAPI with async/await |
|
|
277
|
+
| Validation | Pydantic v2 |
|
|
278
|
+
| ORM | SQLAlchemy 2.0 with async support |
|
|
279
|
+
| Tests | pytest + httpx |
|
|
280
|
+
| Migrations | Alembic |
|
|
269
281
|
|
|
270
282
|
</details>
|
|
271
283
|
|
|
272
284
|
<details>
|
|
273
285
|
<summary><strong>Flask</strong></summary>
|
|
274
286
|
|
|
275
|
-
| Aspect
|
|
276
|
-
|
|
277
|
-
| Framework
|
|
278
|
-
| Validation | Marshmallow schemas
|
|
279
|
-
| ORM
|
|
280
|
-
| Tests
|
|
287
|
+
| Aspect | Convention |
|
|
288
|
+
| ---------- | --------------------------------------------------- |
|
|
289
|
+
| Framework | Flask 3.0 with Application Factory |
|
|
290
|
+
| Validation | Marshmallow schemas |
|
|
291
|
+
| ORM | SQLAlchemy 2.0 |
|
|
292
|
+
| Tests | pytest |
|
|
281
293
|
| Extensions | Flask-SQLAlchemy, Flask-Migrate, Flask-JWT-Extended |
|
|
282
294
|
|
|
283
295
|
</details>
|
|
@@ -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.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malamute/ai-rules",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Claude Code configuration boilerplates for Angular, Next.js, NestJS, .NET, Python and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
"test:coverage": "vitest run --coverage",
|
|
12
12
|
"lint": "eslint src bin",
|
|
13
13
|
"lint:rules": "node scripts/lint-rules.js",
|
|
14
|
+
"format": "prettier --write .",
|
|
15
|
+
"format:check": "prettier --check .",
|
|
14
16
|
"validate": "npm test && npm run lint:rules"
|
|
15
17
|
},
|
|
16
18
|
"keywords": [
|
|
@@ -46,10 +48,14 @@
|
|
|
46
48
|
"engines": {
|
|
47
49
|
"node": ">=18.0.0"
|
|
48
50
|
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@inquirer/prompts": "^7.5.3"
|
|
53
|
+
},
|
|
49
54
|
"devDependencies": {
|
|
50
55
|
"@eslint/js": "^9.39.2",
|
|
51
56
|
"eslint": "^9.39.2",
|
|
52
57
|
"husky": "^9.1.7",
|
|
58
|
+
"prettier": "^3.5.3",
|
|
53
59
|
"vitest": "^2.0.0"
|
|
54
60
|
},
|
|
55
61
|
"packageManager": "yarn@4.12.0"
|
package/src/cli.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { checkbox, input } from '@inquirer/prompts';
|
|
2
2
|
import { colors, log } from './utils.js';
|
|
3
3
|
import { VERSION, AVAILABLE_TECHS } from './config.js';
|
|
4
4
|
import { init, update, status, listTechnologies } from './installer.js';
|
|
5
|
+
import { readManifest } from './merge.js';
|
|
5
6
|
|
|
6
7
|
function printUsage() {
|
|
7
8
|
console.log(`
|
|
@@ -9,12 +10,14 @@ ${colors.bold('AI Rules')} v${VERSION} - Claude Code configuration boilerplates
|
|
|
9
10
|
|
|
10
11
|
${colors.bold('Usage:')}
|
|
11
12
|
ai-rules init [tech] [tech2] [options]
|
|
13
|
+
ai-rules add <tech> [options]
|
|
12
14
|
ai-rules update [options]
|
|
13
15
|
ai-rules status
|
|
14
16
|
ai-rules list
|
|
15
17
|
|
|
16
18
|
${colors.bold('Commands:')}
|
|
17
19
|
init Install configuration (interactive if no tech specified)
|
|
20
|
+
add Add a technology to existing installation
|
|
18
21
|
update Update installed configs to latest version
|
|
19
22
|
status Show current installation status
|
|
20
23
|
list List available technologies
|
|
@@ -28,7 +31,7 @@ ${colors.bold('Technologies:')}
|
|
|
28
31
|
flask Flask + SQLAlchemy + Marshmallow
|
|
29
32
|
|
|
30
33
|
${colors.bold('Options:')}
|
|
31
|
-
--minimal Only install
|
|
34
|
+
--minimal Only install settings.json and tech rules (no shared skills/rules)
|
|
32
35
|
--target <dir> Target directory (default: current directory)
|
|
33
36
|
--dry-run Preview changes without writing files
|
|
34
37
|
--force Overwrite files without backup (update command)
|
|
@@ -37,78 +40,54 @@ ${colors.bold('Examples:')}
|
|
|
37
40
|
ai-rules init # Interactive mode
|
|
38
41
|
ai-rules init angular # Full install (skills + rules)
|
|
39
42
|
ai-rules init angular --minimal # Minimal install
|
|
40
|
-
ai-rules
|
|
43
|
+
ai-rules add nestjs # Add NestJS to existing install
|
|
41
44
|
ai-rules update
|
|
42
|
-
ai-rules update --force
|
|
43
45
|
ai-rules status
|
|
44
46
|
`);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
async function prompt(question) {
|
|
48
|
-
const rl = readline.createInterface({
|
|
49
|
-
input: process.stdin,
|
|
50
|
-
output: process.stdout,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
return new Promise((resolve) => {
|
|
54
|
-
rl.question(question, (answer) => {
|
|
55
|
-
rl.close();
|
|
56
|
-
resolve(answer.trim());
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async function multiSelect(message, choices) {
|
|
62
|
-
console.log(`\n${colors.bold(message)}`);
|
|
63
|
-
console.log(colors.dim('(enter numbers separated by spaces, or "all")'));
|
|
64
|
-
console.log('');
|
|
65
|
-
|
|
66
|
-
choices.forEach((choice, i) => {
|
|
67
|
-
console.log(` ${colors.cyan(i + 1)}. ${choice.name} ${colors.dim(`- ${choice.description}`)}`);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
console.log('');
|
|
71
|
-
const answer = await prompt('Your selection: ');
|
|
72
|
-
|
|
73
|
-
if (answer.toLowerCase() === 'all') {
|
|
74
|
-
return choices.map((c) => c.value);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const indices = answer
|
|
78
|
-
.split(/[\s,]+/)
|
|
79
|
-
.map((s) => parseInt(s, 10) - 1)
|
|
80
|
-
.filter((i) => i >= 0 && i < choices.length);
|
|
81
|
-
|
|
82
|
-
return indices.map((i) => choices[i].value);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
49
|
async function interactiveInit() {
|
|
86
50
|
console.log(`\n${colors.bold('AI Rules')} - Interactive Setup\n`);
|
|
87
51
|
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
52
|
+
const techs = await checkbox({
|
|
53
|
+
message: 'Select technologies:',
|
|
54
|
+
instructions: '(Space to select, Enter to confirm)',
|
|
55
|
+
choices: [
|
|
56
|
+
{ name: 'Angular - Angular 21 + Nx + NgRx + Signals', value: 'angular' },
|
|
57
|
+
{ name: 'Next.js - Next.js 15 + React 19 + App Router', value: 'nextjs' },
|
|
58
|
+
{ name: 'NestJS - NestJS 11 + Prisma/TypeORM + Passport', value: 'nestjs' },
|
|
59
|
+
{ name: '.NET - .NET 9 + ASP.NET Core + EF Core', value: 'dotnet' },
|
|
60
|
+
{ name: 'FastAPI - FastAPI + SQLAlchemy 2.0 + Pydantic v2', value: 'fastapi' },
|
|
61
|
+
{ name: 'Flask - Flask + SQLAlchemy 2.0 + Marshmallow', value: 'flask' },
|
|
62
|
+
],
|
|
63
|
+
});
|
|
98
64
|
|
|
99
65
|
if (techs.length === 0) {
|
|
100
66
|
log.error('No technology selected');
|
|
101
67
|
process.exit(1);
|
|
102
68
|
}
|
|
103
69
|
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
70
|
+
const extras = await checkbox({
|
|
71
|
+
message: 'Include extras:',
|
|
72
|
+
instructions: '(Space to toggle, Enter to confirm)',
|
|
73
|
+
choices: [
|
|
74
|
+
{
|
|
75
|
+
name: 'Skills - /learning, /review, /spec, /debug, etc.',
|
|
76
|
+
value: 'skills',
|
|
77
|
+
checked: true,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'Shared Rules - security, performance, accessibility',
|
|
81
|
+
value: 'rules',
|
|
82
|
+
checked: true,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
});
|
|
110
86
|
|
|
111
|
-
const targetDir = await
|
|
87
|
+
const targetDir = await input({
|
|
88
|
+
message: 'Target directory:',
|
|
89
|
+
default: '.',
|
|
90
|
+
});
|
|
112
91
|
|
|
113
92
|
const options = {
|
|
114
93
|
target: targetDir === '.' ? null : targetDir,
|
|
@@ -164,6 +143,61 @@ export async function run(args) {
|
|
|
164
143
|
return;
|
|
165
144
|
}
|
|
166
145
|
|
|
146
|
+
if (command === 'add') {
|
|
147
|
+
const targetIndex = args.indexOf('--target');
|
|
148
|
+
const targetDir = targetIndex !== -1 ? args[targetIndex + 1] : process.cwd();
|
|
149
|
+
|
|
150
|
+
const manifest = readManifest(targetDir);
|
|
151
|
+
if (!manifest) {
|
|
152
|
+
log.error('No ai-rules installation found.');
|
|
153
|
+
console.log(`Run ${colors.cyan('ai-rules init')} first.`);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const newTechs = [];
|
|
158
|
+
for (let i = 1; i < args.length; i++) {
|
|
159
|
+
const arg = args[i];
|
|
160
|
+
if (arg === '--target') {
|
|
161
|
+
i++; // Skip next arg
|
|
162
|
+
} else if (arg === '--dry-run' || arg === '--force') {
|
|
163
|
+
// Handled below
|
|
164
|
+
} else if (!arg.startsWith('-')) {
|
|
165
|
+
if (AVAILABLE_TECHS.includes(arg)) {
|
|
166
|
+
if (manifest.technologies.includes(arg)) {
|
|
167
|
+
log.warning(`${arg} is already installed, skipping`);
|
|
168
|
+
} else {
|
|
169
|
+
newTechs.push(arg);
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
log.error(`Unknown technology: ${arg}`);
|
|
173
|
+
console.log(`Available: ${AVAILABLE_TECHS.join(', ')}`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (newTechs.length === 0) {
|
|
180
|
+
log.error('No new technology to add');
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const allTechs = [...manifest.technologies, ...newTechs];
|
|
185
|
+
|
|
186
|
+
const options = {
|
|
187
|
+
target: targetDir === process.cwd() ? null : targetDir,
|
|
188
|
+
withSkills: manifest.options?.withSkills ?? true,
|
|
189
|
+
withRules: manifest.options?.withRules ?? true,
|
|
190
|
+
dryRun: args.includes('--dry-run'),
|
|
191
|
+
force: args.includes('--force'),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
console.log('');
|
|
195
|
+
log.info(`Adding ${newTechs.join(', ')} to existing installation`);
|
|
196
|
+
|
|
197
|
+
init(allTechs, options);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
167
201
|
if (command === 'init') {
|
|
168
202
|
const minimal = args.includes('--minimal');
|
|
169
203
|
const options = {
|
package/src/installer.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { colors, log, getFilesRecursive, copyDirRecursive, backupFile } from './utils.js';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
CONFIGS_DIR,
|
|
6
|
+
AVAILABLE_TECHS,
|
|
7
|
+
VERSION,
|
|
8
|
+
getRulePathsToInclude,
|
|
9
|
+
shouldIncludeRule,
|
|
10
|
+
} from './config.js';
|
|
5
11
|
import { mergeSettingsJson, readManifest, writeManifest } from './merge.js';
|
|
6
12
|
|
|
7
13
|
/**
|
|
@@ -114,8 +120,12 @@ export function listTechnologies() {
|
|
|
114
120
|
const skills = fs.existsSync(path.join(sharedPath, 'skills'));
|
|
115
121
|
const rules = fs.existsSync(path.join(sharedPath, 'rules'));
|
|
116
122
|
|
|
117
|
-
console.log(
|
|
118
|
-
|
|
123
|
+
console.log(
|
|
124
|
+
` ${skills ? colors.green('✓') : colors.red('✗')} skills /learning, /review, /spec, /debug, and more`
|
|
125
|
+
);
|
|
126
|
+
console.log(
|
|
127
|
+
` ${rules ? colors.green('✓') : colors.red('✗')} rules security, performance, accessibility`
|
|
128
|
+
);
|
|
119
129
|
console.log('');
|
|
120
130
|
}
|
|
121
131
|
|
|
@@ -136,7 +146,9 @@ export function status(targetDir) {
|
|
|
136
146
|
|
|
137
147
|
console.log(` ${colors.bold('Installed version:')} ${manifest.version}`);
|
|
138
148
|
console.log(` ${colors.bold('Latest version:')} ${VERSION}`);
|
|
139
|
-
console.log(
|
|
149
|
+
console.log(
|
|
150
|
+
` ${colors.bold('Installed at:')} ${new Date(manifest.installedAt).toLocaleString()}`
|
|
151
|
+
);
|
|
140
152
|
console.log('');
|
|
141
153
|
|
|
142
154
|
if (manifest.technologies?.length) {
|
|
@@ -155,7 +167,9 @@ export function status(targetDir) {
|
|
|
155
167
|
}
|
|
156
168
|
|
|
157
169
|
if (manifest.version !== VERSION) {
|
|
158
|
-
console.log(
|
|
170
|
+
console.log(
|
|
171
|
+
` ${colors.yellow('⚠')} Update available! Run ${colors.cyan('ai-rules update')} to update.`
|
|
172
|
+
);
|
|
159
173
|
console.log('');
|
|
160
174
|
}
|
|
161
175
|
|
|
@@ -199,11 +213,11 @@ export function init(techs, options) {
|
|
|
199
213
|
|
|
200
214
|
const settingsPath = path.join(techDir, 'settings.json');
|
|
201
215
|
if (fs.existsSync(settingsPath)) {
|
|
202
|
-
const op = mergeSettingsJson(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
);
|
|
216
|
+
const op = mergeSettingsJson(path.join(targetDir, '.claude', 'settings.json'), settingsPath, {
|
|
217
|
+
dryRun,
|
|
218
|
+
backup,
|
|
219
|
+
targetDir,
|
|
220
|
+
});
|
|
207
221
|
operations.push(op);
|
|
208
222
|
|
|
209
223
|
if (dryRun) {
|
|
@@ -215,11 +229,11 @@ export function init(techs, options) {
|
|
|
215
229
|
|
|
216
230
|
const rulesDir = path.join(techDir, 'rules');
|
|
217
231
|
if (fs.existsSync(rulesDir)) {
|
|
218
|
-
const ops = copyDirRecursive(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
);
|
|
232
|
+
const ops = copyDirRecursive(rulesDir, path.join(targetDir, '.claude', 'rules', tech), {
|
|
233
|
+
dryRun,
|
|
234
|
+
backup,
|
|
235
|
+
targetDir,
|
|
236
|
+
});
|
|
223
237
|
operations.push(...ops);
|
|
224
238
|
|
|
225
239
|
if (dryRun) {
|
|
@@ -232,11 +246,11 @@ export function init(techs, options) {
|
|
|
232
246
|
if (options.withSkills) {
|
|
233
247
|
const techSkillsDir = path.join(techDir, 'skills');
|
|
234
248
|
if (fs.existsSync(techSkillsDir)) {
|
|
235
|
-
const ops = copySkillsToTarget(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
);
|
|
249
|
+
const ops = copySkillsToTarget(techSkillsDir, path.join(targetDir, '.claude', 'skills'), {
|
|
250
|
+
dryRun,
|
|
251
|
+
backup,
|
|
252
|
+
targetDir,
|
|
253
|
+
});
|
|
240
254
|
operations.push(...ops);
|
|
241
255
|
}
|
|
242
256
|
}
|
|
@@ -248,11 +262,11 @@ export function init(techs, options) {
|
|
|
248
262
|
log.info(`${dryRun ? 'Would install' : 'Installing'} skills...`);
|
|
249
263
|
const skillsDir = path.join(sharedDir, 'skills');
|
|
250
264
|
if (fs.existsSync(skillsDir)) {
|
|
251
|
-
const ops = copySkillsToTarget(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
);
|
|
265
|
+
const ops = copySkillsToTarget(skillsDir, path.join(targetDir, '.claude', 'skills'), {
|
|
266
|
+
dryRun,
|
|
267
|
+
backup,
|
|
268
|
+
targetDir,
|
|
269
|
+
});
|
|
256
270
|
operations.push(...ops);
|
|
257
271
|
|
|
258
272
|
if (dryRun) {
|