ai-dev-starter 0.0.0-stage → 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 (42) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +383 -2
  3. package/adapters/antigravity/AGENTS.md +59 -0
  4. package/adapters/antigravity/skills/architecture-review/SKILL.md +33 -0
  5. package/adapters/antigravity/skills/bug-fix/SKILL.md +36 -0
  6. package/adapters/antigravity/skills/code-review/SKILL.md +34 -0
  7. package/adapters/antigravity/skills/feature-development/SKILL.md +37 -0
  8. package/adapters/antigravity/skills/performance-audit/SKILL.md +34 -0
  9. package/adapters/antigravity/skills/refactor/SKILL.md +35 -0
  10. package/adapters/antigravity/skills/security-audit/SKILL.md +35 -0
  11. package/adapters/antigravity/skills/test-generation/SKILL.md +34 -0
  12. package/adapters/cursor/bug-fix.mdc +38 -0
  13. package/adapters/cursor/code-review.mdc +56 -0
  14. package/adapters/cursor/core-rules.mdc +49 -0
  15. package/adapters/cursor/feature-development.mdc +31 -0
  16. package/adapters/cursor/performance-arch-tests.mdc +57 -0
  17. package/adapters/cursor/refactor-and-security.mdc +50 -0
  18. package/bin/ai-dev-starter.js +2 -0
  19. package/core/agent-principles.md +96 -0
  20. package/core/git-analysis.md +150 -0
  21. package/core/review-methodology.md +154 -0
  22. package/core/safety-policy.md +70 -0
  23. package/core/severity-levels.md +104 -0
  24. package/package.json +40 -4
  25. package/scripts/ai-dev-starter.js +709 -0
  26. package/scripts/detector.js +405 -0
  27. package/standards/backend.md +84 -0
  28. package/standards/database.md +100 -0
  29. package/standards/frontend.md +81 -0
  30. package/standards/security.md +84 -0
  31. package/standards/testing.md +132 -0
  32. package/templates/architecture-decision.md +92 -0
  33. package/templates/project-context.md +144 -0
  34. package/templates/review-report.md +102 -0
  35. package/workflows/architecture-review.md +186 -0
  36. package/workflows/bug-fix.md +149 -0
  37. package/workflows/code-review.md +189 -0
  38. package/workflows/feature-development.md +154 -0
  39. package/workflows/performance-audit.md +169 -0
  40. package/workflows/refactor.md +128 -0
  41. package/workflows/security-audit.md +164 -0
  42. package/workflows/test-generation.md +168 -0
@@ -0,0 +1,405 @@
1
+ /**
2
+ * detector.js - Project Context Detector for ai-dev-starter
3
+ *
4
+ * Inspects a repository to detect tech stack, frameworks, test setups,
5
+ * database usage, directories, and development commands.
6
+ * Zero external dependencies.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ function safeReadJson(filePath) {
13
+ try {
14
+ if (fs.existsSync(filePath)) {
15
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
16
+ }
17
+ } catch {
18
+ // Malformed JSON or read error
19
+ }
20
+ return null;
21
+ }
22
+
23
+ function safeReadFile(filePath) {
24
+ try {
25
+ if (fs.existsSync(filePath)) {
26
+ return fs.readFileSync(filePath, 'utf8');
27
+ }
28
+ } catch {}
29
+ return '';
30
+ }
31
+
32
+ function dirExists(dirPath) {
33
+ try {
34
+ return fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory();
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Detect project metadata and architecture
42
+ */
43
+ function detectProjectContext(projectRoot) {
44
+ const context = {
45
+ projectName: path.basename(projectRoot) || 'Unknown Project',
46
+ version: '1.0.0',
47
+ description: '',
48
+ repository: '',
49
+ backendLanguage: '',
50
+ backendFramework: '',
51
+ backendVersion: '',
52
+ frontendLanguage: '',
53
+ frontendFramework: 'none',
54
+ styling: '',
55
+ databases: [],
56
+ architecturePattern: 'Modular / Layered',
57
+ layers: [],
58
+ keyDirectories: [],
59
+ commands: {
60
+ install: '',
61
+ dev_server: '',
62
+ build: '',
63
+ test: '',
64
+ lint: '',
65
+ migrate: '',
66
+ },
67
+ testFramework: '',
68
+ testDirectory: 'tests',
69
+ reportLanguage: 'fa',
70
+ severityThreshold: 'P2',
71
+ constraints: [],
72
+ };
73
+
74
+ // 1. Detect Composer (PHP / Laravel / Symfony)
75
+ const composerJsonPath = path.join(projectRoot, 'composer.json');
76
+ const composer = safeReadJson(composerJsonPath);
77
+ if (composer) {
78
+ if (composer.name) context.projectName = composer.name.split('/').pop() || context.projectName;
79
+ if (composer.description) context.description = composer.description;
80
+ context.backendLanguage = 'php';
81
+
82
+ const req = { ...(composer.require || {}), ...(composer['require-dev'] || {}) };
83
+
84
+ if (req['laravel/framework']) {
85
+ context.backendFramework = 'laravel';
86
+ context.backendVersion = req['laravel/framework'];
87
+ context.architecturePattern = 'MVC (Laravel)';
88
+ context.commands.dev_server = 'php artisan serve';
89
+ context.commands.migrate = 'php artisan migrate';
90
+ context.databases.push({ type: 'mysql / postgresql', orm: 'eloquent' });
91
+ } else if (req['symfony/framework-bundle'] || req['symfony/symfony']) {
92
+ context.backendFramework = 'symfony';
93
+ context.architecturePattern = 'MVC (Symfony)';
94
+ context.commands.dev_server = 'symfony server:start';
95
+ context.databases.push({ type: 'relational', orm: 'doctrine' });
96
+ } else {
97
+ context.backendFramework = 'custom-php';
98
+ }
99
+
100
+ // Testing in PHP
101
+ if (req['pestphp/pest']) {
102
+ context.testFramework = 'pest';
103
+ context.commands.test = 'vendor/bin/pest';
104
+ } else if (req['phpunit/phpunit']) {
105
+ context.testFramework = 'phpunit';
106
+ context.commands.test = context.backendFramework === 'laravel' ? 'php artisan test' : 'vendor/bin/phpunit';
107
+ }
108
+
109
+ context.commands.install = context.commands.install ? context.commands.install + ' && composer install' : 'composer install';
110
+ }
111
+
112
+ // 2. Detect Package.json (Node.js / React / Next.js / Vue / Angular / TypeScript)
113
+ const pkgJsonPath = path.join(projectRoot, 'package.json');
114
+ const pkg = safeReadJson(pkgJsonPath);
115
+ if (pkg) {
116
+ if (!composer && pkg.name) context.projectName = pkg.name;
117
+ if (!context.description && pkg.description) context.description = pkg.description;
118
+ if (pkg.version) context.version = pkg.version;
119
+
120
+ const allDeps = {
121
+ ...(pkg.dependencies || {}),
122
+ ...(pkg.devDependencies || {}),
123
+ };
124
+
125
+ const hasTs = Boolean(allDeps['typescript'] || fs.existsSync(path.join(projectRoot, 'tsconfig.json')));
126
+
127
+ // Framework detection
128
+ if (allDeps['next']) {
129
+ context.frontendFramework = 'next';
130
+ context.frontendLanguage = hasTs ? 'typescript' : 'javascript';
131
+ if (!context.backendFramework) {
132
+ context.backendFramework = 'nextjs-api';
133
+ context.backendLanguage = hasTs ? 'typescript' : 'javascript';
134
+ }
135
+ context.architecturePattern = dirExists(path.join(projectRoot, 'src', 'app')) || dirExists(path.join(projectRoot, 'app'))
136
+ ? 'Next.js App Router'
137
+ : 'Next.js Pages Router';
138
+ } else if (allDeps['react']) {
139
+ context.frontendFramework = 'react';
140
+ context.frontendLanguage = hasTs ? 'typescript' : 'javascript';
141
+ } else if (allDeps['vue']) {
142
+ context.frontendFramework = 'vue';
143
+ context.frontendLanguage = hasTs ? 'typescript' : 'javascript';
144
+ } else if (allDeps['@angular/core']) {
145
+ context.frontendFramework = 'angular';
146
+ context.frontendLanguage = 'typescript';
147
+ }
148
+
149
+ // Backend Node detection if not PHP
150
+ if (!context.backendFramework) {
151
+ if (allDeps['@nestjs/core']) {
152
+ context.backendFramework = 'nestjs';
153
+ context.backendLanguage = 'typescript';
154
+ context.architecturePattern = 'Modular / Dependency Injection (NestJS)';
155
+ } else if (allDeps['express']) {
156
+ context.backendFramework = 'express';
157
+ context.backendLanguage = hasTs ? 'typescript' : 'javascript';
158
+ } else if (allDeps['fastify']) {
159
+ context.backendFramework = 'fastify';
160
+ context.backendLanguage = hasTs ? 'typescript' : 'javascript';
161
+ } else if (allDeps['koa']) {
162
+ context.backendFramework = 'koa';
163
+ context.backendLanguage = hasTs ? 'typescript' : 'javascript';
164
+ }
165
+ }
166
+
167
+ // Styling
168
+ if (allDeps['tailwindcss'] || fs.existsSync(path.join(projectRoot, 'tailwind.config.js')) || fs.existsSync(path.join(projectRoot, 'tailwind.config.ts'))) {
169
+ context.styling = 'tailwind';
170
+ } else if (allDeps['styled-components']) {
171
+ context.styling = 'styled-components';
172
+ } else if (allDeps['@mui/material'] || allDeps['@material-ui/core']) {
173
+ context.styling = 'material-ui';
174
+ }
175
+
176
+ // Database / ORM in Node
177
+ if (allDeps['prisma'] || allDeps['@prisma/client']) {
178
+ context.databases.push({ type: 'relational / mongodb', orm: 'prisma' });
179
+ if (!context.commands.migrate) context.commands.migrate = 'npx prisma migrate dev';
180
+ }
181
+ if (allDeps['typeorm']) {
182
+ context.databases.push({ type: 'relational', orm: 'typeorm' });
183
+ }
184
+ if (allDeps['drizzle-orm']) {
185
+ context.databases.push({ type: 'relational', orm: 'drizzle' });
186
+ }
187
+ if (allDeps['mongoose']) {
188
+ context.databases.push({ type: 'mongodb', orm: 'mongoose' });
189
+ }
190
+
191
+ // Testing framework
192
+ if (allDeps['vitest']) {
193
+ context.testFramework = context.testFramework || 'vitest';
194
+ context.commands.test = context.commands.test || 'npx vitest run';
195
+ } else if (allDeps['jest']) {
196
+ context.testFramework = context.testFramework || 'jest';
197
+ context.commands.test = context.commands.test || 'npm test';
198
+ }
199
+
200
+ // Scripts extraction
201
+ const scripts = pkg.scripts || {};
202
+ if (scripts.dev) context.commands.dev_server = context.commands.dev_server || 'npm run dev';
203
+ else if (scripts.start) context.commands.dev_server = context.commands.dev_server || 'npm start';
204
+
205
+ if (scripts.build) context.commands.build = 'npm run build';
206
+ if (scripts.test) context.commands.test = context.commands.test || 'npm test';
207
+ if (scripts.lint) context.commands.lint = 'npm run lint';
208
+
209
+ context.commands.install = context.commands.install ? context.commands.install + ' && npm install' : 'npm install';
210
+ }
211
+
212
+ // 3. Detect Python (Django / FastAPI / Flask)
213
+ const pyproject = safeReadFile(path.join(projectRoot, 'pyproject.toml'));
214
+ const reqTxt = safeReadFile(path.join(projectRoot, 'requirements.txt'));
215
+ const pyContent = pyproject + '\n' + reqTxt;
216
+ if (pyContent.trim()) {
217
+ if (!context.backendLanguage) {
218
+ context.backendLanguage = 'python';
219
+ if (/fastapi/i.test(pyContent)) {
220
+ context.backendFramework = 'fastapi';
221
+ context.commands.dev_server = 'uvicorn main:app --reload';
222
+ } else if (/django/i.test(pyContent)) {
223
+ context.backendFramework = 'django';
224
+ context.architecturePattern = 'MVT (Django)';
225
+ context.commands.dev_server = 'python manage.py runserver';
226
+ context.commands.migrate = 'python manage.py migrate';
227
+ context.databases.push({ type: 'relational', orm: 'django-orm' });
228
+ } else if (/flask/i.test(pyContent)) {
229
+ context.backendFramework = 'flask';
230
+ context.commands.dev_server = 'flask run';
231
+ }
232
+
233
+ if (/pytest/i.test(pyContent) || fs.existsSync(path.join(projectRoot, 'pytest.ini'))) {
234
+ context.testFramework = 'pytest';
235
+ context.commands.test = 'pytest';
236
+ }
237
+ context.commands.install = context.commands.install || 'pip install -r requirements.txt';
238
+ }
239
+ }
240
+
241
+ // 4. Inspect Directory Structure for Layers & Key Dirs
242
+ const candidateDirs = [
243
+ { path: 'app/Http/Controllers', layer: 'Controllers (HTTP Layer)' },
244
+ { path: 'app/Services', layer: 'Services (Business Logic)' },
245
+ { path: 'app/Models', layer: 'Models (Data Access)' },
246
+ { path: 'database/migrations', layer: 'Database Migrations' },
247
+ { path: 'routes', layer: 'Routing' },
248
+ { path: 'src/app', layer: 'Next.js App Router Pages & APIs' },
249
+ { path: 'src/components', layer: 'UI Components' },
250
+ { path: 'src/services', layer: 'Frontend / API Services' },
251
+ { path: 'src/hooks', layer: 'Custom React Hooks' },
252
+ { path: 'src/controllers', layer: 'Controllers' },
253
+ { path: 'src/models', layer: 'Data Models' },
254
+ { path: 'tests', layer: 'Automated Tests' },
255
+ { path: '__tests__', layer: 'Automated Tests' },
256
+ ];
257
+
258
+ for (const c of candidateDirs) {
259
+ if (dirExists(path.join(projectRoot, c.path))) {
260
+ context.layers.push({ name: c.layer, path: c.path });
261
+ context.keyDirectories.push({ path: c.path, purpose: c.layer });
262
+ }
263
+ }
264
+
265
+ // Fallback defaults if empty
266
+ if (!context.commands.test) {
267
+ context.commands.test = 'echo "No test runner configured"';
268
+ }
269
+ if (!context.commands.install) {
270
+ context.commands.install = 'echo "No package manager detected"';
271
+ }
272
+
273
+ return context;
274
+ }
275
+
276
+ /**
277
+ * Generate formatted Markdown for .agents/project-context.md
278
+ */
279
+ function renderProjectContextMarkdown(ctx) {
280
+ const dbLines = ctx.databases.length > 0
281
+ ? ctx.databases.map(d => ` - type: "${d.type}"\n orm: "${d.orm}"`).join('\n')
282
+ : ` - type: "unknown"\n orm: "unknown"`;
283
+
284
+ const layerLines = ctx.layers.length > 0
285
+ ? ctx.layers.map(l => ` - name: "${l.name}"\n path: "${l.path}"`).join('\n')
286
+ : ` - name: "Core"\n path: "src/"`;
287
+
288
+ const keyDirLines = ctx.keyDirectories.length > 0
289
+ ? ctx.keyDirectories.map(k => ` - path: "${k.path}"\n purpose: "${k.purpose}"`).join('\n')
290
+ : ` - path: "src/"\n purpose: "Source files"`;
291
+
292
+ return `# Project Context
293
+
294
+ > **Instructions for the AI agent:** Read this file first when starting any task in this project.
295
+ > Do NOT overwrite this file without user permission.
296
+ > Generated automatically by \`ai-dev-starter init\`.
297
+
298
+ ---
299
+
300
+ ## Project Identity
301
+
302
+ \`\`\`yaml
303
+ project_name: "${ctx.projectName}"
304
+ version: "${ctx.version}"
305
+ description: "${ctx.description || 'Application codebase'}"
306
+ repository: "${ctx.repository || 'local'}"
307
+ \`\`\`
308
+
309
+ ---
310
+
311
+ ## Tech Stack
312
+
313
+ \`\`\`yaml
314
+ # Backend
315
+ backend_language: "${ctx.backendLanguage || 'not-specified'}"
316
+ backend_framework: "${ctx.backendFramework || 'not-specified'}"
317
+ backend_version: "${ctx.backendVersion || ''}"
318
+
319
+ # Frontend
320
+ frontend_framework: "${ctx.frontendFramework}"
321
+ frontend_language: "${ctx.frontendLanguage || 'not-specified'}"
322
+ styling: "${ctx.styling || 'not-specified'}"
323
+
324
+ # Database
325
+ databases:
326
+ ${dbLines}
327
+
328
+ # Infrastructure
329
+ hosting: "unknown"
330
+ containerized: ${fs.existsSync(path.join(ctx.projectRoot || '', 'Dockerfile')) || fs.existsSync(path.join(ctx.projectRoot || '', 'docker-compose.yml'))}
331
+ ci_cd: "${dirExists(path.join(ctx.projectRoot || '', '.github', 'workflows')) ? 'GitHub Actions' : 'none'}"
332
+ \`\`\`
333
+
334
+ ---
335
+
336
+ ## Architecture
337
+
338
+ \`\`\`yaml
339
+ pattern: "${ctx.architecturePattern}"
340
+ layers:
341
+ ${layerLines}
342
+
343
+ key_directories:
344
+ ${keyDirLines}
345
+ \`\`\`
346
+
347
+ ---
348
+
349
+ ## Development Commands
350
+
351
+ \`\`\`yaml
352
+ install: "${ctx.commands.install}"
353
+ dev_server: "${ctx.commands.dev_server}"
354
+ build: "${ctx.commands.build}"
355
+ test: "${ctx.commands.test}"
356
+ lint: "${ctx.commands.lint}"
357
+ migrate: "${ctx.commands.migrate}"
358
+ \`\`\`
359
+
360
+ ---
361
+
362
+ ## Testing Setup
363
+
364
+ \`\`\`yaml
365
+ backend_test_framework: "${ctx.testFramework || 'unknown'}"
366
+ test_directory: "${ctx.testDirectory}"
367
+ integration_tests: false
368
+ e2e_tests: false
369
+ coverage_target: 80
370
+ \`\`\`
371
+
372
+ ---
373
+
374
+ ## Review and Report Settings
375
+
376
+ \`\`\`yaml
377
+ report_language: "${ctx.reportLanguage}" # fa = Persian (Farsi), en = English
378
+ severity_threshold: "${ctx.severityThreshold}" # Minimum severity to include in reports (P0, P1, P2, P3)
379
+ \`\`\`
380
+
381
+ ---
382
+
383
+ ## Important Constraints
384
+
385
+ \`\`\`yaml
386
+ # Safety constraints enforced across all AI workflows
387
+ never_do:
388
+ - "Never commit or push directly to git"
389
+ - "Never run destructive migrations in production"
390
+ - "Never expose environment credentials or secrets"
391
+ - "Never make edits outside the user-requested scope"
392
+ \`\`\`
393
+
394
+ ---
395
+
396
+ ## Project Notes
397
+
398
+ <!-- Add any additional context the AI should know about this project. -->
399
+ `;
400
+ }
401
+
402
+ module.exports = {
403
+ detectProjectContext,
404
+ renderProjectContextMarkdown,
405
+ };
@@ -0,0 +1,84 @@
1
+ # Backend Code Standards
2
+
3
+ > **Scope:** Universal backend principles. Technology-specific extensions are in project-context.md.
4
+ > **Load:** On demand when performing backend reviews or development.
5
+
6
+ ---
7
+
8
+ ## Universal Principles
9
+
10
+ ### Error Handling
11
+ - Never swallow exceptions silently (`catch {}` without logging or re-throwing).
12
+ - Return consistent error formats from APIs (standardized error envelope).
13
+ - Distinguish between client errors (4xx) and server errors (5xx).
14
+ - Do not expose internal stack traces or database errors to API consumers.
15
+
16
+ ### Input Validation
17
+ - Validate all input at the entry point (controller, job handler, CLI command).
18
+ - Validate type, format, range, and length.
19
+ - Reject unexpected fields (allowlist approach, not denylist).
20
+ - Return specific, actionable validation error messages to the client.
21
+
22
+ ### Database Interactions
23
+ - Use parameterized queries or ORM query builders. Never concatenate user input into SQL.
24
+ - Wrap multi-step mutations in transactions.
25
+ - Use `SELECT` with explicit column lists, not `SELECT *` in production code.
26
+ - Add `LIMIT` to any query that could return unbounded results.
27
+
28
+ ### Authentication and Authorization
29
+ - Authenticate identity before any data operation.
30
+ - Authorize the specific action after authentication.
31
+ - Use established, framework-provided mechanisms — do not roll custom auth.
32
+ - Expire tokens and sessions. Implement refresh token rotation.
33
+
34
+ ### Logging
35
+ - Log security-relevant events: login, logout, permission denial, data export.
36
+ - Never log credentials, tokens, passwords, or PII.
37
+ - Include correlation IDs for request tracing.
38
+
39
+ ---
40
+
41
+ ## PHP / Laravel Specifics
42
+
43
+ ### Eloquent
44
+ - Define `$fillable` on all models. Never use `$guarded = []` in production.
45
+ - Use eager loading (`with()`) to prevent N+1 queries.
46
+ - Scope complex queries to dedicated query scopes or repository classes.
47
+ - Use `firstOrFail()` / `findOrFail()` for user-facing lookups.
48
+
49
+ ### Services
50
+ - One responsibility per service class.
51
+ - Inject dependencies via constructor, not facade or static calls, in service classes.
52
+ - Return typed results (custom response objects or typed arrays) rather than raw arrays.
53
+
54
+ ### Commands and Jobs
55
+ - Validate input data before long-running operations.
56
+ - Implement idempotency for jobs that may be retried.
57
+ - Use `$this->fail()` explicitly when a job should not be retried.
58
+
59
+ ---
60
+
61
+ ## Node.js / TypeScript Specifics
62
+
63
+ ### Async
64
+ - Use `async/await` throughout. Avoid callback style in new code.
65
+ - Handle Promise rejections. Never leave unhandled rejections.
66
+ - Do not block the event loop with synchronous I/O in request handlers.
67
+
68
+ ### Type Safety
69
+ - Prefer `unknown` over `any` for external data.
70
+ - Validate and narrow external data at boundaries (API responses, request bodies).
71
+ - Use Zod, io-ts, or similar for runtime type validation.
72
+
73
+ ### Error Handling
74
+ - Use `Result` types or typed error classes rather than relying on caught `Error` objects.
75
+ - Never use `try/catch` to suppress errors — rethrow or handle explicitly.
76
+
77
+ ---
78
+
79
+ ## Python Specifics
80
+
81
+ - Use type annotations (`def fn(x: int) -> str`).
82
+ - Validate external data with Pydantic or similar.
83
+ - Use context managers (`with`) for resource cleanup.
84
+ - Prefer explicit error handling over broad `except Exception`.
@@ -0,0 +1,100 @@
1
+ # Database Standards
2
+
3
+ > **Scope:** Universal database principles for relational (MySQL, PostgreSQL) and document (MongoDB) databases.
4
+ > **Load:** On demand for database-related reviews and development.
5
+
6
+ ---
7
+
8
+ ## Schema Design
9
+
10
+ ### Naming Conventions
11
+ - Table names: `snake_case`, plural nouns (`orders`, `user_profiles`).
12
+ - Column names: `snake_case`.
13
+ - Primary keys: `id` (auto-increment integer or UUID — be consistent).
14
+ - Foreign keys: `{referenced_table_singular}_id` (e.g., `user_id`, `order_id`).
15
+ - Boolean columns: `is_`, `has_`, `can_` prefix (`is_active`, `has_subscription`).
16
+ - Timestamp columns: `created_at`, `updated_at`, `deleted_at`.
17
+
18
+ ### Data Integrity
19
+ - Define foreign key constraints. Do not rely on application-level integrity alone.
20
+ - Specify `ON DELETE` behavior explicitly (`CASCADE`, `SET NULL`, `RESTRICT`).
21
+ - Use `NOT NULL` by default. Allow `NULL` only when absence is semantically meaningful.
22
+ - Use appropriate column types — do not store numbers as strings or dates as strings.
23
+ - Apply `UNIQUE` constraints at the database level for unique business identifiers.
24
+
25
+ ### Indexes
26
+ - Index every foreign key column.
27
+ - Index columns used in frequent `WHERE`, `ORDER BY`, and `JOIN` conditions.
28
+ - Use composite indexes when queries filter on multiple columns together.
29
+ - Do not over-index: indexes slow down writes and consume space.
30
+ - Review the `EXPLAIN` plan for queries on large tables.
31
+
32
+ ---
33
+
34
+ ## Migrations
35
+
36
+ ### Rules
37
+ - Migrations must be reversible (implement `down()` method).
38
+ - Never modify an already-applied migration. Create a new migration.
39
+ - Do not perform data transformations in schema migrations that could fail on large datasets — use a separate data migration or background job.
40
+ - Never drop a column immediately after removing it from application code. Deprecate first, deploy, then drop.
41
+
42
+ ### Dangerous Migration Patterns
43
+ - Adding a `NOT NULL` column without a default to a large table (table lock risk).
44
+ - Renaming a column while application code still references the old name.
45
+ - Dropping a table that still has application references.
46
+ - Running heavy data updates in a migration (causes long deployment downtime).
47
+
48
+ ---
49
+
50
+ ## Query Standards
51
+
52
+ ### Safety
53
+ - Never concatenate user input into SQL strings.
54
+ - Always use parameterized queries or ORM query builders.
55
+ - Apply row-level authorization in queries, not only at the route level.
56
+
57
+ ### Performance
58
+ - Avoid `SELECT *`. Specify columns explicitly.
59
+ - Apply `LIMIT` to all queries that could return unbounded result sets.
60
+ - Avoid `SELECT COUNT(*)` on large tables in hot paths — use approximate counts or cache.
61
+ - Prefer `EXISTS` over `IN` for subqueries on large sets.
62
+ - Do not load entire result sets into memory when streaming would suffice.
63
+
64
+ ### Transactions
65
+ Wrap the following in transactions:
66
+ - Any operation that modifies 2+ related tables.
67
+ - Financial or inventory operations.
68
+ - Operations that must succeed atomically.
69
+
70
+ ```php
71
+ // PHP / Laravel
72
+ DB::transaction(function () {
73
+ // Multiple operations
74
+ });
75
+ ```
76
+
77
+ ```javascript
78
+ // Prisma (Node.js)
79
+ await prisma.$transaction([
80
+ prisma.order.create({ data: orderData }),
81
+ prisma.inventory.update({ where: { id }, data: { stock: { decrement: qty } } }),
82
+ ]);
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Soft Deletes
88
+
89
+ - Use soft deletes (`deleted_at`) for user-visible records (users, orders, products).
90
+ - Ensure soft-deleted records are excluded from all queries by default.
91
+ - Ensure indexes account for `deleted_at IS NULL` filter where relevant.
92
+
93
+ ---
94
+
95
+ ## Sensitive Data
96
+
97
+ - Never store passwords in plaintext or with weak hashing (MD5, SHA1).
98
+ - Use bcrypt, argon2, or scrypt with appropriate cost factors.
99
+ - Encrypt PII at rest when regulations require it.
100
+ - Never store payment card numbers or CVVs directly — use a payment vault.
@@ -0,0 +1,81 @@
1
+ # Frontend Code Standards
2
+
3
+ > **Scope:** Universal frontend principles. Technology-specific additions via project-context.md.
4
+ > **Load:** On demand when performing frontend reviews or development.
5
+
6
+ ---
7
+
8
+ ## Universal Principles
9
+
10
+ ### Component Design
11
+ - One component, one responsibility.
12
+ - Keep components small. When a component exceeds 150 lines, consider extracting sub-components.
13
+ - Separate presentation from business logic and data fetching.
14
+ - Props should be the minimum necessary. Do not thread unrelated data through components.
15
+
16
+ ### State Management
17
+ - Minimize global state. Prefer local component state when data is not shared.
18
+ - Colocate state as close to its usage as possible.
19
+ - Do not mutate state directly. Always use the framework's state setter.
20
+
21
+ ### Accessibility
22
+ - All interactive elements must be keyboard accessible.
23
+ - Images must have descriptive `alt` attributes.
24
+ - Forms must have associated labels.
25
+ - Color must not be the only way to convey information.
26
+
27
+ ### Performance
28
+ - Lazy load routes and heavy components.
29
+ - Do not perform expensive operations in render functions.
30
+ - Avoid unnecessary re-renders (memoize when measurable benefit exists).
31
+ - Paginate or virtualize long lists.
32
+
33
+ ### Security
34
+ - Sanitize before inserting HTML dynamically (`dangerouslySetInnerHTML`, `v-html`, `innerHTML`).
35
+ - Do not store sensitive data in `localStorage` or `sessionStorage`.
36
+ - Apply Content Security Policy headers.
37
+ - Validate data from API responses before trusting it.
38
+
39
+ ---
40
+
41
+ ## React / Next.js Specifics
42
+
43
+ ### Components
44
+ - Use functional components with hooks. No class components in new code.
45
+ - Use TypeScript interfaces for all props.
46
+ - Use named exports, not default exports, for components.
47
+ - Use `React.memo()` only when profiling identifies unnecessary re-renders.
48
+
49
+ ### Hooks
50
+ - Follow rules of hooks (call only at top level, only in React functions).
51
+ - Extract complex hook logic into custom hooks.
52
+ - Include all reactive values in `useEffect` dependency arrays.
53
+ - Clean up side effects in `useEffect` return functions.
54
+
55
+ ### Data Fetching
56
+ - Use React Query / SWR / Next.js data fetching patterns — not bare `useEffect` with `fetch`.
57
+ - Handle loading, error, and empty states explicitly.
58
+ - Do not expose API error details to end users.
59
+
60
+ ### Forms
61
+ - Use a form library (React Hook Form, Formik) for complex forms.
62
+ - Validate both client-side (UX) and server-side (security).
63
+ - Disable submit buttons on submission to prevent double submission.
64
+
65
+ ---
66
+
67
+ ## Vue / Nuxt Specifics
68
+
69
+ - Use Composition API (not Options API) in new code.
70
+ - Define prop types with `defineProps<Props>()` using TypeScript.
71
+ - Avoid `v-html` unless the content is explicitly sanitized.
72
+ - Use `defineEmits` for component events with type safety.
73
+
74
+ ---
75
+
76
+ ## General CSS / Styling
77
+
78
+ - Use CSS custom properties (variables) for design tokens.
79
+ - Prefer utility frameworks (Tailwind) or CSS Modules to avoid global CSS conflicts.
80
+ - Do not use `!important` to work around specificity issues.
81
+ - Test responsive layouts at defined breakpoints.