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.
- package/LICENSE +35 -0
- package/README.md +383 -2
- package/adapters/antigravity/AGENTS.md +59 -0
- package/adapters/antigravity/skills/architecture-review/SKILL.md +33 -0
- package/adapters/antigravity/skills/bug-fix/SKILL.md +36 -0
- package/adapters/antigravity/skills/code-review/SKILL.md +34 -0
- package/adapters/antigravity/skills/feature-development/SKILL.md +37 -0
- package/adapters/antigravity/skills/performance-audit/SKILL.md +34 -0
- package/adapters/antigravity/skills/refactor/SKILL.md +35 -0
- package/adapters/antigravity/skills/security-audit/SKILL.md +35 -0
- package/adapters/antigravity/skills/test-generation/SKILL.md +34 -0
- package/adapters/cursor/bug-fix.mdc +38 -0
- package/adapters/cursor/code-review.mdc +56 -0
- package/adapters/cursor/core-rules.mdc +49 -0
- package/adapters/cursor/feature-development.mdc +31 -0
- package/adapters/cursor/performance-arch-tests.mdc +57 -0
- package/adapters/cursor/refactor-and-security.mdc +50 -0
- package/bin/ai-dev-starter.js +2 -0
- package/core/agent-principles.md +96 -0
- package/core/git-analysis.md +150 -0
- package/core/review-methodology.md +154 -0
- package/core/safety-policy.md +70 -0
- package/core/severity-levels.md +104 -0
- package/package.json +40 -4
- package/scripts/ai-dev-starter.js +709 -0
- package/scripts/detector.js +405 -0
- package/standards/backend.md +84 -0
- package/standards/database.md +100 -0
- package/standards/frontend.md +81 -0
- package/standards/security.md +84 -0
- package/standards/testing.md +132 -0
- package/templates/architecture-decision.md +92 -0
- package/templates/project-context.md +144 -0
- package/templates/review-report.md +102 -0
- package/workflows/architecture-review.md +186 -0
- package/workflows/bug-fix.md +149 -0
- package/workflows/code-review.md +189 -0
- package/workflows/feature-development.md +154 -0
- package/workflows/performance-audit.md +169 -0
- package/workflows/refactor.md +128 -0
- package/workflows/security-audit.md +164 -0
- 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.
|