@nano-step/skill-manager 5.6.1 → 5.6.2
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/package.json +1 -1
- package/private-catalog.json +5 -0
- package/skills/deep-design/SKILL.md +402 -0
- package/skills/deep-design/evals/evals.json +23 -0
- package/skills/deep-design/skill.json +7 -0
- package/skills/feature-analysis/SKILL.md +290 -0
- package/skills/feature-analysis/skill.json +15 -0
- package/skills/nano-brain/skill.json +7 -0
- package/skills/pr-code-reviewer/CHANGELOG.md +287 -0
- package/skills/pr-code-reviewer/RESEARCH.md +60 -0
- package/skills/pr-code-reviewer/SKILL.md +530 -0
- package/skills/pr-code-reviewer/assets/config.json +47 -0
- package/skills/pr-code-reviewer/checklists/backend-express.md +357 -0
- package/skills/pr-code-reviewer/checklists/ci-cd.md +428 -0
- package/skills/pr-code-reviewer/checklists/consumer-search-matrix.md +339 -0
- package/skills/pr-code-reviewer/checklists/database.md +382 -0
- package/skills/pr-code-reviewer/checklists/frontend-vue-nuxt.md +426 -0
- package/skills/pr-code-reviewer/checklists/review-checklist.md +116 -0
- package/skills/pr-code-reviewer/references/framework-rules/express.md +39 -0
- package/skills/pr-code-reviewer/references/framework-rules/nestjs.md +41 -0
- package/skills/pr-code-reviewer/references/framework-rules/typeorm.md +52 -0
- package/skills/pr-code-reviewer/references/framework-rules/typescript.md +50 -0
- package/skills/pr-code-reviewer/references/framework-rules/vue-nuxt.md +53 -0
- package/skills/pr-code-reviewer/references/nano-brain-integration.md +61 -0
- package/skills/pr-code-reviewer/references/performance-patterns.md +26 -0
- package/skills/pr-code-reviewer/references/quality-patterns.md +25 -0
- package/skills/pr-code-reviewer/references/report-template.md +167 -0
- package/skills/pr-code-reviewer/references/security-patterns.md +31 -0
- package/skills/pr-code-reviewer/references/subagent-prompts.md +323 -0
- package/skills/pr-code-reviewer/skill.json +15 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
# Frontend Vue/Nuxt Checklist
|
|
2
|
+
|
|
3
|
+
Comprehensive review checklist for Vue 3/Nuxt 3 frontend PRs (tradeit, tradeit-admin, audit-dashboard-frontend, etc.)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. State Management
|
|
8
|
+
|
|
9
|
+
### CRITICAL - Must Check
|
|
10
|
+
|
|
11
|
+
| Check | Pattern | Why |
|
|
12
|
+
|-------|---------|-----|
|
|
13
|
+
| Vuex vs Pinia correct | `rootState.x` vs `useXStore()` | Vuex can't access Pinia |
|
|
14
|
+
| Store registered | Module in `store/index.js` | Undefined state |
|
|
15
|
+
| Computed get/set pattern | `computed({ get, set })` | Two-way binding |
|
|
16
|
+
| storeToRefs for destructuring | `storeToRefs(store)` | Maintains reactivity |
|
|
17
|
+
|
|
18
|
+
### Detection Patterns
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
// CRITICAL: Vuex accessing Pinia store (will be undefined)
|
|
22
|
+
// In Vuex action:
|
|
23
|
+
const rate = rootState.currency.selectedRate // currency is Pinia!
|
|
24
|
+
|
|
25
|
+
// SECURE: Use Pinia directly
|
|
26
|
+
import { useCurrencyStore } from '~/store/useCurrencyStore'
|
|
27
|
+
const currencyStore = useCurrencyStore()
|
|
28
|
+
const rate = currencyStore.selectedRate
|
|
29
|
+
|
|
30
|
+
// CRITICAL: Store not registered
|
|
31
|
+
// store/index.js missing:
|
|
32
|
+
modules: {
|
|
33
|
+
// currency: currencyModule // MISSING!
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// WARNING: Destructuring loses reactivity
|
|
37
|
+
const { items, loading } = useInventoryStore() // Not reactive!
|
|
38
|
+
|
|
39
|
+
// SECURE: Use storeToRefs
|
|
40
|
+
const store = useInventoryStore()
|
|
41
|
+
const { items, loading } = storeToRefs(store)
|
|
42
|
+
|
|
43
|
+
// CRITICAL: Direct mutation (Vuex)
|
|
44
|
+
state.items.push(newItem) // Mutation outside mutation handler!
|
|
45
|
+
|
|
46
|
+
// SECURE: Use mutation
|
|
47
|
+
commit('ADD_ITEM', newItem)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 2. Reactivity
|
|
53
|
+
|
|
54
|
+
### CRITICAL - Must Check
|
|
55
|
+
|
|
56
|
+
| Check | Pattern | Why |
|
|
57
|
+
|-------|---------|-----|
|
|
58
|
+
| ref() for primitives | `ref(0)` not `reactive(0)` | reactive() doesn't work on primitives |
|
|
59
|
+
| .value in script | `count.value++` | Required for refs in script |
|
|
60
|
+
| toRefs for destructuring | `toRefs(props)` | Maintains reactivity |
|
|
61
|
+
| No direct prop mutation | `emit('update:x', newVal)` | One-way data flow |
|
|
62
|
+
|
|
63
|
+
### Detection Patterns
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
// WARNING: reactive() on primitive
|
|
67
|
+
const count = reactive(0) // Won't work!
|
|
68
|
+
|
|
69
|
+
// SECURE: ref() for primitives
|
|
70
|
+
const count = ref(0)
|
|
71
|
+
|
|
72
|
+
// CRITICAL: Missing .value in script
|
|
73
|
+
const count = ref(0)
|
|
74
|
+
count++ // Wrong! count is a ref object
|
|
75
|
+
|
|
76
|
+
// SECURE: Use .value
|
|
77
|
+
count.value++
|
|
78
|
+
|
|
79
|
+
// CRITICAL: Mutating prop directly
|
|
80
|
+
props.items.push(newItem) // Mutating parent state!
|
|
81
|
+
|
|
82
|
+
// SECURE: Emit event
|
|
83
|
+
emit('add-item', newItem)
|
|
84
|
+
|
|
85
|
+
// WARNING: Destructuring props loses reactivity
|
|
86
|
+
const { modelValue } = defineProps(['modelValue'])
|
|
87
|
+
watch(modelValue, ...) // Won't trigger!
|
|
88
|
+
|
|
89
|
+
// SECURE: Use toRefs or computed
|
|
90
|
+
const props = defineProps(['modelValue'])
|
|
91
|
+
watch(() => props.modelValue, ...)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 3. Network Calls
|
|
97
|
+
|
|
98
|
+
### CRITICAL - Must Check
|
|
99
|
+
|
|
100
|
+
| Check | Pattern | Why |
|
|
101
|
+
|-------|---------|-----|
|
|
102
|
+
| Use getAxiosInstance() | Not raw axios | Includes auth, base URL |
|
|
103
|
+
| Error handling | try/catch or .catch() | User feedback |
|
|
104
|
+
| Loading state managed | `loading.value = true/false` | UX |
|
|
105
|
+
| Response fields validated | Check before access | Defensive coding |
|
|
106
|
+
|
|
107
|
+
### Detection Patterns
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
// WARNING: Raw axios
|
|
111
|
+
import axios from 'axios'
|
|
112
|
+
const { data } = await axios.get('/api/user')
|
|
113
|
+
|
|
114
|
+
// SECURE: Use instance
|
|
115
|
+
import { getAxiosInstance } from '~/network/axiosInstance'
|
|
116
|
+
const axios = getAxiosInstance()
|
|
117
|
+
const { data } = await axios.get('/api/user')
|
|
118
|
+
|
|
119
|
+
// CRITICAL: No error handling
|
|
120
|
+
const fetchData = async () => {
|
|
121
|
+
const { data } = await axios.get('/api/items')
|
|
122
|
+
items.value = data.items
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// SECURE: With error handling
|
|
126
|
+
const fetchData = async () => {
|
|
127
|
+
try {
|
|
128
|
+
loading.value = true
|
|
129
|
+
const { data } = await axios.get('/api/items')
|
|
130
|
+
items.value = data.items
|
|
131
|
+
} catch (e) {
|
|
132
|
+
error.value = e.message
|
|
133
|
+
} finally {
|
|
134
|
+
loading.value = false
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// CRITICAL: Assuming response field exists (PR #1101 issue!)
|
|
139
|
+
const imgUrl = item.imgURL // undefined if backend changed!
|
|
140
|
+
|
|
141
|
+
// SECURE: Defensive access
|
|
142
|
+
const imgUrl = item.imgURL ?? getFallbackImage(item.groupId)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 4. Component Patterns
|
|
148
|
+
|
|
149
|
+
### CRITICAL - Must Check
|
|
150
|
+
|
|
151
|
+
| Check | Pattern | Why |
|
|
152
|
+
|-------|---------|-----|
|
|
153
|
+
| Props typed | `defineProps<{ x: string }>()` | Type safety |
|
|
154
|
+
| Emits declared | `defineEmits(['update:x'])` | Documentation |
|
|
155
|
+
| v-if/v-for not on same element | Use template wrapper | Vue limitation |
|
|
156
|
+
| Key on v-for | `:key="item.id"` | Efficient updates |
|
|
157
|
+
|
|
158
|
+
### Detection Patterns
|
|
159
|
+
|
|
160
|
+
```vue
|
|
161
|
+
<!-- CRITICAL: v-if and v-for on same element -->
|
|
162
|
+
<div v-for="item in items" v-if="item.active"> <!-- Wrong! -->
|
|
163
|
+
|
|
164
|
+
<!-- SECURE: Use template -->
|
|
165
|
+
<template v-for="item in items" :key="item.id">
|
|
166
|
+
<div v-if="item.active">...</div>
|
|
167
|
+
</template>
|
|
168
|
+
|
|
169
|
+
<!-- WARNING: Missing key -->
|
|
170
|
+
<div v-for="item in items"> <!-- No key! -->
|
|
171
|
+
|
|
172
|
+
<!-- SECURE: With key -->
|
|
173
|
+
<div v-for="item in items" :key="item.id">
|
|
174
|
+
|
|
175
|
+
<!-- WARNING: Untyped props -->
|
|
176
|
+
const props = defineProps(['modelValue', 'items'])
|
|
177
|
+
|
|
178
|
+
<!-- SECURE: Typed props -->
|
|
179
|
+
interface Props {
|
|
180
|
+
modelValue: string
|
|
181
|
+
items: Item[]
|
|
182
|
+
}
|
|
183
|
+
const props = defineProps<Props>()
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 5. Composables
|
|
189
|
+
|
|
190
|
+
### CRITICAL - Must Check
|
|
191
|
+
|
|
192
|
+
| Check | Pattern | Why |
|
|
193
|
+
|-------|---------|-----|
|
|
194
|
+
| Returns reactive refs | `return { items, loading }` | Reactivity preserved |
|
|
195
|
+
| No `this` usage | Composables don't have `this` | Runtime error |
|
|
196
|
+
| Cleanup on unmount | `onUnmounted(() => ...)` | Memory leaks |
|
|
197
|
+
| Named exports | `export function useX()` | Tree shaking |
|
|
198
|
+
|
|
199
|
+
### Detection Patterns
|
|
200
|
+
|
|
201
|
+
```javascript
|
|
202
|
+
// CRITICAL: Using this in composable
|
|
203
|
+
export function useInventory() {
|
|
204
|
+
this.items = [] // TypeError! No 'this' in composables
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// SECURE: Use refs
|
|
208
|
+
export function useInventory() {
|
|
209
|
+
const items = ref([])
|
|
210
|
+
return { items }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// WARNING: Not returning reactive
|
|
214
|
+
export function useCounter() {
|
|
215
|
+
let count = 0 // Not reactive!
|
|
216
|
+
return { count }
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// SECURE: Return refs
|
|
220
|
+
export function useCounter() {
|
|
221
|
+
const count = ref(0)
|
|
222
|
+
return { count }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// WARNING: No cleanup
|
|
226
|
+
export function useWebSocket() {
|
|
227
|
+
const ws = new WebSocket(url)
|
|
228
|
+
// Never closed!
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// SECURE: Cleanup on unmount
|
|
232
|
+
export function useWebSocket() {
|
|
233
|
+
const ws = new WebSocket(url)
|
|
234
|
+
onUnmounted(() => ws.close())
|
|
235
|
+
return { ws }
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 6. Nuxt-Specific
|
|
242
|
+
|
|
243
|
+
### CRITICAL - Must Check
|
|
244
|
+
|
|
245
|
+
| Check | Pattern | Why |
|
|
246
|
+
|-------|---------|-----|
|
|
247
|
+
| Client-only code guarded | `import.meta.client` | SSR errors |
|
|
248
|
+
| Auto-imports from composables/ | Not utils/ | Only composables auto-imported |
|
|
249
|
+
| useFetch for SSR data | Not axios in setup | SSR hydration |
|
|
250
|
+
| definePageMeta for layouts | `definePageMeta({ layout: 'x' })` | Nuxt convention |
|
|
251
|
+
|
|
252
|
+
### Detection Patterns
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
// CRITICAL: Browser API in SSR
|
|
256
|
+
const width = window.innerWidth // ReferenceError on server!
|
|
257
|
+
|
|
258
|
+
// SECURE: Guard with import.meta.client
|
|
259
|
+
const width = import.meta.client ? window.innerWidth : 0
|
|
260
|
+
|
|
261
|
+
// Or use onMounted
|
|
262
|
+
onMounted(() => {
|
|
263
|
+
width.value = window.innerWidth
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
// CRITICAL: Using function from utils/ without import
|
|
267
|
+
// In component:
|
|
268
|
+
stripImageSizeFromUrl(url) // undefined! utils/ not auto-imported
|
|
269
|
+
|
|
270
|
+
// SECURE: Import explicitly
|
|
271
|
+
import { stripImageSizeFromUrl } from '~/utils/helpers'
|
|
272
|
+
|
|
273
|
+
// WARNING: axios in setup (SSR issues)
|
|
274
|
+
const { data } = await axios.get('/api/items')
|
|
275
|
+
|
|
276
|
+
// SECURE: useFetch for SSR
|
|
277
|
+
const { data } = await useFetch('/api/items')
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## 7. i18n / Localization
|
|
283
|
+
|
|
284
|
+
### WARNING - Should Check
|
|
285
|
+
|
|
286
|
+
| Check | Pattern | Why |
|
|
287
|
+
|-------|---------|-----|
|
|
288
|
+
| No hardcoded strings | Use `$t('key')` | Localization |
|
|
289
|
+
| Keys exist in locale files | Check en.js | Runtime errors |
|
|
290
|
+
| Interpolation correct | `$t('key', { name })` | Dynamic content |
|
|
291
|
+
|
|
292
|
+
### Detection Patterns
|
|
293
|
+
|
|
294
|
+
```vue
|
|
295
|
+
<!-- WARNING: Hardcoded string -->
|
|
296
|
+
<button>Submit</button>
|
|
297
|
+
|
|
298
|
+
<!-- SECURE: Use i18n -->
|
|
299
|
+
<button>{{ $t('common.submit') }}</button>
|
|
300
|
+
|
|
301
|
+
<!-- WARNING: Missing interpolation -->
|
|
302
|
+
<p>{{ $t('welcome') }}</p> <!-- "Welcome, {name}" shows literally -->
|
|
303
|
+
|
|
304
|
+
<!-- SECURE: With interpolation -->
|
|
305
|
+
<p>{{ $t('welcome', { name: user.name }) }}</p>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## 8. Images & CDN
|
|
311
|
+
|
|
312
|
+
### CRITICAL - Must Check
|
|
313
|
+
|
|
314
|
+
| Check | Pattern | Why |
|
|
315
|
+
|-------|---------|-----|
|
|
316
|
+
| Use useCDNImage() | Not raw URLs | CDN optimization |
|
|
317
|
+
| ImageWithFallback component | For item images | Handles missing images |
|
|
318
|
+
| Lazy loading | `loading="lazy"` | Performance |
|
|
319
|
+
|
|
320
|
+
### Detection Patterns
|
|
321
|
+
|
|
322
|
+
```vue
|
|
323
|
+
<!-- WARNING: Raw image URL -->
|
|
324
|
+
<img :src="item.imgURL"> <!-- No fallback if undefined! -->
|
|
325
|
+
|
|
326
|
+
<!-- SECURE: Use ImageWithFallback -->
|
|
327
|
+
<ImageWithFallback :src="item.imgURL" :fallback="getFallback(item)" />
|
|
328
|
+
|
|
329
|
+
<!-- WARNING: No lazy loading -->
|
|
330
|
+
<img :src="url">
|
|
331
|
+
|
|
332
|
+
<!-- SECURE: Lazy load -->
|
|
333
|
+
<img :src="url" loading="lazy">
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## 9. Breaking Change Detection
|
|
339
|
+
|
|
340
|
+
### Auto-Flag These Changes
|
|
341
|
+
|
|
342
|
+
| Signal | Severity | Action |
|
|
343
|
+
|--------|----------|--------|
|
|
344
|
+
| Store module removed | CRITICAL | Search all consumers |
|
|
345
|
+
| Composable return value changed | CRITICAL | Search all usages |
|
|
346
|
+
| Network function signature changed | CRITICAL | Search all callers |
|
|
347
|
+
| Component prop removed | WARNING | Search all usages |
|
|
348
|
+
| Emit event renamed | WARNING | Search parent listeners |
|
|
349
|
+
|
|
350
|
+
### Consumer Search Required
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
# For store changes
|
|
354
|
+
grep -rn "useXStore" ./
|
|
355
|
+
grep -rn "rootState.x" ./
|
|
356
|
+
|
|
357
|
+
# For composable changes
|
|
358
|
+
grep -rn "useComposable" ./
|
|
359
|
+
|
|
360
|
+
# For component changes
|
|
361
|
+
grep -rn "<ComponentName" ./
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## 10. Performance Checks
|
|
367
|
+
|
|
368
|
+
### WARNING - Should Check
|
|
369
|
+
|
|
370
|
+
| Check | Pattern | Why |
|
|
371
|
+
|-------|---------|-----|
|
|
372
|
+
| Large lists virtualized | `vue-virtual-scroller` | Memory |
|
|
373
|
+
| Computed for derived state | Not methods | Caching |
|
|
374
|
+
| v-once for static content | `<div v-once>` | Skip re-renders |
|
|
375
|
+
| Async components for heavy | `defineAsyncComponent` | Code splitting |
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Quick Checklist
|
|
380
|
+
|
|
381
|
+
Copy this for PR reviews:
|
|
382
|
+
|
|
383
|
+
```markdown
|
|
384
|
+
## Vue/Nuxt Frontend Review
|
|
385
|
+
|
|
386
|
+
### State Management
|
|
387
|
+
- [ ] Vuex/Pinia used correctly (not mixed)
|
|
388
|
+
- [ ] Store modules registered
|
|
389
|
+
- [ ] storeToRefs used for destructuring
|
|
390
|
+
- [ ] No direct state mutation
|
|
391
|
+
|
|
392
|
+
### Reactivity
|
|
393
|
+
- [ ] ref() for primitives, reactive() for objects
|
|
394
|
+
- [ ] .value used in script
|
|
395
|
+
- [ ] Props not mutated directly
|
|
396
|
+
|
|
397
|
+
### Network
|
|
398
|
+
- [ ] getAxiosInstance() used (not raw axios)
|
|
399
|
+
- [ ] Error handling present
|
|
400
|
+
- [ ] Loading states managed
|
|
401
|
+
- [ ] Response fields validated (defensive)
|
|
402
|
+
|
|
403
|
+
### Components
|
|
404
|
+
- [ ] Props typed with TypeScript
|
|
405
|
+
- [ ] Emits declared
|
|
406
|
+
- [ ] v-if/v-for not on same element
|
|
407
|
+
- [ ] Keys on v-for
|
|
408
|
+
|
|
409
|
+
### Nuxt-Specific
|
|
410
|
+
- [ ] Client-only code guarded (import.meta.client)
|
|
411
|
+
- [ ] Auto-imports only from composables/
|
|
412
|
+
- [ ] useFetch for SSR data
|
|
413
|
+
|
|
414
|
+
### i18n
|
|
415
|
+
- [ ] No hardcoded user-facing strings
|
|
416
|
+
- [ ] $t() keys exist in locale files
|
|
417
|
+
|
|
418
|
+
### Images
|
|
419
|
+
- [ ] useCDNImage() or ImageWithFallback used
|
|
420
|
+
- [ ] Fallbacks for missing images
|
|
421
|
+
|
|
422
|
+
### Breaking Changes
|
|
423
|
+
- [ ] No store modules removed without consumer check
|
|
424
|
+
- [ ] No composable signatures changed without search
|
|
425
|
+
- [ ] No component props removed without search
|
|
426
|
+
```
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# PR Review Checklist
|
|
2
|
+
|
|
3
|
+
Use this checklist for every PR review. Check off each item as you complete it.
|
|
4
|
+
|
|
5
|
+
## Resume Detection (Phase -1)
|
|
6
|
+
|
|
7
|
+
- [ ] Look for existing checkpoint: `find /tmp -maxdepth 1 -type d -name "pr-review-${repo}-${pr_number}-*"`
|
|
8
|
+
- [ ] If found: read `manifest.json` from `.checkpoints/`
|
|
9
|
+
- [ ] Validate checkpoint: check PR number and head SHA match
|
|
10
|
+
- [ ] Ask user: "Resume from phase {next_phase}? (y/n)"
|
|
11
|
+
- [ ] If yes: load manifest + phase files, jump to `next_phase`
|
|
12
|
+
- [ ] If no or invalid: delete old directory, start fresh from Phase 0
|
|
13
|
+
|
|
14
|
+
## Pre-Review (Phase 0)
|
|
15
|
+
|
|
16
|
+
- [ ] Extract repo info: `owner/repo`, `pr_number`, `head_branch`
|
|
17
|
+
- [ ] Create unique temp dir: `/tmp/pr-review-{repo}-{pr}-{timestamp}`
|
|
18
|
+
- [ ] Clone repo to temp dir (shallow clone with `--depth=50`)
|
|
19
|
+
- [ ] Verify correct branch is checked out (`git log --oneline -1`)
|
|
20
|
+
- [ ] Record `$REVIEW_DIR` path for all subsequent phases
|
|
21
|
+
- [ ] Print confirmation with path and branch name
|
|
22
|
+
- [ ] Save checkpoint: `.checkpoints/phase-0-clone.json`
|
|
23
|
+
- [ ] Update manifest: `completed_phase: 0`, `next_phase: 1`
|
|
24
|
+
|
|
25
|
+
## Context Gathering (Phase 1)
|
|
26
|
+
|
|
27
|
+
- [ ] Get PR metadata: title, description, author, base branch
|
|
28
|
+
- [ ] Get changed files with diff
|
|
29
|
+
- [ ] Read full file context from `$REVIEW_DIR` (not workspace repo)
|
|
30
|
+
- [ ] Classify each file: LOGIC / STYLE / REFACTOR / NEW
|
|
31
|
+
- [ ] Query nano-brain for past context on changed modules
|
|
32
|
+
- [ ] Save checkpoint: `.checkpoints/phase-1-context.json`
|
|
33
|
+
- [ ] Update manifest: `completed_phase: 1`, `next_phase: 1.5`
|
|
34
|
+
|
|
35
|
+
## Linear Ticket Context (Phase 1.5)
|
|
36
|
+
|
|
37
|
+
- [ ] Extract ticket ID from branch name, PR description, or PR title
|
|
38
|
+
- [ ] If found: `linear_get_issue(id)` → fetch ticket details
|
|
39
|
+
- [ ] If found: `linear_list_comments(issueId)` → fetch discussion
|
|
40
|
+
- [ ] Extract acceptance criteria from ticket description
|
|
41
|
+
- [ ] If images in description: `linear_extract_images(description)`
|
|
42
|
+
- [ ] If not found: skip silently, continue without ticket context
|
|
43
|
+
- [ ] Save checkpoint: `.checkpoints/phase-1.5-linear.json`
|
|
44
|
+
- [ ] Update manifest: `completed_phase: 1.5`, `next_phase: 2`
|
|
45
|
+
|
|
46
|
+
## Smart Tracing (Phase 2)
|
|
47
|
+
|
|
48
|
+
- [ ] LOGIC changes: trace callers, callees, tests, types, data flow
|
|
49
|
+
- [ ] STYLE changes: verify no hidden logic changes
|
|
50
|
+
- [ ] REFACTOR changes: verify behavior preservation
|
|
51
|
+
- [ ] Query nano-brain for known issues on changed functions
|
|
52
|
+
- [ ] Save checkpoint: `.checkpoints/phase-2-tracing.json`
|
|
53
|
+
- [ ] Update manifest: `completed_phase: 2`, `next_phase: 2.5`
|
|
54
|
+
|
|
55
|
+
## PR Summary (Phase 2.5)
|
|
56
|
+
|
|
57
|
+
- [ ] Write "What This PR Does" (1-3 sentences)
|
|
58
|
+
- [ ] Categorize key changes (Feature/Bugfix/Refactor/etc.)
|
|
59
|
+
- [ ] File-by-file summary with line numbers
|
|
60
|
+
- [ ] Save checkpoint: `.checkpoints/phase-2.5-summary.json`
|
|
61
|
+
- [ ] Update manifest: `completed_phase: 2.5`, `next_phase: 3`
|
|
62
|
+
|
|
63
|
+
## Subagent Execution (Phase 3)
|
|
64
|
+
|
|
65
|
+
- [ ] Include `$REVIEW_DIR` path in ALL subagent prompts
|
|
66
|
+
- [ ] Launch Code Quality agent (explore)
|
|
67
|
+
- [ ] After Code Quality completes: update `.checkpoints/phase-3-subagents.json` + manifest subagent_status
|
|
68
|
+
- [ ] Launch Security & Logic agent (oracle)
|
|
69
|
+
- [ ] After Security & Logic completes: update `.checkpoints/phase-3-subagents.json` + manifest subagent_status
|
|
70
|
+
- [ ] Launch Docs & Best Practices agent (librarian)
|
|
71
|
+
- [ ] After Docs & Best Practices completes: update `.checkpoints/phase-3-subagents.json` + manifest subagent_status
|
|
72
|
+
- [ ] Launch Tests & Integration agent (general/quick)
|
|
73
|
+
- [ ] After Tests & Integration completes: update `.checkpoints/phase-3-subagents.json` + manifest subagent_status
|
|
74
|
+
- [ ] Collect ALL results (especially Oracle — never skip)
|
|
75
|
+
- [ ] Update manifest: `completed_phase: 3`, `next_phase: 4`
|
|
76
|
+
|
|
77
|
+
## Refinement (Phase 4)
|
|
78
|
+
|
|
79
|
+
- [ ] Merge and deduplicate findings across agents
|
|
80
|
+
- [ ] Apply severity filter (critical/warning keep, suggestion count-only)
|
|
81
|
+
- [ ] Gap analysis — any subagent fail? Unreviewed files?
|
|
82
|
+
- [ ] Second pass on gaps if needed
|
|
83
|
+
- [ ] Save checkpoint: `.checkpoints/phase-4-refined.json`
|
|
84
|
+
- [ ] Update manifest: `completed_phase: 4`, `next_phase: 5`
|
|
85
|
+
|
|
86
|
+
## Report (Phase 5)
|
|
87
|
+
|
|
88
|
+
- [ ] Save to `.opencode/reviews/{type}_{identifier}_{date}.md`
|
|
89
|
+
- [ ] TL;DR with verdict and counts
|
|
90
|
+
- [ ] Critical issues with full detail
|
|
91
|
+
- [ ] Warnings with full detail
|
|
92
|
+
- [ ] Improvements as one-liners
|
|
93
|
+
- [ ] File summary table
|
|
94
|
+
- [ ] Save checkpoint: `.checkpoints/phase-5-report.md`
|
|
95
|
+
- [ ] Update manifest: `completed_phase: 5`, `next_phase: 5.5`
|
|
96
|
+
|
|
97
|
+
## Save to Memory (Phase 5.5)
|
|
98
|
+
|
|
99
|
+
- [ ] Write key findings to nano-brain with tags: review, {repo}
|
|
100
|
+
- [ ] Verify searchable (`npx nano-brain search "PR {number}"`)
|
|
101
|
+
- [ ] Update manifest: `completed_phase: 5.5`, `next_phase: 6`
|
|
102
|
+
|
|
103
|
+
## Cleanup (Phase 6)
|
|
104
|
+
|
|
105
|
+
- [ ] Show temp folder path and size to user
|
|
106
|
+
- [ ] **ASK user** before removing (NEVER auto-delete)
|
|
107
|
+
- [ ] If user confirms → `rm -rf "$REVIEW_DIR"` (also removes `.checkpoints/`)
|
|
108
|
+
- [ ] If user declines → remind them to clean up later
|
|
109
|
+
- [ ] For multiple PRs: ask about each temp folder individually
|
|
110
|
+
- [ ] Note: Checkpoints auto-removed with clone dir (PR reviews) or remain in `.checkpoints/` (local reviews)
|
|
111
|
+
|
|
112
|
+
## Final Notification
|
|
113
|
+
|
|
114
|
+
- [ ] Report path shown to user
|
|
115
|
+
- [ ] Issue counts (critical/warning/suggestion) displayed
|
|
116
|
+
- [ ] Temp folder cleanup status communicated
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Express Code Review Rules
|
|
2
|
+
|
|
3
|
+
## Critical Rules
|
|
4
|
+
- Unhandled async errors in route handlers → use express-async-handler or try-catch
|
|
5
|
+
- SQL injection in raw queries → use parameterized queries
|
|
6
|
+
- Missing authentication middleware on protected routes
|
|
7
|
+
- Exposing sensitive data in error responses
|
|
8
|
+
|
|
9
|
+
## Warning Rules
|
|
10
|
+
- Missing input validation → use express-validator or joi
|
|
11
|
+
- Exposing stack traces in production → check NODE_ENV
|
|
12
|
+
- Missing rate limiting on public endpoints
|
|
13
|
+
- No request body size limit → use express.json({ limit: '10kb' })
|
|
14
|
+
|
|
15
|
+
## Suggestions
|
|
16
|
+
- Use helmet for security headers
|
|
17
|
+
- Use compression middleware
|
|
18
|
+
- Centralize error handling middleware
|
|
19
|
+
- Use router.param() for parameter validation
|
|
20
|
+
|
|
21
|
+
## Detection Patterns
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
// CRITICAL: Unhandled async
|
|
25
|
+
app.get('/api', async (req, res) => {
|
|
26
|
+
const data = await fetchData() // if throws, crashes server
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// SECURE: Wrapped async
|
|
30
|
+
app.get('/api', asyncHandler(async (req, res) => {
|
|
31
|
+
const data = await fetchData()
|
|
32
|
+
}))
|
|
33
|
+
|
|
34
|
+
// CRITICAL: SQL injection
|
|
35
|
+
db.query(`SELECT * FROM users WHERE id = ${req.params.id}`)
|
|
36
|
+
|
|
37
|
+
// SECURE: Parameterized
|
|
38
|
+
db.query('SELECT * FROM users WHERE id = ?', [req.params.id])
|
|
39
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# NestJS Code Review Rules
|
|
2
|
+
|
|
3
|
+
## Critical Rules
|
|
4
|
+
- Missing `@Injectable()` decorator on service class
|
|
5
|
+
- Circular dependency without `forwardRef()`
|
|
6
|
+
- Missing guards on sensitive endpoints → use `@UseGuards()`
|
|
7
|
+
- Raw SQL without parameterization in repositories
|
|
8
|
+
|
|
9
|
+
## Warning Rules
|
|
10
|
+
- Missing DTO validation → use `class-validator` decorators
|
|
11
|
+
- Missing `@ApiTags()` / `@ApiOperation()` for Swagger docs
|
|
12
|
+
- Injecting repository directly in controller → use service layer
|
|
13
|
+
- Missing `@Transactional()` on multi-step database operations
|
|
14
|
+
|
|
15
|
+
## Suggestions
|
|
16
|
+
- Use `ConfigService` instead of `process.env` directly
|
|
17
|
+
- Use custom exceptions extending `HttpException`
|
|
18
|
+
- Use interceptors for response transformation
|
|
19
|
+
- Use pipes for input transformation
|
|
20
|
+
|
|
21
|
+
## Detection Patterns
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// CRITICAL: Missing Injectable
|
|
25
|
+
class UserService { // should be @Injectable()
|
|
26
|
+
constructor(private repo: UserRepository) {}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// WARNING: Missing validation
|
|
30
|
+
@Post()
|
|
31
|
+
create(@Body() dto: CreateUserDto) {} // dto needs class-validator decorators
|
|
32
|
+
|
|
33
|
+
// CRITICAL: Circular dependency
|
|
34
|
+
@Injectable()
|
|
35
|
+
export class ServiceA {
|
|
36
|
+
constructor(private serviceB: ServiceB) {} // if B also injects A
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// SECURE: Forward ref
|
|
40
|
+
constructor(@Inject(forwardRef(() => ServiceB)) private serviceB: ServiceB) {}
|
|
41
|
+
```
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# TypeORM Code Review Rules
|
|
2
|
+
|
|
3
|
+
## Critical Rules
|
|
4
|
+
- Raw SQL with string interpolation → SQL injection risk
|
|
5
|
+
- Missing `@Transaction()` on multi-entity operations
|
|
6
|
+
- Forgetting to `await` repository methods → silent failures
|
|
7
|
+
- Using `save()` in loops → N+1 writes, use `save([entities])`
|
|
8
|
+
|
|
9
|
+
## Warning Rules
|
|
10
|
+
- N+1 query pattern → use `relations` option or `leftJoinAndSelect`
|
|
11
|
+
- Missing indexes on frequently queried columns
|
|
12
|
+
- Using `find()` without `take` limit on large tables
|
|
13
|
+
- Eager loading too many relations → performance hit
|
|
14
|
+
|
|
15
|
+
## Suggestions
|
|
16
|
+
- Use QueryBuilder for complex queries
|
|
17
|
+
- Use `@Index()` decorator for query optimization
|
|
18
|
+
- Use `createQueryBuilder().insert()` for bulk inserts
|
|
19
|
+
- Consider `@DeleteDateColumn()` for soft deletes
|
|
20
|
+
|
|
21
|
+
## Detection Patterns
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// CRITICAL: SQL injection
|
|
25
|
+
repository.query(`SELECT * FROM users WHERE name = '${name}'`)
|
|
26
|
+
|
|
27
|
+
// SECURE: Parameterized
|
|
28
|
+
repository.query('SELECT * FROM users WHERE name = ?', [name])
|
|
29
|
+
|
|
30
|
+
// CRITICAL: N+1 in loop
|
|
31
|
+
for (const user of users) {
|
|
32
|
+
await userRepo.save(user) // N writes
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// SECURE: Batch save
|
|
36
|
+
await userRepo.save(users) // 1 write
|
|
37
|
+
|
|
38
|
+
// WARNING: N+1 query
|
|
39
|
+
const users = await userRepo.find()
|
|
40
|
+
for (const user of users) {
|
|
41
|
+
const posts = await postRepo.find({ where: { userId: user.id } })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// SECURE: Eager load
|
|
45
|
+
const users = await userRepo.find({ relations: ['posts'] })
|
|
46
|
+
|
|
47
|
+
// WARNING: Missing limit
|
|
48
|
+
await userRepo.find({ where: { status: 'active' } }) // could return millions
|
|
49
|
+
|
|
50
|
+
// SECURE: With limit
|
|
51
|
+
await userRepo.find({ where: { status: 'active' }, take: 100 })
|
|
52
|
+
```
|