@malamute/ai-rules 1.3.2 → 1.4.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.
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 | 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 |
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 --all
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 | 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
+ | **.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 | Description |
65
- |--------|-------------|
66
- | `--with-skills` | Add skills: `/learning`, `/review`, `/debug`, etc. |
67
- | `--with-rules` | Add shared rules: security, performance, accessibility |
68
- | `--all` | Include both skills and rules |
69
- | `--dry-run` | Preview changes without writing files |
70
- | `--target <dir>` | Install to a specific directory |
71
- | `--force` | Overwrite without creating backups |
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
- - "**/*.component.ts"
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,33 @@ paths:
112
115
 
113
116
  Interactive workflows invoked with `/skill-name`:
114
117
 
115
- | Skill | Description |
116
- |-------|-------------|
117
- | `/learning` | Pedagogical mode — explains before coding |
118
- | `/review` | Code review with security/perf checklist |
119
- | `/debug` | Structured debugging workflow |
120
- | `/spec` | Write technical spec before implementing |
121
- | `/fix-issue` | Analyze GitHub issue and implement fix |
122
- | `/generate-tests` | Generate comprehensive tests |
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
+ | `/fix-issue` | Analyze GitHub issue and implement fix |
125
+ | `/generate-tests` | Generate comprehensive tests |
123
126
 
124
127
  <details>
125
128
  <summary><strong>See all 13 skills</strong></summary>
126
129
 
127
- | Skill | Usage | Description |
128
- |-------|-------|-------------|
129
- | `/learning` | `/learning nextjs` | Explains concepts before implementing |
130
- | `/review` | `/review src/users/` | Code review with checklist |
131
- | `/spec` | `/spec add auth` | Technical specification |
132
- | `/debug` | `/debug TypeError...` | Systematic debugging |
133
- | `/fix-issue` | `/fix-issue 123` | Fix GitHub issue |
134
- | `/review-pr` | `/review-pr 456` | Review pull request |
135
- | `/generate-tests` | `/generate-tests src/user.ts` | Generate tests |
136
- | `/api-endpoint` | `/api-endpoint POST /users` | Generate API endpoint |
137
- | `/migration` | `/migration add users` | Database migration |
138
- | `/security-audit` | `/security-audit` | Security analysis |
139
- | `/docker` | `/docker` | Dockerfile generation |
140
- | `/deploy` | `/deploy` | Deployment config |
141
- | `/explore` | `/explore` | Repository analysis |
130
+ | Skill | Usage | Description |
131
+ | ----------------- | ----------------------------- | ------------------------------------- |
132
+ | `/learning` | `/learning nextjs` | Explains concepts before implementing |
133
+ | `/review` | `/review src/users/` | Code review with checklist |
134
+ | `/spec` | `/spec add auth` | Technical specification |
135
+ | `/debug` | `/debug TypeError...` | Systematic debugging |
136
+ | `/fix-issue` | `/fix-issue 123` | Fix GitHub issue |
137
+ | `/review-pr` | `/review-pr 456` | Review pull request |
138
+ | `/generate-tests` | `/generate-tests src/user.ts` | Generate tests |
139
+ | `/api-endpoint` | `/api-endpoint POST /users` | Generate API endpoint |
140
+ | `/migration` | `/migration add users` | Database migration |
141
+ | `/security-audit` | `/security-audit` | Security analysis |
142
+ | `/docker` | `/docker` | Dockerfile generation |
143
+ | `/deploy` | `/deploy` | Deployment config |
144
+ | `/explore` | `/explore` | Repository analysis |
142
145
 
143
146
  </details>
144
147
 
@@ -146,15 +149,15 @@ Interactive workflows invoked with `/skill-name`:
146
149
 
147
150
  Cross-framework rules included with `--with-rules`:
148
151
 
149
- | Rule | What It Covers |
150
- |------|----------------|
151
- | **security.md** | OWASP Top 10: injection, XSS, CSRF, secrets |
152
- | **performance.md** | N+1 queries, caching, lazy loading |
153
- | **accessibility.md** | WCAG 2.1, semantic HTML, ARIA |
154
- | **testing-patterns.md** | AAA pattern, mocking, coverage |
155
- | **error-handling.md** | Error categories, response formats |
156
- | **git.md** | Conventional commits, branching, PRs |
157
- | **observability.md** | Logging, metrics, tracing |
152
+ | Rule | What It Covers |
153
+ | ----------------------- | ------------------------------------------- |
154
+ | **security.md** | OWASP Top 10: injection, XSS, CSRF, secrets |
155
+ | **performance.md** | N+1 queries, caching, lazy loading |
156
+ | **accessibility.md** | WCAG 2.1, semantic HTML, ARIA |
157
+ | **testing-patterns.md** | AAA pattern, mocking, coverage |
158
+ | **error-handling.md** | Error categories, response formats |
159
+ | **git.md** | Conventional commits, branching, PRs |
160
+ | **observability.md** | Logging, metrics, tracing |
158
161
 
159
162
  ## Examples
160
163
 
@@ -162,10 +165,16 @@ Cross-framework rules included with `--with-rules`:
162
165
 
163
166
  ```bash
164
167
  # Angular frontend + NestJS backend
165
- ai-rules init angular nestjs --all
168
+ ai-rules init angular nestjs
166
169
 
167
170
  # Next.js frontend + FastAPI backend
168
- ai-rules init nextjs fastapi --all
171
+ ai-rules init nextjs fastapi
172
+
173
+ # Add a technology to existing installation
174
+ ai-rules add nestjs
175
+
176
+ # Minimal install (no skills/shared rules)
177
+ ai-rules init angular --minimal
169
178
  ```
170
179
 
171
180
  ### Preview Before Installing
@@ -175,6 +184,7 @@ ai-rules init angular --dry-run
175
184
  ```
176
185
 
177
186
  Output:
187
+
178
188
  ```
179
189
  DRY RUN - No files will be modified
180
190
 
@@ -207,77 +217,77 @@ ai-rules update
207
217
  <details>
208
218
  <summary><strong>Angular</strong></summary>
209
219
 
210
- | Aspect | Convention |
211
- |--------|------------|
212
- | Components | Standalone, OnPush change detection |
213
- | Signals | `input()`, `output()`, `model()` functions |
214
- | State | NgRx with Entity Adapter + Functional Effects |
215
- | Structure | Nx monorepo with feature/ui/data-access libs |
216
- | Tests | Vitest + Marble testing |
220
+ | Aspect | Convention |
221
+ | ---------- | --------------------------------------------- |
222
+ | Components | Standalone, OnPush change detection |
223
+ | Signals | `input()`, `output()`, `model()` functions |
224
+ | State | NgRx with Entity Adapter + Functional Effects |
225
+ | Structure | Nx monorepo with feature/ui/data-access libs |
226
+ | Tests | Vitest + Marble testing |
217
227
 
218
228
  </details>
219
229
 
220
230
  <details>
221
231
  <summary><strong>Next.js</strong></summary>
222
232
 
223
- | Aspect | Convention |
224
- |--------|------------|
225
- | Components | Server Components by default |
226
- | Client | `'use client'` directive for interactivity |
227
- | Data | Server Components + fetch, Server Actions |
228
- | State | Zustand (simple) / Redux Toolkit (complex) |
229
- | Structure | App Router with route groups |
233
+ | Aspect | Convention |
234
+ | ---------- | ------------------------------------------ |
235
+ | Components | Server Components by default |
236
+ | Client | `'use client'` directive for interactivity |
237
+ | Data | Server Components + fetch, Server Actions |
238
+ | State | Zustand (simple) / Redux Toolkit (complex) |
239
+ | Structure | App Router with route groups |
230
240
 
231
241
  </details>
232
242
 
233
243
  <details>
234
244
  <summary><strong>NestJS</strong></summary>
235
245
 
236
- | Aspect | Convention |
237
- |--------|------------|
238
- | Architecture | Modular Monolith |
239
- | Validation | class-validator + class-transformer |
240
- | Database | Prisma (modern) / TypeORM (decorators) |
241
- | Auth | Passport + JWT |
242
- | Tests | Vitest + Supertest |
246
+ | Aspect | Convention |
247
+ | ------------ | -------------------------------------- |
248
+ | Architecture | Modular Monolith |
249
+ | Validation | class-validator + class-transformer |
250
+ | Database | Prisma (modern) / TypeORM (decorators) |
251
+ | Auth | Passport + JWT |
252
+ | Tests | Vitest + Supertest |
243
253
 
244
254
  </details>
245
255
 
246
256
  <details>
247
257
  <summary><strong>.NET</strong></summary>
248
258
 
249
- | Aspect | Convention |
250
- |--------|------------|
259
+ | Aspect | Convention |
260
+ | ------------ | ----------------------------------------- |
251
261
  | Architecture | Clean Architecture (Domain → App → Infra) |
252
- | API | Minimal APIs (preferred) or Controllers |
253
- | CQRS | MediatR for Commands/Queries |
254
- | ORM | Entity Framework Core |
255
- | Tests | xUnit + NSubstitute + FluentAssertions |
262
+ | API | Minimal APIs (preferred) or Controllers |
263
+ | CQRS | MediatR for Commands/Queries |
264
+ | ORM | Entity Framework Core |
265
+ | Tests | xUnit + NSubstitute + FluentAssertions |
256
266
 
257
267
  </details>
258
268
 
259
269
  <details>
260
270
  <summary><strong>FastAPI</strong></summary>
261
271
 
262
- | Aspect | Convention |
263
- |--------|------------|
264
- | Framework | FastAPI with async/await |
265
- | Validation | Pydantic v2 |
266
- | ORM | SQLAlchemy 2.0 with async support |
267
- | Tests | pytest + httpx |
268
- | Migrations | Alembic |
272
+ | Aspect | Convention |
273
+ | ---------- | --------------------------------- |
274
+ | Framework | FastAPI with async/await |
275
+ | Validation | Pydantic v2 |
276
+ | ORM | SQLAlchemy 2.0 with async support |
277
+ | Tests | pytest + httpx |
278
+ | Migrations | Alembic |
269
279
 
270
280
  </details>
271
281
 
272
282
  <details>
273
283
  <summary><strong>Flask</strong></summary>
274
284
 
275
- | Aspect | Convention |
276
- |--------|------------|
277
- | Framework | Flask 3.0 with Application Factory |
278
- | Validation | Marshmallow schemas |
279
- | ORM | SQLAlchemy 2.0 |
280
- | Tests | pytest |
285
+ | Aspect | Convention |
286
+ | ---------- | --------------------------------------------------- |
287
+ | Framework | Flask 3.0 with Application Factory |
288
+ | Validation | Marshmallow schemas |
289
+ | ORM | SQLAlchemy 2.0 |
290
+ | Tests | pytest |
281
291
  | Extensions | Flask-SQLAlchemy, Flask-Migrate, Flask-JWT-Extended |
282
292
 
283
293
  </details>
@@ -9,6 +9,8 @@ paths:
9
9
 
10
10
  Format: `type(scope): description`
11
11
 
12
+ **No `Co-Authored-By`** - Do not add Co-Authored-By trailers to commits.
13
+
12
14
  ```bash
13
15
  # Types
14
16
  feat # New feature
@@ -1,13 +1,11 @@
1
- # Python Conventions
2
-
3
- ## Activation
4
-
5
- ```yaml
1
+ ---
6
2
  paths:
7
3
  - "**/*.py"
8
4
  - "**/pyproject.toml"
9
5
  - "**/requirements*.txt"
10
- ```
6
+ ---
7
+
8
+ # Python Conventions
11
9
 
12
10
  ## Type Hints (Required)
13
11
 
@@ -1,11 +1,9 @@
1
- # Flask Rules
2
-
3
- ## Activation
4
-
5
- ```yaml
1
+ ---
6
2
  paths:
7
3
  - "**/*.py"
8
- ```
4
+ ---
5
+
6
+ # Flask Rules
9
7
 
10
8
  ## Application Factory
11
9
 
@@ -1,11 +1,9 @@
1
- # Marshmallow Validation Rules
2
-
3
- ## Activation
4
-
5
- ```yaml
1
+ ---
6
2
  paths:
7
3
  - "**/*.py"
8
- ```
4
+ ---
5
+
6
+ # Marshmallow Validation Rules
9
7
 
10
8
  ## Schema Definition
11
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malamute/ai-rules",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
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 readline from 'readline';
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 CLAUDE.md, settings.json, and tech rules (no shared skills/rules)
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 init nextjs --dry-run
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 techChoices = [
89
- { name: 'Angular', value: 'angular', description: 'Angular 21 + Nx + NgRx + Signals' },
90
- { name: 'Next.js', value: 'nextjs', description: 'Next.js 15 + React 19 + App Router' },
91
- { name: 'NestJS', value: 'nestjs', description: 'NestJS 11 + Prisma/TypeORM + Passport' },
92
- { name: '.NET', value: 'dotnet', description: '.NET 9 + ASP.NET Core + EF Core' },
93
- { name: 'FastAPI', value: 'fastapi', description: 'FastAPI + SQLAlchemy 2.0 + Pydantic v2' },
94
- { name: 'Flask', value: 'flask', description: 'Flask + SQLAlchemy 2.0 + Marshmallow' },
95
- ];
96
-
97
- const techs = await multiSelect('Select technologies:', techChoices);
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 extraChoices = [
105
- { name: 'Skills', value: 'skills', description: '/learning, /review, /spec, /debug, etc.' },
106
- { name: 'Shared Rules', value: 'rules', description: 'security, performance, accessibility' },
107
- ];
108
-
109
- const extras = await multiSelect('Include extras:', extraChoices);
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 prompt(`Target directory ${colors.dim('(. for current)')}: `) || '.';
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 { CONFIGS_DIR, AVAILABLE_TECHS, VERSION, getRulePathsToInclude, shouldIncludeRule } from './config.js';
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(` ${skills ? colors.green('✓') : colors.red('✗')} skills /learning, /review, /spec, /debug, and more`);
118
- console.log(` ${rules ? colors.green('✓') : colors.red('✗')} rules security, performance, accessibility`);
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(` ${colors.bold('Installed at:')} ${new Date(manifest.installedAt).toLocaleString()}`);
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(` ${colors.yellow('⚠')} Update available! Run ${colors.cyan('ai-rules update')} to update.`);
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
- path.join(targetDir, '.claude', 'settings.json'),
204
- settingsPath,
205
- { dryRun, backup, targetDir }
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
- rulesDir,
220
- path.join(targetDir, '.claude', 'rules', tech),
221
- { dryRun, backup, targetDir }
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
- techSkillsDir,
237
- path.join(targetDir, '.claude', 'skills'),
238
- { dryRun, backup, targetDir }
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
- skillsDir,
253
- path.join(targetDir, '.claude', 'skills'),
254
- { dryRun, backup, targetDir }
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) {
@@ -36,10 +36,5 @@
36
36
  "backend": "domain/backend"
37
37
  }
38
38
  },
39
- "alwaysInclude": [
40
- "conventions",
41
- "quality",
42
- "security",
43
- "devops"
44
- ]
39
+ "alwaysInclude": ["conventions", "quality", "security", "devops"]
45
40
  }