class-ai-agent 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +155 -0
- package/.claude/agents/backend.md +395 -0
- package/.claude/agents/code-reviewer.md +110 -0
- package/.claude/agents/copywriter-seo.md +236 -0
- package/.claude/agents/frontend.md +384 -0
- package/.claude/agents/project-manager.md +201 -0
- package/.claude/agents/qa.md +221 -0
- package/.claude/agents/security-auditor.md +143 -0
- package/.claude/agents/systems-architect.md +211 -0
- package/.claude/agents/test-engineer.md +123 -0
- package/.claude/agents/ui-ux-designer.md +210 -0
- package/.claude/commands/build.md +132 -0
- package/.claude/commands/debug.md +242 -0
- package/.claude/commands/deploy.md +40 -0
- package/.claude/commands/fix-issue.md +42 -0
- package/.claude/commands/plan.md +125 -0
- package/.claude/commands/review.md +50 -0
- package/.claude/commands/simplify.md +222 -0
- package/.claude/commands/spec.md +95 -0
- package/.claude/commands/test.md +214 -0
- package/.claude/references/accessibility-checklist.md +174 -0
- package/.claude/references/performance-checklist.md +150 -0
- package/.claude/references/security-checklist.md +94 -0
- package/.claude/references/testing-patterns.md +183 -0
- package/.claude/rules/api-conventions.md +79 -0
- package/.claude/rules/clean-code.md +205 -0
- package/.claude/rules/code-style.md +86 -0
- package/.claude/rules/database.md +60 -0
- package/.claude/rules/error-handling.md +92 -0
- package/.claude/rules/git-workflow.md +77 -0
- package/.claude/rules/monitoring.md +311 -0
- package/.claude/rules/naming-conventions.md +260 -0
- package/.claude/rules/project-structure.md +65 -0
- package/.claude/rules/security.md +90 -0
- package/.claude/rules/system-design.md +162 -0
- package/.claude/rules/tech-stack.md +456 -0
- package/.claude/rules/testing.md +104 -0
- package/.claude/settings.json +14 -0
- package/.claude/skills/code-review/SKILL.md +208 -0
- package/.claude/skills/deploy/SKILL.md +68 -0
- package/.claude/skills/deploy/deploy.md +735 -0
- package/.claude/skills/incremental-implementation/SKILL.md +210 -0
- package/.claude/skills/security-review/SKILL.md +71 -0
- package/.claude/skills/tdd/SKILL.md +217 -0
- package/.cursor/CURSOR.md +112 -0
- package/.cursor/agents/backend.md +395 -0
- package/.cursor/agents/code-reviewer.md +110 -0
- package/.cursor/agents/copywriter-seo.md +236 -0
- package/.cursor/agents/frontend.md +384 -0
- package/.cursor/agents/project-manager.md +201 -0
- package/.cursor/agents/qa.md +221 -0
- package/.cursor/agents/security-auditor.md +143 -0
- package/.cursor/agents/systems-architect.md +211 -0
- package/.cursor/agents/test-engineer.md +123 -0
- package/.cursor/agents/ui-ux-designer.md +210 -0
- package/.cursor/commands/build.md +132 -0
- package/.cursor/commands/debug.md +242 -0
- package/.cursor/commands/deploy.md +40 -0
- package/.cursor/commands/fix-issue.md +42 -0
- package/.cursor/commands/plan.md +125 -0
- package/.cursor/commands/review.md +50 -0
- package/.cursor/commands/simplify.md +222 -0
- package/.cursor/commands/spec.md +95 -0
- package/.cursor/commands/test.md +214 -0
- package/.cursor/references/accessibility-checklist.md +174 -0
- package/.cursor/references/performance-checklist.md +150 -0
- package/.cursor/references/security-checklist.md +94 -0
- package/.cursor/references/testing-patterns.md +183 -0
- package/.cursor/rules/api-conventions.mdc +85 -0
- package/.cursor/rules/clean-code.mdc +211 -0
- package/.cursor/rules/code-style.mdc +92 -0
- package/.cursor/rules/cursor-overview.mdc +35 -0
- package/.cursor/rules/database.mdc +66 -0
- package/.cursor/rules/error-handling.mdc +98 -0
- package/.cursor/rules/git-workflow.mdc +83 -0
- package/.cursor/rules/monitoring.mdc +317 -0
- package/.cursor/rules/naming-conventions.mdc +266 -0
- package/.cursor/rules/project-structure.mdc +71 -0
- package/.cursor/rules/security.mdc +95 -0
- package/.cursor/rules/system-design.mdc +168 -0
- package/.cursor/rules/tech-stack.mdc +462 -0
- package/.cursor/rules/testing.mdc +110 -0
- package/.cursor/settings.json +8 -0
- package/.cursor/skills/code-review/SKILL.md +208 -0
- package/.cursor/skills/deploy/SKILL.md +68 -0
- package/.cursor/skills/deploy/deploy.md +735 -0
- package/.cursor/skills/incremental-implementation/SKILL.md +210 -0
- package/.cursor/skills/security-review/SKILL.md +71 -0
- package/.cursor/skills/tdd/SKILL.md +217 -0
- package/AGENTS.md +11 -0
- package/README.md +405 -0
- package/bin/class-ai-agent.cjs +176 -0
- package/package.json +38 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Security Checklist
|
|
2
|
+
|
|
3
|
+
> Quick reference for security review. See `.claude/rules/security.md` for full rules.
|
|
4
|
+
|
|
5
|
+
## Pre-Commit Checks
|
|
6
|
+
|
|
7
|
+
- [ ] No secrets in code (API keys, passwords, tokens)
|
|
8
|
+
- [ ] `.gitignore` excludes sensitive files (`.env`, credentials)
|
|
9
|
+
- [ ] `.env.example` contains only placeholder values
|
|
10
|
+
- [ ] No hardcoded URLs with credentials
|
|
11
|
+
|
|
12
|
+
## Authentication
|
|
13
|
+
|
|
14
|
+
- [ ] Passwords hashed with bcrypt (rounds >= 12) or argon2
|
|
15
|
+
- [ ] Session cookies: `httpOnly`, `secure`, `sameSite: 'lax'`
|
|
16
|
+
- [ ] JWT tokens have reasonable expiry (15min access, 7d refresh)
|
|
17
|
+
- [ ] Rate limiting on auth endpoints (max 10 attempts/15min)
|
|
18
|
+
- [ ] Logout invalidates session/token
|
|
19
|
+
|
|
20
|
+
## Authorization
|
|
21
|
+
|
|
22
|
+
- [ ] Every endpoint checks authentication
|
|
23
|
+
- [ ] Resource ownership verified (no IDOR)
|
|
24
|
+
- [ ] API keys are scoped appropriately
|
|
25
|
+
- [ ] JWT signature, expiration, and issuer validated
|
|
26
|
+
- [ ] Admin functions protected
|
|
27
|
+
|
|
28
|
+
## Input Validation
|
|
29
|
+
|
|
30
|
+
- [ ] All user input validated at system boundary
|
|
31
|
+
- [ ] Allowlist validation preferred over blocklist
|
|
32
|
+
- [ ] String lengths constrained
|
|
33
|
+
- [ ] Numeric ranges validated
|
|
34
|
+
- [ ] File uploads restricted by type and size
|
|
35
|
+
- [ ] SQL queries parameterized (never string concat)
|
|
36
|
+
|
|
37
|
+
## Security Headers
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
// Required headers
|
|
41
|
+
Content-Security-Policy: default-src 'self'
|
|
42
|
+
Strict-Transport-Security: max-age=31536000; includeSubDomains
|
|
43
|
+
X-Content-Type-Options: nosniff
|
|
44
|
+
X-Frame-Options: DENY
|
|
45
|
+
Permissions-Policy: geolocation=(), camera=()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## CORS
|
|
49
|
+
|
|
50
|
+
- [ ] Restrictive origin allowlist (no `*` in production)
|
|
51
|
+
- [ ] Credentials mode appropriate
|
|
52
|
+
- [ ] Methods and headers restricted
|
|
53
|
+
|
|
54
|
+
## Data Protection
|
|
55
|
+
|
|
56
|
+
- [ ] Sensitive fields excluded from API responses
|
|
57
|
+
- [ ] No secrets in logs
|
|
58
|
+
- [ ] PII encrypted when required
|
|
59
|
+
- [ ] HTTPS enforced
|
|
60
|
+
- [ ] Database backups encrypted
|
|
61
|
+
|
|
62
|
+
## Dependencies
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Run regularly
|
|
66
|
+
npm audit
|
|
67
|
+
npm audit fix
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- [ ] No critical vulnerabilities
|
|
71
|
+
- [ ] Dependencies up to date
|
|
72
|
+
- [ ] Lock file committed
|
|
73
|
+
|
|
74
|
+
## Error Handling
|
|
75
|
+
|
|
76
|
+
- [ ] Generic error messages in production
|
|
77
|
+
- [ ] No stack traces exposed
|
|
78
|
+
- [ ] No database details in errors
|
|
79
|
+
- [ ] No internal paths revealed
|
|
80
|
+
|
|
81
|
+
## OWASP Top 10 Quick Check
|
|
82
|
+
|
|
83
|
+
| # | Vulnerability | Check |
|
|
84
|
+
|---|--------------|-------|
|
|
85
|
+
| 1 | Broken Access Control | Auth on all endpoints? |
|
|
86
|
+
| 2 | Cryptographic Failures | Secrets encrypted? HTTPS? |
|
|
87
|
+
| 3 | Injection | Inputs sanitized? Queries parameterized? |
|
|
88
|
+
| 4 | Insecure Design | Threat modeling done? |
|
|
89
|
+
| 5 | Security Misconfiguration | Headers set? Defaults changed? |
|
|
90
|
+
| 6 | Vulnerable Components | `npm audit` clean? |
|
|
91
|
+
| 7 | Auth Failures | Rate limiting? Strong passwords? |
|
|
92
|
+
| 8 | Data Integrity | Signatures verified? |
|
|
93
|
+
| 9 | Logging Failures | Security events logged? |
|
|
94
|
+
| 10 | SSRF | External URLs validated? |
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Testing Patterns Reference
|
|
2
|
+
|
|
3
|
+
> Quick reference for test patterns. See `.claude/rules/testing.md` for full rules.
|
|
4
|
+
|
|
5
|
+
## Test Pyramid
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
βββββββββββ
|
|
9
|
+
β E2E β 5% Critical user flows
|
|
10
|
+
βββββββββββ€
|
|
11
|
+
β Integ β 15% API + DB interactions
|
|
12
|
+
βββββββββββ€
|
|
13
|
+
β Unit β 80% Pure logic, fast
|
|
14
|
+
βββββββββββ
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Test Structure (AAA)
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
it('should [expected behavior] when [condition]', () => {
|
|
21
|
+
// Arrange β Setup
|
|
22
|
+
const user = createTestUser({ role: 'admin' });
|
|
23
|
+
|
|
24
|
+
// Act β Execute
|
|
25
|
+
const result = checkPermission(user, 'delete');
|
|
26
|
+
|
|
27
|
+
// Assert β Verify
|
|
28
|
+
expect(result).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Unit Test Example
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
describe('calculateDiscount', () => {
|
|
36
|
+
it('should return 10% for orders over $100', () => {
|
|
37
|
+
expect(calculateDiscount(150)).toBe(15);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should return 0 for orders under $100', () => {
|
|
41
|
+
expect(calculateDiscount(50)).toBe(0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle edge case at exactly $100', () => {
|
|
45
|
+
expect(calculateDiscount(100)).toBe(0);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Integration Test Example
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
describe('POST /api/users', () => {
|
|
54
|
+
it('should create user and return 201', async () => {
|
|
55
|
+
const response = await request(app)
|
|
56
|
+
.post('/api/users')
|
|
57
|
+
.send({ email: 'test@example.com', name: 'Test' })
|
|
58
|
+
.set('Authorization', `Bearer ${token}`);
|
|
59
|
+
|
|
60
|
+
expect(response.status).toBe(201);
|
|
61
|
+
expect(response.body.data.email).toBe('test@example.com');
|
|
62
|
+
|
|
63
|
+
// Verify in database
|
|
64
|
+
const user = await db.user.findUnique({
|
|
65
|
+
where: { email: 'test@example.com' }
|
|
66
|
+
});
|
|
67
|
+
expect(user).toBeDefined();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## E2E Test Example (Playwright)
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
test('user can complete checkout flow', async ({ page }) => {
|
|
76
|
+
// Login
|
|
77
|
+
await page.goto('/login');
|
|
78
|
+
await page.fill('[name="email"]', 'user@example.com');
|
|
79
|
+
await page.fill('[name="password"]', 'password');
|
|
80
|
+
await page.click('button[type="submit"]');
|
|
81
|
+
|
|
82
|
+
// Add to cart
|
|
83
|
+
await page.goto('/products/1');
|
|
84
|
+
await page.click('button:has-text("Add to Cart")');
|
|
85
|
+
|
|
86
|
+
// Checkout
|
|
87
|
+
await page.goto('/checkout');
|
|
88
|
+
await page.fill('[name="card"]', '4242424242424242');
|
|
89
|
+
await page.click('button:has-text("Pay")');
|
|
90
|
+
|
|
91
|
+
// Verify
|
|
92
|
+
await expect(page.locator('.success-message')).toBeVisible();
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## React Component Test
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
100
|
+
|
|
101
|
+
describe('Counter', () => {
|
|
102
|
+
it('should increment count when button clicked', () => {
|
|
103
|
+
render(<Counter initialCount={0} />);
|
|
104
|
+
|
|
105
|
+
const button = screen.getByRole('button', { name: /increment/i });
|
|
106
|
+
fireEvent.click(button);
|
|
107
|
+
|
|
108
|
+
expect(screen.getByText('Count: 1')).toBeInTheDocument();
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Test Doubles
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
// 1. Real (preferred)
|
|
117
|
+
const db = createTestDatabase();
|
|
118
|
+
|
|
119
|
+
// 2. Fake (in-memory)
|
|
120
|
+
const fakeUserRepo = {
|
|
121
|
+
users: [],
|
|
122
|
+
create(user) { this.users.push(user); return user; },
|
|
123
|
+
findById(id) { return this.users.find(u => u.id === id); }
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// 3. Stub (canned response)
|
|
127
|
+
const stubbedApi = {
|
|
128
|
+
getUser: () => Promise.resolve({ id: '1', name: 'Test' })
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// 4. Mock (verify interactions β use sparingly)
|
|
132
|
+
const mockLogger = vi.fn();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Naming Convention
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
// Pattern: should [expected] when [condition]
|
|
139
|
+
|
|
140
|
+
// β
Good
|
|
141
|
+
'should return null when user not found'
|
|
142
|
+
'should throw ValidationError when email invalid'
|
|
143
|
+
'should emit event when order placed'
|
|
144
|
+
|
|
145
|
+
// β Bad
|
|
146
|
+
'works'
|
|
147
|
+
'test user'
|
|
148
|
+
'error handling'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Anti-Patterns
|
|
152
|
+
|
|
153
|
+
| Pattern | Problem | Fix |
|
|
154
|
+
|---------|---------|-----|
|
|
155
|
+
| Testing internals | Breaks on refactor | Test behavior |
|
|
156
|
+
| Shared state | Tests affect each other | Reset in beforeEach |
|
|
157
|
+
| Flaky tests | Random failures | Deterministic data |
|
|
158
|
+
| Over-mocking | False confidence | Real implementations |
|
|
159
|
+
| No assertions | Test always passes | Assert outcomes |
|
|
160
|
+
| Magic numbers | Hard to understand | Named constants |
|
|
161
|
+
|
|
162
|
+
## Coverage Thresholds
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
// vitest.config.js
|
|
166
|
+
coverage: {
|
|
167
|
+
thresholds: {
|
|
168
|
+
lines: 80,
|
|
169
|
+
branches: 80,
|
|
170
|
+
functions: 80,
|
|
171
|
+
statements: 80
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Commands
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npm test # Run all tests
|
|
180
|
+
npm test -- --watch # Watch mode
|
|
181
|
+
npm test -- --coverage # Coverage report
|
|
182
|
+
npm run test:e2e # E2E tests
|
|
183
|
+
```
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "API Conventions"
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
globs: "**/*.{ts,tsx,js,jsx,mjs,cjs,json,md,prisma,yml,yaml}"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# API Conventions
|
|
8
|
+
|
|
9
|
+
## REST API Design Standards
|
|
10
|
+
|
|
11
|
+
### URL Structure
|
|
12
|
+
- Use **kebab-case** for URL paths: `/api/user-profiles`
|
|
13
|
+
- Use **plural nouns** for resource collections: `/api/users`, `/api/products`
|
|
14
|
+
- Nest related resources: `/api/users/:id/orders`
|
|
15
|
+
- API version prefix: `/api/v1/...`
|
|
16
|
+
|
|
17
|
+
### HTTP Methods
|
|
18
|
+
| Method | Usage |
|
|
19
|
+
|--------|-------|
|
|
20
|
+
| GET | Read resources (idempotent) |
|
|
21
|
+
| POST | Create new resource |
|
|
22
|
+
| PUT | Replace entire resource |
|
|
23
|
+
| PATCH | Partial update |
|
|
24
|
+
| DELETE | Remove resource |
|
|
25
|
+
|
|
26
|
+
### HTTP Status Codes
|
|
27
|
+
| Code | Meaning |
|
|
28
|
+
|------|---------|
|
|
29
|
+
| 200 | OK β Successful GET/PUT/PATCH |
|
|
30
|
+
| 201 | Created β Successful POST |
|
|
31
|
+
| 204 | No Content β Successful DELETE |
|
|
32
|
+
| 400 | Bad Request β Invalid input |
|
|
33
|
+
| 401 | Unauthorized β Not authenticated |
|
|
34
|
+
| 403 | Forbidden β No permission |
|
|
35
|
+
| 404 | Not Found |
|
|
36
|
+
| 409 | Conflict |
|
|
37
|
+
| 422 | Unprocessable Entity β Validation failed |
|
|
38
|
+
| 500 | Internal Server Error |
|
|
39
|
+
|
|
40
|
+
### Request/Response Format
|
|
41
|
+
```json
|
|
42
|
+
// Success response
|
|
43
|
+
{
|
|
44
|
+
"success": true,
|
|
45
|
+
"data": { ... },
|
|
46
|
+
"message": "Optional message"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Error response
|
|
50
|
+
{
|
|
51
|
+
"success": false,
|
|
52
|
+
"error": {
|
|
53
|
+
"code": "VALIDATION_ERROR",
|
|
54
|
+
"message": "Human readable message",
|
|
55
|
+
"details": [ ... ]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Paginated list response
|
|
60
|
+
{
|
|
61
|
+
"success": true,
|
|
62
|
+
"data": [ ... ],
|
|
63
|
+
"pagination": {
|
|
64
|
+
"page": 1,
|
|
65
|
+
"limit": 20,
|
|
66
|
+
"total": 100,
|
|
67
|
+
"totalPages": 5
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Naming Conventions
|
|
73
|
+
- Request/response body fields: **camelCase**
|
|
74
|
+
- Query parameters: **camelCase**
|
|
75
|
+
- Always return consistent field names
|
|
76
|
+
|
|
77
|
+
### Filtering & Pagination
|
|
78
|
+
```
|
|
79
|
+
GET /api/users?page=1&limit=20&sortBy=createdAt&order=desc
|
|
80
|
+
GET /api/products?category=electronics&minPrice=100
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Documentation
|
|
84
|
+
- Every endpoint MUST have JSDoc/Swagger annotations
|
|
85
|
+
- Include request body schema, response schema, and error codes
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Clean Code β JavaScript Rules"
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
globs: "**/*.{ts,tsx,js,jsx,mjs,cjs,json,md,prisma,yml,yaml}"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Clean Code β JavaScript Rules
|
|
8
|
+
> Source: [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript) by Ryan McDermott
|
|
9
|
+
|
|
10
|
+
## π¦ Variables
|
|
11
|
+
|
|
12
|
+
### β
Use meaningful, pronounceable names
|
|
13
|
+
```js
|
|
14
|
+
// β Bad
|
|
15
|
+
const yyyymmdstr = moment().format('YYYY/MM/DD');
|
|
16
|
+
|
|
17
|
+
// β
Good
|
|
18
|
+
const currentDate = moment().format('YYYY/MM/DD');
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### β
Same vocabulary for same type
|
|
22
|
+
```js
|
|
23
|
+
// β getUserInfo(), getClientData(), getCustomerRecord()
|
|
24
|
+
// β
getUser()
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### β
Use searchable names (no magic numbers)
|
|
28
|
+
```js
|
|
29
|
+
// β setTimeout(blastOff, 86400000);
|
|
30
|
+
const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000;
|
|
31
|
+
setTimeout(blastOff, MILLISECONDS_PER_DAY); // β
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### β
Use explanatory variables
|
|
35
|
+
```js
|
|
36
|
+
// β Bad
|
|
37
|
+
saveCityZipCode(address.match(regex)[1], address.match(regex)[2]);
|
|
38
|
+
|
|
39
|
+
// β
Good
|
|
40
|
+
const [_, city, zipCode] = address.match(cityZipCodeRegex) || [];
|
|
41
|
+
saveCityZipCode(city, zipCode);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### β
Avoid mental mapping β be explicit
|
|
45
|
+
```js
|
|
46
|
+
// β locations.forEach(l => { dispatch(l); });
|
|
47
|
+
// β
locations.forEach(location => { dispatch(location); });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### β
Don't add redundant context
|
|
51
|
+
```js
|
|
52
|
+
// β const Car = { carMake, carModel, carColor }
|
|
53
|
+
// β
const Car = { make, model, color }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### β
Use default parameters
|
|
57
|
+
```js
|
|
58
|
+
// β const name = name || 'Default';
|
|
59
|
+
// β
function create(name = 'Default') {}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## π§ Functions
|
|
65
|
+
|
|
66
|
+
### β
2 arguments or fewer β use object destructuring for more
|
|
67
|
+
```js
|
|
68
|
+
// β function createMenu(title, body, buttonText, cancellable) {}
|
|
69
|
+
// β
function createMenu({ title, body, buttonText, cancellable }) {}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### β
Functions should do ONE thing
|
|
73
|
+
```js
|
|
74
|
+
// β emailClients() β fetches DB + checks active + sends email
|
|
75
|
+
// β
emailActiveClients() calls isActiveClient() separately
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### β
Function names should say what they do
|
|
79
|
+
```js
|
|
80
|
+
// β addToDate(date, 1) β unclear what is added
|
|
81
|
+
// β
addMonthToDate(1, date) β crystal clear
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### β
One level of abstraction per function
|
|
85
|
+
```js
|
|
86
|
+
// β parseBetterJSAlternative() β tokenizes + parses + AST walks in one function
|
|
87
|
+
// β
parseBetterJSAlternative() β calls tokenize() β calls parse()
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### β
Remove duplicate code β extract shared logic
|
|
91
|
+
### β
No flag parameters β split into separate functions
|
|
92
|
+
```js
|
|
93
|
+
// β function createFile(name, isTemp) { if (isTemp) ... }
|
|
94
|
+
// β
function createFile(name) {}
|
|
95
|
+
// function createTempFile(name) {}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### β
Avoid Side Effects
|
|
99
|
+
- Don't mutate global state
|
|
100
|
+
- Don't mutate function arguments (use copies)
|
|
101
|
+
```js
|
|
102
|
+
// β
Return new array instead of mutating
|
|
103
|
+
function addItemToCart(cart, item) {
|
|
104
|
+
return [...cart, { item, date: Date.now() }];
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### β
Favor functional programming
|
|
109
|
+
```js
|
|
110
|
+
// β for loop with mutations
|
|
111
|
+
// β
filter(), map(), reduce()
|
|
112
|
+
const totalOutput = programmerOutput
|
|
113
|
+
.filter(p => p.linesOfCode > 0)
|
|
114
|
+
.reduce((acc, p) => acc + p.linesOfCode, 0);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### β
Encapsulate conditionals
|
|
118
|
+
```js
|
|
119
|
+
// β if (fsm.state === 'fetching' && isEmpty(listNode)) {}
|
|
120
|
+
// β
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### β
Avoid negative conditionals
|
|
124
|
+
```js
|
|
125
|
+
// β if (!isNotDOMNodePresent(node)) {}
|
|
126
|
+
// β
if (isDOMNodePresent(node)) {}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### β
Remove dead code immediately
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## ποΈ Classes
|
|
134
|
+
|
|
135
|
+
### β
Prefer ES6 classes
|
|
136
|
+
### β
Use method chaining (builder pattern)
|
|
137
|
+
```js
|
|
138
|
+
class QueryBuilder {
|
|
139
|
+
select(fields) { this.fields = fields; return this; }
|
|
140
|
+
from(table) { this.table = table; return this; }
|
|
141
|
+
build() { return `SELECT ${this.fields} FROM ${this.table}`; }
|
|
142
|
+
}
|
|
143
|
+
new QueryBuilder().select('*').from('users').build();
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### β
Prefer composition over inheritance
|
|
147
|
+
> "Favor has-a over is-a"
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## π§± SOLID Principles
|
|
152
|
+
|
|
153
|
+
| Principle | Rule |
|
|
154
|
+
|-----------|------|
|
|
155
|
+
| **S** β Single Responsibility | One class = one job. Never combine unrelated concerns |
|
|
156
|
+
| **O** β Open/Closed | Open for extension, closed for modification |
|
|
157
|
+
| **L** β Liskov Substitution | Subclasses must be substitutable for their base class |
|
|
158
|
+
| **I** β Interface Segregation | Clients shouldn't depend on interfaces they don't use |
|
|
159
|
+
| **D** β Dependency Inversion | Depend on abstractions, not concretions |
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
// β
D β Dependency Inversion
|
|
163
|
+
class InventoryService {
|
|
164
|
+
constructor(inventoryRequester) { // inject the dependency
|
|
165
|
+
this.inventoryRequester = inventoryRequester;
|
|
166
|
+
}
|
|
167
|
+
requestItems(customer) {
|
|
168
|
+
return this.inventoryRequester.requestItem(customer.purchaseHistory);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## β‘ Concurrency
|
|
176
|
+
|
|
177
|
+
### β
Use Promises over callbacks
|
|
178
|
+
### β
Use async/await over Promises
|
|
179
|
+
```js
|
|
180
|
+
// β
Clearest form
|
|
181
|
+
async function getCleanCodeArticle() {
|
|
182
|
+
try {
|
|
183
|
+
const response = await request.get(cleanCodeUrl);
|
|
184
|
+
await fs.writeFile('article.html', response);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
console.error(err);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## ποΈ Comments
|
|
194
|
+
|
|
195
|
+
### β
Only comment business logic complexity
|
|
196
|
+
### β Never leave commented-out code
|
|
197
|
+
### β No journal comments (use git log instead)
|
|
198
|
+
### β No positional markers (`/////`)
|
|
199
|
+
```js
|
|
200
|
+
// β
Good comment β explains WHY
|
|
201
|
+
// We retry 3x because OAuth2 tokens can have clock skew
|
|
202
|
+
const MAX_RETRIES = 3;
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## π Formatting
|
|
208
|
+
|
|
209
|
+
- Use consistent capitalization (camelCase for vars/fns, PascalCase for classes, UPPER_SNAKE for constants)
|
|
210
|
+
- Keep callers and callees close in the file
|
|
211
|
+
- Related code should appear together
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Code Style Guide"
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
globs: "**/*.{ts,tsx,js,jsx,mjs,cjs,json,md,prisma,yml,yaml}"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Code Style Guide
|
|
8
|
+
|
|
9
|
+
## General Principles
|
|
10
|
+
- **Clarity over cleverness** β Write code that is easy to read and understand
|
|
11
|
+
- **Consistency** β Follow existing patterns in the codebase
|
|
12
|
+
- **DRY** β Don't Repeat Yourself, but don't over-abstract
|
|
13
|
+
|
|
14
|
+
## JavaScript / TypeScript
|
|
15
|
+
|
|
16
|
+
### Formatting
|
|
17
|
+
- Indentation: **2 spaces** (no tabs)
|
|
18
|
+
- Max line length: **100 characters**
|
|
19
|
+
- Use **single quotes** for strings
|
|
20
|
+
- Always use **semicolons**
|
|
21
|
+
- Trailing commas in multi-line structures
|
|
22
|
+
|
|
23
|
+
### Naming
|
|
24
|
+
```js
|
|
25
|
+
// Variables and functions: camelCase
|
|
26
|
+
const userProfile = {};
|
|
27
|
+
function getUserById(id) {}
|
|
28
|
+
|
|
29
|
+
// Classes and interfaces: PascalCase
|
|
30
|
+
class UserService {}
|
|
31
|
+
interface UserRepository {}
|
|
32
|
+
|
|
33
|
+
// Constants: UPPER_SNAKE_CASE
|
|
34
|
+
const MAX_RETRY_COUNT = 3;
|
|
35
|
+
const API_BASE_URL = 'https://api.example.com';
|
|
36
|
+
|
|
37
|
+
// Files: kebab-case
|
|
38
|
+
// user-service.js, auth-middleware.js
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Functions
|
|
42
|
+
```js
|
|
43
|
+
// β
Good β Arrow functions for simple operations
|
|
44
|
+
const double = (x) => x * 2;
|
|
45
|
+
|
|
46
|
+
// β
Good β Named functions for complex logic
|
|
47
|
+
function processUserData(user) {
|
|
48
|
+
// ...
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// β Avoid β Functions longer than 30 lines (extract helpers)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Async/Await
|
|
55
|
+
```js
|
|
56
|
+
// β
Always use async/await over raw promises
|
|
57
|
+
async function fetchUser(id) {
|
|
58
|
+
try {
|
|
59
|
+
const user = await userRepository.findById(id);
|
|
60
|
+
return user;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
throw new AppError('User not found', 404);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// β Avoid promise chains
|
|
67
|
+
fetchUser(id).then(...).catch(...);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Imports
|
|
71
|
+
```js
|
|
72
|
+
// Order: 1. Node built-ins, 2. External deps, 3. Internal modules
|
|
73
|
+
import path from 'path';
|
|
74
|
+
import express from 'express';
|
|
75
|
+
import { UserService } from './user-service.js';
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Comments
|
|
79
|
+
```js
|
|
80
|
+
// β
Explain WHY, not WHAT
|
|
81
|
+
// We retry 3 times because the external API has transient failures
|
|
82
|
+
const MAX_RETRIES = 3;
|
|
83
|
+
|
|
84
|
+
// β Avoid obvious comments
|
|
85
|
+
// Set x to 5
|
|
86
|
+
const x = 5;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## File Organization
|
|
90
|
+
- One class/service per file
|
|
91
|
+
- Group related files in feature folders
|
|
92
|
+
- Index files for clean imports
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Cursor agent workflow, principles, and where project guidance lives"
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cursor agent β project workflow
|
|
7
|
+
|
|
8
|
+
This repo mirrors Claude Codeβs `.claude/` layout under **`.cursor/`** for Cursor (Composer, Agent, inline Chat).
|
|
9
|
+
|
|
10
|
+
## Development workflow
|
|
11
|
+
|
|
12
|
+
Use the same phase order as in `.cursor/CURSOR.md`:
|
|
13
|
+
|
|
14
|
+
1. **Define** β prompt from `.cursor/commands/spec.md` (paste or attach)
|
|
15
|
+
2. **Plan** β `.cursor/commands/plan.md`
|
|
16
|
+
3. **Build** β `.cursor/commands/build.md` (TDD: `.cursor/skills/tdd/`)
|
|
17
|
+
4. **Verify** β `.cursor/commands/test.md`
|
|
18
|
+
5. **Review** β `.cursor/commands/review.md` (five-axis: `.cursor/skills/code-review/`)
|
|
19
|
+
6. **Ship** β `.cursor/commands/deploy.md`
|
|
20
|
+
|
|
21
|
+
Supporting prompts: `debug`, `simplify`, `fix-issue` in `.cursor/commands/`.
|
|
22
|
+
|
|
23
|
+
## Mandatory standards
|
|
24
|
+
|
|
25
|
+
- Follow **`.cursor/rules/`** (`.mdc`). **`security.mdc`** is always applied; other rule files apply when matching globs are in scope (see each fileβs frontmatter).
|
|
26
|
+
- Prefer **tests first** and **small vertical slices** (see `.cursor/skills/incremental-implementation/`).
|
|
27
|
+
- Use **`.cursor/references/`** for checklists (security, testing, performance, accessibility).
|
|
28
|
+
|
|
29
|
+
## Agents (personas)
|
|
30
|
+
|
|
31
|
+
Specialized instructions live in **`.cursor/agents/`**. In Cursor, **@ mention** the file you want (e.g. `@.cursor/agents/code-reviewer.md`) when you need that role.
|
|
32
|
+
|
|
33
|
+
## Relation to `.claude/`
|
|
34
|
+
|
|
35
|
+
If both folders exist: **`.claude/`** targets Claude Code; **`.cursor/`** targets Cursor. Keep them aligned when you change workflows or standards.
|