ai-factory 2.0.0 → 2.2.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 (91) hide show
  1. package/README.md +17 -4
  2. package/dist/cli/commands/extension.d.ts +4 -0
  3. package/dist/cli/commands/extension.d.ts.map +1 -0
  4. package/dist/cli/commands/extension.js +288 -0
  5. package/dist/cli/commands/extension.js.map +1 -0
  6. package/dist/cli/commands/init.d.ts.map +1 -1
  7. package/dist/cli/commands/init.js +31 -35
  8. package/dist/cli/commands/init.js.map +1 -1
  9. package/dist/cli/commands/update.d.ts.map +1 -1
  10. package/dist/cli/commands/update.js +86 -7
  11. package/dist/cli/commands/update.js.map +1 -1
  12. package/dist/cli/commands/upgrade.d.ts.map +1 -1
  13. package/dist/cli/commands/upgrade.js +44 -41
  14. package/dist/cli/commands/upgrade.js.map +1 -1
  15. package/dist/cli/index.js +48 -1
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/cli/wizard/prompts.d.ts +2 -1
  18. package/dist/cli/wizard/prompts.d.ts.map +1 -1
  19. package/dist/cli/wizard/prompts.js +13 -20
  20. package/dist/cli/wizard/prompts.js.map +1 -1
  21. package/dist/core/agents.d.ts.map +1 -1
  22. package/dist/core/agents.js +9 -0
  23. package/dist/core/agents.js.map +1 -1
  24. package/dist/core/config.d.ts +8 -2
  25. package/dist/core/config.d.ts.map +1 -1
  26. package/dist/core/config.js +5 -8
  27. package/dist/core/config.js.map +1 -1
  28. package/dist/core/extension-ops.d.ts +32 -0
  29. package/dist/core/extension-ops.d.ts.map +1 -0
  30. package/dist/core/extension-ops.js +83 -0
  31. package/dist/core/extension-ops.js.map +1 -0
  32. package/dist/core/extensions.d.ts +53 -0
  33. package/dist/core/extensions.d.ts.map +1 -0
  34. package/dist/core/extensions.js +141 -0
  35. package/dist/core/extensions.js.map +1 -0
  36. package/dist/core/injections.d.ts +10 -0
  37. package/dist/core/injections.d.ts.map +1 -0
  38. package/dist/core/injections.js +154 -0
  39. package/dist/core/injections.js.map +1 -0
  40. package/dist/core/installer.d.ts +7 -3
  41. package/dist/core/installer.d.ts.map +1 -1
  42. package/dist/core/installer.js +68 -26
  43. package/dist/core/installer.js.map +1 -1
  44. package/dist/core/mcp.d.ts +12 -0
  45. package/dist/core/mcp.d.ts.map +1 -1
  46. package/dist/core/mcp.js +112 -67
  47. package/dist/core/mcp.js.map +1 -1
  48. package/dist/core/transformer.d.ts +10 -1
  49. package/dist/core/transformer.d.ts.map +1 -1
  50. package/dist/core/transformer.js +19 -1
  51. package/dist/core/transformer.js.map +1 -1
  52. package/dist/core/transformers/antigravity.d.ts +1 -0
  53. package/dist/core/transformers/antigravity.d.ts.map +1 -1
  54. package/dist/core/transformers/antigravity.js +19 -4
  55. package/dist/core/transformers/antigravity.js.map +1 -1
  56. package/dist/core/transformers/codex.d.ts +7 -0
  57. package/dist/core/transformers/codex.d.ts.map +1 -0
  58. package/dist/core/transformers/codex.js +24 -0
  59. package/dist/core/transformers/codex.js.map +1 -0
  60. package/dist/core/transformers/default.d.ts +1 -0
  61. package/dist/core/transformers/default.d.ts.map +1 -1
  62. package/dist/core/transformers/default.js +6 -0
  63. package/dist/core/transformers/default.js.map +1 -1
  64. package/dist/core/transformers/kilocode.js +1 -1
  65. package/dist/core/transformers/kilocode.js.map +1 -1
  66. package/dist/core/transformers/qwen.d.ts +7 -0
  67. package/dist/core/transformers/qwen.d.ts.map +1 -0
  68. package/dist/core/transformers/qwen.js +25 -0
  69. package/dist/core/transformers/qwen.js.map +1 -0
  70. package/dist/utils/fs.d.ts +0 -2
  71. package/dist/utils/fs.d.ts.map +1 -1
  72. package/dist/utils/fs.js +1 -5
  73. package/dist/utils/fs.js.map +1 -1
  74. package/mcp/templates/playwright.json +4 -0
  75. package/package.json +16 -1
  76. package/skills/aif/SKILL.md +27 -52
  77. package/skills/aif-commit/SKILL.md +13 -1
  78. package/skills/aif-grounded/SKILL.md +90 -0
  79. package/skills/aif-implement/SKILL.md +39 -2
  80. package/skills/aif-implement/references/IMPLEMENTATION-GUIDE.md +16 -0
  81. package/skills/aif-loop/SKILL.md +2 -2
  82. package/dist/cli/wizard/detector.d.ts +0 -10
  83. package/dist/cli/wizard/detector.d.ts.map +0 -1
  84. package/dist/cli/wizard/detector.js +0 -231
  85. package/dist/cli/wizard/detector.js.map +0 -1
  86. package/skills/_templates/nextjs/nextjs-patterns/SKILL.md +0 -146
  87. package/skills/_templates/node-api/api-patterns/SKILL.md +0 -245
  88. package/skills/_templates/php/php-patterns/SKILL.md +0 -491
  89. package/skills/_templates/python/python-patterns/SKILL.md +0 -236
  90. package/skills/_templates/react/react-patterns/SKILL.md +0 -181
  91. package/skills/aif-deploy/SKILL.md +0 -138
@@ -1,491 +0,0 @@
1
- ---
2
- name: php-patterns
3
- description: PHP development patterns and best practices. Covers Laravel, Symfony, modern PHP 8+, PSR standards, Composer, and testing with PHPUnit/Pest.
4
- argument-hint: "[topic: laravel|symfony|php8|psr|testing|security]"
5
- ---
6
-
7
- # PHP Patterns Guide
8
-
9
- Modern PHP patterns and best practices for building robust applications.
10
-
11
- ## Topics
12
-
13
- ### Laravel (`/php-patterns laravel`)
14
-
15
- **Project Structure:**
16
- ```
17
- app/
18
- ├── Console/Commands/ # Artisan commands
19
- ├── Exceptions/ # Exception handlers
20
- ├── Http/
21
- │ ├── Controllers/ # Request handlers
22
- │ ├── Middleware/ # Request/response filters
23
- │ ├── Requests/ # Form requests (validation)
24
- │ └── Resources/ # API resources
25
- ├── Models/ # Eloquent models
26
- ├── Policies/ # Authorization policies
27
- ├── Providers/ # Service providers
28
- └── Services/ # Business logic
29
- ```
30
-
31
- **Eloquent Best Practices:**
32
- ```php
33
- // ✅ Good: Eager loading to prevent N+1
34
- $users = User::with(['posts', 'profile'])->get();
35
-
36
- // ❌ Bad: N+1 query problem
37
- $users = User::all();
38
- foreach ($users as $user) {
39
- echo $user->posts->count(); // Query per user!
40
- }
41
-
42
- // ✅ Good: Query scopes for reusable queries
43
- class User extends Model
44
- {
45
- public function scopeActive(Builder $query): Builder
46
- {
47
- return $query->where('status', 'active');
48
- }
49
-
50
- public function scopeVerified(Builder $query): Builder
51
- {
52
- return $query->whereNotNull('email_verified_at');
53
- }
54
- }
55
-
56
- // Usage
57
- $users = User::active()->verified()->get();
58
- ```
59
-
60
- **Form Requests:**
61
- ```php
62
- class StoreUserRequest extends FormRequest
63
- {
64
- public function authorize(): bool
65
- {
66
- return true;
67
- }
68
-
69
- public function rules(): array
70
- {
71
- return [
72
- 'email' => ['required', 'email', 'unique:users'],
73
- 'password' => ['required', 'min:12', 'confirmed'],
74
- 'name' => ['required', 'string', 'max:255'],
75
- ];
76
- }
77
- }
78
-
79
- // Controller stays clean
80
- public function store(StoreUserRequest $request): JsonResponse
81
- {
82
- $user = User::create($request->validated());
83
- return response()->json($user, 201);
84
- }
85
- ```
86
-
87
- **API Resources:**
88
- ```php
89
- class UserResource extends JsonResource
90
- {
91
- public function toArray(Request $request): array
92
- {
93
- return [
94
- 'id' => $this->id,
95
- 'name' => $this->name,
96
- 'email' => $this->email,
97
- 'created_at' => $this->created_at->toISOString(),
98
- 'posts' => PostResource::collection($this->whenLoaded('posts')),
99
- ];
100
- }
101
- }
102
- ```
103
-
104
- ### Symfony (`/php-patterns symfony`)
105
-
106
- **Service Architecture:**
107
- ```php
108
- // src/Service/UserService.php
109
- #[AsService]
110
- class UserService
111
- {
112
- public function __construct(
113
- private UserRepository $userRepository,
114
- private PasswordHasherInterface $passwordHasher,
115
- private EventDispatcherInterface $dispatcher,
116
- ) {}
117
-
118
- public function createUser(CreateUserDTO $dto): User
119
- {
120
- $user = new User();
121
- $user->setEmail($dto->email);
122
- $user->setPassword(
123
- $this->passwordHasher->hashPassword($user, $dto->password)
124
- );
125
-
126
- $this->userRepository->save($user, flush: true);
127
- $this->dispatcher->dispatch(new UserCreatedEvent($user));
128
-
129
- return $user;
130
- }
131
- }
132
- ```
133
-
134
- **Repository Pattern:**
135
- ```php
136
- // src/Repository/UserRepository.php
137
- class UserRepository extends ServiceEntityRepository
138
- {
139
- public function __construct(ManagerRegistry $registry)
140
- {
141
- parent::__construct($registry, User::class);
142
- }
143
-
144
- public function findActiveByEmail(string $email): ?User
145
- {
146
- return $this->createQueryBuilder('u')
147
- ->andWhere('u.email = :email')
148
- ->andWhere('u.status = :status')
149
- ->setParameter('email', $email)
150
- ->setParameter('status', 'active')
151
- ->getQuery()
152
- ->getOneOrNullResult();
153
- }
154
-
155
- public function save(User $entity, bool $flush = false): void
156
- {
157
- $this->getEntityManager()->persist($entity);
158
- if ($flush) {
159
- $this->getEntityManager()->flush();
160
- }
161
- }
162
- }
163
- ```
164
-
165
- **DTOs with Validation:**
166
- ```php
167
- // src/DTO/CreateUserDTO.php
168
- class CreateUserDTO
169
- {
170
- public function __construct(
171
- #[Assert\NotBlank]
172
- #[Assert\Email]
173
- public readonly string $email,
174
-
175
- #[Assert\NotBlank]
176
- #[Assert\Length(min: 12)]
177
- public readonly string $password,
178
-
179
- #[Assert\NotBlank]
180
- #[Assert\Length(max: 255)]
181
- public readonly string $name,
182
- ) {}
183
- }
184
- ```
185
-
186
- ### Modern PHP 8+ (`/php-patterns php8`)
187
-
188
- **Constructor Property Promotion:**
189
- ```php
190
- // ✅ PHP 8+ concise
191
- class User
192
- {
193
- public function __construct(
194
- public readonly int $id,
195
- public string $name,
196
- public string $email,
197
- private ?string $password = null,
198
- ) {}
199
- }
200
-
201
- // ❌ Old verbose style
202
- class User
203
- {
204
- public int $id;
205
- public string $name;
206
-
207
- public function __construct(int $id, string $name)
208
- {
209
- $this->id = $id;
210
- $this->name = $name;
211
- }
212
- }
213
- ```
214
-
215
- **Named Arguments:**
216
- ```php
217
- // Clear intent
218
- $user = new User(
219
- id: 1,
220
- name: 'John',
221
- email: 'john@example.com',
222
- );
223
-
224
- // Skip optional params
225
- sendEmail(
226
- to: $user->email,
227
- subject: 'Welcome',
228
- // body uses default
229
- );
230
- ```
231
-
232
- **Match Expression:**
233
- ```php
234
- // ✅ PHP 8+ match
235
- $result = match($status) {
236
- 'pending' => 'Awaiting review',
237
- 'approved' => 'Ready to publish',
238
- 'rejected' => 'Please revise',
239
- default => 'Unknown status',
240
- };
241
-
242
- // ❌ Verbose switch
243
- switch($status) {
244
- case 'pending':
245
- $result = 'Awaiting review';
246
- break;
247
- // ...
248
- }
249
- ```
250
-
251
- **Enums:**
252
- ```php
253
- enum OrderStatus: string
254
- {
255
- case Pending = 'pending';
256
- case Processing = 'processing';
257
- case Shipped = 'shipped';
258
- case Delivered = 'delivered';
259
- case Cancelled = 'cancelled';
260
-
261
- public function label(): string
262
- {
263
- return match($this) {
264
- self::Pending => 'Pending',
265
- self::Processing => 'Processing',
266
- self::Shipped => 'Shipped',
267
- self::Delivered => 'Delivered',
268
- self::Cancelled => 'Cancelled',
269
- };
270
- }
271
-
272
- public function canCancel(): bool
273
- {
274
- return in_array($this, [self::Pending, self::Processing]);
275
- }
276
- }
277
-
278
- // Usage
279
- $order->status = OrderStatus::Pending;
280
- if ($order->status->canCancel()) {
281
- // ...
282
- }
283
- ```
284
-
285
- **Attributes:**
286
- ```php
287
- #[Route('/api/users', methods: ['GET'])]
288
- #[IsGranted('ROLE_ADMIN')]
289
- public function list(): JsonResponse
290
- {
291
- // ...
292
- }
293
-
294
- // Custom attribute
295
- #[Attribute(Attribute::TARGET_PROPERTY)]
296
- class Encrypted
297
- {
298
- public function __construct(
299
- public string $algorithm = 'aes-256-cbc'
300
- ) {}
301
- }
302
-
303
- class User
304
- {
305
- #[Encrypted]
306
- private string $ssn;
307
- }
308
- ```
309
-
310
- ### PSR Standards (`/php-patterns psr`)
311
-
312
- **PSR-4 Autoloading:**
313
- ```json
314
- // composer.json
315
- {
316
- "autoload": {
317
- "psr-4": {
318
- "App\\": "src/",
319
- "Tests\\": "tests/"
320
- }
321
- }
322
- }
323
- ```
324
-
325
- **PSR-12 Code Style:**
326
- ```php
327
- <?php
328
-
329
- declare(strict_types=1);
330
-
331
- namespace App\Service;
332
-
333
- use App\Repository\UserRepository;
334
- use Psr\Log\LoggerInterface;
335
-
336
- class UserService
337
- {
338
- public function __construct(
339
- private UserRepository $repository,
340
- private LoggerInterface $logger,
341
- ) {
342
- }
343
-
344
- public function findUser(int $id): ?User
345
- {
346
- if ($id <= 0) {
347
- throw new InvalidArgumentException('ID must be positive');
348
- }
349
-
350
- return $this->repository->find($id);
351
- }
352
- }
353
- ```
354
-
355
- ### Testing (`/php-patterns testing`)
356
-
357
- **PHPUnit:**
358
- ```php
359
- class UserServiceTest extends TestCase
360
- {
361
- private UserService $service;
362
- private MockObject $repository;
363
-
364
- protected function setUp(): void
365
- {
366
- $this->repository = $this->createMock(UserRepository::class);
367
- $this->service = new UserService($this->repository);
368
- }
369
-
370
- public function testCreateUserHashesPassword(): void
371
- {
372
- // Arrange
373
- $dto = new CreateUserDTO(
374
- email: 'test@example.com',
375
- password: 'plainpassword',
376
- name: 'Test User',
377
- );
378
-
379
- $this->repository
380
- ->expects($this->once())
381
- ->method('save')
382
- ->with($this->callback(fn(User $user) =>
383
- password_verify('plainpassword', $user->getPassword())
384
- ));
385
-
386
- // Act
387
- $user = $this->service->createUser($dto);
388
-
389
- // Assert
390
- $this->assertEquals('test@example.com', $user->getEmail());
391
- }
392
-
393
- /**
394
- * @dataProvider invalidEmailProvider
395
- */
396
- public function testRejectsInvalidEmail(string $email): void
397
- {
398
- $this->expectException(ValidationException::class);
399
-
400
- new CreateUserDTO($email, 'password123', 'Name');
401
- }
402
-
403
- public static function invalidEmailProvider(): array
404
- {
405
- return [
406
- 'empty' => [''],
407
- 'no at sign' => ['invalid'],
408
- 'no domain' => ['test@'],
409
- ];
410
- }
411
- }
412
- ```
413
-
414
- **Pest (Modern alternative):**
415
- ```php
416
- test('user can be created', function () {
417
- $user = User::factory()->create();
418
-
419
- expect($user)
420
- ->toBeInstanceOf(User::class)
421
- ->id->toBeInt()
422
- ->email->toContain('@');
423
- });
424
-
425
- test('password is hashed on create')
426
- ->expect(fn() => User::factory()->create(['password' => 'secret']))
427
- ->password->not->toBe('secret');
428
-
429
- it('validates email format', function (string $email) {
430
- expect(fn() => new CreateUserDTO($email, 'pass', 'name'))
431
- ->toThrow(ValidationException::class);
432
- })->with(['', 'invalid', 'test@']);
433
- ```
434
-
435
- ### Security (`/php-patterns security`)
436
-
437
- **Password Hashing:**
438
- ```php
439
- // ✅ Good: Use password_hash
440
- $hash = password_hash($password, PASSWORD_ARGON2ID, [
441
- 'memory_cost' => 65536,
442
- 'time_cost' => 4,
443
- 'threads' => 3,
444
- ]);
445
-
446
- // Verify
447
- if (password_verify($inputPassword, $storedHash)) {
448
- // Valid
449
- }
450
-
451
- // ❌ Never use: md5, sha1, sha256 for passwords
452
- ```
453
-
454
- **SQL Injection Prevention:**
455
- ```php
456
- // ✅ PDO prepared statements
457
- $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
458
- $stmt->execute(['email' => $email]);
459
-
460
- // ✅ Eloquent (auto-escaped)
461
- User::where('email', $email)->first();
462
-
463
- // ✅ Query Builder
464
- DB::table('users')->where('email', '=', $email)->first();
465
-
466
- // ❌ NEVER: String interpolation
467
- $pdo->query("SELECT * FROM users WHERE email = '$email'");
468
- ```
469
-
470
- **XSS Prevention:**
471
- ```php
472
- // ✅ Blade auto-escapes
473
- {{ $userInput }}
474
-
475
- // ❌ Raw output - only when certain it's safe
476
- {!! $trustedHtml !!}
477
-
478
- // ✅ Manual escaping
479
- echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
480
- ```
481
-
482
- ## Best Practices Summary
483
-
484
- 1. **Use strict types** - `declare(strict_types=1);`
485
- 2. **Type everything** - Parameters, returns, properties
486
- 3. **Prefer readonly** - Immutable by default
487
- 4. **Use enums** - Instead of string constants
488
- 5. **Dependency injection** - Constructor injection preferred
489
- 6. **Follow PSR-12** - Code style consistency
490
- 7. **Write tests** - PHPUnit or Pest
491
- 8. **Use static analysis** - PHPStan level 8+
@@ -1,236 +0,0 @@
1
- ---
2
- name: python-patterns
3
- description: Python development patterns and best practices. Covers typing, async, testing, project structure, and common frameworks.
4
- argument-hint: "[topic: typing|async|testing|structure|fastapi|django]"
5
- ---
6
-
7
- # Python Patterns Guide
8
-
9
- Modern Python patterns and best practices for building robust applications.
10
-
11
- ## Topics
12
-
13
- ### Type Hints (`/python-patterns typing`)
14
-
15
- **Basic Types:**
16
- ```python
17
- from typing import Optional, List, Dict, Union, Callable, TypeVar, Generic
18
-
19
- def greet(name: str) -> str:
20
- return f"Hello, {name}"
21
-
22
- def process_items(items: List[str]) -> Dict[str, int]:
23
- return {item: len(item) for item in items}
24
-
25
- def find_user(user_id: int) -> Optional[User]:
26
- return db.get(user_id)
27
-
28
- # Union types (Python 3.10+)
29
- def process(value: int | str) -> None:
30
- pass
31
- ```
32
-
33
- **Generics:**
34
- ```python
35
- T = TypeVar('T')
36
-
37
- class Repository(Generic[T]):
38
- def get(self, id: int) -> Optional[T]:
39
- ...
40
-
41
- def save(self, entity: T) -> T:
42
- ...
43
-
44
- # Usage
45
- user_repo: Repository[User] = Repository()
46
- ```
47
-
48
- **Dataclasses & Pydantic:**
49
- ```python
50
- from dataclasses import dataclass
51
- from pydantic import BaseModel, Field
52
-
53
- # Dataclass for simple data containers
54
- @dataclass
55
- class Point:
56
- x: float
57
- y: float
58
-
59
- # Pydantic for validation
60
- class UserCreate(BaseModel):
61
- email: str = Field(..., pattern=r'^[\w\.-]+@[\w\.-]+\.\w+$')
62
- password: str = Field(..., min_length=8)
63
- name: str = Field(..., max_length=100)
64
- ```
65
-
66
- ### Async (`/python-patterns async`)
67
-
68
- **Async Basics:**
69
- ```python
70
- import asyncio
71
- from typing import List
72
-
73
- async def fetch_data(url: str) -> dict:
74
- async with aiohttp.ClientSession() as session:
75
- async with session.get(url) as response:
76
- return await response.json()
77
-
78
- # Run multiple async operations concurrently
79
- async def fetch_all(urls: List[str]) -> List[dict]:
80
- tasks = [fetch_data(url) for url in urls]
81
- return await asyncio.gather(*tasks)
82
-
83
- # Context manager for async resources
84
- class AsyncDatabase:
85
- async def __aenter__(self):
86
- await self.connect()
87
- return self
88
-
89
- async def __aexit__(self, exc_type, exc_val, exc_tb):
90
- await self.disconnect()
91
- ```
92
-
93
- **Async Patterns:**
94
- ```python
95
- # Semaphore for limiting concurrency
96
- async def fetch_with_limit(urls: List[str], limit: int = 10):
97
- semaphore = asyncio.Semaphore(limit)
98
-
99
- async def fetch_one(url: str):
100
- async with semaphore:
101
- return await fetch_data(url)
102
-
103
- return await asyncio.gather(*[fetch_one(url) for url in urls])
104
-
105
- # Timeout handling
106
- async def fetch_with_timeout(url: str, timeout: float = 5.0):
107
- try:
108
- return await asyncio.wait_for(fetch_data(url), timeout=timeout)
109
- except asyncio.TimeoutError:
110
- raise TimeoutError(f"Request to {url} timed out")
111
- ```
112
-
113
- ### Testing (`/python-patterns testing`)
114
-
115
- **Pytest Patterns:**
116
- ```python
117
- import pytest
118
- from unittest.mock import Mock, patch, AsyncMock
119
-
120
- # Fixtures
121
- @pytest.fixture
122
- def user():
123
- return User(id=1, name="Test User", email="test@example.com")
124
-
125
- @pytest.fixture
126
- def db_session():
127
- session = create_test_session()
128
- yield session
129
- session.rollback()
130
- session.close()
131
-
132
- # Parametrized tests
133
- @pytest.mark.parametrize("input,expected", [
134
- ("hello", "HELLO"),
135
- ("world", "WORLD"),
136
- ("", ""),
137
- ])
138
- def test_uppercase(input: str, expected: str):
139
- assert input.upper() == expected
140
-
141
- # Mocking
142
- def test_fetch_user(mocker):
143
- mock_db = mocker.patch('app.db.get_user')
144
- mock_db.return_value = User(id=1, name="Test")
145
-
146
- result = fetch_user(1)
147
-
148
- mock_db.assert_called_once_with(1)
149
- assert result.name == "Test"
150
-
151
- # Async tests
152
- @pytest.mark.asyncio
153
- async def test_async_fetch():
154
- with patch('app.fetch_data', new_callable=AsyncMock) as mock:
155
- mock.return_value = {"data": "test"}
156
- result = await fetch_data("http://example.com")
157
- assert result == {"data": "test"}
158
- ```
159
-
160
- ### Project Structure (`/python-patterns structure`)
161
-
162
- ```
163
- project/
164
- ├── pyproject.toml # Project config (Poetry/PDM)
165
- ├── src/
166
- │ └── myapp/
167
- │ ├── __init__.py
168
- │ ├── main.py # Entry point
169
- │ ├── config.py # Settings
170
- │ ├── models/ # Data models
171
- │ │ ├── __init__.py
172
- │ │ └── user.py
173
- │ ├── services/ # Business logic
174
- │ │ ├── __init__.py
175
- │ │ └── user_service.py
176
- │ ├── repositories/ # Data access
177
- │ │ ├── __init__.py
178
- │ │ └── user_repo.py
179
- │ ├── api/ # API routes
180
- │ │ ├── __init__.py
181
- │ │ └── routes/
182
- │ └── utils/ # Helpers
183
- ├── tests/
184
- │ ├── conftest.py
185
- │ ├── unit/
186
- │ └── integration/
187
- └── scripts/ # CLI scripts
188
- ```
189
-
190
- ### FastAPI (`/python-patterns fastapi`)
191
-
192
- ```python
193
- from fastapi import FastAPI, Depends, HTTPException, status
194
- from fastapi.security import OAuth2PasswordBearer
195
-
196
- app = FastAPI()
197
-
198
- # Dependency injection
199
- async def get_db():
200
- db = SessionLocal()
201
- try:
202
- yield db
203
- finally:
204
- db.close()
205
-
206
- # Route with dependencies
207
- @app.get("/users/{user_id}", response_model=UserResponse)
208
- async def get_user(
209
- user_id: int,
210
- db: Session = Depends(get_db),
211
- current_user: User = Depends(get_current_user),
212
- ):
213
- user = db.query(User).filter(User.id == user_id).first()
214
- if not user:
215
- raise HTTPException(status_code=404, detail="User not found")
216
- return user
217
-
218
- # Background tasks
219
- @app.post("/email")
220
- async def send_email(
221
- email: EmailSchema,
222
- background_tasks: BackgroundTasks,
223
- ):
224
- background_tasks.add_task(send_email_task, email)
225
- return {"message": "Email queued"}
226
- ```
227
-
228
- ## Best Practices
229
-
230
- 1. **Use type hints** - Better IDE support and documentation
231
- 2. **Prefer composition** - Over inheritance
232
- 3. **Use context managers** - For resource management
233
- 4. **Write tests** - Pytest with fixtures
234
- 5. **Use virtual environments** - Poetry or PDM
235
- 6. **Format code** - Black + isort + ruff
236
- 7. **Document with docstrings** - Google or NumPy style