@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,273 @@
1
+ # Python Project Guidelines
2
+
3
+ @../_shared/CLAUDE.md
4
+
5
+ ## Stack
6
+
7
+ - Python 3.11+
8
+ - FastAPI or Flask
9
+ - SQLAlchemy 2.0+ (async support)
10
+ - Pydantic v2 for validation
11
+ - pytest for testing
12
+ - uv or poetry for dependency management
13
+
14
+ ## Architecture
15
+
16
+ ### Domain-Based Structure (Recommended for Monoliths)
17
+
18
+ ```
19
+ src/
20
+ ├── app/
21
+ │ ├── __init__.py
22
+ │ ├── main.py # Application entry point
23
+ │ ├── config.py # Settings with pydantic-settings
24
+ │ ├── database.py # DB session, engine
25
+ │ │
26
+ │ ├── users/ # Domain module
27
+ │ │ ├── __init__.py
28
+ │ │ ├── router.py # API endpoints
29
+ │ │ ├── schemas.py # Pydantic models (request/response)
30
+ │ │ ├── models.py # SQLAlchemy models
31
+ │ │ ├── service.py # Business logic
32
+ │ │ ├── repository.py # Data access
33
+ │ │ ├── dependencies.py # Route dependencies
34
+ │ │ └── exceptions.py # Domain exceptions
35
+ │ │
36
+ │ ├── auth/
37
+ │ │ ├── router.py
38
+ │ │ ├── schemas.py
39
+ │ │ ├── service.py
40
+ │ │ ├── jwt.py
41
+ │ │ └── dependencies.py
42
+ │ │
43
+ │ ├── core/ # Shared utilities
44
+ │ │ ├── __init__.py
45
+ │ │ ├── exceptions.py # Base exceptions
46
+ │ │ ├── security.py # Password hashing, etc.
47
+ │ │ └── pagination.py
48
+ │ │
49
+ │ └── common/
50
+ │ ├── models.py # Base model classes
51
+ │ └── schemas.py # Shared schemas
52
+
53
+ ├── tests/
54
+ │ ├── conftest.py
55
+ │ ├── users/
56
+ │ │ ├── test_router.py
57
+ │ │ └── test_service.py
58
+ │ └── auth/
59
+
60
+ ├── alembic/ # Migrations
61
+ │ ├── versions/
62
+ │ └── env.py
63
+
64
+ ├── pyproject.toml
65
+ └── alembic.ini
66
+ ```
67
+
68
+ ### File-Type Structure (For Microservices)
69
+
70
+ ```
71
+ src/
72
+ ├── app/
73
+ │ ├── main.py
74
+ │ ├── config.py
75
+ │ ├── routers/
76
+ │ │ ├── users.py
77
+ │ │ └── auth.py
78
+ │ ├── schemas/
79
+ │ │ ├── user.py
80
+ │ │ └── auth.py
81
+ │ ├── models/
82
+ │ │ └── user.py
83
+ │ ├── services/
84
+ │ │ └── user_service.py
85
+ │ └── repositories/
86
+ │ └── user_repository.py
87
+ ```
88
+
89
+ ## Code Style
90
+
91
+ ### Type Hints (Required)
92
+
93
+ ```python
94
+ # Always use type hints
95
+ def get_user_by_email(email: str) -> User | None:
96
+ ...
97
+
98
+ async def create_user(user_data: UserCreate) -> User:
99
+ ...
100
+
101
+ # Use modern syntax (Python 3.10+)
102
+ def process_items(items: list[str]) -> dict[str, int]: # Not List[str]
103
+ ...
104
+ ```
105
+
106
+ ### Naming Conventions
107
+
108
+ | Element | Convention | Example |
109
+ |---------|------------|---------|
110
+ | Modules | snake_case | `user_service.py` |
111
+ | Classes | PascalCase | `UserService` |
112
+ | Functions | snake_case | `get_user_by_id` |
113
+ | Variables | snake_case | `user_count` |
114
+ | Constants | UPPER_SNAKE | `MAX_RETRY_COUNT` |
115
+ | Private | _prefix | `_internal_method` |
116
+
117
+ ### Async/Await
118
+
119
+ ```python
120
+ # FastAPI is async-first - use async for I/O operations
121
+ async def get_users(db: AsyncSession) -> list[User]:
122
+ result = await db.execute(select(User))
123
+ return result.scalars().all()
124
+
125
+ # Don't mix sync I/O in async functions
126
+ # Bad - blocks event loop
127
+ async def bad_example():
128
+ time.sleep(1) # Blocks!
129
+ requests.get(url) # Blocks!
130
+
131
+ # Good - use async versions
132
+ async def good_example():
133
+ await asyncio.sleep(1)
134
+ async with httpx.AsyncClient() as client:
135
+ await client.get(url)
136
+ ```
137
+
138
+ ## Commands
139
+
140
+ ```bash
141
+ # Development
142
+ uvicorn app.main:app --reload
143
+ # or
144
+ fastapi dev app/main.py
145
+
146
+ # Tests
147
+ pytest
148
+ pytest -v --cov=app
149
+ pytest -k "test_users"
150
+
151
+ # Linting
152
+ ruff check .
153
+ ruff format .
154
+
155
+ # Type checking
156
+ mypy app/
157
+
158
+ # Migrations (Alembic)
159
+ alembic revision --autogenerate -m "Add users table"
160
+ alembic upgrade head
161
+ alembic downgrade -1
162
+ ```
163
+
164
+ ## Common Patterns
165
+
166
+ ### Pydantic Settings
167
+
168
+ ```python
169
+ from pydantic_settings import BaseSettings, SettingsConfigDict
170
+
171
+ class Settings(BaseSettings):
172
+ model_config = SettingsConfigDict(
173
+ env_file=".env",
174
+ env_file_encoding="utf-8",
175
+ extra="ignore",
176
+ )
177
+
178
+ # Database
179
+ database_url: str
180
+
181
+ # Auth
182
+ secret_key: str
183
+ algorithm: str = "HS256"
184
+ access_token_expire_minutes: int = 30
185
+
186
+ # App
187
+ debug: bool = False
188
+ api_prefix: str = "/api/v1"
189
+
190
+ settings = Settings()
191
+ ```
192
+
193
+ ### Dependency Injection
194
+
195
+ ```python
196
+ from typing import Annotated
197
+ from fastapi import Depends
198
+
199
+ # Database session dependency
200
+ async def get_db() -> AsyncGenerator[AsyncSession, None]:
201
+ async with async_session_maker() as session:
202
+ try:
203
+ yield session
204
+ await session.commit()
205
+ except Exception:
206
+ await session.rollback()
207
+ raise
208
+
209
+ DbSession = Annotated[AsyncSession, Depends(get_db)]
210
+
211
+ # Service dependency
212
+ def get_user_service(db: DbSession) -> UserService:
213
+ return UserService(UserRepository(db))
214
+
215
+ UserServiceDep = Annotated[UserService, Depends(get_user_service)]
216
+
217
+ # Usage in router
218
+ @router.get("/{user_id}")
219
+ async def get_user(user_id: int, service: UserServiceDep) -> UserResponse:
220
+ return await service.get_by_id(user_id)
221
+ ```
222
+
223
+ ### Exception Handling
224
+
225
+ ```python
226
+ from fastapi import HTTPException, status
227
+
228
+ # Domain exceptions
229
+ class UserNotFoundError(Exception):
230
+ def __init__(self, user_id: int):
231
+ self.user_id = user_id
232
+ super().__init__(f"User {user_id} not found")
233
+
234
+ class EmailAlreadyExistsError(Exception):
235
+ def __init__(self, email: str):
236
+ self.email = email
237
+
238
+ # Global exception handler
239
+ @app.exception_handler(UserNotFoundError)
240
+ async def user_not_found_handler(request: Request, exc: UserNotFoundError):
241
+ return JSONResponse(
242
+ status_code=status.HTTP_404_NOT_FOUND,
243
+ content={"detail": str(exc)},
244
+ )
245
+
246
+ # Or use HTTPException directly in service
247
+ class UserService:
248
+ async def get_by_id(self, user_id: int) -> User:
249
+ user = await self.repository.get(user_id)
250
+ if not user:
251
+ raise HTTPException(
252
+ status_code=status.HTTP_404_NOT_FOUND,
253
+ detail=f"User {user_id} not found",
254
+ )
255
+ return user
256
+ ```
257
+
258
+ ### API Versioning
259
+
260
+ ```python
261
+ from fastapi import APIRouter
262
+
263
+ # Versioned routers
264
+ v1_router = APIRouter(prefix="/api/v1")
265
+ v1_router.include_router(users.router, prefix="/users", tags=["users"])
266
+ v1_router.include_router(auth.router, prefix="/auth", tags=["auth"])
267
+
268
+ v2_router = APIRouter(prefix="/api/v2")
269
+ # ... v2 routes
270
+
271
+ app.include_router(v1_router)
272
+ app.include_router(v2_router)
273
+ ```
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@malamute/ai-rules",
3
+ "version": "1.0.0",
4
+ "description": "Claude Code configuration boilerplates for Angular, Next.js, NestJS, .NET, Python and more",
5
+ "main": "src/install.js",
6
+ "bin": {
7
+ "ai-rules": "./bin/cli.js"
8
+ },
9
+ "scripts": {
10
+ "test": "node bin/cli.js --help"
11
+ },
12
+ "keywords": [
13
+ "claude",
14
+ "claude-code",
15
+ "anthropic",
16
+ "ai",
17
+ "coding",
18
+ "config",
19
+ "boilerplate",
20
+ "angular",
21
+ "nextjs",
22
+ "nestjs",
23
+ "dotnet",
24
+ "python",
25
+ "fastapi"
26
+ ],
27
+ "author": "",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": ""
32
+ },
33
+ "files": [
34
+ "bin",
35
+ "src",
36
+ "configs"
37
+ ],
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ }
41
+ }
package/src/install.js ADDED
@@ -0,0 +1,315 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const CONFIGS_DIR = path.join(__dirname, '..', 'configs');
5
+ const AVAILABLE_TECHS = ['angular', 'nextjs', 'nestjs', 'dotnet', 'python'];
6
+
7
+ const colors = {
8
+ red: (text) => `\x1b[31m${text}\x1b[0m`,
9
+ green: (text) => `\x1b[32m${text}\x1b[0m`,
10
+ blue: (text) => `\x1b[34m${text}\x1b[0m`,
11
+ yellow: (text) => `\x1b[33m${text}\x1b[0m`,
12
+ bold: (text) => `\x1b[1m${text}\x1b[0m`,
13
+ };
14
+
15
+ const log = {
16
+ info: (msg) => console.log(`${colors.blue('ℹ')} ${msg}`),
17
+ success: (msg) => console.log(`${colors.green('✓')} ${msg}`),
18
+ warning: (msg) => console.log(`${colors.yellow('⚠')} ${msg}`),
19
+ error: (msg) => console.log(`${colors.red('✗')} ${msg}`),
20
+ };
21
+
22
+ function printUsage() {
23
+ console.log(`
24
+ ${colors.bold('AI Rules')} - Claude Code configuration boilerplates
25
+
26
+ ${colors.bold('Usage:')}
27
+ ai-rules init <tech> [tech2] [options]
28
+ ai-rules list
29
+
30
+ ${colors.bold('Technologies:')}
31
+ angular Angular 21 + Nx + NgRx
32
+ nextjs Next.js 15 + React 19
33
+ nestjs NestJS + Prisma/TypeORM
34
+ dotnet .NET 8 + EF Core
35
+ python FastAPI/Flask + SQLAlchemy
36
+
37
+ ${colors.bold('Options:')}
38
+ --with-skills Include skills (/learning, /review, /spec, /debug)
39
+ --with-commands Include commands (fix-issue, review-pr, generate-tests)
40
+ --with-rules Include shared rules (security, performance, accessibility)
41
+ --all Include skills, commands, and rules
42
+ --target <dir> Target directory (default: current directory)
43
+
44
+ ${colors.bold('Examples:')}
45
+ ai-rules init angular
46
+ ai-rules init angular nestjs --all
47
+ ai-rules init nextjs python --with-skills
48
+ ai-rules init dotnet --target ./my-project
49
+ ai-rules list
50
+ `);
51
+ }
52
+
53
+ function copyDirRecursive(src, dest) {
54
+ if (!fs.existsSync(src)) return;
55
+
56
+ fs.mkdirSync(dest, { recursive: true });
57
+
58
+ const entries = fs.readdirSync(src, { withFileTypes: true });
59
+
60
+ for (const entry of entries) {
61
+ const srcPath = path.join(src, entry.name);
62
+ const destPath = path.join(dest, entry.name);
63
+
64
+ if (entry.isDirectory()) {
65
+ copyDirRecursive(srcPath, destPath);
66
+ } else {
67
+ fs.copyFileSync(srcPath, destPath);
68
+ }
69
+ }
70
+ }
71
+
72
+ function mergeClaudeMd(targetPath, sourcePath, isFirst) {
73
+ const content = fs.readFileSync(sourcePath, 'utf8');
74
+
75
+ if (isFirst) {
76
+ fs.writeFileSync(targetPath, content);
77
+ } else {
78
+ const existing = fs.readFileSync(targetPath, 'utf8');
79
+ fs.writeFileSync(targetPath, `${existing}\n\n---\n\n${content}`);
80
+ }
81
+ }
82
+
83
+ function listTechnologies() {
84
+ console.log(`\n${colors.bold('Available technologies:')}\n`);
85
+
86
+ const techInfo = {
87
+ angular: 'Angular 21 + Nx + NgRx + Signals',
88
+ nextjs: 'Next.js 15 + React 19 + App Router',
89
+ nestjs: 'NestJS 10 + Prisma/TypeORM + Passport',
90
+ dotnet: '.NET 8 + ASP.NET Core + EF Core',
91
+ python: 'FastAPI/Flask + SQLAlchemy 2.0',
92
+ };
93
+
94
+ for (const tech of AVAILABLE_TECHS) {
95
+ const techPath = path.join(CONFIGS_DIR, tech);
96
+ const exists = fs.existsSync(techPath);
97
+ const status = exists ? colors.green('✓') : colors.red('✗');
98
+ console.log(` ${status} ${colors.bold(tech.padEnd(10))} ${techInfo[tech]}`);
99
+ }
100
+
101
+ console.log(`\n${colors.bold('Shared resources:')}\n`);
102
+
103
+ const sharedPath = path.join(CONFIGS_DIR, '_shared');
104
+ const skills = fs.existsSync(path.join(sharedPath, '.claude', 'skills'));
105
+ const commands = fs.existsSync(path.join(sharedPath, '.claude', 'commands'));
106
+ const rules = fs.existsSync(path.join(sharedPath, '.claude', 'rules'));
107
+
108
+ console.log(` ${skills ? colors.green('✓') : colors.red('✗')} skills /learning, /review, /spec, /debug`);
109
+ console.log(` ${commands ? colors.green('✓') : colors.red('✗')} commands fix-issue, review-pr, generate-tests`);
110
+ console.log(` ${rules ? colors.green('✓') : colors.red('✗')} rules security, performance, accessibility`);
111
+ console.log('');
112
+ }
113
+
114
+ function init(techs, options) {
115
+ const targetDir = options.target || process.cwd();
116
+
117
+ log.info(`Installing Claude Code config to: ${targetDir}`);
118
+ console.log('');
119
+
120
+ // Create directories
121
+ fs.mkdirSync(path.join(targetDir, '.claude', 'rules'), { recursive: true });
122
+
123
+ // Install each technology
124
+ let isFirstClaudeMd = true;
125
+
126
+ for (const tech of techs) {
127
+ log.info(`Installing ${tech}...`);
128
+
129
+ const techDir = path.join(CONFIGS_DIR, tech);
130
+
131
+ if (!fs.existsSync(techDir)) {
132
+ log.error(`Technology directory not found: ${tech}`);
133
+ process.exit(1);
134
+ }
135
+
136
+ // Copy CLAUDE.md
137
+ const claudeMdPath = path.join(techDir, 'CLAUDE.md');
138
+ if (fs.existsSync(claudeMdPath)) {
139
+ mergeClaudeMd(
140
+ path.join(targetDir, 'CLAUDE.md'),
141
+ claudeMdPath,
142
+ isFirstClaudeMd
143
+ );
144
+ isFirstClaudeMd = false;
145
+ log.success(' CLAUDE.md');
146
+ }
147
+
148
+ // Copy settings.json
149
+ const settingsPath = path.join(techDir, '.claude', 'settings.json');
150
+ if (fs.existsSync(settingsPath)) {
151
+ const targetSettingsPath = path.join(targetDir, '.claude', 'settings.json');
152
+
153
+ if (!fs.existsSync(targetSettingsPath)) {
154
+ fs.copyFileSync(settingsPath, targetSettingsPath);
155
+ } else {
156
+ // Merge permissions
157
+ try {
158
+ const existing = JSON.parse(fs.readFileSync(targetSettingsPath, 'utf8'));
159
+ const incoming = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
160
+
161
+ const merged = {
162
+ permissions: {
163
+ allow: [...new Set([
164
+ ...(existing.permissions?.allow || []),
165
+ ...(incoming.permissions?.allow || []),
166
+ ])],
167
+ deny: [],
168
+ },
169
+ };
170
+
171
+ fs.writeFileSync(targetSettingsPath, JSON.stringify(merged, null, 2));
172
+ } catch (e) {
173
+ log.warning(' Could not merge settings.json');
174
+ }
175
+ }
176
+ log.success(' settings.json');
177
+ }
178
+
179
+ // Copy rules
180
+ const rulesDir = path.join(techDir, '.claude', 'rules');
181
+ if (fs.existsSync(rulesDir)) {
182
+ copyDirRecursive(rulesDir, path.join(targetDir, '.claude', 'rules'));
183
+ log.success(' rules/');
184
+ }
185
+ }
186
+
187
+ // Install shared resources
188
+ const sharedDir = path.join(CONFIGS_DIR, '_shared');
189
+
190
+ if (options.withSkills || options.all) {
191
+ log.info('Installing skills...');
192
+ const skillsDir = path.join(sharedDir, '.claude', 'skills');
193
+ if (fs.existsSync(skillsDir)) {
194
+ copyDirRecursive(skillsDir, path.join(targetDir, '.claude', 'skills'));
195
+ log.success(' skills/ (learning, review, spec, debug)');
196
+ }
197
+ }
198
+
199
+ if (options.withCommands || options.all) {
200
+ log.info('Installing commands...');
201
+ const commandsDir = path.join(sharedDir, '.claude', 'commands');
202
+ if (fs.existsSync(commandsDir)) {
203
+ copyDirRecursive(commandsDir, path.join(targetDir, '.claude', 'commands'));
204
+ log.success(' commands/ (fix-issue, review-pr, generate-tests)');
205
+ }
206
+ }
207
+
208
+ if (options.withRules || options.all) {
209
+ log.info('Installing shared rules...');
210
+ const rulesDir = path.join(sharedDir, '.claude', 'rules');
211
+ if (fs.existsSync(rulesDir)) {
212
+ copyDirRecursive(rulesDir, path.join(targetDir, '.claude', 'rules'));
213
+ log.success(' rules/ (security, performance, accessibility)');
214
+ }
215
+ }
216
+
217
+ // Resolve @../_shared/CLAUDE.md imports
218
+ const targetClaudeMd = path.join(targetDir, 'CLAUDE.md');
219
+ if (fs.existsSync(targetClaudeMd)) {
220
+ let content = fs.readFileSync(targetClaudeMd, 'utf8');
221
+
222
+ if (content.includes('@../_shared/CLAUDE.md')) {
223
+ const sharedClaudeMd = path.join(sharedDir, 'CLAUDE.md');
224
+ if (fs.existsSync(sharedClaudeMd)) {
225
+ const sharedContent = fs.readFileSync(sharedClaudeMd, 'utf8');
226
+ content = content.replace(/@..\/_shared\/CLAUDE\.md/g, '');
227
+ content = sharedContent + '\n\n' + content;
228
+ fs.writeFileSync(targetClaudeMd, content);
229
+ log.success('Merged shared conventions into CLAUDE.md');
230
+ }
231
+ }
232
+ }
233
+
234
+ console.log('');
235
+ log.success('Installation complete!');
236
+ console.log('');
237
+ console.log('Installed:');
238
+ console.log(` - Technologies: ${techs.join(', ')}`);
239
+ if (options.withSkills || options.all) {
240
+ console.log(' - Skills: /learning, /review, /spec, /debug');
241
+ }
242
+ if (options.withCommands || options.all) {
243
+ console.log(' - Commands: fix-issue, review-pr, generate-tests');
244
+ }
245
+ if (options.withRules || options.all) {
246
+ console.log(' - Rules: security, performance, accessibility');
247
+ }
248
+ console.log('');
249
+ console.log(`Files created in: ${targetDir}`);
250
+ }
251
+
252
+ function run(args) {
253
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
254
+ printUsage();
255
+ return;
256
+ }
257
+
258
+ const command = args[0];
259
+
260
+ if (command === 'list') {
261
+ listTechnologies();
262
+ return;
263
+ }
264
+
265
+ if (command === 'init') {
266
+ const options = {
267
+ target: null,
268
+ withSkills: false,
269
+ withCommands: false,
270
+ withRules: false,
271
+ all: false,
272
+ };
273
+
274
+ const techs = [];
275
+
276
+ for (let i = 1; i < args.length; i++) {
277
+ const arg = args[i];
278
+
279
+ if (arg === '--with-skills') {
280
+ options.withSkills = true;
281
+ } else if (arg === '--with-commands') {
282
+ options.withCommands = true;
283
+ } else if (arg === '--with-rules') {
284
+ options.withRules = true;
285
+ } else if (arg === '--all') {
286
+ options.all = true;
287
+ } else if (arg === '--target') {
288
+ options.target = args[++i];
289
+ } else if (!arg.startsWith('-')) {
290
+ if (AVAILABLE_TECHS.includes(arg)) {
291
+ techs.push(arg);
292
+ } else {
293
+ log.error(`Unknown technology: ${arg}`);
294
+ console.log(`Available: ${AVAILABLE_TECHS.join(', ')}`);
295
+ process.exit(1);
296
+ }
297
+ }
298
+ }
299
+
300
+ if (techs.length === 0) {
301
+ log.error('No technology specified');
302
+ printUsage();
303
+ process.exit(1);
304
+ }
305
+
306
+ init(techs, options);
307
+ return;
308
+ }
309
+
310
+ log.error(`Unknown command: ${command}`);
311
+ printUsage();
312
+ process.exit(1);
313
+ }
314
+
315
+ module.exports = { run };