@softspark/ai-toolkit 3.0.2 → 3.1.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/AGENTS.md +13 -0
- package/CHANGELOG.md +35 -0
- package/README.md +25 -39
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/skills/cpp-rules/SKILL.md +275 -0
- package/app/skills/csharp-rules/SKILL.md +282 -0
- package/app/skills/dart-rules/SKILL.md +299 -0
- package/app/skills/golang-rules/SKILL.md +262 -0
- package/app/skills/java-rules/SKILL.md +273 -0
- package/app/skills/kotlin-rules/SKILL.md +271 -0
- package/app/skills/medplum-rules/SKILL.md +271 -0
- package/app/skills/php-rules/SKILL.md +292 -0
- package/app/skills/python-rules/SKILL.md +257 -0
- package/app/skills/ruby-rules/SKILL.md +286 -0
- package/app/skills/rust-rules/SKILL.md +276 -0
- package/app/skills/swift-rules/SKILL.md +293 -0
- package/app/skills/typescript-rules/SKILL.md +249 -0
- package/benchmarks/ecosystem-doctor-snapshot.json +14 -14
- package/kb/history/completed/deep-coverage-v3-20260423.md +3 -3
- package/kb/history/completed/ecosystem-deep-sweep-20260423.md +1 -1
- package/kb/procedures/release-preparation-sop.md +4 -4
- package/kb/procedures/release-verification-sop.md +11 -12
- package/kb/reference/architecture-overview.md +1 -1
- package/kb/reference/global-install-model.md +29 -6
- package/kb/reference/language-rules.md +54 -18
- package/kb/reference/mcp-editor-compatibility.md +4 -3
- package/kb/reference/mcp-templates.md +3 -2
- package/kb/reference/supported-tools-registry.md +10 -8
- package/llms-full.txt +133 -58
- package/manifest.json +3 -3
- package/package.json +10 -3
- package/scripts/codex_skill_adapter.py +19 -3
- package/scripts/ecosystem_tools.json +7 -7
- package/scripts/generate_cline_rules.py +17 -8
- package/scripts/generate_codex_skills.py +33 -96
- package/scripts/generate_language_rules_skills.py +232 -0
- package/scripts/generate_roo_rules.py +11 -3
- package/scripts/install.py +6 -1
- package/scripts/install_steps/ai_tools.py +154 -51
- package/scripts/install_steps/install_state.py +14 -2
- package/scripts/install_steps/project_registry.py +38 -5
- package/scripts/mcp_editors.py +7 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: php-rules
|
|
3
|
+
description: "PHP coding rules from ai-toolkit: coding-style, frameworks, patterns, security, testing. Triggers: .php, composer.json, Laravel, Symfony, PHPUnit, PSR-12, Composer. Load when writing, reviewing, or editing PHP code."
|
|
4
|
+
effort: medium
|
|
5
|
+
user-invocable: false
|
|
6
|
+
allowed-tools: Read
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# PHP Rules
|
|
10
|
+
|
|
11
|
+
These rules come from `app/rules/php/` in ai-toolkit. They cover
|
|
12
|
+
the project's standards for coding style, frameworks, patterns,
|
|
13
|
+
security, and testing in PHP. Apply them when writing or
|
|
14
|
+
reviewing PHP code.
|
|
15
|
+
|
|
16
|
+
# PHP Coding Style
|
|
17
|
+
|
|
18
|
+
## Standards
|
|
19
|
+
- Follow PSR-12 extended coding style.
|
|
20
|
+
- Use `declare(strict_types=1)` at the top of every file.
|
|
21
|
+
- Use PHP 8.1+ features: enums, fibers, readonly properties, intersection types.
|
|
22
|
+
- Use PHP CS Fixer or Pint for automated formatting.
|
|
23
|
+
|
|
24
|
+
## Naming
|
|
25
|
+
- PascalCase: classes, interfaces, traits, enums.
|
|
26
|
+
- camelCase: methods, functions, variables.
|
|
27
|
+
- UPPER_SNAKE: class constants (`public const MAX_RETRIES = 3`).
|
|
28
|
+
- snake_case: not used for methods. PSR convention is camelCase.
|
|
29
|
+
- Suffix interfaces with `Interface` or prefix with contract name (project convention).
|
|
30
|
+
|
|
31
|
+
## Type System
|
|
32
|
+
- Use typed properties: `private readonly string $name;`.
|
|
33
|
+
- Use union types: `string|int`. Use intersection types: `Countable&Iterator`.
|
|
34
|
+
- Use `enum` (PHP 8.1) for fixed sets of values. Use backed enums for persistence.
|
|
35
|
+
- Use `readonly` classes (PHP 8.2) for immutable DTOs.
|
|
36
|
+
- Use constructor promotion: `public function __construct(private string $name)`.
|
|
37
|
+
- Use `never` return type for functions that throw or exit.
|
|
38
|
+
|
|
39
|
+
## Functions
|
|
40
|
+
- Use typed parameters and return types on all functions/methods.
|
|
41
|
+
- Use named arguments for readability: `new User(name: 'Ada', age: 36)`.
|
|
42
|
+
- Use null-safe operator: `$user?->address?->city`.
|
|
43
|
+
- Use match expression over switch for value mapping.
|
|
44
|
+
- Use first-class callable syntax: `array_map($this->transform(...), $items)`.
|
|
45
|
+
|
|
46
|
+
## Imports and Namespaces
|
|
47
|
+
- Use PSR-4 autoloading via Composer.
|
|
48
|
+
- Group `use` statements: classes, functions, constants.
|
|
49
|
+
- Never use `require`/`include` for class loading. Use Composer autoloader.
|
|
50
|
+
- Use one class per file. File name matches class name.
|
|
51
|
+
|
|
52
|
+
## Error Handling
|
|
53
|
+
- Use exceptions for error conditions. Never return error codes.
|
|
54
|
+
- Create domain exception hierarchies extending `RuntimeException` or `LogicException`.
|
|
55
|
+
- Use `match` with `throw` for exhaustive error mapping.
|
|
56
|
+
- Log exceptions with context using PSR-3 logger.
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
- Use PHPStan at level 8+ for static analysis.
|
|
60
|
+
- Use Rector for automated code upgrades and refactoring.
|
|
61
|
+
- Use `.php-cs-fixer.dist.php` for formatting rules.
|
|
62
|
+
- Run `composer analyse` (PHPStan) and `composer format` (Pint) in CI.
|
|
63
|
+
|
|
64
|
+
# PHP Frameworks
|
|
65
|
+
|
|
66
|
+
## Laravel
|
|
67
|
+
- Use route model binding: `Route::get('/users/{user}', ...)`.
|
|
68
|
+
- Use Form Requests for validation: `class StoreUserRequest extends FormRequest`.
|
|
69
|
+
- Use Eloquent scopes for reusable query constraints: `scopeActive()`.
|
|
70
|
+
- Use API Resources for response transformation: `UserResource::collection($users)`.
|
|
71
|
+
- Use `config()` helper for configuration. Never access `env()` outside config files.
|
|
72
|
+
- Use middleware groups for auth, throttling, and CORS.
|
|
73
|
+
|
|
74
|
+
## Eloquent ORM
|
|
75
|
+
- Use relationships: `hasMany`, `belongsTo`, `belongsToMany`, `morphMany`.
|
|
76
|
+
- Use eager loading: `User::with('posts.comments')->get()` to prevent N+1.
|
|
77
|
+
- Use `$fillable` or `$guarded` on models. Prefer `$fillable` (explicit whitelist).
|
|
78
|
+
- Use model events or observers for lifecycle hooks.
|
|
79
|
+
- Use `upsert()` for bulk insert-or-update operations.
|
|
80
|
+
- Use `cursor()` for memory-efficient iteration over large result sets.
|
|
81
|
+
|
|
82
|
+
## Symfony
|
|
83
|
+
- Use attributes for route definitions: `#[Route('/api/users', methods: ['GET'])]`.
|
|
84
|
+
- Use autowiring for dependency injection. Register services in `services.yaml`.
|
|
85
|
+
- Use Symfony Forms for complex validation and data mapping.
|
|
86
|
+
- Use Messenger component for async message handling (commands, events).
|
|
87
|
+
- Use Doctrine ORM with repository pattern and query builders.
|
|
88
|
+
|
|
89
|
+
## Doctrine ORM
|
|
90
|
+
- Use entity classes with annotations or attributes for mapping.
|
|
91
|
+
- Use repositories for data access: `$em->getRepository(User::class)`.
|
|
92
|
+
- Use DQL for type-safe queries. Use QueryBuilder for dynamic queries.
|
|
93
|
+
- Use migrations: `bin/console doctrine:migrations:diff` and `migrate`.
|
|
94
|
+
- Use lifecycle callbacks (`@PrePersist`, `@PostUpdate`) for entity events.
|
|
95
|
+
|
|
96
|
+
## Symfony Serializer
|
|
97
|
+
- Default behavior uses property names as-is. Combined with PSR-12 `camelCase` property names, JSON output is `camelCase` with zero configuration.
|
|
98
|
+
- Avoid adding `api_platform.name_converter: CamelCaseToSnakeCaseNameConverter` globally. Known side-effect ([api-platform/core #6101](https://github.com/api-platform/core/issues/6101)): overrides the project-wide `MetadataAwareNameConverter`, affecting Messenger serializers, custom normalizers, and CLI JSON output — not just the HTTP API.
|
|
99
|
+
- Use `#[SerializedName]` only when justified: legacy field alias during rename, external contract mapping, ObjectNormalizer cross-version stabilization. Community practice ([Symfony docs](https://symfony.com/doc/current/serializer.html), Sylius, SymfonyCasts): prefer clean property/getter naming over aliases. When using, document the reason next to the attribute.
|
|
100
|
+
- Symfony 7.3.5+ `ObjectNormalizer` produces `isActive` natively for a `isActive(): bool` getter ([symfony/symfony #62353](https://github.com/symfony/symfony/issues/62353)). Older `#[SerializedName('isActive')]` aliases added for pre-7.3.5 `ObjectNormalizer` (which produced `active`) are redundant after upgrade — remove them.
|
|
101
|
+
- Avoid duplicate getters like `isActive()` + `getIsActive()` on the same property — `ObjectNormalizer` treats them as two fields and serializes ambiguously. Keep one (`isXxx()` for booleans, `getXxx()` otherwise).
|
|
102
|
+
|
|
103
|
+
## API Platform
|
|
104
|
+
- Use API Platform for rapid REST/GraphQL API generation from entities.
|
|
105
|
+
- Use `#[ApiResource]` attribute for automatic CRUD endpoint generation.
|
|
106
|
+
- Use custom state providers and processors for business logic.
|
|
107
|
+
- Use serialization groups for controlling response shape.
|
|
108
|
+
- Use filters for query parameter support: pagination, search, ordering.
|
|
109
|
+
- Property names on `ApiResource` DTOs drive JSON keys directly (see Symfony Serializer above). Write them in `camelCase` — that is both the Symfony default and the dominant JSON API convention.
|
|
110
|
+
- Use `operation_name` in `extraProperties` for dispatch metadata (e.g., `extraProperties: ['operation_name' => 'club_activate']`). The key `operation_name` and its `snake_case` values are framework metadata, not JSON wire keys — keeping them `snake_case` is expected.
|
|
111
|
+
|
|
112
|
+
## Livewire (Laravel)
|
|
113
|
+
- Use Livewire components for reactive UI without JavaScript.
|
|
114
|
+
- Use `wire:model` for two-way data binding on form inputs.
|
|
115
|
+
- Use `$rules` property for inline validation on component properties.
|
|
116
|
+
- Use component actions for server-side event handling.
|
|
117
|
+
- Use `wire:loading` for loading state indicators.
|
|
118
|
+
|
|
119
|
+
## Queues and Workers
|
|
120
|
+
- Use Laravel Horizon for Redis queue monitoring and management.
|
|
121
|
+
- Use Symfony Messenger with transports (Redis, AMQP, Doctrine).
|
|
122
|
+
- Use dead letter queues for failed job inspection and replay.
|
|
123
|
+
- Use rate limiting on queue workers to prevent downstream overload.
|
|
124
|
+
|
|
125
|
+
# PHP Patterns
|
|
126
|
+
|
|
127
|
+
## Error Handling
|
|
128
|
+
- Use custom exception hierarchies: `class DomainException extends RuntimeException`.
|
|
129
|
+
- Add context to exceptions: `throw new UserNotFoundException(userId: $id)`.
|
|
130
|
+
- Use `match` with `default => throw` for exhaustive error mapping.
|
|
131
|
+
- Use `set_exception_handler()` for global uncaught exception handling.
|
|
132
|
+
- Log exceptions with PSR-3 logger and structured context.
|
|
133
|
+
|
|
134
|
+
## Enums and Value Objects
|
|
135
|
+
- Use backed enums for database-persisted values: `enum Status: string`.
|
|
136
|
+
- Use `from()` for strict conversion, `tryFrom()` for nullable safe conversion.
|
|
137
|
+
- Implement methods on enums for behavior: `public function label(): string`.
|
|
138
|
+
- Use readonly classes for value objects: `readonly class Money { ... }`.
|
|
139
|
+
- Use constructor promotion for concise value object definitions.
|
|
140
|
+
|
|
141
|
+
## Repository Pattern
|
|
142
|
+
- Abstract data access behind repository interfaces.
|
|
143
|
+
- Repositories return domain entities, not Eloquent models or arrays.
|
|
144
|
+
- Use constructor injection for repository dependencies.
|
|
145
|
+
- Use specifications or criteria objects for complex query building.
|
|
146
|
+
- Keep repository methods focused: one query per method.
|
|
147
|
+
|
|
148
|
+
## Service Layer
|
|
149
|
+
- Use service classes for business logic. Keep controllers thin.
|
|
150
|
+
- Use action classes (single-method services) for discrete operations.
|
|
151
|
+
- Use DTOs for data transfer between layers. Never pass request objects to services.
|
|
152
|
+
- Use command/query separation: commands mutate, queries read.
|
|
153
|
+
- Inject dependencies via constructor. Never use `app()` helper in services.
|
|
154
|
+
|
|
155
|
+
## Collections and Iterators
|
|
156
|
+
- Use Laravel Collections or standalone `illuminate/collections` for data manipulation.
|
|
157
|
+
- Chain `map()`, `filter()`, `reduce()` for declarative data transformation.
|
|
158
|
+
- Use `LazyCollection` for memory-efficient processing of large datasets.
|
|
159
|
+
- Use generators (`yield`) for lazy iteration over large result sets.
|
|
160
|
+
- Prefer `collect()` pipeline over nested loops.
|
|
161
|
+
|
|
162
|
+
## Async Patterns
|
|
163
|
+
- Use Laravel Queues for background job processing.
|
|
164
|
+
- Use `dispatch()` for fire-and-forget. Use `Bus::chain()` for sequential jobs.
|
|
165
|
+
- Use `ShouldQueue` interface on jobs, listeners, and mailables.
|
|
166
|
+
- Set `$tries`, `$timeout`, `$backoff` on job classes.
|
|
167
|
+
- Use `batch()` for parallel job execution with completion callback.
|
|
168
|
+
|
|
169
|
+
## Event-Driven
|
|
170
|
+
- Use events and listeners for decoupled side effects.
|
|
171
|
+
- Use domain events for cross-boundary communication.
|
|
172
|
+
- Use `ShouldQueue` on listeners for async event handling.
|
|
173
|
+
- Use event subscribers for grouping related listeners.
|
|
174
|
+
- Keep event payloads minimal: IDs and timestamps, not full objects.
|
|
175
|
+
|
|
176
|
+
## Anti-Patterns
|
|
177
|
+
- Fat controllers: move logic to services/actions.
|
|
178
|
+
- God models: split into focused models with traits or separate classes.
|
|
179
|
+
- Using `DB::raw()` without parameterization: SQL injection risk.
|
|
180
|
+
- Static method calls for testable dependencies: use DI instead.
|
|
181
|
+
- Returning mixed types: use typed returns or Result objects.
|
|
182
|
+
|
|
183
|
+
# PHP Security
|
|
184
|
+
|
|
185
|
+
## SQL Injection
|
|
186
|
+
- Use PDO prepared statements with bound parameters for all queries.
|
|
187
|
+
- Use Eloquent/Doctrine ORM for type-safe query building.
|
|
188
|
+
- Never concatenate user input into SQL strings. Never use `DB::raw($input)`.
|
|
189
|
+
- Use `whereIn()` with arrays, not string interpolation for IN clauses.
|
|
190
|
+
- Audit raw queries: `DB::select(DB::raw(...))` must use `?` placeholders.
|
|
191
|
+
|
|
192
|
+
## XSS Prevention
|
|
193
|
+
- Blade templates auto-escape with `{{ }}`. Never use `{!! !!}` with user data.
|
|
194
|
+
- Use `htmlspecialchars()` with `ENT_QUOTES` when outputting outside Blade.
|
|
195
|
+
- Set `Content-Security-Policy` headers to restrict inline scripts.
|
|
196
|
+
- Sanitize rich-text input with HTMLPurifier before storage.
|
|
197
|
+
- Use `strip_tags()` only as a secondary measure, not primary defense.
|
|
198
|
+
|
|
199
|
+
## CSRF Protection
|
|
200
|
+
- Use `@csrf` directive in all Blade forms.
|
|
201
|
+
- Use `VerifyCsrfToken` middleware (enabled by default in Laravel).
|
|
202
|
+
- Use `X-CSRF-TOKEN` header for AJAX requests from SPA frontends.
|
|
203
|
+
- Exclude only webhook endpoints from CSRF verification (with careful validation).
|
|
204
|
+
|
|
205
|
+
## Authentication
|
|
206
|
+
- Use `password_hash()` with `PASSWORD_ARGON2ID` or `PASSWORD_BCRYPT`.
|
|
207
|
+
- Use Laravel Sanctum for SPA/mobile API authentication.
|
|
208
|
+
- Use Laravel Passport for full OAuth2 server implementation.
|
|
209
|
+
- Implement rate limiting on login endpoints: `ThrottleRequests` middleware.
|
|
210
|
+
- Use multi-factor authentication for admin accounts.
|
|
211
|
+
|
|
212
|
+
## Authorization
|
|
213
|
+
- Use Laravel Gates and Policies for authorization logic.
|
|
214
|
+
- Use `$this->authorize('update', $post)` in controllers.
|
|
215
|
+
- Check resource ownership in policies, not just role membership.
|
|
216
|
+
- Default deny: use `Gate::before()` for super-admin bypass, nothing else.
|
|
217
|
+
- Use middleware `can:permission` for route-level authorization.
|
|
218
|
+
|
|
219
|
+
## File Upload
|
|
220
|
+
- Validate file MIME type server-side. Do not trust `Content-Type` header.
|
|
221
|
+
- Store uploads outside the web root. Use `storage/` with `Storage::disk()`.
|
|
222
|
+
- Generate random filenames. Never use original user-provided filenames.
|
|
223
|
+
- Set maximum file size limits in validation and PHP `upload_max_filesize`.
|
|
224
|
+
- Scan uploaded files for malware in production environments.
|
|
225
|
+
|
|
226
|
+
## Mass Assignment
|
|
227
|
+
- Use `$fillable` on Eloquent models. Never use `$guarded = []`.
|
|
228
|
+
- Use Form Requests to whitelist fields before model assignment.
|
|
229
|
+
- Use DTOs for data transfer. Never pass `$request->all()` to `create()`.
|
|
230
|
+
- Audit `forceFill()` and `forceCreate()` usage (bypasses guarding).
|
|
231
|
+
|
|
232
|
+
## Secrets and Configuration
|
|
233
|
+
- Use `.env` files for local secrets. Use Vault or SSM for production.
|
|
234
|
+
- Never commit `.env` to version control. Commit `.env.example` as template.
|
|
235
|
+
- Use `config()` helper, never `env()` outside of config files (caching issue).
|
|
236
|
+
- Never log request content containing passwords or tokens.
|
|
237
|
+
- Use `APP_DEBUG=false` in production. Debug mode leaks sensitive data.
|
|
238
|
+
|
|
239
|
+
# PHP Testing
|
|
240
|
+
|
|
241
|
+
## Framework
|
|
242
|
+
- Use PHPUnit 10+ as the primary test framework.
|
|
243
|
+
- Use Pest PHP for expressive, minimal-boilerplate testing (built on PHPUnit).
|
|
244
|
+
- Use Mockery for flexible mocking. Use PHPUnit built-in mocks for simple cases.
|
|
245
|
+
- Use Testcontainers (via Docker) for integration tests with databases.
|
|
246
|
+
|
|
247
|
+
## File Naming
|
|
248
|
+
- Test files: `FooTest.php` in `tests/` mirroring `src/` namespace structure.
|
|
249
|
+
- Unit tests: `tests/Unit/`. Integration tests: `tests/Integration/` or `tests/Feature/`.
|
|
250
|
+
- PHPUnit config: `phpunit.xml.dist` at project root.
|
|
251
|
+
- Use `@group` annotations for test categorization.
|
|
252
|
+
|
|
253
|
+
## Structure (PHPUnit)
|
|
254
|
+
- Use `#[Test]` attribute (PHP 8) or `test` prefix for test methods.
|
|
255
|
+
- Use `setUp()` / `tearDown()` for per-test initialization and cleanup.
|
|
256
|
+
- Use `#[DataProvider('dataMethodName')]` for parameterized tests.
|
|
257
|
+
- Name tests: `testMethodName_Scenario_ExpectedResult` or descriptive snake_case.
|
|
258
|
+
|
|
259
|
+
## Structure (Pest)
|
|
260
|
+
- Use `test('description', function () { ... })` for test cases.
|
|
261
|
+
- Use `it('should do something', ...)` for BDD-style descriptions.
|
|
262
|
+
- Use `beforeEach()` / `afterEach()` for setup and teardown.
|
|
263
|
+
- Use `dataset()` for shared test data across multiple tests.
|
|
264
|
+
- Use `->with([...])` for inline parameterized tests.
|
|
265
|
+
|
|
266
|
+
## Assertions
|
|
267
|
+
- Use `$this->assertSame()` for strict equality (type + value).
|
|
268
|
+
- Use `$this->assertInstanceOf(Foo::class, $result)` for type checks.
|
|
269
|
+
- Use `$this->expectException(FooException::class)` before the throwing call.
|
|
270
|
+
- Use `$this->assertCount()`, `$this->assertContains()` for collections.
|
|
271
|
+
- Pest: use `expect($value)->toBe()`, `->toBeInstanceOf()`, `->toThrow()`.
|
|
272
|
+
|
|
273
|
+
## Mocking (Mockery)
|
|
274
|
+
- Create mocks: `$mock = Mockery::mock(UserRepository::class)`.
|
|
275
|
+
- Stub: `$mock->shouldReceive('find')->with(1)->andReturn($user)`.
|
|
276
|
+
- Verify: `$mock->shouldHaveReceived('save')->once()`.
|
|
277
|
+
- Use `Mockery::close()` in `tearDown()` or `afterEach()`.
|
|
278
|
+
- Use `spy()` to verify interactions without stubbing.
|
|
279
|
+
|
|
280
|
+
## Laravel Testing
|
|
281
|
+
- Use `RefreshDatabase` trait for database test isolation.
|
|
282
|
+
- Use `$this->actingAs($user)` for authenticated request testing.
|
|
283
|
+
- Use `$this->getJson('/api/users')->assertOk()->assertJsonCount(3)`.
|
|
284
|
+
- Use factories: `User::factory()->create()` for test data.
|
|
285
|
+
- Use `Bus::fake()`, `Event::fake()`, `Mail::fake()` for side-effect assertion.
|
|
286
|
+
|
|
287
|
+
## Best Practices
|
|
288
|
+
- Test behavior, not implementation. Do not test private methods.
|
|
289
|
+
- Use in-memory SQLite for fast database tests when schema is compatible.
|
|
290
|
+
- Run `php artisan test --parallel` for faster Laravel test execution.
|
|
291
|
+
- Use `--coverage-html` for visual coverage reports.
|
|
292
|
+
- Keep tests fast: mock external HTTP calls with `Http::fake()`.
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python-rules
|
|
3
|
+
description: "Python coding rules from ai-toolkit: coding-style, frameworks, patterns, security, testing. Triggers: .py, .pyi, pyproject.toml, requirements.txt, Pipfile, FastAPI, Django, Flask, pytest, SQLAlchemy, ruff, mypy. Load when writing, reviewing, or editing Python code."
|
|
4
|
+
effort: medium
|
|
5
|
+
user-invocable: false
|
|
6
|
+
allowed-tools: Read
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Python Rules
|
|
10
|
+
|
|
11
|
+
These rules come from `app/rules/python/` in ai-toolkit. They cover
|
|
12
|
+
the project's standards for coding style, frameworks, patterns,
|
|
13
|
+
security, and testing in Python. Apply them when writing or
|
|
14
|
+
reviewing Python code.
|
|
15
|
+
|
|
16
|
+
# Python Coding Style
|
|
17
|
+
|
|
18
|
+
## Type Hints
|
|
19
|
+
- Type all public function signatures (parameters + return).
|
|
20
|
+
- Use `str | None` (PEP 604) over `Optional[str]` on Python 3.10+.
|
|
21
|
+
- Use `from __future__ import annotations` for forward references.
|
|
22
|
+
- Use `TypeAlias` or `type` (3.12+) for complex type aliases.
|
|
23
|
+
- Use `Protocol` for structural subtyping instead of ABCs where possible.
|
|
24
|
+
|
|
25
|
+
## Naming
|
|
26
|
+
- snake_case: variables, functions, methods, modules.
|
|
27
|
+
- PascalCase: classes, type aliases, Protocols.
|
|
28
|
+
- UPPER_SNAKE: module-level constants.
|
|
29
|
+
- Prefix private: `_internal_helper`. No double underscore unless name mangling needed.
|
|
30
|
+
- Prefix unused: `_` for intentionally unused variables.
|
|
31
|
+
|
|
32
|
+
## Functions
|
|
33
|
+
- Prefer keyword arguments for functions with >2 params.
|
|
34
|
+
- Use `*` to force keyword-only: `def fetch(*, limit: int, offset: int)`.
|
|
35
|
+
- Return early to reduce nesting. Avoid deep if/else chains.
|
|
36
|
+
- Use `@staticmethod` only for pure utility. Prefer module-level functions.
|
|
37
|
+
|
|
38
|
+
## Imports
|
|
39
|
+
- Group: stdlib, third-party, local. Separated by blank lines.
|
|
40
|
+
- Use absolute imports. Relative imports only within packages.
|
|
41
|
+
- Never `from module import *`. Be explicit.
|
|
42
|
+
- Use `if TYPE_CHECKING:` for import-only-for-types to avoid circular imports.
|
|
43
|
+
|
|
44
|
+
## Data Structures
|
|
45
|
+
- Use `dataclasses` for plain data containers.
|
|
46
|
+
- Use Pydantic `BaseModel` for validated data / API schemas.
|
|
47
|
+
- Use `NamedTuple` for lightweight immutable records.
|
|
48
|
+
- Use `Enum` for fixed sets of values. Prefer `StrEnum` on 3.11+.
|
|
49
|
+
- Prefer `dict` / `list` literals over `dict()` / `list()` constructors.
|
|
50
|
+
|
|
51
|
+
## Modern Python
|
|
52
|
+
- Use f-strings for formatting. Never `.format()` or `%` for new code.
|
|
53
|
+
- Use `pathlib.Path` over `os.path` for file operations.
|
|
54
|
+
- Use `contextlib.suppress(KeyError)` over bare try/except for simple cases.
|
|
55
|
+
- Use walrus operator `:=` when it genuinely improves readability.
|
|
56
|
+
- Use `match/case` (3.10+) for complex conditionals on structured data.
|
|
57
|
+
|
|
58
|
+
## Tooling
|
|
59
|
+
- Formatter: `ruff format` or `black`. No manual formatting.
|
|
60
|
+
- Linter: `ruff check`. Fix all errors before committing.
|
|
61
|
+
- Type checker: `mypy --strict` or `pyright` in CI.
|
|
62
|
+
|
|
63
|
+
# Python Frameworks
|
|
64
|
+
|
|
65
|
+
## FastAPI
|
|
66
|
+
- Use Pydantic v2 models for request/response schemas.
|
|
67
|
+
- Use dependency injection (`Depends()`) for shared logic (auth, DB sessions).
|
|
68
|
+
- Use `APIRouter` to organize routes by domain.
|
|
69
|
+
- Return Pydantic models directly -- FastAPI handles serialization.
|
|
70
|
+
- Use `BackgroundTasks` for non-critical async work (emails, logging).
|
|
71
|
+
- Use `lifespan` context manager for startup/shutdown (not `on_event`).
|
|
72
|
+
|
|
73
|
+
## Django
|
|
74
|
+
- Use class-based views for CRUD, function-based for custom logic.
|
|
75
|
+
- Use `select_related` and `prefetch_related` to prevent N+1 queries.
|
|
76
|
+
- Use Django REST Framework serializers for API validation.
|
|
77
|
+
- Use Django ORM migrations. Never modify database schema manually.
|
|
78
|
+
- Use `transaction.atomic()` for multi-model operations.
|
|
79
|
+
- Use signals sparingly: prefer explicit service calls.
|
|
80
|
+
|
|
81
|
+
## SQLAlchemy 2.0
|
|
82
|
+
- Use the 2.0-style with `select()` statements, not legacy `query()`.
|
|
83
|
+
- Use `Mapped[type]` annotations for typed column definitions.
|
|
84
|
+
- Use `sessionmaker` with `expire_on_commit=False` for API responses.
|
|
85
|
+
- Use `async_sessionmaker` with `asyncpg` for async applications.
|
|
86
|
+
- Always use `session.begin()` context manager for transaction scope.
|
|
87
|
+
|
|
88
|
+
## Pydantic v2
|
|
89
|
+
- Use `model_validator(mode="before")` for cross-field validation.
|
|
90
|
+
- Use `field_validator` for single-field validation.
|
|
91
|
+
- Use `model_config = ConfigDict(strict=True)` for strict type coercion.
|
|
92
|
+
- Use `Annotated[str, Field(min_length=1)]` for reusable constrained types.
|
|
93
|
+
- Use `model_dump(exclude_unset=True)` for PATCH operations.
|
|
94
|
+
|
|
95
|
+
## CLI (click / typer)
|
|
96
|
+
- Use Typer for new CLI tools (type-hint-driven, less boilerplate).
|
|
97
|
+
- Use `click.group()` for multi-command CLIs.
|
|
98
|
+
- Use `rich` for formatted terminal output (tables, progress bars).
|
|
99
|
+
|
|
100
|
+
## Task Queues
|
|
101
|
+
- Use Celery with Redis/RabbitMQ for background job processing.
|
|
102
|
+
- Use `arq` for lightweight async job queues.
|
|
103
|
+
- Always set task timeouts. Never let tasks run indefinitely.
|
|
104
|
+
- Use idempotent tasks: safe to retry on failure.
|
|
105
|
+
|
|
106
|
+
## Package Management
|
|
107
|
+
- Use `uv` for fast dependency resolution and virtual environments.
|
|
108
|
+
- Use `pyproject.toml` for all project configuration (no setup.py/setup.cfg).
|
|
109
|
+
- Pin dependencies with lockfile (`uv.lock`, `poetry.lock`).
|
|
110
|
+
|
|
111
|
+
# Python Patterns
|
|
112
|
+
|
|
113
|
+
## Error Handling
|
|
114
|
+
- Catch specific exceptions, never bare `except:` or `except Exception`.
|
|
115
|
+
- Use custom exception hierarchies: `class AppError(Exception)` as base.
|
|
116
|
+
- Add context when re-raising: `raise AppError("context") from original`.
|
|
117
|
+
- Use `contextlib.suppress()` for expected, ignorable exceptions.
|
|
118
|
+
- Log exceptions with `logger.exception("msg")` to include traceback.
|
|
119
|
+
|
|
120
|
+
## Context Managers
|
|
121
|
+
- Use `with` for any resource that needs cleanup (files, connections, locks).
|
|
122
|
+
- Create custom context managers with `@contextmanager` decorator.
|
|
123
|
+
- Use `contextlib.AsyncExitStack` for dynamic async resource management.
|
|
124
|
+
- Use `atexit.register()` for process-level cleanup only.
|
|
125
|
+
|
|
126
|
+
## Async
|
|
127
|
+
- Use `asyncio` for I/O-bound concurrency. Use `multiprocessing` for CPU-bound.
|
|
128
|
+
- Use `asyncio.gather()` for concurrent independent operations.
|
|
129
|
+
- Use `asyncio.TaskGroup` (3.11+) for structured concurrency.
|
|
130
|
+
- Never mix `asyncio.run()` inside already-running event loops.
|
|
131
|
+
- Use `async for` and `async with` for streaming and resource patterns.
|
|
132
|
+
|
|
133
|
+
## Dataclass Patterns
|
|
134
|
+
- Use `frozen=True` for immutable value objects.
|
|
135
|
+
- Use `field(default_factory=list)` for mutable defaults, never `field(default=[])`.
|
|
136
|
+
- Use `__post_init__` for validation, not complex logic.
|
|
137
|
+
- Use `slots=True` (3.10+) for memory efficiency in high-volume objects.
|
|
138
|
+
|
|
139
|
+
## Functional Patterns
|
|
140
|
+
- Use `functools.lru_cache` for pure function memoization.
|
|
141
|
+
- Use `itertools` for efficient iteration (chain, islice, groupby).
|
|
142
|
+
- Use generators (`yield`) for lazy sequences and large data processing.
|
|
143
|
+
- Prefer comprehensions over `map/filter` with lambdas.
|
|
144
|
+
- Use `functools.partial` to create specialized versions of functions.
|
|
145
|
+
|
|
146
|
+
## Dependency Injection
|
|
147
|
+
- Use constructor injection: pass dependencies as `__init__` params.
|
|
148
|
+
- Use `Protocol` classes to define dependency interfaces.
|
|
149
|
+
- Use factory functions to wire dependencies at application startup.
|
|
150
|
+
- Avoid global state and singletons. Use module-level instances if needed.
|
|
151
|
+
|
|
152
|
+
## Anti-Patterns
|
|
153
|
+
- Mutable default arguments: use `None` and create inside function.
|
|
154
|
+
- Catching `Exception` broadly: masks bugs and interrupts.
|
|
155
|
+
- Using `type()` for type checking: use `isinstance()`.
|
|
156
|
+
- Nested try/except: flatten with early returns or separate functions.
|
|
157
|
+
- Using `global` keyword: pass state through parameters or classes.
|
|
158
|
+
|
|
159
|
+
# Python Security
|
|
160
|
+
|
|
161
|
+
## Input Validation
|
|
162
|
+
- Validate all input with Pydantic models at API boundaries.
|
|
163
|
+
- Use `constr`, `conint`, `conlist` for constrained types.
|
|
164
|
+
- Never use `eval()`, `exec()`, or `compile()` with user input.
|
|
165
|
+
- Never use `pickle.loads()` on untrusted data (arbitrary code execution).
|
|
166
|
+
|
|
167
|
+
## SQL Injection
|
|
168
|
+
- Use ORM query builders (SQLAlchemy, Django ORM) for all queries.
|
|
169
|
+
- For raw SQL, always use parameterized queries: `cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))`.
|
|
170
|
+
- Never use f-strings or `.format()` to build SQL queries.
|
|
171
|
+
- Use `text()` with `:param` syntax in SQLAlchemy raw queries.
|
|
172
|
+
|
|
173
|
+
## SSTI (Server-Side Template Injection)
|
|
174
|
+
- Use Jinja2 with autoescaping enabled: `Environment(autoescape=True)`.
|
|
175
|
+
- Never render user input as a template string.
|
|
176
|
+
- Use `markupsafe.Markup` only for trusted HTML content.
|
|
177
|
+
|
|
178
|
+
## Command Injection
|
|
179
|
+
- Never use `os.system()` or `subprocess.run(shell=True)` with user input.
|
|
180
|
+
- Use `subprocess.run()` with list arguments: `subprocess.run(["ls", "-la", path])`.
|
|
181
|
+
- Use `shlex.quote()` if shell=True is absolutely necessary.
|
|
182
|
+
|
|
183
|
+
## Path Traversal
|
|
184
|
+
- Use `pathlib.Path.resolve()` and verify the result is within allowed directory.
|
|
185
|
+
- Never concatenate user input into file paths without validation.
|
|
186
|
+
- Use `os.path.commonpath()` to verify path containment.
|
|
187
|
+
|
|
188
|
+
## Secrets
|
|
189
|
+
- Use `secrets` module for tokens: `secrets.token_urlsafe(32)`.
|
|
190
|
+
- Use `hashlib.scrypt` or `bcrypt` for password hashing.
|
|
191
|
+
- Use `hmac.compare_digest()` for constant-time secret comparison.
|
|
192
|
+
- Load secrets from environment: `os.environ["SECRET_KEY"]`, never hardcode.
|
|
193
|
+
|
|
194
|
+
## Dependencies
|
|
195
|
+
- Run `pip-audit` or `safety check` in CI.
|
|
196
|
+
- Use `uv` or `pip-compile` for reproducible dependency resolution.
|
|
197
|
+
- Avoid installing packages with native extensions from untrusted sources.
|
|
198
|
+
- Pin all dependency versions. Review dependency updates carefully.
|
|
199
|
+
|
|
200
|
+
## Deserialization
|
|
201
|
+
- Never deserialize untrusted data with `pickle`, `yaml.load()`, or `marshal`.
|
|
202
|
+
- Use `yaml.safe_load()` instead of `yaml.load()`.
|
|
203
|
+
- Use `json.loads()` for untrusted data (safe by default).
|
|
204
|
+
- Validate deserialized data with Pydantic before use.
|
|
205
|
+
|
|
206
|
+
## Django-Specific
|
|
207
|
+
- Set `DEBUG = False` in production. Never expose debug pages.
|
|
208
|
+
- Use `django.utils.html.escape()` for manual HTML escaping.
|
|
209
|
+
- Use `CSRF_COOKIE_HTTPONLY = True` and `SESSION_COOKIE_SECURE = True`.
|
|
210
|
+
- Keep `SECRET_KEY` unique per environment and out of version control.
|
|
211
|
+
|
|
212
|
+
# Python Testing
|
|
213
|
+
|
|
214
|
+
## Framework
|
|
215
|
+
- Use pytest as the default test framework. No unittest for new code.
|
|
216
|
+
- Use pytest-asyncio for async test functions.
|
|
217
|
+
- Use pytest-cov for coverage measurement.
|
|
218
|
+
- Use hypothesis for property-based testing on parsing/validation logic.
|
|
219
|
+
|
|
220
|
+
## File Naming
|
|
221
|
+
- Test files: `test_*.py` in `tests/` directory.
|
|
222
|
+
- Conftest: `conftest.py` at each test directory level for shared fixtures.
|
|
223
|
+
- Mirror source: `src/auth/service.py` -> `tests/auth/test_service.py`.
|
|
224
|
+
|
|
225
|
+
## Fixtures
|
|
226
|
+
- Use `@pytest.fixture` for setup. Prefer fixtures over setup/teardown methods.
|
|
227
|
+
- Scope fixtures appropriately: `function` (default), `module`, `session`.
|
|
228
|
+
- Use `yield` fixtures for setup + teardown: `yield resource; cleanup()`.
|
|
229
|
+
- Use `tmp_path` fixture for temporary files, not manual `tempfile`.
|
|
230
|
+
- Use `monkeypatch` for patching env vars, attributes, and dict items.
|
|
231
|
+
|
|
232
|
+
## Parametrize
|
|
233
|
+
- Use `@pytest.mark.parametrize` for testing multiple inputs/outputs.
|
|
234
|
+
- Use `pytest.param(..., id="descriptive_name")` for readable test IDs.
|
|
235
|
+
- Combine parametrize decorators for cross-product testing.
|
|
236
|
+
|
|
237
|
+
## Mocking
|
|
238
|
+
- Use `unittest.mock.patch` or `monkeypatch` for dependency replacement.
|
|
239
|
+
- Mock at the import location: `patch("myapp.service.http_client")`.
|
|
240
|
+
- Use `MagicMock(spec=ClassName)` to get attribute checking.
|
|
241
|
+
- Use `AsyncMock` for async functions.
|
|
242
|
+
- Prefer dependency injection over patching when possible.
|
|
243
|
+
|
|
244
|
+
## Markers
|
|
245
|
+
- Use `@pytest.mark.slow` for tests >1s. Exclude from default runs.
|
|
246
|
+
- Use `@pytest.mark.integration` for tests requiring external services.
|
|
247
|
+
- Register all custom markers in `pyproject.toml` to avoid warnings.
|
|
248
|
+
|
|
249
|
+
## Async Testing
|
|
250
|
+
- Use `@pytest.mark.anyio` or `@pytest.mark.asyncio` for async tests.
|
|
251
|
+
- Use `httpx.AsyncClient` for testing FastAPI/Starlette apps.
|
|
252
|
+
- Use `aiosqlite` or test containers for async database tests.
|
|
253
|
+
|
|
254
|
+
## Configuration
|
|
255
|
+
- Configure pytest in `pyproject.toml` under `[tool.pytest.ini_options]`.
|
|
256
|
+
- Set `addopts = "--strict-markers -ra"` for strict mode.
|
|
257
|
+
- Set `testpaths = ["tests"]` to avoid scanning the entire repo.
|