oh-my-ag 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/.agent/skills/_shared/api-contracts/README.md +56 -0
- package/.agent/skills/_shared/api-contracts/template.md +88 -0
- package/.agent/skills/_shared/clarification-protocol.md +217 -0
- package/.agent/skills/_shared/common-checklist.md +31 -0
- package/.agent/skills/_shared/context-budget.md +118 -0
- package/.agent/skills/_shared/context-loading.md +105 -0
- package/.agent/skills/_shared/difficulty-guide.md +55 -0
- package/.agent/skills/_shared/lessons-learned.md +113 -0
- package/.agent/skills/_shared/memory-protocol.md +79 -0
- package/.agent/skills/_shared/reasoning-templates.md +161 -0
- package/.agent/skills/_shared/skill-routing.md +80 -0
- package/.agent/skills/_shared/verify.sh +252 -0
- package/.agent/skills/backend-agent/SKILL.md +47 -0
- package/.agent/skills/backend-agent/resources/api-template.py +326 -0
- package/.agent/skills/backend-agent/resources/checklist.md +36 -0
- package/.agent/skills/backend-agent/resources/error-playbook.md +98 -0
- package/.agent/skills/backend-agent/resources/examples.md +85 -0
- package/.agent/skills/backend-agent/resources/execution-protocol.md +45 -0
- package/.agent/skills/backend-agent/resources/snippets.md +197 -0
- package/.agent/skills/backend-agent/resources/tech-stack.md +39 -0
- package/.agent/skills/commit/SKILL.md +121 -0
- package/.agent/skills/commit/config/commit-config.yaml +55 -0
- package/.agent/skills/commit/resources/conventional-commits.md +166 -0
- package/.agent/skills/debug-agent/SKILL.md +51 -0
- package/.agent/skills/debug-agent/resources/bug-report-template.md +332 -0
- package/.agent/skills/debug-agent/resources/checklist.md +30 -0
- package/.agent/skills/debug-agent/resources/common-patterns.md +734 -0
- package/.agent/skills/debug-agent/resources/debugging-checklist.md +362 -0
- package/.agent/skills/debug-agent/resources/error-playbook.md +94 -0
- package/.agent/skills/debug-agent/resources/examples.md +87 -0
- package/.agent/skills/debug-agent/resources/execution-protocol.md +51 -0
- package/.agent/skills/frontend-agent/SKILL.md +48 -0
- package/.agent/skills/frontend-agent/resources/checklist.md +38 -0
- package/.agent/skills/frontend-agent/resources/component-template.tsx +92 -0
- package/.agent/skills/frontend-agent/resources/error-playbook.md +108 -0
- package/.agent/skills/frontend-agent/resources/examples.md +77 -0
- package/.agent/skills/frontend-agent/resources/execution-protocol.md +49 -0
- package/.agent/skills/frontend-agent/resources/snippets.md +205 -0
- package/.agent/skills/frontend-agent/resources/tailwind-rules.md +343 -0
- package/.agent/skills/frontend-agent/resources/tech-stack.md +36 -0
- package/.agent/skills/mobile-agent/SKILL.md +46 -0
- package/.agent/skills/mobile-agent/resources/checklist.md +35 -0
- package/.agent/skills/mobile-agent/resources/error-playbook.md +106 -0
- package/.agent/skills/mobile-agent/resources/examples.md +79 -0
- package/.agent/skills/mobile-agent/resources/execution-protocol.md +49 -0
- package/.agent/skills/mobile-agent/resources/screen-template.dart +298 -0
- package/.agent/skills/mobile-agent/resources/snippets.md +235 -0
- package/.agent/skills/mobile-agent/resources/tech-stack.md +45 -0
- package/.agent/skills/orchestrator/SKILL.md +99 -0
- package/.agent/skills/orchestrator/config/cli-config.yaml +78 -0
- package/.agent/skills/orchestrator/resources/memory-schema.md +212 -0
- package/.agent/skills/orchestrator/resources/subagent-prompt-template.md +153 -0
- package/.agent/skills/orchestrator/scripts/parallel-run.sh +330 -0
- package/.agent/skills/orchestrator/scripts/spawn-agent.sh +263 -0
- package/.agent/skills/orchestrator/templates/backend-task.md +18 -0
- package/.agent/skills/orchestrator/templates/debug-task.md +16 -0
- package/.agent/skills/orchestrator/templates/frontend-task.md +17 -0
- package/.agent/skills/orchestrator/templates/mobile-task.md +17 -0
- package/.agent/skills/orchestrator/templates/qa-task.md +16 -0
- package/.agent/skills/orchestrator/templates/tasks-example.yaml +15 -0
- package/.agent/skills/pm-agent/SKILL.md +47 -0
- package/.agent/skills/pm-agent/resources/error-playbook.md +75 -0
- package/.agent/skills/pm-agent/resources/examples.md +121 -0
- package/.agent/skills/pm-agent/resources/execution-protocol.md +46 -0
- package/.agent/skills/pm-agent/resources/task-template.json +57 -0
- package/.agent/skills/qa-agent/SKILL.md +43 -0
- package/.agent/skills/qa-agent/resources/checklist.md +294 -0
- package/.agent/skills/qa-agent/resources/error-playbook.md +95 -0
- package/.agent/skills/qa-agent/resources/examples.md +100 -0
- package/.agent/skills/qa-agent/resources/execution-protocol.md +50 -0
- package/.agent/skills/qa-agent/resources/self-check.md +27 -0
- package/.agent/skills/workflow-guide/SKILL.md +57 -0
- package/.agent/skills/workflow-guide/resources/examples.md +68 -0
- package/README.ko.md +459 -0
- package/README.md +563 -0
- package/bin/cli.js +205 -0
- package/package.json +75 -0
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
# Common Bug Patterns & Solutions
|
|
2
|
+
|
|
3
|
+
Quick reference guide for frequently encountered bugs and their fixes.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🔴 Frontend Bugs
|
|
8
|
+
|
|
9
|
+
### 1. Undefined/Null Errors
|
|
10
|
+
|
|
11
|
+
**❌ Problem**: `Cannot read property 'X' of undefined`
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// Crash when data not loaded yet
|
|
15
|
+
const name = user.profile.name;
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**✅ Solutions**:
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
// Option 1: Optional chaining + nullish coalescing
|
|
22
|
+
const name = user?.profile?.name ?? 'Unknown';
|
|
23
|
+
|
|
24
|
+
// Option 2: Conditional rendering
|
|
25
|
+
{user?.profile && <div>{user.profile.name}</div>}
|
|
26
|
+
|
|
27
|
+
// Option 3: Early return
|
|
28
|
+
if (!user?.profile) return <div>Loading...</div>;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
### 2. Stale Closures in useEffect
|
|
34
|
+
|
|
35
|
+
**❌ Problem**: Event handlers/callbacks use old state values
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
function Counter() {
|
|
39
|
+
const [count, setCount] = useState(0);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const interval = setInterval(() => {
|
|
43
|
+
console.log(count); // Always logs 0!
|
|
44
|
+
}, 1000);
|
|
45
|
+
return () => clearInterval(interval);
|
|
46
|
+
}, []); // Missing dependency
|
|
47
|
+
|
|
48
|
+
return <button onClick={() => setCount(c => c + 1)}>+</button>;
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**✅ Solutions**:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// Option 1: Include dependency
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const interval = setInterval(() => {
|
|
58
|
+
console.log(count); // Now updates!
|
|
59
|
+
}, 1000);
|
|
60
|
+
return () => clearInterval(interval);
|
|
61
|
+
}, [count]); // Dependency added
|
|
62
|
+
|
|
63
|
+
// Option 2: Use functional update
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
const interval = setInterval(() => {
|
|
66
|
+
setCount(c => {
|
|
67
|
+
console.log(c); // Current value
|
|
68
|
+
return c;
|
|
69
|
+
});
|
|
70
|
+
}, 1000);
|
|
71
|
+
return () => clearInterval(interval);
|
|
72
|
+
}, []); // Can stay empty
|
|
73
|
+
|
|
74
|
+
// Option 3: Use ref for latest value
|
|
75
|
+
const countRef = useRef(count);
|
|
76
|
+
countRef.current = count;
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const interval = setInterval(() => {
|
|
80
|
+
console.log(countRef.current); // Latest value
|
|
81
|
+
}, 1000);
|
|
82
|
+
return () => clearInterval(interval);
|
|
83
|
+
}, []);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### 3. Missing Cleanup in useEffect
|
|
89
|
+
|
|
90
|
+
**❌ Problem**: Memory leaks from subscriptions/listeners
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
const subscription = api.subscribe(data => setData(data));
|
|
95
|
+
// Missing cleanup!
|
|
96
|
+
}, []);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**✅ Solution**:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
const subscription = api.subscribe(data => setData(data));
|
|
104
|
+
|
|
105
|
+
return () => {
|
|
106
|
+
subscription.unsubscribe(); // Cleanup
|
|
107
|
+
};
|
|
108
|
+
}, []);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Common things that need cleanup**:
|
|
112
|
+
- Event listeners (`addEventListener`)
|
|
113
|
+
- Intervals (`setInterval`)
|
|
114
|
+
- Timeouts (`setTimeout`)
|
|
115
|
+
- Subscriptions (WebSockets, observables)
|
|
116
|
+
- API requests (cancellation tokens)
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### 4. Race Conditions in Async Effects
|
|
121
|
+
|
|
122
|
+
**❌ Problem**: Old requests overwrite new ones
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
fetchUser(userId).then(setUser);
|
|
127
|
+
// If userId changes quickly, old responses arrive after new ones
|
|
128
|
+
}, [userId]);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**✅ Solution**:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
let cancelled = false;
|
|
136
|
+
|
|
137
|
+
fetchUser(userId).then(user => {
|
|
138
|
+
if (!cancelled) {
|
|
139
|
+
setUser(user);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
return () => {
|
|
144
|
+
cancelled = true;
|
|
145
|
+
};
|
|
146
|
+
}, [userId]);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
### 5. Infinite Re-render Loops
|
|
152
|
+
|
|
153
|
+
**❌ Problem**: Component re-renders infinitely
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
function Component() {
|
|
157
|
+
const [data, setData] = useState([]);
|
|
158
|
+
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
setData([...data, 'new']); // Triggers effect again!
|
|
161
|
+
}, [data]); // Dependency causes loop
|
|
162
|
+
|
|
163
|
+
return <div>{data.length}</div>;
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**✅ Solutions**:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// Option 1: Remove problematic dependency
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
setData(prevData => [...prevData, 'new']);
|
|
173
|
+
}, []); // Empty deps - runs once
|
|
174
|
+
|
|
175
|
+
// Option 2: Use ref instead of state
|
|
176
|
+
const dataRef = useRef([]);
|
|
177
|
+
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
dataRef.current = [...dataRef.current, 'new'];
|
|
180
|
+
}, []);
|
|
181
|
+
|
|
182
|
+
// Option 3: Add condition
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
if (data.length === 0) { // Only run when empty
|
|
185
|
+
setData(['new']);
|
|
186
|
+
}
|
|
187
|
+
}, [data]);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### 6. Key Prop Issues in Lists
|
|
193
|
+
|
|
194
|
+
**❌ Problem**: List items reordering incorrectly
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// Using index as key
|
|
198
|
+
{todos.map((todo, index) => (
|
|
199
|
+
<TodoItem key={index} todo={todo} />
|
|
200
|
+
))}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**✅ Solution**:
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// Use stable, unique ID
|
|
207
|
+
{todos.map(todo => (
|
|
208
|
+
<TodoItem key={todo.id} todo={todo} />
|
|
209
|
+
))}
|
|
210
|
+
|
|
211
|
+
// If no ID, generate stable key
|
|
212
|
+
{todos.map((todo, index) => (
|
|
213
|
+
<TodoItem key={`${todo.title}-${index}`} todo={todo} />
|
|
214
|
+
))}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
### 7. Form Input Controlled/Uncontrolled Switch
|
|
220
|
+
|
|
221
|
+
**❌ Problem**: `Warning: A component is changing an uncontrolled input to be controlled`
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const [value, setValue] = useState(); // undefined initially
|
|
225
|
+
|
|
226
|
+
<input value={value} onChange={e => setValue(e.target.value)} />
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**✅ Solution**:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// Initialize with empty string
|
|
233
|
+
const [value, setValue] = useState('');
|
|
234
|
+
|
|
235
|
+
<input value={value} onChange={e => setValue(e.target.value)} />
|
|
236
|
+
|
|
237
|
+
// Or use defaultValue for uncontrolled
|
|
238
|
+
<input defaultValue="" onChange={e => console.log(e.target.value)} />
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 🔴 Backend Bugs
|
|
244
|
+
|
|
245
|
+
### 1. SQL Injection
|
|
246
|
+
|
|
247
|
+
**❌ Problem**: User input directly in SQL query
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
# DANGEROUS!
|
|
251
|
+
email = request.args.get('email')
|
|
252
|
+
query = f"SELECT * FROM users WHERE email = '{email}'"
|
|
253
|
+
db.execute(query)
|
|
254
|
+
# User can input: ' OR '1'='1
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**✅ Solution**:
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
# Use parameterized queries
|
|
261
|
+
from sqlalchemy import text
|
|
262
|
+
|
|
263
|
+
email = request.args.get('email')
|
|
264
|
+
query = text("SELECT * FROM users WHERE email = :email")
|
|
265
|
+
result = db.execute(query, {"email": email})
|
|
266
|
+
|
|
267
|
+
# Or use ORM
|
|
268
|
+
user = db.query(User).filter(User.email == email).first()
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
### 2. N+1 Query Problem
|
|
274
|
+
|
|
275
|
+
**❌ Problem**: One query per item in a loop
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
# 1 query to get todos
|
|
279
|
+
todos = db.query(Todo).all()
|
|
280
|
+
|
|
281
|
+
# N queries (one per todo)
|
|
282
|
+
for todo in todos:
|
|
283
|
+
user = db.query(User).filter(User.id == todo.user_id).first()
|
|
284
|
+
print(f"{todo.title} by {user.name}")
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**✅ Solution**:
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
# Use JOIN - single query
|
|
291
|
+
from sqlalchemy.orm import joinedload
|
|
292
|
+
|
|
293
|
+
todos = db.query(Todo).options(joinedload(Todo.user)).all()
|
|
294
|
+
|
|
295
|
+
for todo in todos:
|
|
296
|
+
print(f"{todo.title} by {todo.user.name}") # No extra query
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
### 3. Missing Authentication Check
|
|
302
|
+
|
|
303
|
+
**❌ Problem**: Protected endpoint accessible without auth
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
@app.get("/api/admin/users")
|
|
307
|
+
async def get_all_users(db: DatabaseDep):
|
|
308
|
+
return db.query(User).all() # Anyone can access!
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**✅ Solution**:
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
@app.get("/api/admin/users")
|
|
315
|
+
async def get_all_users(
|
|
316
|
+
db: DatabaseDep,
|
|
317
|
+
current_user: User = Depends(get_current_user) # Require auth
|
|
318
|
+
):
|
|
319
|
+
if current_user.role != "admin": # Check role
|
|
320
|
+
raise HTTPException(403, "Admin only")
|
|
321
|
+
return db.query(User).all()
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
### 4. Missing Input Validation
|
|
327
|
+
|
|
328
|
+
**❌ Problem**: Invalid data causes errors
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
@app.post("/api/users")
|
|
332
|
+
async def create_user(email: str, age: int):
|
|
333
|
+
# No validation!
|
|
334
|
+
user = User(email=email, age=age)
|
|
335
|
+
db.add(user)
|
|
336
|
+
db.commit()
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
**✅ Solution**:
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
from pydantic import BaseModel, EmailStr, Field
|
|
343
|
+
|
|
344
|
+
class UserCreate(BaseModel):
|
|
345
|
+
email: EmailStr # Validates email format
|
|
346
|
+
age: int = Field(ge=0, le=150) # Must be 0-150
|
|
347
|
+
|
|
348
|
+
@app.post("/api/users")
|
|
349
|
+
async def create_user(user: UserCreate):
|
|
350
|
+
# Pydantic validates automatically
|
|
351
|
+
db_user = User(**user.model_dump())
|
|
352
|
+
db.add(db_user)
|
|
353
|
+
db.commit()
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
### 5. Unhandled Exceptions
|
|
359
|
+
|
|
360
|
+
**❌ Problem**: Server crashes on error
|
|
361
|
+
|
|
362
|
+
```python
|
|
363
|
+
@app.post("/api/todos")
|
|
364
|
+
async def create_todo(todo: TodoCreate, user: User = Depends(get_current_user)):
|
|
365
|
+
db_todo = Todo(**todo.model_dump(), user_id=user.id)
|
|
366
|
+
db.add(db_todo)
|
|
367
|
+
db.commit() # Could fail!
|
|
368
|
+
return db_todo
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**✅ Solution**:
|
|
372
|
+
|
|
373
|
+
```python
|
|
374
|
+
from fastapi import HTTPException
|
|
375
|
+
|
|
376
|
+
@app.post("/api/todos")
|
|
377
|
+
async def create_todo(todo: TodoCreate, user: User = Depends(get_current_user)):
|
|
378
|
+
try:
|
|
379
|
+
db_todo = Todo(**todo.model_dump(), user_id=user.id)
|
|
380
|
+
db.add(db_todo)
|
|
381
|
+
db.commit()
|
|
382
|
+
db.refresh(db_todo)
|
|
383
|
+
return db_todo
|
|
384
|
+
except IntegrityError as e:
|
|
385
|
+
db.rollback()
|
|
386
|
+
raise HTTPException(409, "Duplicate entry")
|
|
387
|
+
except Exception as e:
|
|
388
|
+
db.rollback()
|
|
389
|
+
logger.error(f"Failed to create todo: {e}")
|
|
390
|
+
raise HTTPException(500, "Internal server error")
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
### 6. Missing CORS Configuration
|
|
396
|
+
|
|
397
|
+
**❌ Problem**: Frontend can't call API
|
|
398
|
+
|
|
399
|
+
```
|
|
400
|
+
Access to fetch at 'http://localhost:8000/api/todos' from origin
|
|
401
|
+
'http://localhost:3000' has been blocked by CORS policy
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**✅ Solution**:
|
|
405
|
+
|
|
406
|
+
```python
|
|
407
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
408
|
+
|
|
409
|
+
app.add_middleware(
|
|
410
|
+
CORSMiddleware,
|
|
411
|
+
allow_origins=["http://localhost:3000"], # Frontend URL
|
|
412
|
+
allow_credentials=True,
|
|
413
|
+
allow_methods=["*"],
|
|
414
|
+
allow_headers=["*"],
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
# For production, be specific:
|
|
418
|
+
# allow_origins=["https://yourdomain.com"]
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
### 7. Password Storage
|
|
424
|
+
|
|
425
|
+
**❌ Problem**: Passwords stored in plain text
|
|
426
|
+
|
|
427
|
+
```python
|
|
428
|
+
user = User(email=email, password=password) # NEVER DO THIS!
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**✅ Solution**:
|
|
432
|
+
|
|
433
|
+
```python
|
|
434
|
+
from passlib.context import CryptContext
|
|
435
|
+
|
|
436
|
+
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
|
437
|
+
|
|
438
|
+
# Hash before storing
|
|
439
|
+
password_hash = pwd_context.hash(password)
|
|
440
|
+
user = User(email=email, password_hash=password_hash)
|
|
441
|
+
|
|
442
|
+
# Verify on login
|
|
443
|
+
if not pwd_context.verify(plain_password, user.password_hash):
|
|
444
|
+
raise HTTPException(401, "Invalid credentials")
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## 🔴 Mobile Bugs
|
|
450
|
+
|
|
451
|
+
### 1. Memory Leaks in Flutter
|
|
452
|
+
|
|
453
|
+
**❌ Problem**: Controllers not disposed
|
|
454
|
+
|
|
455
|
+
```dart
|
|
456
|
+
class MyWidget extends StatefulWidget {
|
|
457
|
+
@override
|
|
458
|
+
_MyWidgetState createState() => _MyWidgetState();
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
class _MyWidgetState extends State<MyWidget> {
|
|
462
|
+
final controller = TextEditingController();
|
|
463
|
+
|
|
464
|
+
@override
|
|
465
|
+
Widget build(BuildContext context) {
|
|
466
|
+
return TextField(controller: controller);
|
|
467
|
+
}
|
|
468
|
+
// Missing dispose!
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**✅ Solution**:
|
|
473
|
+
|
|
474
|
+
```dart
|
|
475
|
+
class _MyWidgetState extends State<MyWidget> {
|
|
476
|
+
final controller = TextEditingController();
|
|
477
|
+
|
|
478
|
+
@override
|
|
479
|
+
void dispose() {
|
|
480
|
+
controller.dispose(); // Clean up
|
|
481
|
+
super.dispose();
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
@override
|
|
485
|
+
Widget build(BuildContext context) {
|
|
486
|
+
return TextField(controller: controller);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
### 2. Platform-Specific Code Not Checked
|
|
494
|
+
|
|
495
|
+
**❌ Problem**: iOS-specific code crashes on Android
|
|
496
|
+
|
|
497
|
+
```dart
|
|
498
|
+
// Crashes on Android
|
|
499
|
+
import 'dart:io' show Platform;
|
|
500
|
+
|
|
501
|
+
final deviceName = Platform.isIOS ? 'iPhone' : 'Unknown';
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**✅ Solution**:
|
|
505
|
+
|
|
506
|
+
```dart
|
|
507
|
+
import 'dart:io' show Platform;
|
|
508
|
+
|
|
509
|
+
final deviceName = Platform.isIOS
|
|
510
|
+
? 'iPhone'
|
|
511
|
+
: Platform.isAndroid
|
|
512
|
+
? 'Android'
|
|
513
|
+
: 'Unknown';
|
|
514
|
+
|
|
515
|
+
// Or use conditional imports
|
|
516
|
+
if (Platform.isIOS) {
|
|
517
|
+
// iOS-specific code
|
|
518
|
+
} else if (Platform.isAndroid) {
|
|
519
|
+
// Android-specific code
|
|
520
|
+
}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## 🔴 Performance Bugs
|
|
526
|
+
|
|
527
|
+
### 1. Unnecessary Re-renders (React)
|
|
528
|
+
|
|
529
|
+
**❌ Problem**: Component re-renders on every parent render
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
function Parent() {
|
|
533
|
+
const [count, setCount] = useState(0);
|
|
534
|
+
|
|
535
|
+
return (
|
|
536
|
+
<div>
|
|
537
|
+
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
|
|
538
|
+
<ExpensiveChild data={someData} /> {/* Re-renders every time! */}
|
|
539
|
+
</div>
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**✅ Solution**:
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
// Memoize the expensive component
|
|
548
|
+
const ExpensiveChild = React.memo(function ExpensiveChild({ data }) {
|
|
549
|
+
// Only re-renders when data changes
|
|
550
|
+
return <div>{/* expensive computation */}</div>;
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
// Or memoize the props
|
|
554
|
+
function Parent() {
|
|
555
|
+
const [count, setCount] = useState(0);
|
|
556
|
+
const memoizedData = useMemo(() => computeData(), []);
|
|
557
|
+
|
|
558
|
+
return (
|
|
559
|
+
<div>
|
|
560
|
+
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
|
|
561
|
+
<ExpensiveChild data={memoizedData} />
|
|
562
|
+
</div>
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
### 2. Large Bundle Size
|
|
570
|
+
|
|
571
|
+
**❌ Problem**: Importing entire library
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
// Imports all of lodash (~70KB)
|
|
575
|
+
import _ from 'lodash';
|
|
576
|
+
|
|
577
|
+
const unique = _.uniq(array);
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
**✅ Solution**:
|
|
581
|
+
|
|
582
|
+
```typescript
|
|
583
|
+
// Import only what you need
|
|
584
|
+
import uniq from 'lodash/uniq'; // ~2KB
|
|
585
|
+
|
|
586
|
+
const unique = uniq(array);
|
|
587
|
+
|
|
588
|
+
// Or use native methods
|
|
589
|
+
const unique = [...new Set(array)];
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
---
|
|
593
|
+
|
|
594
|
+
## 🔴 Security Bugs
|
|
595
|
+
|
|
596
|
+
### 1. XSS (Cross-Site Scripting)
|
|
597
|
+
|
|
598
|
+
**❌ Problem**: User input rendered as HTML
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
// Dangerous!
|
|
602
|
+
<div dangerouslySetInnerHTML={{ __html: userComment }} />
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
**✅ Solution**:
|
|
606
|
+
|
|
607
|
+
```typescript
|
|
608
|
+
// React escapes by default
|
|
609
|
+
<div>{userComment}</div>
|
|
610
|
+
|
|
611
|
+
// If HTML needed, sanitize first
|
|
612
|
+
import DOMPurify from 'dompurify';
|
|
613
|
+
|
|
614
|
+
<div dangerouslySetInnerHTML={{
|
|
615
|
+
__html: DOMPurify.sanitize(userComment)
|
|
616
|
+
}} />
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
### 2. Missing Rate Limiting
|
|
622
|
+
|
|
623
|
+
**❌ Problem**: API can be abused
|
|
624
|
+
|
|
625
|
+
```python
|
|
626
|
+
@app.post("/api/auth/login")
|
|
627
|
+
async def login(credentials: LoginRequest):
|
|
628
|
+
# No rate limiting - brute force possible!
|
|
629
|
+
...
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
**✅ Solution**:
|
|
633
|
+
|
|
634
|
+
```python
|
|
635
|
+
from slowapi import Limiter
|
|
636
|
+
from slowapi.util import get_remote_address
|
|
637
|
+
|
|
638
|
+
limiter = Limiter(key_func=get_remote_address)
|
|
639
|
+
|
|
640
|
+
@app.post("/api/auth/login")
|
|
641
|
+
@limiter.limit("5/minute") # Max 5 attempts per minute
|
|
642
|
+
async def login(request: Request, credentials: LoginRequest):
|
|
643
|
+
...
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
648
|
+
## 📊 Common Error Messages & Solutions
|
|
649
|
+
|
|
650
|
+
| Error | Likely Cause | Solution |
|
|
651
|
+
|-------|--------------|----------|
|
|
652
|
+
| `Cannot read property 'X' of undefined` | Accessing property before data loads | Add null check or optional chaining |
|
|
653
|
+
| `Maximum update depth exceeded` | Infinite re-render loop | Check useEffect dependencies |
|
|
654
|
+
| `Warning: Each child should have unique key` | Missing/duplicate keys in list | Use unique, stable IDs as keys |
|
|
655
|
+
| `401 Unauthorized` | Missing/invalid auth token | Check token in request headers |
|
|
656
|
+
| `403 Forbidden` | Insufficient permissions | Verify user role/permissions |
|
|
657
|
+
| `404 Not Found` | Wrong URL or resource doesn't exist | Check endpoint path and resource ID |
|
|
658
|
+
| `500 Internal Server Error` | Backend exception | Check server logs for stack trace |
|
|
659
|
+
| `CORS Error` | Cross-origin request blocked | Configure CORS middleware |
|
|
660
|
+
| `ERR_CONNECTION_REFUSED` | Server not running | Start the backend server |
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
## 🎯 Quick Debugging Commands
|
|
665
|
+
|
|
666
|
+
### Frontend
|
|
667
|
+
```bash
|
|
668
|
+
# Check for errors
|
|
669
|
+
npm run lint
|
|
670
|
+
|
|
671
|
+
# Run tests
|
|
672
|
+
npm test
|
|
673
|
+
|
|
674
|
+
# Build and check bundle size
|
|
675
|
+
npm run build
|
|
676
|
+
npm run analyze
|
|
677
|
+
|
|
678
|
+
# Check for unused dependencies
|
|
679
|
+
npx depcheck
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### Backend
|
|
683
|
+
```bash
|
|
684
|
+
# Check for security issues
|
|
685
|
+
pip install safety
|
|
686
|
+
safety check
|
|
687
|
+
|
|
688
|
+
# Run tests with coverage
|
|
689
|
+
pytest --cov=app
|
|
690
|
+
|
|
691
|
+
# Check for SQL injection
|
|
692
|
+
bandit -r app/
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### Mobile
|
|
696
|
+
```bash
|
|
697
|
+
# Check for issues
|
|
698
|
+
flutter analyze
|
|
699
|
+
|
|
700
|
+
# Run tests
|
|
701
|
+
flutter test
|
|
702
|
+
|
|
703
|
+
# Check app size
|
|
704
|
+
flutter build apk --analyze-size
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
## 🔍 When to Use Each Agent
|
|
710
|
+
|
|
711
|
+
| Bug Type | Best Agent | Reason |
|
|
712
|
+
|----------|-----------|---------|
|
|
713
|
+
| Frontend crash | debug-agent | Specializes in bug diagnosis |
|
|
714
|
+
| Backend API error | debug-agent | Can trace through stack |
|
|
715
|
+
| Complex multi-domain | workflow-guide | Coordinates multiple agents |
|
|
716
|
+
| Security vulnerability | qa-agent | Security expertise |
|
|
717
|
+
| Performance issue | qa-agent | Performance profiling tools |
|
|
718
|
+
| New feature needed | Specialist agent | Not a bug, it's a feature |
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
## 💡 Prevention Tips
|
|
723
|
+
|
|
724
|
+
1. **Write tests first** - Catch bugs before they ship
|
|
725
|
+
2. **Use TypeScript** - Catch type errors at compile time
|
|
726
|
+
3. **Enable strict mode** - More safety checks
|
|
727
|
+
4. **Code review** - Second pair of eyes
|
|
728
|
+
5. **Automated linting** - Enforce best practices
|
|
729
|
+
6. **Error monitoring** - Sentry, LogRocket, etc.
|
|
730
|
+
7. **User testing** - Real users find real bugs
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
**Remember**: The best bug is the one that never happens. Write defensive code, test thoroughly, and document lessons learned!
|