mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-31 06:03:29 +08:00
Compare commits
15 Commits
fix/stop-h
...
fix/harnes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
142600a2fc | ||
|
|
0124f6f6dc | ||
|
|
0cf7738720 | ||
|
|
fbd91aeef4 | ||
|
|
d20378cfba | ||
|
|
cff28efb34 | ||
|
|
b575f2e3eb | ||
|
|
0f065af311 | ||
|
|
1b4597a3d6 | ||
|
|
ded5d826a4 | ||
|
|
ae272da28d | ||
|
|
c39aa22c5a | ||
|
|
7483d646e4 | ||
|
|
432a45274e | ||
|
|
866d9ebb53 |
@@ -23,7 +23,7 @@ Backend architecture patterns and best practices for scalable server-side applic
|
|||||||
### RESTful API Structure
|
### RESTful API Structure
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ Resource-based URLs
|
// PASS: Resource-based URLs
|
||||||
GET /api/markets # List resources
|
GET /api/markets # List resources
|
||||||
GET /api/markets/:id # Get single resource
|
GET /api/markets/:id # Get single resource
|
||||||
POST /api/markets # Create resource
|
POST /api/markets # Create resource
|
||||||
@@ -31,7 +31,7 @@ PUT /api/markets/:id # Replace resource
|
|||||||
PATCH /api/markets/:id # Update resource
|
PATCH /api/markets/:id # Update resource
|
||||||
DELETE /api/markets/:id # Delete resource
|
DELETE /api/markets/:id # Delete resource
|
||||||
|
|
||||||
// ✅ Query parameters for filtering, sorting, pagination
|
// PASS: Query parameters for filtering, sorting, pagination
|
||||||
GET /api/markets?status=active&sort=volume&limit=20&offset=0
|
GET /api/markets?status=active&sort=volume&limit=20&offset=0
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ export default withAuth(async (req, res) => {
|
|||||||
### Query Optimization
|
### Query Optimization
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Select only needed columns
|
// PASS: GOOD: Select only needed columns
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('id, name, status, volume')
|
.select('id, name, status, volume')
|
||||||
@@ -139,7 +139,7 @@ const { data } = await supabase
|
|||||||
.order('volume', { ascending: false })
|
.order('volume', { ascending: false })
|
||||||
.limit(10)
|
.limit(10)
|
||||||
|
|
||||||
// ❌ BAD: Select everything
|
// FAIL: BAD: Select everything
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('*')
|
.select('*')
|
||||||
@@ -148,13 +148,13 @@ const { data } = await supabase
|
|||||||
### N+1 Query Prevention
|
### N+1 Query Prevention
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: N+1 query problem
|
// FAIL: BAD: N+1 query problem
|
||||||
const markets = await getMarkets()
|
const markets = await getMarkets()
|
||||||
for (const market of markets) {
|
for (const market of markets) {
|
||||||
market.creator = await getUser(market.creator_id) // N queries
|
market.creator = await getUser(market.creator_id) // N queries
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ GOOD: Batch fetch
|
// PASS: GOOD: Batch fetch
|
||||||
const markets = await getMarkets()
|
const markets = await getMarkets()
|
||||||
const creatorIds = markets.map(m => m.creator_id)
|
const creatorIds = markets.map(m => m.creator_id)
|
||||||
const creators = await getUsers(creatorIds) // 1 query
|
const creators = await getUsers(creatorIds) // 1 query
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ Universal coding standards applicable across all projects.
|
|||||||
### Variable Naming
|
### Variable Naming
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Descriptive names
|
// PASS: GOOD: Descriptive names
|
||||||
const marketSearchQuery = 'election'
|
const marketSearchQuery = 'election'
|
||||||
const isUserAuthenticated = true
|
const isUserAuthenticated = true
|
||||||
const totalRevenue = 1000
|
const totalRevenue = 1000
|
||||||
|
|
||||||
// ❌ BAD: Unclear names
|
// FAIL: BAD: Unclear names
|
||||||
const q = 'election'
|
const q = 'election'
|
||||||
const flag = true
|
const flag = true
|
||||||
const x = 1000
|
const x = 1000
|
||||||
@@ -62,12 +62,12 @@ const x = 1000
|
|||||||
### Function Naming
|
### Function Naming
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Verb-noun pattern
|
// PASS: GOOD: Verb-noun pattern
|
||||||
async function fetchMarketData(marketId: string) { }
|
async function fetchMarketData(marketId: string) { }
|
||||||
function calculateSimilarity(a: number[], b: number[]) { }
|
function calculateSimilarity(a: number[], b: number[]) { }
|
||||||
function isValidEmail(email: string): boolean { }
|
function isValidEmail(email: string): boolean { }
|
||||||
|
|
||||||
// ❌ BAD: Unclear or noun-only
|
// FAIL: BAD: Unclear or noun-only
|
||||||
async function market(id: string) { }
|
async function market(id: string) { }
|
||||||
function similarity(a, b) { }
|
function similarity(a, b) { }
|
||||||
function email(e) { }
|
function email(e) { }
|
||||||
@@ -76,7 +76,7 @@ function email(e) { }
|
|||||||
### Immutability Pattern (CRITICAL)
|
### Immutability Pattern (CRITICAL)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ ALWAYS use spread operator
|
// PASS: ALWAYS use spread operator
|
||||||
const updatedUser = {
|
const updatedUser = {
|
||||||
...user,
|
...user,
|
||||||
name: 'New Name'
|
name: 'New Name'
|
||||||
@@ -84,7 +84,7 @@ const updatedUser = {
|
|||||||
|
|
||||||
const updatedArray = [...items, newItem]
|
const updatedArray = [...items, newItem]
|
||||||
|
|
||||||
// ❌ NEVER mutate directly
|
// FAIL: NEVER mutate directly
|
||||||
user.name = 'New Name' // BAD
|
user.name = 'New Name' // BAD
|
||||||
items.push(newItem) // BAD
|
items.push(newItem) // BAD
|
||||||
```
|
```
|
||||||
@@ -92,7 +92,7 @@ items.push(newItem) // BAD
|
|||||||
### Error Handling
|
### Error Handling
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Comprehensive error handling
|
// PASS: GOOD: Comprehensive error handling
|
||||||
async function fetchData(url: string) {
|
async function fetchData(url: string) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
@@ -108,7 +108,7 @@ async function fetchData(url: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: No error handling
|
// FAIL: BAD: No error handling
|
||||||
async function fetchData(url) {
|
async function fetchData(url) {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
return response.json()
|
return response.json()
|
||||||
@@ -118,14 +118,14 @@ async function fetchData(url) {
|
|||||||
### Async/Await Best Practices
|
### Async/Await Best Practices
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Parallel execution when possible
|
// PASS: GOOD: Parallel execution when possible
|
||||||
const [users, markets, stats] = await Promise.all([
|
const [users, markets, stats] = await Promise.all([
|
||||||
fetchUsers(),
|
fetchUsers(),
|
||||||
fetchMarkets(),
|
fetchMarkets(),
|
||||||
fetchStats()
|
fetchStats()
|
||||||
])
|
])
|
||||||
|
|
||||||
// ❌ BAD: Sequential when unnecessary
|
// FAIL: BAD: Sequential when unnecessary
|
||||||
const users = await fetchUsers()
|
const users = await fetchUsers()
|
||||||
const markets = await fetchMarkets()
|
const markets = await fetchMarkets()
|
||||||
const stats = await fetchStats()
|
const stats = await fetchStats()
|
||||||
@@ -134,7 +134,7 @@ const stats = await fetchStats()
|
|||||||
### Type Safety
|
### Type Safety
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Proper types
|
// PASS: GOOD: Proper types
|
||||||
interface Market {
|
interface Market {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@@ -146,7 +146,7 @@ function getMarket(id: string): Promise<Market> {
|
|||||||
// Implementation
|
// Implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: Using 'any'
|
// FAIL: BAD: Using 'any'
|
||||||
function getMarket(id: any): Promise<any> {
|
function getMarket(id: any): Promise<any> {
|
||||||
// Implementation
|
// Implementation
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,7 @@ function getMarket(id: any): Promise<any> {
|
|||||||
### Component Structure
|
### Component Structure
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Functional component with types
|
// PASS: GOOD: Functional component with types
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
@@ -182,7 +182,7 @@ export function Button({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: No types, unclear structure
|
// FAIL: BAD: No types, unclear structure
|
||||||
export function Button(props) {
|
export function Button(props) {
|
||||||
return <button onClick={props.onClick}>{props.children}</button>
|
return <button onClick={props.onClick}>{props.children}</button>
|
||||||
}
|
}
|
||||||
@@ -191,7 +191,7 @@ export function Button(props) {
|
|||||||
### Custom Hooks
|
### Custom Hooks
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Reusable custom hook
|
// PASS: GOOD: Reusable custom hook
|
||||||
export function useDebounce<T>(value: T, delay: number): T {
|
export function useDebounce<T>(value: T, delay: number): T {
|
||||||
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
||||||
|
|
||||||
@@ -213,25 +213,25 @@ const debouncedQuery = useDebounce(searchQuery, 500)
|
|||||||
### State Management
|
### State Management
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Proper state updates
|
// PASS: GOOD: Proper state updates
|
||||||
const [count, setCount] = useState(0)
|
const [count, setCount] = useState(0)
|
||||||
|
|
||||||
// Functional update for state based on previous state
|
// Functional update for state based on previous state
|
||||||
setCount(prev => prev + 1)
|
setCount(prev => prev + 1)
|
||||||
|
|
||||||
// ❌ BAD: Direct state reference
|
// FAIL: BAD: Direct state reference
|
||||||
setCount(count + 1) // Can be stale in async scenarios
|
setCount(count + 1) // Can be stale in async scenarios
|
||||||
```
|
```
|
||||||
|
|
||||||
### Conditional Rendering
|
### Conditional Rendering
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Clear conditional rendering
|
// PASS: GOOD: Clear conditional rendering
|
||||||
{isLoading && <Spinner />}
|
{isLoading && <Spinner />}
|
||||||
{error && <ErrorMessage error={error} />}
|
{error && <ErrorMessage error={error} />}
|
||||||
{data && <DataDisplay data={data} />}
|
{data && <DataDisplay data={data} />}
|
||||||
|
|
||||||
// ❌ BAD: Ternary hell
|
// FAIL: BAD: Ternary hell
|
||||||
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
|
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ GET /api/markets?status=active&limit=10&offset=0
|
|||||||
### Response Format
|
### Response Format
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Consistent response structure
|
// PASS: GOOD: Consistent response structure
|
||||||
interface ApiResponse<T> {
|
interface ApiResponse<T> {
|
||||||
success: boolean
|
success: boolean
|
||||||
data?: T
|
data?: T
|
||||||
@@ -285,7 +285,7 @@ return NextResponse.json({
|
|||||||
```typescript
|
```typescript
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
// ✅ GOOD: Schema validation
|
// PASS: GOOD: Schema validation
|
||||||
const CreateMarketSchema = z.object({
|
const CreateMarketSchema = z.object({
|
||||||
name: z.string().min(1).max(200),
|
name: z.string().min(1).max(200),
|
||||||
description: z.string().min(1).max(2000),
|
description: z.string().min(1).max(2000),
|
||||||
@@ -348,14 +348,14 @@ types/market.types.ts # camelCase with .types suffix
|
|||||||
### When to Comment
|
### When to Comment
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Explain WHY, not WHAT
|
// PASS: GOOD: Explain WHY, not WHAT
|
||||||
// Use exponential backoff to avoid overwhelming the API during outages
|
// Use exponential backoff to avoid overwhelming the API during outages
|
||||||
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
|
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
|
||||||
|
|
||||||
// Deliberately using mutation here for performance with large arrays
|
// Deliberately using mutation here for performance with large arrays
|
||||||
items.push(newItem)
|
items.push(newItem)
|
||||||
|
|
||||||
// ❌ BAD: Stating the obvious
|
// FAIL: BAD: Stating the obvious
|
||||||
// Increment counter by 1
|
// Increment counter by 1
|
||||||
count++
|
count++
|
||||||
|
|
||||||
@@ -395,12 +395,12 @@ export async function searchMarkets(
|
|||||||
```typescript
|
```typescript
|
||||||
import { useMemo, useCallback } from 'react'
|
import { useMemo, useCallback } from 'react'
|
||||||
|
|
||||||
// ✅ GOOD: Memoize expensive computations
|
// PASS: GOOD: Memoize expensive computations
|
||||||
const sortedMarkets = useMemo(() => {
|
const sortedMarkets = useMemo(() => {
|
||||||
return markets.sort((a, b) => b.volume - a.volume)
|
return markets.sort((a, b) => b.volume - a.volume)
|
||||||
}, [markets])
|
}, [markets])
|
||||||
|
|
||||||
// ✅ GOOD: Memoize callbacks
|
// PASS: GOOD: Memoize callbacks
|
||||||
const handleSearch = useCallback((query: string) => {
|
const handleSearch = useCallback((query: string) => {
|
||||||
setSearchQuery(query)
|
setSearchQuery(query)
|
||||||
}, [])
|
}, [])
|
||||||
@@ -411,7 +411,7 @@ const handleSearch = useCallback((query: string) => {
|
|||||||
```typescript
|
```typescript
|
||||||
import { lazy, Suspense } from 'react'
|
import { lazy, Suspense } from 'react'
|
||||||
|
|
||||||
// ✅ GOOD: Lazy load heavy components
|
// PASS: GOOD: Lazy load heavy components
|
||||||
const HeavyChart = lazy(() => import('./HeavyChart'))
|
const HeavyChart = lazy(() => import('./HeavyChart'))
|
||||||
|
|
||||||
export function Dashboard() {
|
export function Dashboard() {
|
||||||
@@ -426,13 +426,13 @@ export function Dashboard() {
|
|||||||
### Database Queries
|
### Database Queries
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Select only needed columns
|
// PASS: GOOD: Select only needed columns
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('id, name, status')
|
.select('id, name, status')
|
||||||
.limit(10)
|
.limit(10)
|
||||||
|
|
||||||
// ❌ BAD: Select everything
|
// FAIL: BAD: Select everything
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('*')
|
.select('*')
|
||||||
@@ -459,12 +459,12 @@ test('calculates similarity correctly', () => {
|
|||||||
### Test Naming
|
### Test Naming
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Descriptive test names
|
// PASS: GOOD: Descriptive test names
|
||||||
test('returns empty array when no markets match query', () => { })
|
test('returns empty array when no markets match query', () => { })
|
||||||
test('throws error when OpenAI API key is missing', () => { })
|
test('throws error when OpenAI API key is missing', () => { })
|
||||||
test('falls back to substring search when Redis unavailable', () => { })
|
test('falls back to substring search when Redis unavailable', () => { })
|
||||||
|
|
||||||
// ❌ BAD: Vague test names
|
// FAIL: BAD: Vague test names
|
||||||
test('works', () => { })
|
test('works', () => { })
|
||||||
test('test search', () => { })
|
test('test search', () => { })
|
||||||
```
|
```
|
||||||
@@ -475,12 +475,12 @@ Watch for these anti-patterns:
|
|||||||
|
|
||||||
### 1. Long Functions
|
### 1. Long Functions
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: Function > 50 lines
|
// FAIL: BAD: Function > 50 lines
|
||||||
function processMarketData() {
|
function processMarketData() {
|
||||||
// 100 lines of code
|
// 100 lines of code
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ GOOD: Split into smaller functions
|
// PASS: GOOD: Split into smaller functions
|
||||||
function processMarketData() {
|
function processMarketData() {
|
||||||
const validated = validateData()
|
const validated = validateData()
|
||||||
const transformed = transformData(validated)
|
const transformed = transformData(validated)
|
||||||
@@ -490,7 +490,7 @@ function processMarketData() {
|
|||||||
|
|
||||||
### 2. Deep Nesting
|
### 2. Deep Nesting
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: 5+ levels of nesting
|
// FAIL: BAD: 5+ levels of nesting
|
||||||
if (user) {
|
if (user) {
|
||||||
if (user.isAdmin) {
|
if (user.isAdmin) {
|
||||||
if (market) {
|
if (market) {
|
||||||
@@ -503,7 +503,7 @@ if (user) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ GOOD: Early returns
|
// PASS: GOOD: Early returns
|
||||||
if (!user) return
|
if (!user) return
|
||||||
if (!user.isAdmin) return
|
if (!user.isAdmin) return
|
||||||
if (!market) return
|
if (!market) return
|
||||||
@@ -515,11 +515,11 @@ if (!hasPermission) return
|
|||||||
|
|
||||||
### 3. Magic Numbers
|
### 3. Magic Numbers
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: Unexplained numbers
|
// FAIL: BAD: Unexplained numbers
|
||||||
if (retryCount > 3) { }
|
if (retryCount > 3) { }
|
||||||
setTimeout(callback, 500)
|
setTimeout(callback, 500)
|
||||||
|
|
||||||
// ✅ GOOD: Named constants
|
// PASS: GOOD: Named constants
|
||||||
const MAX_RETRIES = 3
|
const MAX_RETRIES = 3
|
||||||
const DEBOUNCE_DELAY_MS = 500
|
const DEBOUNCE_DELAY_MS = 500
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Modern frontend patterns for React, Next.js, and performant user interfaces.
|
|||||||
### Composition Over Inheritance
|
### Composition Over Inheritance
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Component composition
|
// PASS: GOOD: Component composition
|
||||||
interface CardProps {
|
interface CardProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
variant?: 'default' | 'outlined'
|
variant?: 'default' | 'outlined'
|
||||||
@@ -294,17 +294,17 @@ export function useMarkets() {
|
|||||||
### Memoization
|
### Memoization
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ useMemo for expensive computations
|
// PASS: useMemo for expensive computations
|
||||||
const sortedMarkets = useMemo(() => {
|
const sortedMarkets = useMemo(() => {
|
||||||
return markets.sort((a, b) => b.volume - a.volume)
|
return markets.sort((a, b) => b.volume - a.volume)
|
||||||
}, [markets])
|
}, [markets])
|
||||||
|
|
||||||
// ✅ useCallback for functions passed to children
|
// PASS: useCallback for functions passed to children
|
||||||
const handleSearch = useCallback((query: string) => {
|
const handleSearch = useCallback((query: string) => {
|
||||||
setSearchQuery(query)
|
setSearchQuery(query)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// ✅ React.memo for pure components
|
// PASS: React.memo for pure components
|
||||||
export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
||||||
return (
|
return (
|
||||||
<div className="market-card">
|
<div className="market-card">
|
||||||
@@ -320,7 +320,7 @@ export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
|||||||
```typescript
|
```typescript
|
||||||
import { lazy, Suspense } from 'react'
|
import { lazy, Suspense } from 'react'
|
||||||
|
|
||||||
// ✅ Lazy load heavy components
|
// PASS: Lazy load heavy components
|
||||||
const HeavyChart = lazy(() => import('./HeavyChart'))
|
const HeavyChart = lazy(() => import('./HeavyChart'))
|
||||||
const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
|
const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
|
||||||
|
|
||||||
@@ -515,7 +515,7 @@ export class ErrorBoundary extends React.Component<
|
|||||||
```typescript
|
```typescript
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
|
|
||||||
// ✅ List animations
|
// PASS: List animations
|
||||||
export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
@@ -534,7 +534,7 @@ export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Modal animations
|
// PASS: Modal animations
|
||||||
export function Modal({ isOpen, onClose, children }: ModalProps) {
|
export function Modal({ isOpen, onClose, children }: ModalProps) {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ This skill ensures all code follows security best practices and identifies poten
|
|||||||
|
|
||||||
### 1. Secrets Management
|
### 1. Secrets Management
|
||||||
|
|
||||||
#### ❌ NEVER Do This
|
#### FAIL: NEVER Do This
|
||||||
```typescript
|
```typescript
|
||||||
const apiKey = "sk-proj-xxxxx" // Hardcoded secret
|
const apiKey = "sk-proj-xxxxx" // Hardcoded secret
|
||||||
const dbPassword = "password123" // In source code
|
const dbPassword = "password123" // In source code
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ✅ ALWAYS Do This
|
#### PASS: ALWAYS Do This
|
||||||
```typescript
|
```typescript
|
||||||
const apiKey = process.env.OPENAI_API_KEY
|
const apiKey = process.env.OPENAI_API_KEY
|
||||||
const dbUrl = process.env.DATABASE_URL
|
const dbUrl = process.env.DATABASE_URL
|
||||||
@@ -108,14 +108,14 @@ function validateFileUpload(file: File) {
|
|||||||
|
|
||||||
### 3. SQL Injection Prevention
|
### 3. SQL Injection Prevention
|
||||||
|
|
||||||
#### ❌ NEVER Concatenate SQL
|
#### FAIL: NEVER Concatenate SQL
|
||||||
```typescript
|
```typescript
|
||||||
// DANGEROUS - SQL Injection vulnerability
|
// DANGEROUS - SQL Injection vulnerability
|
||||||
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
|
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
|
||||||
await db.query(query)
|
await db.query(query)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ✅ ALWAYS Use Parameterized Queries
|
#### PASS: ALWAYS Use Parameterized Queries
|
||||||
```typescript
|
```typescript
|
||||||
// Safe - parameterized query
|
// Safe - parameterized query
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
@@ -140,10 +140,10 @@ await db.query(
|
|||||||
|
|
||||||
#### JWT Token Handling
|
#### JWT Token Handling
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ WRONG: localStorage (vulnerable to XSS)
|
// FAIL: WRONG: localStorage (vulnerable to XSS)
|
||||||
localStorage.setItem('token', token)
|
localStorage.setItem('token', token)
|
||||||
|
|
||||||
// ✅ CORRECT: httpOnly cookies
|
// PASS: CORRECT: httpOnly cookies
|
||||||
res.setHeader('Set-Cookie',
|
res.setHeader('Set-Cookie',
|
||||||
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
|
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
|
||||||
```
|
```
|
||||||
@@ -300,18 +300,18 @@ app.use('/api/search', searchLimiter)
|
|||||||
|
|
||||||
#### Logging
|
#### Logging
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ WRONG: Logging sensitive data
|
// FAIL: WRONG: Logging sensitive data
|
||||||
console.log('User login:', { email, password })
|
console.log('User login:', { email, password })
|
||||||
console.log('Payment:', { cardNumber, cvv })
|
console.log('Payment:', { cardNumber, cvv })
|
||||||
|
|
||||||
// ✅ CORRECT: Redact sensitive data
|
// PASS: CORRECT: Redact sensitive data
|
||||||
console.log('User login:', { email, userId })
|
console.log('User login:', { email, userId })
|
||||||
console.log('Payment:', { last4: card.last4, userId })
|
console.log('Payment:', { last4: card.last4, userId })
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Error Messages
|
#### Error Messages
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ WRONG: Exposing internal details
|
// FAIL: WRONG: Exposing internal details
|
||||||
catch (error) {
|
catch (error) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: error.message, stack: error.stack },
|
{ error: error.message, stack: error.stack },
|
||||||
@@ -319,7 +319,7 @@ catch (error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ CORRECT: Generic error messages
|
// PASS: CORRECT: Generic error messages
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('Internal error:', error)
|
console.error('Internal error:', error)
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
@@ -314,39 +314,39 @@ npm run test:coverage
|
|||||||
|
|
||||||
## Common Testing Mistakes to Avoid
|
## Common Testing Mistakes to Avoid
|
||||||
|
|
||||||
### ❌ WRONG: Testing Implementation Details
|
### FAIL: WRONG: Testing Implementation Details
|
||||||
```typescript
|
```typescript
|
||||||
// Don't test internal state
|
// Don't test internal state
|
||||||
expect(component.state.count).toBe(5)
|
expect(component.state.count).toBe(5)
|
||||||
```
|
```
|
||||||
|
|
||||||
### ✅ CORRECT: Test User-Visible Behavior
|
### PASS: CORRECT: Test User-Visible Behavior
|
||||||
```typescript
|
```typescript
|
||||||
// Test what users see
|
// Test what users see
|
||||||
expect(screen.getByText('Count: 5')).toBeInTheDocument()
|
expect(screen.getByText('Count: 5')).toBeInTheDocument()
|
||||||
```
|
```
|
||||||
|
|
||||||
### ❌ WRONG: Brittle Selectors
|
### FAIL: WRONG: Brittle Selectors
|
||||||
```typescript
|
```typescript
|
||||||
// Breaks easily
|
// Breaks easily
|
||||||
await page.click('.css-class-xyz')
|
await page.click('.css-class-xyz')
|
||||||
```
|
```
|
||||||
|
|
||||||
### ✅ CORRECT: Semantic Selectors
|
### PASS: CORRECT: Semantic Selectors
|
||||||
```typescript
|
```typescript
|
||||||
// Resilient to changes
|
// Resilient to changes
|
||||||
await page.click('button:has-text("Submit")')
|
await page.click('button:has-text("Submit")')
|
||||||
await page.click('[data-testid="submit-button"]')
|
await page.click('[data-testid="submit-button"]')
|
||||||
```
|
```
|
||||||
|
|
||||||
### ❌ WRONG: No Test Isolation
|
### FAIL: WRONG: No Test Isolation
|
||||||
```typescript
|
```typescript
|
||||||
// Tests depend on each other
|
// Tests depend on each other
|
||||||
test('creates user', () => { /* ... */ })
|
test('creates user', () => { /* ... */ })
|
||||||
test('updates same user', () => { /* depends on previous test */ })
|
test('updates same user', () => { /* depends on previous test */ })
|
||||||
```
|
```
|
||||||
|
|
||||||
### ✅ CORRECT: Independent Tests
|
### PASS: CORRECT: Independent Tests
|
||||||
```typescript
|
```typescript
|
||||||
// Each test sets up its own data
|
// Each test sets up its own data
|
||||||
test('creates user', () => {
|
test('creates user', () => {
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ Assume the validator is hostile and literal.
|
|||||||
|
|
||||||
## The `hooks` Field: DO NOT ADD
|
## The `hooks` Field: DO NOT ADD
|
||||||
|
|
||||||
> ⚠️ **CRITICAL:** Do NOT add a `"hooks"` field to `plugin.json`. This is enforced by a regression test.
|
> WARNING: **CRITICAL:** Do NOT add a `"hooks"` field to `plugin.json`. This is enforced by a regression test.
|
||||||
|
|
||||||
### Why This Matters
|
### Why This Matters
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -198,6 +198,10 @@ jobs:
|
|||||||
run: node scripts/ci/catalog.js --text
|
run: node scripts/ci/catalog.js --text
|
||||||
continue-on-error: false
|
continue-on-error: false
|
||||||
|
|
||||||
|
- name: Check unicode safety
|
||||||
|
run: node scripts/ci/check-unicode-safety.js
|
||||||
|
continue-on-error: false
|
||||||
|
|
||||||
security:
|
security:
|
||||||
name: Security Scan
|
name: Security Scan
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
3
.github/workflows/reusable-validate.yml
vendored
3
.github/workflows/reusable-validate.yml
vendored
@@ -44,3 +44,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Validate rules
|
- name: Validate rules
|
||||||
run: node scripts/ci/validate-rules.js
|
run: node scripts/ci/validate-rules.js
|
||||||
|
|
||||||
|
- name: Check unicode safety
|
||||||
|
run: node scripts/ci/check-unicode-safety.js
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ detect_pm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PM=$(detect_pm)
|
PM=$(detect_pm)
|
||||||
echo "📦 Package manager: $PM"
|
echo "Package manager: $PM"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── Helper: run a check ─────────────────────────────────────
|
# ── Helper: run a check ─────────────────────────────────────
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Backend architecture patterns and best practices for scalable server-side applic
|
|||||||
### RESTful API Structure
|
### RESTful API Structure
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ Resource-based URLs
|
// PASS: Resource-based URLs
|
||||||
GET /api/markets # List resources
|
GET /api/markets # List resources
|
||||||
GET /api/markets/:id # Get single resource
|
GET /api/markets/:id # Get single resource
|
||||||
POST /api/markets # Create resource
|
POST /api/markets # Create resource
|
||||||
@@ -33,7 +33,7 @@ PUT /api/markets/:id # Replace resource
|
|||||||
PATCH /api/markets/:id # Update resource
|
PATCH /api/markets/:id # Update resource
|
||||||
DELETE /api/markets/:id # Delete resource
|
DELETE /api/markets/:id # Delete resource
|
||||||
|
|
||||||
// ✅ Query parameters for filtering, sorting, pagination
|
// PASS: Query parameters for filtering, sorting, pagination
|
||||||
GET /api/markets?status=active&sort=volume&limit=20&offset=0
|
GET /api/markets?status=active&sort=volume&limit=20&offset=0
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ export default withAuth(async (req, res) => {
|
|||||||
### Query Optimization
|
### Query Optimization
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Select only needed columns
|
// PASS: GOOD: Select only needed columns
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('id, name, status, volume')
|
.select('id, name, status, volume')
|
||||||
@@ -141,7 +141,7 @@ const { data } = await supabase
|
|||||||
.order('volume', { ascending: false })
|
.order('volume', { ascending: false })
|
||||||
.limit(10)
|
.limit(10)
|
||||||
|
|
||||||
// ❌ BAD: Select everything
|
// FAIL: BAD: Select everything
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('*')
|
.select('*')
|
||||||
@@ -150,13 +150,13 @@ const { data } = await supabase
|
|||||||
### N+1 Query Prevention
|
### N+1 Query Prevention
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: N+1 query problem
|
// FAIL: BAD: N+1 query problem
|
||||||
const markets = await getMarkets()
|
const markets = await getMarkets()
|
||||||
for (const market of markets) {
|
for (const market of markets) {
|
||||||
market.creator = await getUser(market.creator_id) // N queries
|
market.creator = await getUser(market.creator_id) // N queries
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ GOOD: Batch fetch
|
// PASS: GOOD: Batch fetch
|
||||||
const markets = await getMarkets()
|
const markets = await getMarkets()
|
||||||
const creatorIds = markets.map(m => m.creator_id)
|
const creatorIds = markets.map(m => m.creator_id)
|
||||||
const creators = await getUsers(creatorIds) // 1 query
|
const creators = await getUsers(creatorIds) // 1 query
|
||||||
|
|||||||
@@ -50,12 +50,12 @@ Universal coding standards applicable across all projects.
|
|||||||
### Variable Naming
|
### Variable Naming
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Descriptive names
|
// PASS: GOOD: Descriptive names
|
||||||
const marketSearchQuery = 'election'
|
const marketSearchQuery = 'election'
|
||||||
const isUserAuthenticated = true
|
const isUserAuthenticated = true
|
||||||
const totalRevenue = 1000
|
const totalRevenue = 1000
|
||||||
|
|
||||||
// ❌ BAD: Unclear names
|
// FAIL: BAD: Unclear names
|
||||||
const q = 'election'
|
const q = 'election'
|
||||||
const flag = true
|
const flag = true
|
||||||
const x = 1000
|
const x = 1000
|
||||||
@@ -64,12 +64,12 @@ const x = 1000
|
|||||||
### Function Naming
|
### Function Naming
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Verb-noun pattern
|
// PASS: GOOD: Verb-noun pattern
|
||||||
async function fetchMarketData(marketId: string) { }
|
async function fetchMarketData(marketId: string) { }
|
||||||
function calculateSimilarity(a: number[], b: number[]) { }
|
function calculateSimilarity(a: number[], b: number[]) { }
|
||||||
function isValidEmail(email: string): boolean { }
|
function isValidEmail(email: string): boolean { }
|
||||||
|
|
||||||
// ❌ BAD: Unclear or noun-only
|
// FAIL: BAD: Unclear or noun-only
|
||||||
async function market(id: string) { }
|
async function market(id: string) { }
|
||||||
function similarity(a, b) { }
|
function similarity(a, b) { }
|
||||||
function email(e) { }
|
function email(e) { }
|
||||||
@@ -78,7 +78,7 @@ function email(e) { }
|
|||||||
### Immutability Pattern (CRITICAL)
|
### Immutability Pattern (CRITICAL)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ ALWAYS use spread operator
|
// PASS: ALWAYS use spread operator
|
||||||
const updatedUser = {
|
const updatedUser = {
|
||||||
...user,
|
...user,
|
||||||
name: 'New Name'
|
name: 'New Name'
|
||||||
@@ -86,7 +86,7 @@ const updatedUser = {
|
|||||||
|
|
||||||
const updatedArray = [...items, newItem]
|
const updatedArray = [...items, newItem]
|
||||||
|
|
||||||
// ❌ NEVER mutate directly
|
// FAIL: NEVER mutate directly
|
||||||
user.name = 'New Name' // BAD
|
user.name = 'New Name' // BAD
|
||||||
items.push(newItem) // BAD
|
items.push(newItem) // BAD
|
||||||
```
|
```
|
||||||
@@ -94,7 +94,7 @@ items.push(newItem) // BAD
|
|||||||
### Error Handling
|
### Error Handling
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Comprehensive error handling
|
// PASS: GOOD: Comprehensive error handling
|
||||||
async function fetchData(url: string) {
|
async function fetchData(url: string) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
@@ -110,7 +110,7 @@ async function fetchData(url: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: No error handling
|
// FAIL: BAD: No error handling
|
||||||
async function fetchData(url) {
|
async function fetchData(url) {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
return response.json()
|
return response.json()
|
||||||
@@ -120,14 +120,14 @@ async function fetchData(url) {
|
|||||||
### Async/Await Best Practices
|
### Async/Await Best Practices
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Parallel execution when possible
|
// PASS: GOOD: Parallel execution when possible
|
||||||
const [users, markets, stats] = await Promise.all([
|
const [users, markets, stats] = await Promise.all([
|
||||||
fetchUsers(),
|
fetchUsers(),
|
||||||
fetchMarkets(),
|
fetchMarkets(),
|
||||||
fetchStats()
|
fetchStats()
|
||||||
])
|
])
|
||||||
|
|
||||||
// ❌ BAD: Sequential when unnecessary
|
// FAIL: BAD: Sequential when unnecessary
|
||||||
const users = await fetchUsers()
|
const users = await fetchUsers()
|
||||||
const markets = await fetchMarkets()
|
const markets = await fetchMarkets()
|
||||||
const stats = await fetchStats()
|
const stats = await fetchStats()
|
||||||
@@ -136,7 +136,7 @@ const stats = await fetchStats()
|
|||||||
### Type Safety
|
### Type Safety
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Proper types
|
// PASS: GOOD: Proper types
|
||||||
interface Market {
|
interface Market {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@@ -148,7 +148,7 @@ function getMarket(id: string): Promise<Market> {
|
|||||||
// Implementation
|
// Implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: Using 'any'
|
// FAIL: BAD: Using 'any'
|
||||||
function getMarket(id: any): Promise<any> {
|
function getMarket(id: any): Promise<any> {
|
||||||
// Implementation
|
// Implementation
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ function getMarket(id: any): Promise<any> {
|
|||||||
### Component Structure
|
### Component Structure
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Functional component with types
|
// PASS: GOOD: Functional component with types
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
@@ -184,7 +184,7 @@ export function Button({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: No types, unclear structure
|
// FAIL: BAD: No types, unclear structure
|
||||||
export function Button(props) {
|
export function Button(props) {
|
||||||
return <button onClick={props.onClick}>{props.children}</button>
|
return <button onClick={props.onClick}>{props.children}</button>
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ export function Button(props) {
|
|||||||
### Custom Hooks
|
### Custom Hooks
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Reusable custom hook
|
// PASS: GOOD: Reusable custom hook
|
||||||
export function useDebounce<T>(value: T, delay: number): T {
|
export function useDebounce<T>(value: T, delay: number): T {
|
||||||
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
||||||
|
|
||||||
@@ -215,25 +215,25 @@ const debouncedQuery = useDebounce(searchQuery, 500)
|
|||||||
### State Management
|
### State Management
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Proper state updates
|
// PASS: GOOD: Proper state updates
|
||||||
const [count, setCount] = useState(0)
|
const [count, setCount] = useState(0)
|
||||||
|
|
||||||
// Functional update for state based on previous state
|
// Functional update for state based on previous state
|
||||||
setCount(prev => prev + 1)
|
setCount(prev => prev + 1)
|
||||||
|
|
||||||
// ❌ BAD: Direct state reference
|
// FAIL: BAD: Direct state reference
|
||||||
setCount(count + 1) // Can be stale in async scenarios
|
setCount(count + 1) // Can be stale in async scenarios
|
||||||
```
|
```
|
||||||
|
|
||||||
### Conditional Rendering
|
### Conditional Rendering
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Clear conditional rendering
|
// PASS: GOOD: Clear conditional rendering
|
||||||
{isLoading && <Spinner />}
|
{isLoading && <Spinner />}
|
||||||
{error && <ErrorMessage error={error} />}
|
{error && <ErrorMessage error={error} />}
|
||||||
{data && <DataDisplay data={data} />}
|
{data && <DataDisplay data={data} />}
|
||||||
|
|
||||||
// ❌ BAD: Ternary hell
|
// FAIL: BAD: Ternary hell
|
||||||
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
|
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ GET /api/markets?status=active&limit=10&offset=0
|
|||||||
### Response Format
|
### Response Format
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Consistent response structure
|
// PASS: GOOD: Consistent response structure
|
||||||
interface ApiResponse<T> {
|
interface ApiResponse<T> {
|
||||||
success: boolean
|
success: boolean
|
||||||
data?: T
|
data?: T
|
||||||
@@ -287,7 +287,7 @@ return NextResponse.json({
|
|||||||
```typescript
|
```typescript
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
// ✅ GOOD: Schema validation
|
// PASS: GOOD: Schema validation
|
||||||
const CreateMarketSchema = z.object({
|
const CreateMarketSchema = z.object({
|
||||||
name: z.string().min(1).max(200),
|
name: z.string().min(1).max(200),
|
||||||
description: z.string().min(1).max(2000),
|
description: z.string().min(1).max(2000),
|
||||||
@@ -350,14 +350,14 @@ types/market.types.ts # camelCase with .types suffix
|
|||||||
### When to Comment
|
### When to Comment
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Explain WHY, not WHAT
|
// PASS: GOOD: Explain WHY, not WHAT
|
||||||
// Use exponential backoff to avoid overwhelming the API during outages
|
// Use exponential backoff to avoid overwhelming the API during outages
|
||||||
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
|
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
|
||||||
|
|
||||||
// Deliberately using mutation here for performance with large arrays
|
// Deliberately using mutation here for performance with large arrays
|
||||||
items.push(newItem)
|
items.push(newItem)
|
||||||
|
|
||||||
// ❌ BAD: Stating the obvious
|
// FAIL: BAD: Stating the obvious
|
||||||
// Increment counter by 1
|
// Increment counter by 1
|
||||||
count++
|
count++
|
||||||
|
|
||||||
@@ -397,12 +397,12 @@ export async function searchMarkets(
|
|||||||
```typescript
|
```typescript
|
||||||
import { useMemo, useCallback } from 'react'
|
import { useMemo, useCallback } from 'react'
|
||||||
|
|
||||||
// ✅ GOOD: Memoize expensive computations
|
// PASS: GOOD: Memoize expensive computations
|
||||||
const sortedMarkets = useMemo(() => {
|
const sortedMarkets = useMemo(() => {
|
||||||
return markets.sort((a, b) => b.volume - a.volume)
|
return markets.sort((a, b) => b.volume - a.volume)
|
||||||
}, [markets])
|
}, [markets])
|
||||||
|
|
||||||
// ✅ GOOD: Memoize callbacks
|
// PASS: GOOD: Memoize callbacks
|
||||||
const handleSearch = useCallback((query: string) => {
|
const handleSearch = useCallback((query: string) => {
|
||||||
setSearchQuery(query)
|
setSearchQuery(query)
|
||||||
}, [])
|
}, [])
|
||||||
@@ -413,7 +413,7 @@ const handleSearch = useCallback((query: string) => {
|
|||||||
```typescript
|
```typescript
|
||||||
import { lazy, Suspense } from 'react'
|
import { lazy, Suspense } from 'react'
|
||||||
|
|
||||||
// ✅ GOOD: Lazy load heavy components
|
// PASS: GOOD: Lazy load heavy components
|
||||||
const HeavyChart = lazy(() => import('./HeavyChart'))
|
const HeavyChart = lazy(() => import('./HeavyChart'))
|
||||||
|
|
||||||
export function Dashboard() {
|
export function Dashboard() {
|
||||||
@@ -428,13 +428,13 @@ export function Dashboard() {
|
|||||||
### Database Queries
|
### Database Queries
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Select only needed columns
|
// PASS: GOOD: Select only needed columns
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('id, name, status')
|
.select('id, name, status')
|
||||||
.limit(10)
|
.limit(10)
|
||||||
|
|
||||||
// ❌ BAD: Select everything
|
// FAIL: BAD: Select everything
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('*')
|
.select('*')
|
||||||
@@ -461,12 +461,12 @@ test('calculates similarity correctly', () => {
|
|||||||
### Test Naming
|
### Test Naming
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Descriptive test names
|
// PASS: GOOD: Descriptive test names
|
||||||
test('returns empty array when no markets match query', () => { })
|
test('returns empty array when no markets match query', () => { })
|
||||||
test('throws error when OpenAI API key is missing', () => { })
|
test('throws error when OpenAI API key is missing', () => { })
|
||||||
test('falls back to substring search when Redis unavailable', () => { })
|
test('falls back to substring search when Redis unavailable', () => { })
|
||||||
|
|
||||||
// ❌ BAD: Vague test names
|
// FAIL: BAD: Vague test names
|
||||||
test('works', () => { })
|
test('works', () => { })
|
||||||
test('test search', () => { })
|
test('test search', () => { })
|
||||||
```
|
```
|
||||||
@@ -477,12 +477,12 @@ Watch for these anti-patterns:
|
|||||||
|
|
||||||
### 1. Long Functions
|
### 1. Long Functions
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: Function > 50 lines
|
// FAIL: BAD: Function > 50 lines
|
||||||
function processMarketData() {
|
function processMarketData() {
|
||||||
// 100 lines of code
|
// 100 lines of code
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ GOOD: Split into smaller functions
|
// PASS: GOOD: Split into smaller functions
|
||||||
function processMarketData() {
|
function processMarketData() {
|
||||||
const validated = validateData()
|
const validated = validateData()
|
||||||
const transformed = transformData(validated)
|
const transformed = transformData(validated)
|
||||||
@@ -492,7 +492,7 @@ function processMarketData() {
|
|||||||
|
|
||||||
### 2. Deep Nesting
|
### 2. Deep Nesting
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: 5+ levels of nesting
|
// FAIL: BAD: 5+ levels of nesting
|
||||||
if (user) {
|
if (user) {
|
||||||
if (user.isAdmin) {
|
if (user.isAdmin) {
|
||||||
if (market) {
|
if (market) {
|
||||||
@@ -505,7 +505,7 @@ if (user) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ GOOD: Early returns
|
// PASS: GOOD: Early returns
|
||||||
if (!user) return
|
if (!user) return
|
||||||
if (!user.isAdmin) return
|
if (!user.isAdmin) return
|
||||||
if (!market) return
|
if (!market) return
|
||||||
@@ -517,11 +517,11 @@ if (!hasPermission) return
|
|||||||
|
|
||||||
### 3. Magic Numbers
|
### 3. Magic Numbers
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: Unexplained numbers
|
// FAIL: BAD: Unexplained numbers
|
||||||
if (retryCount > 3) { }
|
if (retryCount > 3) { }
|
||||||
setTimeout(callback, 500)
|
setTimeout(callback, 500)
|
||||||
|
|
||||||
// ✅ GOOD: Named constants
|
// PASS: GOOD: Named constants
|
||||||
const MAX_RETRIES = 3
|
const MAX_RETRIES = 3
|
||||||
const DEBOUNCE_DELAY_MS = 500
|
const DEBOUNCE_DELAY_MS = 500
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Modern frontend patterns for React, Next.js, and performant user interfaces.
|
|||||||
### Composition Over Inheritance
|
### Composition Over Inheritance
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Component composition
|
// PASS: GOOD: Component composition
|
||||||
interface CardProps {
|
interface CardProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
variant?: 'default' | 'outlined'
|
variant?: 'default' | 'outlined'
|
||||||
@@ -296,17 +296,17 @@ export function useMarkets() {
|
|||||||
### Memoization
|
### Memoization
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ useMemo for expensive computations
|
// PASS: useMemo for expensive computations
|
||||||
const sortedMarkets = useMemo(() => {
|
const sortedMarkets = useMemo(() => {
|
||||||
return markets.sort((a, b) => b.volume - a.volume)
|
return markets.sort((a, b) => b.volume - a.volume)
|
||||||
}, [markets])
|
}, [markets])
|
||||||
|
|
||||||
// ✅ useCallback for functions passed to children
|
// PASS: useCallback for functions passed to children
|
||||||
const handleSearch = useCallback((query: string) => {
|
const handleSearch = useCallback((query: string) => {
|
||||||
setSearchQuery(query)
|
setSearchQuery(query)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// ✅ React.memo for pure components
|
// PASS: React.memo for pure components
|
||||||
export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
||||||
return (
|
return (
|
||||||
<div className="market-card">
|
<div className="market-card">
|
||||||
@@ -322,7 +322,7 @@ export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
|||||||
```typescript
|
```typescript
|
||||||
import { lazy, Suspense } from 'react'
|
import { lazy, Suspense } from 'react'
|
||||||
|
|
||||||
// ✅ Lazy load heavy components
|
// PASS: Lazy load heavy components
|
||||||
const HeavyChart = lazy(() => import('./HeavyChart'))
|
const HeavyChart = lazy(() => import('./HeavyChart'))
|
||||||
const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
|
const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
|
||||||
|
|
||||||
@@ -517,7 +517,7 @@ export class ErrorBoundary extends React.Component<
|
|||||||
```typescript
|
```typescript
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
|
|
||||||
// ✅ List animations
|
// PASS: List animations
|
||||||
export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
@@ -536,7 +536,7 @@ export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Modal animations
|
// PASS: Modal animations
|
||||||
export function Modal({ isOpen, onClose, children }: ModalProps) {
|
export function Modal({ isOpen, onClose, children }: ModalProps) {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ This skill ensures all code follows security best practices and identifies poten
|
|||||||
|
|
||||||
### 1. Secrets Management
|
### 1. Secrets Management
|
||||||
|
|
||||||
#### ❌ NEVER Do This
|
#### FAIL: NEVER Do This
|
||||||
```typescript
|
```typescript
|
||||||
const apiKey = "sk-proj-xxxxx" // Hardcoded secret
|
const apiKey = "sk-proj-xxxxx" // Hardcoded secret
|
||||||
const dbPassword = "password123" // In source code
|
const dbPassword = "password123" // In source code
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ✅ ALWAYS Do This
|
#### PASS: ALWAYS Do This
|
||||||
```typescript
|
```typescript
|
||||||
const apiKey = process.env.OPENAI_API_KEY
|
const apiKey = process.env.OPENAI_API_KEY
|
||||||
const dbUrl = process.env.DATABASE_URL
|
const dbUrl = process.env.DATABASE_URL
|
||||||
@@ -110,14 +110,14 @@ function validateFileUpload(file: File) {
|
|||||||
|
|
||||||
### 3. SQL Injection Prevention
|
### 3. SQL Injection Prevention
|
||||||
|
|
||||||
#### ❌ NEVER Concatenate SQL
|
#### FAIL: NEVER Concatenate SQL
|
||||||
```typescript
|
```typescript
|
||||||
// DANGEROUS - SQL Injection vulnerability
|
// DANGEROUS - SQL Injection vulnerability
|
||||||
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
|
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
|
||||||
await db.query(query)
|
await db.query(query)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ✅ ALWAYS Use Parameterized Queries
|
#### PASS: ALWAYS Use Parameterized Queries
|
||||||
```typescript
|
```typescript
|
||||||
// Safe - parameterized query
|
// Safe - parameterized query
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
@@ -142,10 +142,10 @@ await db.query(
|
|||||||
|
|
||||||
#### JWT Token Handling
|
#### JWT Token Handling
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ WRONG: localStorage (vulnerable to XSS)
|
// FAIL: WRONG: localStorage (vulnerable to XSS)
|
||||||
localStorage.setItem('token', token)
|
localStorage.setItem('token', token)
|
||||||
|
|
||||||
// ✅ CORRECT: httpOnly cookies
|
// PASS: CORRECT: httpOnly cookies
|
||||||
res.setHeader('Set-Cookie',
|
res.setHeader('Set-Cookie',
|
||||||
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
|
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
|
||||||
```
|
```
|
||||||
@@ -302,18 +302,18 @@ app.use('/api/search', searchLimiter)
|
|||||||
|
|
||||||
#### Logging
|
#### Logging
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ WRONG: Logging sensitive data
|
// FAIL: WRONG: Logging sensitive data
|
||||||
console.log('User login:', { email, password })
|
console.log('User login:', { email, password })
|
||||||
console.log('Payment:', { cardNumber, cvv })
|
console.log('Payment:', { cardNumber, cvv })
|
||||||
|
|
||||||
// ✅ CORRECT: Redact sensitive data
|
// PASS: CORRECT: Redact sensitive data
|
||||||
console.log('User login:', { email, userId })
|
console.log('User login:', { email, userId })
|
||||||
console.log('Payment:', { last4: card.last4, userId })
|
console.log('Payment:', { last4: card.last4, userId })
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Error Messages
|
#### Error Messages
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ WRONG: Exposing internal details
|
// FAIL: WRONG: Exposing internal details
|
||||||
catch (error) {
|
catch (error) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: error.message, stack: error.stack },
|
{ error: error.message, stack: error.stack },
|
||||||
@@ -321,7 +321,7 @@ catch (error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ CORRECT: Generic error messages
|
// PASS: CORRECT: Generic error messages
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('Internal error:', error)
|
console.error('Internal error:', error)
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
@@ -318,39 +318,39 @@ npm run test:coverage
|
|||||||
|
|
||||||
## Common Testing Mistakes to Avoid
|
## Common Testing Mistakes to Avoid
|
||||||
|
|
||||||
### ❌ WRONG: Testing Implementation Details
|
### FAIL: WRONG: Testing Implementation Details
|
||||||
```typescript
|
```typescript
|
||||||
// Don't test internal state
|
// Don't test internal state
|
||||||
expect(component.state.count).toBe(5)
|
expect(component.state.count).toBe(5)
|
||||||
```
|
```
|
||||||
|
|
||||||
### ✅ CORRECT: Test User-Visible Behavior
|
### PASS: CORRECT: Test User-Visible Behavior
|
||||||
```typescript
|
```typescript
|
||||||
// Test what users see
|
// Test what users see
|
||||||
expect(screen.getByText('Count: 5')).toBeInTheDocument()
|
expect(screen.getByText('Count: 5')).toBeInTheDocument()
|
||||||
```
|
```
|
||||||
|
|
||||||
### ❌ WRONG: Brittle Selectors
|
### FAIL: WRONG: Brittle Selectors
|
||||||
```typescript
|
```typescript
|
||||||
// Breaks easily
|
// Breaks easily
|
||||||
await page.click('.css-class-xyz')
|
await page.click('.css-class-xyz')
|
||||||
```
|
```
|
||||||
|
|
||||||
### ✅ CORRECT: Semantic Selectors
|
### PASS: CORRECT: Semantic Selectors
|
||||||
```typescript
|
```typescript
|
||||||
// Resilient to changes
|
// Resilient to changes
|
||||||
await page.click('button:has-text("Submit")')
|
await page.click('button:has-text("Submit")')
|
||||||
await page.click('[data-testid="submit-button"]')
|
await page.click('[data-testid="submit-button"]')
|
||||||
```
|
```
|
||||||
|
|
||||||
### ❌ WRONG: No Test Isolation
|
### FAIL: WRONG: No Test Isolation
|
||||||
```typescript
|
```typescript
|
||||||
// Tests depend on each other
|
// Tests depend on each other
|
||||||
test('creates user', () => { /* ... */ })
|
test('creates user', () => { /* ... */ })
|
||||||
test('updates same user', () => { /* depends on previous test */ })
|
test('updates same user', () => { /* depends on previous test */ })
|
||||||
```
|
```
|
||||||
|
|
||||||
### ✅ CORRECT: Independent Tests
|
### PASS: CORRECT: Independent Tests
|
||||||
```typescript
|
```typescript
|
||||||
// Each test sets up its own data
|
// Each test sets up its own data
|
||||||
test('creates user', () => {
|
test('creates user', () => {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"args": ["-y", "@upstash/context7-mcp@2.1.4"]
|
"args": ["-y", "@upstash/context7-mcp@2.1.4"]
|
||||||
},
|
},
|
||||||
"exa": {
|
"exa": {
|
||||||
|
"type": "http",
|
||||||
"url": "https://mcp.exa.ai/mcp"
|
"url": "https://mcp.exa.ai/mcp"
|
||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
|
|||||||
@@ -353,13 +353,13 @@ If you need to switch back:
|
|||||||
|
|
||||||
| Feature | Claude Code | OpenCode | Status |
|
| Feature | Claude Code | OpenCode | Status |
|
||||||
|---------|-------------|----------|--------|
|
|---------|-------------|----------|--------|
|
||||||
| Agents | ✅ 12 agents | ✅ 12 agents | **Full parity** |
|
| Agents | PASS: 12 agents | PASS: 12 agents | **Full parity** |
|
||||||
| Commands | ✅ 23 commands | ✅ 23 commands | **Full parity** |
|
| Commands | PASS: 23 commands | PASS: 23 commands | **Full parity** |
|
||||||
| Skills | ✅ 16 skills | ✅ 16 skills | **Full parity** |
|
| Skills | PASS: 16 skills | PASS: 16 skills | **Full parity** |
|
||||||
| Hooks | ✅ 3 phases | ✅ 20+ events | **OpenCode has MORE** |
|
| Hooks | PASS: 3 phases | PASS: 20+ events | **OpenCode has MORE** |
|
||||||
| Rules | ✅ 8 rules | ✅ 8 rules | **Full parity** |
|
| Rules | PASS: 8 rules | PASS: 8 rules | **Full parity** |
|
||||||
| MCP Servers | ✅ Full | ✅ Full | **Full parity** |
|
| MCP Servers | PASS: Full | PASS: Full | **Full parity** |
|
||||||
| Custom Tools | ✅ Via hooks | ✅ Native support | **OpenCode is better** |
|
| Custom Tools | PASS: Via hooks | PASS: Native support | **OpenCode is better** |
|
||||||
|
|
||||||
## Feedback
|
## Feedback
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# OpenCode ECC Plugin
|
# OpenCode ECC Plugin
|
||||||
|
|
||||||
> ⚠️ This README is specific to OpenCode usage.
|
> WARNING: This README is specific to OpenCode usage.
|
||||||
> If you installed ECC via npm (e.g. `npm install opencode-ecc`), refer to the root README instead.
|
> If you installed ECC via npm (e.g. `npm install opencode-ecc`), refer to the root README instead.
|
||||||
|
|
||||||
Everything Claude Code (ECC) plugin for OpenCode - agents, commands, hooks, and skills.
|
Everything Claude Code (ECC) plugin for OpenCode - agents, commands, hooks, and skills.
|
||||||
|
|||||||
@@ -19,20 +19,20 @@ Fix build and TypeScript errors with minimal changes: $ARGUMENTS
|
|||||||
## Approach
|
## Approach
|
||||||
|
|
||||||
### DO:
|
### DO:
|
||||||
- ✅ Fix type errors with correct types
|
- PASS: Fix type errors with correct types
|
||||||
- ✅ Add missing imports
|
- PASS: Add missing imports
|
||||||
- ✅ Fix syntax errors
|
- PASS: Fix syntax errors
|
||||||
- ✅ Make minimal changes
|
- PASS: Make minimal changes
|
||||||
- ✅ Preserve existing behavior
|
- PASS: Preserve existing behavior
|
||||||
- ✅ Run `tsc --noEmit` after each change
|
- PASS: Run `tsc --noEmit` after each change
|
||||||
|
|
||||||
### DON'T:
|
### DON'T:
|
||||||
- ❌ Refactor code
|
- FAIL: Refactor code
|
||||||
- ❌ Add new features
|
- FAIL: Add new features
|
||||||
- ❌ Change architecture
|
- FAIL: Change architecture
|
||||||
- ❌ Use `any` type (unless absolutely necessary)
|
- FAIL: Use `any` type (unless absolutely necessary)
|
||||||
- ❌ Add `@ts-ignore` comments
|
- FAIL: Add `@ts-ignore` comments
|
||||||
- ❌ Change business logic
|
- FAIL: Change business logic
|
||||||
|
|
||||||
## Common Error Fixes
|
## Common Error Fixes
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Create a snapshot of current progress including:
|
|||||||
- Coverage: XX%
|
- Coverage: XX%
|
||||||
|
|
||||||
**Build**
|
**Build**
|
||||||
- Status: ✅ Passing / ❌ Failing
|
- Status: PASS: Passing / FAIL: Failing
|
||||||
- Errors: [if any]
|
- Errors: [if any]
|
||||||
|
|
||||||
**Changes Since Last Checkpoint**
|
**Changes Since Last Checkpoint**
|
||||||
|
|||||||
@@ -90,9 +90,9 @@ test.describe('Feature: [Name]', () => {
|
|||||||
```
|
```
|
||||||
E2E Test Results
|
E2E Test Results
|
||||||
================
|
================
|
||||||
✅ Passed: X
|
PASS: Passed: X
|
||||||
❌ Failed: Y
|
FAIL: Failed: Y
|
||||||
⏭️ Skipped: Z
|
SKIPPED: Skipped: Z
|
||||||
|
|
||||||
Failed Tests:
|
Failed Tests:
|
||||||
- test-name: Error message
|
- test-name: Error message
|
||||||
|
|||||||
@@ -4,22 +4,23 @@ Run a deterministic repository harness audit and return a prioritized scorecard.
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
`/harness-audit [scope] [--format text|json]`
|
`/harness-audit [scope] [--format text|json] [--root path]`
|
||||||
|
|
||||||
- `scope` (optional): `repo` (default), `hooks`, `skills`, `commands`, `agents`
|
- `scope` (optional): `repo` (default), `hooks`, `skills`, `commands`, `agents`
|
||||||
- `--format`: output style (`text` default, `json` for automation)
|
- `--format`: output style (`text` default, `json` for automation)
|
||||||
|
- `--root`: audit a specific path instead of the current working directory
|
||||||
|
|
||||||
## Deterministic Engine
|
## Deterministic Engine
|
||||||
|
|
||||||
Always run:
|
Always run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
node scripts/harness-audit.js <scope> --format <text|json>
|
node scripts/harness-audit.js <scope> --format <text|json> [--root <path>]
|
||||||
```
|
```
|
||||||
|
|
||||||
This script is the source of truth for scoring and checks. Do not invent additional dimensions or ad-hoc points.
|
This script is the source of truth for scoring and checks. Do not invent additional dimensions or ad-hoc points.
|
||||||
|
|
||||||
Rubric version: `2026-03-16`.
|
Rubric version: `2026-03-30`.
|
||||||
|
|
||||||
The script computes 7 fixed categories (`0-10` normalized each):
|
The script computes 7 fixed categories (`0-10` normalized each):
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ The script computes 7 fixed categories (`0-10` normalized each):
|
|||||||
7. Cost Efficiency
|
7. Cost Efficiency
|
||||||
|
|
||||||
Scores are derived from explicit file/rule checks and are reproducible for the same commit.
|
Scores are derived from explicit file/rule checks and are reproducible for the same commit.
|
||||||
|
The script audits the current working directory by default and auto-detects whether the target is the ECC repo itself or a consumer project using ECC.
|
||||||
|
|
||||||
## Output Contract
|
## Output Contract
|
||||||
|
|
||||||
|
|||||||
@@ -47,17 +47,17 @@ Execute comprehensive verification:
|
|||||||
## Verification Report
|
## Verification Report
|
||||||
|
|
||||||
### Summary
|
### Summary
|
||||||
- Status: ✅ PASS / ❌ FAIL
|
- Status: PASS: PASS / FAIL: FAIL
|
||||||
- Score: X/Y checks passed
|
- Score: X/Y checks passed
|
||||||
|
|
||||||
### Details
|
### Details
|
||||||
| Check | Status | Notes |
|
| Check | Status | Notes |
|
||||||
|-------|--------|-------|
|
|-------|--------|-------|
|
||||||
| TypeScript | ✅/❌ | [details] |
|
| TypeScript | PASS:/FAIL: | [details] |
|
||||||
| Lint | ✅/❌ | [details] |
|
| Lint | PASS:/FAIL: | [details] |
|
||||||
| Tests | ✅/❌ | [details] |
|
| Tests | PASS:/FAIL: | [details] |
|
||||||
| Coverage | ✅/❌ | XX% (target: 80%) |
|
| Coverage | PASS:/FAIL: | XX% (target: 80%) |
|
||||||
| Build | ✅/❌ | [details] |
|
| Build | PASS:/FAIL: | [details] |
|
||||||
|
|
||||||
### Action Items
|
### Action Items
|
||||||
[If FAIL, list what needs to be fixed]
|
[If FAIL, list what needs to be fixed]
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ git add . && git commit -m "feat: add my-skill" && git push -u origin feat/my-co
|
|||||||
|
|
||||||
Skills are knowledge modules that Claude Code loads based on context.
|
Skills are knowledge modules that Claude Code loads based on context.
|
||||||
|
|
||||||
> **📚 Comprehensive Guide:** For detailed guidance on creating effective skills, see [Skill Development Guide](docs/SKILL-DEVELOPMENT-GUIDE.md). It covers:
|
> ** Comprehensive Guide:** For detailed guidance on creating effective skills, see [Skill Development Guide](docs/SKILL-DEVELOPMENT-GUIDE.md). It covers:
|
||||||
> - Skill architecture and categories
|
> - Skill architecture and categories
|
||||||
> - Writing effective content with examples
|
> - Writing effective content with examples
|
||||||
> - Best practices and common patterns
|
> - Best practices and common patterns
|
||||||
|
|||||||
66
README.md
66
README.md
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
**🌐 Language / 语言 / 語言 / Dil**
|
**Language / 语言 / 語言 / Dil**
|
||||||
|
|
||||||
[**English**](README.md) | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md)
|
[**English**](README.md) | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md)
|
||||||
| [Türkçe](docs/tr/README.md)
|
| [Türkçe](docs/tr/README.md)
|
||||||
@@ -151,7 +151,7 @@ See the full changelog in [Releases](https://github.com/affaan-m/everything-clau
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## Quick Start
|
||||||
|
|
||||||
Get up and running in under 2 minutes:
|
Get up and running in under 2 minutes:
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ Get up and running in under 2 minutes:
|
|||||||
|
|
||||||
### Step 2: Install Rules (Required)
|
### Step 2: Install Rules (Required)
|
||||||
|
|
||||||
> ⚠️ **Important:** Claude Code plugins cannot distribute `rules` automatically. Install them manually:
|
> WARNING: **Important:** Claude Code plugins cannot distribute `rules` automatically. Install them manually:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repo first
|
# Clone the repo first
|
||||||
@@ -220,11 +220,11 @@ For manual install instructions see the README in the `rules/` folder. When copy
|
|||||||
/plugin list everything-claude-code@everything-claude-code
|
/plugin list everything-claude-code@everything-claude-code
|
||||||
```
|
```
|
||||||
|
|
||||||
✨ **That's it!** You now have access to 30 agents, 135 skills, and 60 commands.
|
**That's it!** You now have access to 30 agents, 135 skills, and 60 commands.
|
||||||
|
|
||||||
### Multi-model commands require additional setup
|
### Multi-model commands require additional setup
|
||||||
|
|
||||||
> ⚠️ `multi-*` commands are **not** covered by the base plugin/rules install above.
|
> WARNING: `multi-*` commands are **not** covered by the base plugin/rules install above.
|
||||||
>
|
>
|
||||||
> To use `/multi-plan`, `/multi-execute`, `/multi-backend`, `/multi-frontend`, and `/multi-workflow`, you must also install the `ccg-workflow` runtime.
|
> To use `/multi-plan`, `/multi-execute`, `/multi-backend`, `/multi-frontend`, and `/multi-workflow`, you must also install the `ccg-workflow` runtime.
|
||||||
>
|
>
|
||||||
@@ -238,7 +238,7 @@ For manual install instructions see the README in the `rules/` folder. When copy
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌐 Cross-Platform Support
|
## Cross-Platform Support
|
||||||
|
|
||||||
This plugin now fully supports **Windows, macOS, and Linux**, alongside tight integration across major IDEs (Cursor, OpenCode, Antigravity) and CLI harnesses. All hooks and scripts have been rewritten in Node.js for maximum compatibility.
|
This plugin now fully supports **Windows, macOS, and Linux**, alongside tight integration across major IDEs (Cursor, OpenCode, Antigravity) and CLI harnesses. All hooks and scripts have been rewritten in Node.js for maximum compatibility.
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ export ECC_DISABLED_HOOKS="pre:bash:tmux-reminder,post:edit:typecheck"
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 What's Inside
|
## What's Inside
|
||||||
|
|
||||||
This repo is a **Claude Code plugin** - install it directly or copy components manually.
|
This repo is a **Claude Code plugin** - install it directly or copy components manually.
|
||||||
|
|
||||||
@@ -487,7 +487,7 @@ everything-claude-code/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠️ Ecosystem Tools
|
## Ecosystem Tools
|
||||||
|
|
||||||
### Skill Creator
|
### Skill Creator
|
||||||
|
|
||||||
@@ -552,7 +552,7 @@ Use `/security-scan` in Claude Code to run it, or add to CI with the [GitHub Act
|
|||||||
|
|
||||||
[GitHub](https://github.com/affaan-m/agentshield) | [npm](https://www.npmjs.com/package/ecc-agentshield)
|
[GitHub](https://github.com/affaan-m/agentshield) | [npm](https://www.npmjs.com/package/ecc-agentshield)
|
||||||
|
|
||||||
### 🧠 Continuous Learning v2
|
### Continuous Learning v2
|
||||||
|
|
||||||
The instinct-based learning system automatically learns your patterns:
|
The instinct-based learning system automatically learns your patterns:
|
||||||
|
|
||||||
@@ -567,7 +567,7 @@ See `skills/continuous-learning-v2/` for full documentation.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 Requirements
|
## Requirements
|
||||||
|
|
||||||
### Claude Code CLI Version
|
### Claude Code CLI Version
|
||||||
|
|
||||||
@@ -582,7 +582,7 @@ claude --version
|
|||||||
|
|
||||||
### Important: Hooks Auto-Loading Behavior
|
### Important: Hooks Auto-Loading Behavior
|
||||||
|
|
||||||
> ⚠️ **For Contributors:** Do NOT add a `"hooks"` field to `.claude-plugin/plugin.json`. This is enforced by a regression test.
|
> WARNING: **For Contributors:** Do NOT add a `"hooks"` field to `.claude-plugin/plugin.json`. This is enforced by a regression test.
|
||||||
|
|
||||||
Claude Code v2.1+ **automatically loads** `hooks/hooks.json` from any installed plugin by convention. Explicitly declaring it in `plugin.json` causes a duplicate detection error:
|
Claude Code v2.1+ **automatically loads** `hooks/hooks.json` from any installed plugin by convention. Explicitly declaring it in `plugin.json` causes a duplicate detection error:
|
||||||
|
|
||||||
@@ -594,7 +594,7 @@ Duplicate hooks file detected: ./hooks/hooks.json resolves to already-loaded fil
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📥 Installation
|
## Installation
|
||||||
|
|
||||||
### Option 1: Install as Plugin (Recommended)
|
### Option 1: Install as Plugin (Recommended)
|
||||||
|
|
||||||
@@ -650,7 +650,7 @@ This gives you instant access to all commands, agents, skills, and hooks.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 🔧 Option 2: Manual Installation
|
### Option 2: Manual Installation
|
||||||
|
|
||||||
If you prefer manual control over what's installed:
|
If you prefer manual control over what's installed:
|
||||||
|
|
||||||
@@ -679,7 +679,7 @@ cp -r everything-claude-code/skills/search-first ~/.claude/skills/
|
|||||||
|
|
||||||
# Optional: add niche/framework-specific skills only when needed
|
# Optional: add niche/framework-specific skills only when needed
|
||||||
# for s in django-patterns django-tdd laravel-patterns springboot-patterns; do
|
# for s in django-patterns django-tdd laravel-patterns springboot-patterns; do
|
||||||
# cp -r everything-claude-code/skills/$s ~/.claude/skills/
|
# cp -r everything-claude-code/skills/$s ~/.claude/skills/
|
||||||
# done
|
# done
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -695,7 +695,7 @@ Copy desired MCP servers from `mcp-configs/mcp-servers.json` to your `~/.claude.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎯 Key Concepts
|
## Key Concepts
|
||||||
|
|
||||||
### Agents
|
### Agents
|
||||||
|
|
||||||
@@ -758,7 +758,7 @@ See [`rules/README.md`](rules/README.md) for installation and structure details.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🗺️ Which Agent Should I Use?
|
## Which Agent Should I Use?
|
||||||
|
|
||||||
Not sure where to start? Use this quick reference:
|
Not sure where to start? Use this quick reference:
|
||||||
|
|
||||||
@@ -804,7 +804,7 @@ Not sure where to start? Use this quick reference:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ❓ FAQ
|
## FAQ
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>How do I check which agents/commands are installed?</b></summary>
|
<summary><b>How do I check which agents/commands are installed?</b></summary>
|
||||||
@@ -903,7 +903,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md). The short version:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🧪 Running Tests
|
## Running Tests
|
||||||
|
|
||||||
The plugin includes a comprehensive test suite:
|
The plugin includes a comprehensive test suite:
|
||||||
|
|
||||||
@@ -919,7 +919,7 @@ node tests/hooks/hooks.test.js
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🤝 Contributing
|
## Contributing
|
||||||
|
|
||||||
**Contributions are welcome and encouraged.**
|
**Contributions are welcome and encouraged.**
|
||||||
|
|
||||||
@@ -1089,7 +1089,7 @@ ECC ships three sample role configs:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔌 OpenCode Support
|
## OpenCode Support
|
||||||
|
|
||||||
ECC provides **full OpenCode support** including plugins and hooks.
|
ECC provides **full OpenCode support** including plugins and hooks.
|
||||||
|
|
||||||
@@ -1109,13 +1109,13 @@ The configuration is automatically detected from `.opencode/opencode.json`.
|
|||||||
|
|
||||||
| Feature | Claude Code | OpenCode | Status |
|
| Feature | Claude Code | OpenCode | Status |
|
||||||
|---------|-------------|----------|--------|
|
|---------|-------------|----------|--------|
|
||||||
| Agents | ✅ 30 agents | ✅ 12 agents | **Claude Code leads** |
|
| Agents | PASS: 30 agents | PASS: 12 agents | **Claude Code leads** |
|
||||||
| Commands | ✅ 60 commands | ✅ 31 commands | **Claude Code leads** |
|
| Commands | PASS: 60 commands | PASS: 31 commands | **Claude Code leads** |
|
||||||
| Skills | ✅ 135 skills | ✅ 37 skills | **Claude Code leads** |
|
| Skills | PASS: 135 skills | PASS: 37 skills | **Claude Code leads** |
|
||||||
| Hooks | ✅ 8 event types | ✅ 11 events | **OpenCode has more!** |
|
| Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** |
|
||||||
| Rules | ✅ 29 rules | ✅ 13 instructions | **Claude Code leads** |
|
| Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** |
|
||||||
| MCP Servers | ✅ 14 servers | ✅ Full | **Full parity** |
|
| MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** |
|
||||||
| Custom Tools | ✅ Via hooks | ✅ 6 native tools | **OpenCode is better** |
|
| Custom Tools | PASS: Via hooks | PASS: 6 native tools | **OpenCode is better** |
|
||||||
|
|
||||||
### Hook Support via Plugins
|
### Hook Support via Plugins
|
||||||
|
|
||||||
@@ -1240,7 +1240,7 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📖 Background
|
## Background
|
||||||
|
|
||||||
I've been using Claude Code since the experimental rollout. Won the Anthropic x Forum Ventures hackathon in Sep 2025 with [@DRodriguezFX](https://x.com/DRodriguezFX) — built [zenith.chat](https://zenith.chat) entirely using Claude Code.
|
I've been using Claude Code since the experimental rollout. Won the Anthropic x Forum Ventures hackathon in Sep 2025 with [@DRodriguezFX](https://x.com/DRodriguezFX) — built [zenith.chat](https://zenith.chat) entirely using Claude Code.
|
||||||
|
|
||||||
@@ -1314,7 +1314,7 @@ Agent Teams spawns multiple context windows. Each teammate consumes tokens indep
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚠️ Important Notes
|
## WARNING: Important Notes
|
||||||
|
|
||||||
### Token Optimization
|
### Token Optimization
|
||||||
|
|
||||||
@@ -1346,7 +1346,7 @@ These configs work for my workflow. You should:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 💜 Sponsors
|
## Sponsors
|
||||||
|
|
||||||
This project is free and open source. Sponsors help keep it maintained and growing.
|
This project is free and open source. Sponsors help keep it maintained and growing.
|
||||||
|
|
||||||
@@ -1354,13 +1354,13 @@ This project is free and open source. Sponsors help keep it maintained and growi
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌟 Star History
|
## Star History
|
||||||
|
|
||||||
[](https://star-history.com/#affaan-m/everything-claude-code&Date)
|
[](https://star-history.com/#affaan-m/everything-claude-code&Date)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔗 Links
|
## Links
|
||||||
|
|
||||||
- **Shorthand Guide (Start Here):** [The Shorthand Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2012378465664745795)
|
- **Shorthand Guide (Start Here):** [The Shorthand Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2012378465664745795)
|
||||||
- **Longform Guide (Advanced):** [The Longform Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2014040193557471352)
|
- **Longform Guide (Advanced):** [The Longform Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2014040193557471352)
|
||||||
@@ -1369,7 +1369,7 @@ This project is free and open source. Sponsors help keep it maintained and growi
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📄 License
|
## License
|
||||||
|
|
||||||
MIT - Use freely, modify as needed, contribute back if you can.
|
MIT - Use freely, modify as needed, contribute back if you can.
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
**🌐 Language / 语言 / 語言**
|
**Language / 语言 / 語言**
|
||||||
|
|
||||||
[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md)
|
[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md)
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 快速开始
|
## 快速开始
|
||||||
|
|
||||||
在 2 分钟内快速上手:
|
在 2 分钟内快速上手:
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
|
|
||||||
### 第二步:安装规则(必需)
|
### 第二步:安装规则(必需)
|
||||||
|
|
||||||
> ⚠️ **重要提示:** Claude Code 插件无法自动分发 `rules`,需要手动安装:
|
> WARNING: **重要提示:** Claude Code 插件无法自动分发 `rules`,需要手动安装:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 首先克隆仓库
|
# 首先克隆仓库
|
||||||
@@ -106,11 +106,11 @@ cp -r everything-claude-code/rules/perl ~/.claude/rules/
|
|||||||
/plugin list everything-claude-code@everything-claude-code
|
/plugin list everything-claude-code@everything-claude-code
|
||||||
```
|
```
|
||||||
|
|
||||||
✨ **完成!** 你现在可以使用 13 个代理、43 个技能和 31 个命令。
|
**完成!** 你现在可以使用 13 个代理、43 个技能和 31 个命令。
|
||||||
|
|
||||||
### multi-* 命令需要额外配置
|
### multi-* 命令需要额外配置
|
||||||
|
|
||||||
> ⚠️ 上面的基础插件 / rules 安装**不包含** `multi-*` 命令所需的运行时。
|
> WARNING: 上面的基础插件 / rules 安装**不包含** `multi-*` 命令所需的运行时。
|
||||||
>
|
>
|
||||||
> 如果要使用 `/multi-plan`、`/multi-execute`、`/multi-backend`、`/multi-frontend` 和 `/multi-workflow`,还需要额外安装 `ccg-workflow` 运行时。
|
> 如果要使用 `/multi-plan`、`/multi-execute`、`/multi-backend`、`/multi-frontend` 和 `/multi-workflow`,还需要额外安装 `ccg-workflow` 运行时。
|
||||||
>
|
>
|
||||||
@@ -124,7 +124,7 @@ cp -r everything-claude-code/rules/perl ~/.claude/rules/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌐 跨平台支持
|
## 跨平台支持
|
||||||
|
|
||||||
此插件现在完全支持 **Windows、macOS 和 Linux**。所有钩子和脚本都已用 Node.js 重写,以实现最大的兼容性。
|
此插件现在完全支持 **Windows、macOS 和 Linux**。所有钩子和脚本都已用 Node.js 重写,以实现最大的兼容性。
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ node scripts/setup-package-manager.js --detect
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 里面有什么
|
## 里面有什么
|
||||||
|
|
||||||
这个仓库是一个 **Claude Code 插件** - 直接安装或手动复制组件。
|
这个仓库是一个 **Claude Code 插件** - 直接安装或手动复制组件。
|
||||||
|
|
||||||
@@ -276,7 +276,7 @@ everything-claude-code/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠️ 生态系统工具
|
## 生态系统工具
|
||||||
|
|
||||||
### 技能创建器
|
### 技能创建器
|
||||||
|
|
||||||
@@ -311,7 +311,7 @@ everything-claude-code/
|
|||||||
- **直觉集合** - 用于 continuous-learning-v2
|
- **直觉集合** - 用于 continuous-learning-v2
|
||||||
- **模式提取** - 从你的提交历史中学习
|
- **模式提取** - 从你的提交历史中学习
|
||||||
|
|
||||||
### 🧠 持续学习 v2
|
### 持续学习 v2
|
||||||
|
|
||||||
基于直觉的学习系统自动学习你的模式:
|
基于直觉的学习系统自动学习你的模式:
|
||||||
|
|
||||||
@@ -328,7 +328,7 @@ everything-claude-code/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📥 安装
|
## 安装
|
||||||
|
|
||||||
### 选项 1:作为插件安装(推荐)
|
### 选项 1:作为插件安装(推荐)
|
||||||
|
|
||||||
@@ -387,7 +387,7 @@ everything-claude-code/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 🔧 选项 2:手动安装
|
### 选项 2:手动安装
|
||||||
|
|
||||||
如果你希望对安装的内容进行手动控制:
|
如果你希望对安装的内容进行手动控制:
|
||||||
|
|
||||||
@@ -425,7 +425,7 @@ cp -r everything-claude-code/skills/* ~/.claude/skills/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎯 关键概念
|
## 关键概念
|
||||||
|
|
||||||
### 代理
|
### 代理
|
||||||
|
|
||||||
@@ -485,7 +485,7 @@ model: opus
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🧪 运行测试
|
## 运行测试
|
||||||
|
|
||||||
插件包含一个全面的测试套件:
|
插件包含一个全面的测试套件:
|
||||||
|
|
||||||
@@ -501,7 +501,7 @@ node tests/hooks/hooks.test.js
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🤝 贡献
|
## 贡献
|
||||||
|
|
||||||
**欢迎并鼓励贡献。**
|
**欢迎并鼓励贡献。**
|
||||||
|
|
||||||
@@ -523,7 +523,7 @@ node tests/hooks/hooks.test.js
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📖 背景
|
## 背景
|
||||||
|
|
||||||
自实验性推出以来,我一直在使用 Claude Code。2025 年 9 月,与 [@DRodriguezFX](https://x.com/DRodriguezFX) 一起使用 Claude Code 构建 [zenith.chat](https://zenith.chat),赢得了 Anthropic x Forum Ventures 黑客马拉松。
|
自实验性推出以来,我一直在使用 Claude Code。2025 年 9 月,与 [@DRodriguezFX](https://x.com/DRodriguezFX) 一起使用 Claude Code 构建 [zenith.chat](https://zenith.chat),赢得了 Anthropic x Forum Ventures 黑客马拉松。
|
||||||
|
|
||||||
@@ -531,7 +531,7 @@ node tests/hooks/hooks.test.js
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚠️ 重要说明
|
## WARNING: 重要说明
|
||||||
|
|
||||||
### 上下文窗口管理
|
### 上下文窗口管理
|
||||||
|
|
||||||
@@ -554,13 +554,13 @@ node tests/hooks/hooks.test.js
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌟 Star 历史
|
## Star 历史
|
||||||
|
|
||||||
[](https://star-history.com/#affaan-m/everything-claude-code&Date)
|
[](https://star-history.com/#affaan-m/everything-claude-code&Date)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔗 链接
|
## 链接
|
||||||
|
|
||||||
- **精简指南(从这里开始):** [The Shorthand Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2012378465664745795)
|
- **精简指南(从这里开始):** [The Shorthand Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2012378465664745795)
|
||||||
- **详细指南(高级):** [The Longform Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2014040193557471352)
|
- **详细指南(高级):** [The Longform Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2014040193557471352)
|
||||||
@@ -570,7 +570,7 @@ node tests/hooks/hooks.test.js
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📄 许可证
|
## 许可证
|
||||||
|
|
||||||
MIT - 自由使用,根据需要修改,如果可以请回馈。
|
MIT - 自由使用,根据需要修改,如果可以请回馈。
|
||||||
|
|
||||||
|
|||||||
@@ -376,16 +376,16 @@ getTTFB(console.log); // Time to First Byte
|
|||||||
## Bundle Analysis
|
## Bundle Analysis
|
||||||
| Metric | Current | Target | Status |
|
| Metric | Current | Target | Status |
|
||||||
|--------|---------|--------|--------|
|
|--------|---------|--------|--------|
|
||||||
| Total Size (gzip) | XXX KB | < 200 KB | ⚠️ |
|
| Total Size (gzip) | XXX KB | < 200 KB | WARNING: |
|
||||||
| Main Bundle | XXX KB | < 100 KB | ✅ |
|
| Main Bundle | XXX KB | < 100 KB | PASS: |
|
||||||
| Vendor Bundle | XXX KB | < 150 KB | ⚠️ |
|
| Vendor Bundle | XXX KB | < 150 KB | WARNING: |
|
||||||
|
|
||||||
## Web Vitals
|
## Web Vitals
|
||||||
| Metric | Current | Target | Status |
|
| Metric | Current | Target | Status |
|
||||||
|--------|---------|--------|--------|
|
|--------|---------|--------|--------|
|
||||||
| LCP | X.Xs | < 2.5s | ✅ |
|
| LCP | X.Xs | < 2.5s | PASS: |
|
||||||
| FID | XXms | < 100ms | ✅ |
|
| FID | XXms | < 100ms | PASS: |
|
||||||
| CLS | X.XX | < 0.1 | ⚠️ |
|
| CLS | X.XX | < 0.1 | WARNING: |
|
||||||
|
|
||||||
## Critical Issues
|
## Critical Issues
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ Flag it clearly before resuming:
|
|||||||
```
|
```
|
||||||
ASIDE: [answer]
|
ASIDE: [answer]
|
||||||
|
|
||||||
⚠️ Note: This answer suggests [issue] with the current approach. Want to address this before continuing, or proceed as planned?
|
WARNING: Note: This answer suggests [issue] with the current approach. Want to address this before continuing, or proceed as planned?
|
||||||
```
|
```
|
||||||
Wait for the user's decision before resuming.
|
Wait for the user's decision before resuming.
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ Note the change needed but do not make it during the aside:
|
|||||||
```
|
```
|
||||||
ASIDE: [answer]
|
ASIDE: [answer]
|
||||||
|
|
||||||
📝 Worth fixing: [what should be changed]. I'll flag this after the current task unless you want to address it now.
|
Worth fixing: [what should be changed]. I'll flag this after the current task unless you want to address it now.
|
||||||
```
|
```
|
||||||
|
|
||||||
**Question is ambiguous or too vague:**
|
**Question is ambiguous or too vague:**
|
||||||
@@ -150,7 +150,7 @@ No — the shared cache object in src/cache/store.ts:34 is mutated without locki
|
|||||||
Under concurrent requests this is a race condition. It's low risk in a single-process
|
Under concurrent requests this is a race condition. It's low risk in a single-process
|
||||||
Node.js server but would be a real problem with worker threads or clustering.
|
Node.js server but would be a real problem with worker threads or clustering.
|
||||||
|
|
||||||
⚠️ Note: This could affect the feature we're building. Want to address this now or continue and fix it in a follow-up?
|
WARNING: Note: This could affect the feature we're building. Want to address this now or continue and fix it in a follow-up?
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ All tests passed.
|
|||||||
| Files modified | 2 |
|
| Files modified | 2 |
|
||||||
| Remaining issues | 0 |
|
| Remaining issues | 0 |
|
||||||
|
|
||||||
Build Status: ✅ SUCCESS
|
Build Status: PASS: SUCCESS
|
||||||
```
|
```
|
||||||
|
|
||||||
## Common Errors Fixed
|
## Common Errors Fixed
|
||||||
|
|||||||
@@ -108,16 +108,16 @@ void processUser(const User& user) {
|
|||||||
- HIGH: 1
|
- HIGH: 1
|
||||||
- MEDIUM: 0
|
- MEDIUM: 0
|
||||||
|
|
||||||
Recommendation: ❌ Block merge until CRITICAL issue is fixed
|
Recommendation: FAIL: Block merge until CRITICAL issue is fixed
|
||||||
```
|
```
|
||||||
|
|
||||||
## Approval Criteria
|
## Approval Criteria
|
||||||
|
|
||||||
| Status | Condition |
|
| Status | Condition |
|
||||||
|--------|-----------|
|
|--------|-----------|
|
||||||
| ✅ Approve | No CRITICAL or HIGH issues |
|
| PASS: Approve | No CRITICAL or HIGH issues |
|
||||||
| ⚠️ Warning | Only MEDIUM issues (merge with caution) |
|
| WARNING: Warning | Only MEDIUM issues (merge with caution) |
|
||||||
| ❌ Block | CRITICAL or HIGH issues found |
|
| FAIL: Block | CRITICAL or HIGH issues found |
|
||||||
|
|
||||||
## Integration with Other Commands
|
## Integration with Other Commands
|
||||||
|
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ Artifacts generated:
|
|||||||
╔══════════════════════════════════════════════════════════════╗
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
║ E2E Test Results ║
|
║ E2E Test Results ║
|
||||||
╠══════════════════════════════════════════════════════════════╣
|
╠══════════════════════════════════════════════════════════════╣
|
||||||
║ Status: ✅ ALL TESTS PASSED ║
|
║ Status: PASS: ALL TESTS PASSED ║
|
||||||
║ Total: 3 tests ║
|
║ Total: 3 tests ║
|
||||||
║ Passed: 3 (100%) ║
|
║ Passed: 3 (100%) ║
|
||||||
║ Failed: 0 ║
|
║ Failed: 0 ║
|
||||||
@@ -191,15 +191,15 @@ Artifacts generated:
|
|||||||
╚══════════════════════════════════════════════════════════════╝
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
Artifacts:
|
Artifacts:
|
||||||
📸 Screenshots: 2 files
|
Screenshots: 2 files
|
||||||
📹 Videos: 0 files (only on failure)
|
Videos: 0 files (only on failure)
|
||||||
🔍 Traces: 0 files (only on failure)
|
Traces: 0 files (only on failure)
|
||||||
📊 HTML Report: playwright-report/index.html
|
HTML Report: playwright-report/index.html
|
||||||
|
|
||||||
View report: npx playwright show-report
|
View report: npx playwright show-report
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ E2E test suite ready for CI/CD integration!
|
PASS: E2E test suite ready for CI/CD integration!
|
||||||
```
|
```
|
||||||
|
|
||||||
## Test Artifacts
|
## Test Artifacts
|
||||||
@@ -235,7 +235,7 @@ open artifacts/search-results.png
|
|||||||
If a test fails intermittently:
|
If a test fails intermittently:
|
||||||
|
|
||||||
```
|
```
|
||||||
⚠️ FLAKY TEST DETECTED: tests/e2e/markets/trade.spec.ts
|
WARNING: FLAKY TEST DETECTED: tests/e2e/markets/trade.spec.ts
|
||||||
|
|
||||||
Test passed 7/10 runs (70% pass rate)
|
Test passed 7/10 runs (70% pass rate)
|
||||||
|
|
||||||
@@ -254,10 +254,10 @@ Quarantine recommendation: Mark as test.fixme() until fixed
|
|||||||
## Browser Configuration
|
## Browser Configuration
|
||||||
|
|
||||||
Tests run on multiple browsers by default:
|
Tests run on multiple browsers by default:
|
||||||
- ✅ Chromium (Desktop Chrome)
|
- PASS: Chromium (Desktop Chrome)
|
||||||
- ✅ Firefox (Desktop)
|
- PASS: Firefox (Desktop)
|
||||||
- ✅ WebKit (Desktop Safari)
|
- PASS: WebKit (Desktop Safari)
|
||||||
- ✅ Mobile Chrome (optional)
|
- PASS: Mobile Chrome (optional)
|
||||||
|
|
||||||
Configure in `playwright.config.ts` to adjust browsers.
|
Configure in `playwright.config.ts` to adjust browsers.
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ Add to your CI pipeline:
|
|||||||
|
|
||||||
For PMX, prioritize these E2E tests:
|
For PMX, prioritize these E2E tests:
|
||||||
|
|
||||||
**🔴 CRITICAL (Must Always Pass):**
|
**CRITICAL (Must Always Pass):**
|
||||||
1. User can connect wallet
|
1. User can connect wallet
|
||||||
2. User can browse markets
|
2. User can browse markets
|
||||||
3. User can search markets (semantic search)
|
3. User can search markets (semantic search)
|
||||||
@@ -294,7 +294,7 @@ For PMX, prioritize these E2E tests:
|
|||||||
6. Market resolves correctly
|
6. Market resolves correctly
|
||||||
7. User can withdraw funds
|
7. User can withdraw funds
|
||||||
|
|
||||||
**🟡 IMPORTANT:**
|
**IMPORTANT:**
|
||||||
1. Market creation flow
|
1. Market creation flow
|
||||||
2. User profile updates
|
2. User profile updates
|
||||||
3. Real-time price updates
|
3. Real-time price updates
|
||||||
@@ -305,20 +305,20 @@ For PMX, prioritize these E2E tests:
|
|||||||
## Best Practices
|
## Best Practices
|
||||||
|
|
||||||
**DO:**
|
**DO:**
|
||||||
- ✅ Use Page Object Model for maintainability
|
- PASS: Use Page Object Model for maintainability
|
||||||
- ✅ Use data-testid attributes for selectors
|
- PASS: Use data-testid attributes for selectors
|
||||||
- ✅ Wait for API responses, not arbitrary timeouts
|
- PASS: Wait for API responses, not arbitrary timeouts
|
||||||
- ✅ Test critical user journeys end-to-end
|
- PASS: Test critical user journeys end-to-end
|
||||||
- ✅ Run tests before merging to main
|
- PASS: Run tests before merging to main
|
||||||
- ✅ Review artifacts when tests fail
|
- PASS: Review artifacts when tests fail
|
||||||
|
|
||||||
**DON'T:**
|
**DON'T:**
|
||||||
- ❌ Use brittle selectors (CSS classes can change)
|
- FAIL: Use brittle selectors (CSS classes can change)
|
||||||
- ❌ Test implementation details
|
- FAIL: Test implementation details
|
||||||
- ❌ Run tests against production
|
- FAIL: Run tests against production
|
||||||
- ❌ Ignore flaky tests
|
- FAIL: Ignore flaky tests
|
||||||
- ❌ Skip artifact review on failures
|
- FAIL: Skip artifact review on failures
|
||||||
- ❌ Test every edge case with E2E (use unit tests)
|
- FAIL: Test every edge case with E2E (use unit tests)
|
||||||
|
|
||||||
## Important Notes
|
## Important Notes
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ ok project/internal/handler 0.023s
|
|||||||
| Files modified | 2 |
|
| Files modified | 2 |
|
||||||
| Remaining issues | 0 |
|
| Remaining issues | 0 |
|
||||||
|
|
||||||
Build Status: ✅ SUCCESS
|
Build Status: PASS: SUCCESS
|
||||||
```
|
```
|
||||||
|
|
||||||
## Common Errors Fixed
|
## Common Errors Fixed
|
||||||
|
|||||||
@@ -124,16 +124,16 @@ return fmt.Errorf("get user %s: %w", userID, err)
|
|||||||
- HIGH: 1
|
- HIGH: 1
|
||||||
- MEDIUM: 0
|
- MEDIUM: 0
|
||||||
|
|
||||||
Recommendation: ❌ Block merge until CRITICAL issue is fixed
|
Recommendation: FAIL: Block merge until CRITICAL issue is fixed
|
||||||
```
|
```
|
||||||
|
|
||||||
## Approval Criteria
|
## Approval Criteria
|
||||||
|
|
||||||
| Status | Condition |
|
| Status | Condition |
|
||||||
|--------|-----------|
|
|--------|-----------|
|
||||||
| ✅ Approve | No CRITICAL or HIGH issues |
|
| PASS: Approve | No CRITICAL or HIGH issues |
|
||||||
| ⚠️ Warning | Only MEDIUM issues (merge with caution) |
|
| WARNING: Warning | Only MEDIUM issues (merge with caution) |
|
||||||
| ❌ Block | CRITICAL or HIGH issues found |
|
| FAIL: Block | CRITICAL or HIGH issues found |
|
||||||
|
|
||||||
## Integration with Other Commands
|
## Integration with Other Commands
|
||||||
|
|
||||||
|
|||||||
@@ -4,22 +4,23 @@ Run a deterministic repository harness audit and return a prioritized scorecard.
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
`/harness-audit [scope] [--format text|json]`
|
`/harness-audit [scope] [--format text|json] [--root path]`
|
||||||
|
|
||||||
- `scope` (optional): `repo` (default), `hooks`, `skills`, `commands`, `agents`
|
- `scope` (optional): `repo` (default), `hooks`, `skills`, `commands`, `agents`
|
||||||
- `--format`: output style (`text` default, `json` for automation)
|
- `--format`: output style (`text` default, `json` for automation)
|
||||||
|
- `--root`: audit a specific path instead of the current working directory
|
||||||
|
|
||||||
## Deterministic Engine
|
## Deterministic Engine
|
||||||
|
|
||||||
Always run:
|
Always run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
node scripts/harness-audit.js <scope> --format <text|json>
|
node scripts/harness-audit.js <scope> --format <text|json> [--root <path>]
|
||||||
```
|
```
|
||||||
|
|
||||||
This script is the source of truth for scoring and checks. Do not invent additional dimensions or ad-hoc points.
|
This script is the source of truth for scoring and checks. Do not invent additional dimensions or ad-hoc points.
|
||||||
|
|
||||||
Rubric version: `2026-03-16`.
|
Rubric version: `2026-03-30`.
|
||||||
|
|
||||||
The script computes 7 fixed categories (`0-10` normalized each):
|
The script computes 7 fixed categories (`0-10` normalized each):
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ The script computes 7 fixed categories (`0-10` normalized each):
|
|||||||
7. Cost Efficiency
|
7. Cost Efficiency
|
||||||
|
|
||||||
Scores are derived from explicit file/rule checks and are reproducible for the same commit.
|
Scores are derived from explicit file/rule checks and are reproducible for the same commit.
|
||||||
|
The script audits the current working directory by default and auto-detects whether the target is the ECC repo itself or a consumer project using ECC.
|
||||||
|
|
||||||
## Output Contract
|
## Output Contract
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ Import instincts from local file paths or HTTP(S) URLs.
|
|||||||
## Import Process
|
## Import Process
|
||||||
|
|
||||||
```
|
```
|
||||||
📥 Importing instincts from: team-instincts.yaml
|
Importing instincts from: team-instincts.yaml
|
||||||
================================================
|
================================================
|
||||||
|
|
||||||
Found 12 instincts to import.
|
Found 12 instincts to import.
|
||||||
@@ -60,12 +60,12 @@ These will be added:
|
|||||||
|
|
||||||
## Duplicate Instincts (3)
|
## Duplicate Instincts (3)
|
||||||
Already have similar instincts:
|
Already have similar instincts:
|
||||||
⚠️ prefer-functional-style
|
WARNING: prefer-functional-style
|
||||||
Local: 0.8 confidence, 12 observations
|
Local: 0.8 confidence, 12 observations
|
||||||
Import: 0.7 confidence
|
Import: 0.7 confidence
|
||||||
→ Keep local (higher confidence)
|
→ Keep local (higher confidence)
|
||||||
|
|
||||||
⚠️ test-first-workflow
|
WARNING: test-first-workflow
|
||||||
Local: 0.75 confidence
|
Local: 0.75 confidence
|
||||||
Import: 0.9 confidence
|
Import: 0.9 confidence
|
||||||
→ Update to import (higher confidence)
|
→ Update to import (higher confidence)
|
||||||
@@ -102,7 +102,7 @@ project_name: "my-project"
|
|||||||
|
|
||||||
After import:
|
After import:
|
||||||
```
|
```
|
||||||
✅ Import complete!
|
PASS: Import complete!
|
||||||
|
|
||||||
Added: 8 instincts
|
Added: 8 instincts
|
||||||
Updated: 1 instinct
|
Updated: 1 instinct
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ $ ./gradlew test
|
|||||||
| Files modified | 2 |
|
| Files modified | 2 |
|
||||||
| Remaining issues | 0 |
|
| Remaining issues | 0 |
|
||||||
|
|
||||||
Build Status: ✅ SUCCESS
|
Build Status: PASS: SUCCESS
|
||||||
````
|
````
|
||||||
|
|
||||||
## Common Errors Fixed
|
## Common Errors Fixed
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ Agent:
|
|||||||
## Static Analysis Results
|
## Static Analysis Results
|
||||||
✓ Build: Successful
|
✓ Build: Successful
|
||||||
✓ detekt: No issues
|
✓ detekt: No issues
|
||||||
⚠ ktlint: 2 formatting warnings
|
WARNING: ktlint: 2 formatting warnings
|
||||||
|
|
||||||
## Issues Found
|
## Issues Found
|
||||||
|
|
||||||
@@ -116,16 +116,16 @@ launch {
|
|||||||
- HIGH: 1
|
- HIGH: 1
|
||||||
- MEDIUM: 0
|
- MEDIUM: 0
|
||||||
|
|
||||||
Recommendation: ❌ Block merge until CRITICAL issue is fixed
|
Recommendation: FAIL: Block merge until CRITICAL issue is fixed
|
||||||
````
|
````
|
||||||
|
|
||||||
## Approval Criteria
|
## Approval Criteria
|
||||||
|
|
||||||
| Status | Condition |
|
| Status | Condition |
|
||||||
|--------|-----------|
|
|--------|-----------|
|
||||||
| ✅ Approve | No CRITICAL or HIGH issues |
|
| PASS: Approve | No CRITICAL or HIGH issues |
|
||||||
| ⚠️ Warning | Only MEDIUM issues (merge with caution) |
|
| WARNING: Warning | Only MEDIUM issues (merge with caution) |
|
||||||
| ❌ Block | CRITICAL or HIGH issues found |
|
| FAIL: Block | CRITICAL or HIGH issues found |
|
||||||
|
|
||||||
## Integration with Other Commands
|
## Integration with Other Commands
|
||||||
|
|
||||||
|
|||||||
@@ -73,19 +73,19 @@ origin: auto-extracted
|
|||||||
| **Absorb into [X]** | Should be appended to an existing skill | Show target skill and additions → Step 6 |
|
| **Absorb into [X]** | Should be appended to an existing skill | Show target skill and additions → Step 6 |
|
||||||
| **Drop** | Trivial, redundant, or too abstract | Explain reasoning and stop |
|
| **Drop** | Trivial, redundant, or too abstract | Explain reasoning and stop |
|
||||||
|
|
||||||
**Guideline dimensions** (informing the verdict, not scored):
|
**Guideline dimensions** (informing the verdict, not scored):
|
||||||
|
|
||||||
- **Specificity & Actionability**: Contains code examples or commands that are immediately usable
|
- **Specificity & Actionability**: Contains code examples or commands that are immediately usable
|
||||||
- **Scope Fit**: Name, trigger conditions, and content are aligned and focused on a single pattern
|
- **Scope Fit**: Name, trigger conditions, and content are aligned and focused on a single pattern
|
||||||
- **Uniqueness**: Provides value not covered by existing skills (informed by checklist results)
|
- **Uniqueness**: Provides value not covered by existing skills (informed by checklist results)
|
||||||
- **Reusability**: Realistic trigger scenarios exist in future sessions
|
- **Reusability**: Realistic trigger scenarios exist in future sessions
|
||||||
|
|
||||||
6. **Verdict-specific confirmation flow**
|
6. **Verdict-specific confirmation flow**
|
||||||
|
|
||||||
- **Improve then Save**: Present the required improvements + revised draft + updated checklist/verdict after one re-evaluation; if the revised verdict is **Save**, save after user confirmation, otherwise follow the new verdict
|
- **Improve then Save**: Present the required improvements + revised draft + updated checklist/verdict after one re-evaluation; if the revised verdict is **Save**, save after user confirmation, otherwise follow the new verdict
|
||||||
- **Save**: Present save path + checklist results + 1-line verdict rationale + full draft → save after user confirmation
|
- **Save**: Present save path + checklist results + 1-line verdict rationale + full draft → save after user confirmation
|
||||||
- **Absorb into [X]**: Present target path + additions (diff format) + checklist results + verdict rationale → append after user confirmation
|
- **Absorb into [X]**: Present target path + additions (diff format) + checklist results + verdict rationale → append after user confirmation
|
||||||
- **Drop**: Show checklist results + reasoning only (no confirmation needed)
|
- **Drop**: Show checklist results + reasoning only (no confirmation needed)
|
||||||
|
|
||||||
7. Save / Absorb to the determined location
|
7. Save / Absorb to the determined location
|
||||||
|
|
||||||
|
|||||||
@@ -203,19 +203,19 @@ Synthesize both analyses, generate **Step-by-step Implementation Plan**:
|
|||||||
2. Save plan to `.claude/plan/<feature-name>.md` (extract feature name from requirement, e.g., `user-auth`, `payment-module`)
|
2. Save plan to `.claude/plan/<feature-name>.md` (extract feature name from requirement, e.g., `user-auth`, `payment-module`)
|
||||||
3. Output prompt in **bold text** (MUST use actual saved file path):
|
3. Output prompt in **bold text** (MUST use actual saved file path):
|
||||||
|
|
||||||
---
|
---
|
||||||
**Plan generated and saved to `.claude/plan/actual-feature-name.md`**
|
**Plan generated and saved to `.claude/plan/actual-feature-name.md`**
|
||||||
|
|
||||||
**Please review the plan above. You can:**
|
**Please review the plan above. You can:**
|
||||||
- **Modify plan**: Tell me what needs adjustment, I'll update the plan
|
- **Modify plan**: Tell me what needs adjustment, I'll update the plan
|
||||||
- **Execute plan**: Copy the following command to a new session
|
- **Execute plan**: Copy the following command to a new session
|
||||||
|
|
||||||
```
|
```
|
||||||
/ccg:execute .claude/plan/actual-feature-name.md
|
/ccg:execute .claude/plan/actual-feature-name.md
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
|
||||||
**NOTE**: The `actual-feature-name.md` above MUST be replaced with the actual saved filename!
|
**NOTE**: The `actual-feature-name.md` above MUST be replaced with the actual saved filename!
|
||||||
|
|
||||||
4. **Immediately terminate current response** (Stop here. No more tool calls.)
|
4. **Immediately terminate current response** (Stop here. No more tool calls.)
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ Agent:
|
|||||||
## Static Analysis Results
|
## Static Analysis Results
|
||||||
✓ ruff: No issues
|
✓ ruff: No issues
|
||||||
✓ mypy: No errors
|
✓ mypy: No errors
|
||||||
⚠️ black: 2 files need reformatting
|
WARNING: black: 2 files need reformatting
|
||||||
✓ bandit: No security issues
|
✓ bandit: No security issues
|
||||||
|
|
||||||
## Issues Found
|
## Issues Found
|
||||||
@@ -155,7 +155,7 @@ with open("config.json") as f: # Good
|
|||||||
- HIGH: 1
|
- HIGH: 1
|
||||||
- MEDIUM: 2
|
- MEDIUM: 2
|
||||||
|
|
||||||
Recommendation: ❌ Block merge until CRITICAL issue is fixed
|
Recommendation: FAIL: Block merge until CRITICAL issue is fixed
|
||||||
|
|
||||||
## Formatting Required
|
## Formatting Required
|
||||||
Run: `black app/routes/user.py app/services/auth.py`
|
Run: `black app/routes/user.py app/services/auth.py`
|
||||||
@@ -165,9 +165,9 @@ Run: `black app/routes/user.py app/services/auth.py`
|
|||||||
|
|
||||||
| Status | Condition |
|
| Status | Condition |
|
||||||
|--------|-----------|
|
|--------|-----------|
|
||||||
| ✅ Approve | No CRITICAL or HIGH issues |
|
| PASS: Approve | No CRITICAL or HIGH issues |
|
||||||
| ⚠️ Warning | Only MEDIUM issues (merge with caution) |
|
| WARNING: Warning | Only MEDIUM issues (merge with caution) |
|
||||||
| ❌ Block | CRITICAL or HIGH issues found |
|
| FAIL: Block | CRITICAL or HIGH issues found |
|
||||||
|
|
||||||
## Integration with Other Commands
|
## Integration with Other Commands
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ Deleted: 12 unused functions
|
|||||||
Skipped: 2 items (tests failed)
|
Skipped: 2 items (tests failed)
|
||||||
Saved: ~450 lines removed
|
Saved: ~450 lines removed
|
||||||
──────────────────────────────
|
──────────────────────────────
|
||||||
All tests passing ✅
|
All tests passing PASS:
|
||||||
```
|
```
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ WHAT WE'RE BUILDING:
|
|||||||
[2-3 sentence summary in your own words]
|
[2-3 sentence summary in your own words]
|
||||||
|
|
||||||
CURRENT STATE:
|
CURRENT STATE:
|
||||||
✅ Working: [count] items confirmed
|
PASS: Working: [count] items confirmed
|
||||||
🔄 In Progress: [list files that are in progress]
|
In Progress: [list files that are in progress]
|
||||||
🗒️ Not Started: [list planned but untouched]
|
Not Started: [list planned but untouched]
|
||||||
|
|
||||||
WHAT NOT TO RETRY:
|
WHAT NOT TO RETRY:
|
||||||
[list every failed approach with its reason — this is critical]
|
[list every failed approach with its reason — this is critical]
|
||||||
@@ -99,10 +99,10 @@ If no next step is defined — ask the user where to start, and optionally sugge
|
|||||||
Load the most recently modified matching file for that date, regardless of whether it uses the legacy no-id format or the current short-id format.
|
Load the most recently modified matching file for that date, regardless of whether it uses the legacy no-id format or the current short-id format.
|
||||||
|
|
||||||
**Session file references files that no longer exist:**
|
**Session file references files that no longer exist:**
|
||||||
Note this during the briefing — "⚠️ `path/to/file.ts` referenced in session but not found on disk."
|
Note this during the briefing — "WARNING: `path/to/file.ts` referenced in session but not found on disk."
|
||||||
|
|
||||||
**Session file is from more than 7 days ago:**
|
**Session file is from more than 7 days ago:**
|
||||||
Note the gap — "⚠️ This session is from N days ago (threshold: 7 days). Things may have changed." — then proceed normally.
|
Note the gap — "WARNING: This session is from N days ago (threshold: 7 days). Things may have changed." — then proceed normally.
|
||||||
|
|
||||||
**User provides a file path directly (e.g., forwarded from a teammate):**
|
**User provides a file path directly (e.g., forwarded from a teammate):**
|
||||||
Read it and follow the same briefing process — the format is the same regardless of source.
|
Read it and follow the same briefing process — the format is the same regardless of source.
|
||||||
@@ -126,13 +126,13 @@ Register and login endpoints are partially done. Route protection
|
|||||||
via middleware hasn't been started yet.
|
via middleware hasn't been started yet.
|
||||||
|
|
||||||
CURRENT STATE:
|
CURRENT STATE:
|
||||||
✅ Working: 3 items (register endpoint, JWT generation, password hashing)
|
PASS: Working: 3 items (register endpoint, JWT generation, password hashing)
|
||||||
🔄 In Progress: app/api/auth/login/route.ts (token works, cookie not set yet)
|
In Progress: app/api/auth/login/route.ts (token works, cookie not set yet)
|
||||||
🗒️ Not Started: middleware.ts, app/login/page.tsx
|
Not Started: middleware.ts, app/login/page.tsx
|
||||||
|
|
||||||
WHAT NOT TO RETRY:
|
WHAT NOT TO RETRY:
|
||||||
❌ Next-Auth — conflicts with custom Prisma adapter, threw adapter error on every request
|
FAIL: Next-Auth — conflicts with custom Prisma adapter, threw adapter error on every request
|
||||||
❌ localStorage for JWT — causes SSR hydration mismatch, incompatible with Next.js
|
FAIL: localStorage for JWT — causes SSR hydration mismatch, incompatible with Next.js
|
||||||
|
|
||||||
OPEN QUESTIONS / BLOCKERS:
|
OPEN QUESTIONS / BLOCKERS:
|
||||||
- Does cookies().set() work inside a Route Handler or only Server Actions?
|
- Does cookies().set() work inside a Route Handler or only Server Actions?
|
||||||
|
|||||||
@@ -130,10 +130,10 @@ If nothing is queued: "No specific untried approaches identified."
|
|||||||
|
|
||||||
| File | Status | Notes |
|
| File | Status | Notes |
|
||||||
| ----------------- | -------------- | -------------------------- |
|
| ----------------- | -------------- | -------------------------- |
|
||||||
| `path/to/file.ts` | ✅ Complete | [what it does] |
|
| `path/to/file.ts` | PASS: Complete | [what it does] |
|
||||||
| `path/to/file.ts` | 🔄 In Progress | [what's done, what's left] |
|
| `path/to/file.ts` | In Progress | [what's done, what's left] |
|
||||||
| `path/to/file.ts` | ❌ Broken | [what's wrong] |
|
| `path/to/file.ts` | FAIL: Broken | [what's wrong] |
|
||||||
| `path/to/file.ts` | 🗒️ Not Started | [planned but not touched] |
|
| `path/to/file.ts` | Not Started | [planned but not touched] |
|
||||||
|
|
||||||
If no files were touched: "No files modified this session."
|
If no files were touched: "No files modified this session."
|
||||||
|
|
||||||
@@ -235,11 +235,11 @@ refreshes without exposing the token to JavaScript.
|
|||||||
|
|
||||||
| File | Status | Notes |
|
| File | Status | Notes |
|
||||||
| -------------------------------- | -------------- | ----------------------------------------------- |
|
| -------------------------------- | -------------- | ----------------------------------------------- |
|
||||||
| `app/api/auth/register/route.ts` | ✅ Complete | Works, tested |
|
| `app/api/auth/register/route.ts` | PASS: Complete | Works, tested |
|
||||||
| `app/api/auth/login/route.ts` | 🔄 In Progress | Token generates but not setting cookie yet |
|
| `app/api/auth/login/route.ts` | In Progress | Token generates but not setting cookie yet |
|
||||||
| `lib/auth.ts` | ✅ Complete | JWT helpers, all tested |
|
| `lib/auth.ts` | PASS: Complete | JWT helpers, all tested |
|
||||||
| `middleware.ts` | 🗒️ Not Started | Route protection, needs cookie read logic first |
|
| `middleware.ts` | Not Started | Route protection, needs cookie read logic first |
|
||||||
| `app/login/page.tsx` | 🗒️ Not Started | UI not started |
|
| `app/login/page.tsx` | Not Started | UI not started |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ FAIL lib/liquidity.test.ts
|
|||||||
1 test failed, 0 passed
|
1 test failed, 0 passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ Tests fail as expected. Ready to implement.
|
PASS: Tests fail as expected. Ready to implement.
|
||||||
|
|
||||||
## Step 4: Implement Minimal Code (GREEN)
|
## Step 4: Implement Minimal Code (GREEN)
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ PASS lib/liquidity.test.ts
|
|||||||
3 tests passed
|
3 tests passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ All tests passing!
|
PASS: All tests passing!
|
||||||
|
|
||||||
## Step 6: Refactor (IMPROVE)
|
## Step 6: Refactor (IMPROVE)
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ PASS lib/liquidity.test.ts
|
|||||||
3 tests passed
|
3 tests passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ Refactoring complete, tests still passing!
|
PASS: Refactoring complete, tests still passing!
|
||||||
|
|
||||||
## Step 8: Check Coverage
|
## Step 8: Check Coverage
|
||||||
|
|
||||||
@@ -247,29 +247,29 @@ File | % Stmts | % Branch | % Funcs | % Lines
|
|||||||
---------------|---------|----------|---------|--------
|
---------------|---------|----------|---------|--------
|
||||||
liquidity.ts | 100 | 100 | 100 | 100
|
liquidity.ts | 100 | 100 | 100 | 100
|
||||||
|
|
||||||
Coverage: 100% ✅ (Target: 80%)
|
Coverage: 100% PASS: (Target: 80%)
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ TDD session complete!
|
PASS: TDD session complete!
|
||||||
```
|
```
|
||||||
|
|
||||||
## TDD Best Practices
|
## TDD Best Practices
|
||||||
|
|
||||||
**DO:**
|
**DO:**
|
||||||
- ✅ Write the test FIRST, before any implementation
|
- PASS: Write the test FIRST, before any implementation
|
||||||
- ✅ Run tests and verify they FAIL before implementing
|
- PASS: Run tests and verify they FAIL before implementing
|
||||||
- ✅ Write minimal code to make tests pass
|
- PASS: Write minimal code to make tests pass
|
||||||
- ✅ Refactor only after tests are green
|
- PASS: Refactor only after tests are green
|
||||||
- ✅ Add edge cases and error scenarios
|
- PASS: Add edge cases and error scenarios
|
||||||
- ✅ Aim for 80%+ coverage (100% for critical code)
|
- PASS: Aim for 80%+ coverage (100% for critical code)
|
||||||
|
|
||||||
**DON'T:**
|
**DON'T:**
|
||||||
- ❌ Write implementation before tests
|
- FAIL: Write implementation before tests
|
||||||
- ❌ Skip running tests after each change
|
- FAIL: Skip running tests after each change
|
||||||
- ❌ Write too much code at once
|
- FAIL: Write too much code at once
|
||||||
- ❌ Ignore failing tests
|
- FAIL: Ignore failing tests
|
||||||
- ❌ Test implementation details (test behavior)
|
- FAIL: Test implementation details (test behavior)
|
||||||
- ❌ Mock everything (prefer integration tests)
|
- FAIL: Mock everything (prefer integration tests)
|
||||||
|
|
||||||
## Test Types to Include
|
## Test Types to Include
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ File Before After
|
|||||||
src/services/auth.ts 45% 88%
|
src/services/auth.ts 45% 88%
|
||||||
src/utils/validation.ts 32% 82%
|
src/utils/validation.ts 32% 82%
|
||||||
──────────────────────────────
|
──────────────────────────────
|
||||||
Overall: 67% 84% ✅
|
Overall: 67% 84% PASS:
|
||||||
```
|
```
|
||||||
|
|
||||||
## Focus Areas
|
## Focus Areas
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ Link to complementary skills.
|
|||||||
|
|
||||||
Good skills are **focused and actionable**:
|
Good skills are **focused and actionable**:
|
||||||
|
|
||||||
| ✅ Good Focus | ❌ Too Broad |
|
| PASS: Good Focus | FAIL: Too Broad |
|
||||||
|---------------|--------------|
|
|---------------|--------------|
|
||||||
| `react-hook-patterns` | `react` |
|
| `react-hook-patterns` | `react` |
|
||||||
| `postgresql-indexing` | `databases` |
|
| `postgresql-indexing` | `databases` |
|
||||||
@@ -186,11 +186,11 @@ Another pattern with code.
|
|||||||
|
|
||||||
Write content that Claude can **immediately use**:
|
Write content that Claude can **immediately use**:
|
||||||
|
|
||||||
- ✅ Copy-pasteable code examples
|
- PASS: Copy-pasteable code examples
|
||||||
- ✅ Clear decision trees
|
- PASS: Clear decision trees
|
||||||
- ✅ Checklists for verification
|
- PASS: Checklists for verification
|
||||||
- ❌ Vague explanations without examples
|
- FAIL: Vague explanations without examples
|
||||||
- ❌ Long prose without actionable guidance
|
- FAIL: Long prose without actionable guidance
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -361,7 +361,7 @@ Show what NOT to do:
|
|||||||
```markdown
|
```markdown
|
||||||
## Anti-Patterns
|
## Anti-Patterns
|
||||||
|
|
||||||
### ❌ Direct State Mutation
|
### FAIL: Direct State Mutation
|
||||||
|
|
||||||
\`\`\`typescript
|
\`\`\`typescript
|
||||||
// NEVER do this
|
// NEVER do this
|
||||||
@@ -369,7 +369,7 @@ user.name = 'New Name'
|
|||||||
items.push(newItem)
|
items.push(newItem)
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
### ✅ Immutable Updates
|
### PASS: Immutable Updates
|
||||||
|
|
||||||
\`\`\`typescript
|
\`\`\`typescript
|
||||||
// ALWAYS do this
|
// ALWAYS do this
|
||||||
@@ -729,12 +729,12 @@ origin: ECC
|
|||||||
### Borrowing Rules
|
### Borrowing Rules
|
||||||
|
|
||||||
\`\`\`rust
|
\`\`\`rust
|
||||||
// ✅ CORRECT: Borrow when you don't need ownership
|
// PASS: CORRECT: Borrow when you don't need ownership
|
||||||
fn process_data(data: &str) -> usize {
|
fn process_data(data: &str) -> usize {
|
||||||
data.len()
|
data.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ CORRECT: Take ownership when you need to modify or consume
|
// PASS: CORRECT: Take ownership when you need to modify or consume
|
||||||
fn consume_data(data: Vec<u8>) -> String {
|
fn consume_data(data: Vec<u8>) -> String {
|
||||||
String::from_utf8(data).unwrap()
|
String::from_utf8(data).unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
**🌐 言語 / Language / 語言**
|
**言語 / Language / 語言**
|
||||||
|
|
||||||
[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md)
|
[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md)
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 クイックスタート
|
## クイックスタート
|
||||||
|
|
||||||
2分以内に起動できます:
|
2分以内に起動できます:
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
|
|
||||||
### ステップ2:ルールをインストール(必須)
|
### ステップ2:ルールをインストール(必須)
|
||||||
|
|
||||||
> ⚠️ **重要:** Claude Codeプラグインは`rules`を自動配布できません。手動でインストールしてください:
|
> WARNING: **重要:** Claude Codeプラグインは`rules`を自動配布できません。手動でインストールしてください:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# まずリポジトリをクローン
|
# まずリポジトリをクローン
|
||||||
@@ -143,11 +143,11 @@ cp -r everything-claude-code/rules/golang/* ~/.claude/rules/
|
|||||||
/plugin list everything-claude-code@everything-claude-code
|
/plugin list everything-claude-code@everything-claude-code
|
||||||
```
|
```
|
||||||
|
|
||||||
✨ **完了です!** これで13のエージェント、43のスキル、31のコマンドにアクセスできます。
|
**完了です!** これで13のエージェント、43のスキル、31のコマンドにアクセスできます。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌐 クロスプラットフォーム対応
|
## クロスプラットフォーム対応
|
||||||
|
|
||||||
このプラグインは **Windows、macOS、Linux** を完全にサポートしています。すべてのフックとスクリプトが Node.js で書き直され、最大の互換性を実現しています。
|
このプラグインは **Windows、macOS、Linux** を完全にサポートしています。すべてのフックとスクリプトが Node.js で書き直され、最大の互換性を実現しています。
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@ node scripts/setup-package-manager.js --detect
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 含まれるもの
|
## 含まれるもの
|
||||||
|
|
||||||
このリポジトリは**Claude Codeプラグイン**です - 直接インストールするか、コンポーネントを手動でコピーできます。
|
このリポジトリは**Claude Codeプラグイン**です - 直接インストールするか、コンポーネントを手動でコピーできます。
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@ everything-claude-code/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠️ エコシステムツール
|
## エコシステムツール
|
||||||
|
|
||||||
### スキル作成ツール
|
### スキル作成ツール
|
||||||
|
|
||||||
@@ -374,7 +374,7 @@ Claude Codeで`/security-scan`を実行、または[GitHub Action](https://githu
|
|||||||
|
|
||||||
[GitHub](https://github.com/affaan-m/agentshield) | [npm](https://www.npmjs.com/package/ecc-agentshield)
|
[GitHub](https://github.com/affaan-m/agentshield) | [npm](https://www.npmjs.com/package/ecc-agentshield)
|
||||||
|
|
||||||
### 🧠 継続的学習 v2
|
### 継続的学習 v2
|
||||||
|
|
||||||
instinctベースの学習システムがパターンを自動学習:
|
instinctベースの学習システムがパターンを自動学習:
|
||||||
|
|
||||||
@@ -389,7 +389,7 @@ instinctベースの学習システムがパターンを自動学習:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 要件
|
## 要件
|
||||||
|
|
||||||
### Claude Code CLI バージョン
|
### Claude Code CLI バージョン
|
||||||
|
|
||||||
@@ -404,7 +404,7 @@ claude --version
|
|||||||
|
|
||||||
### 重要: フック自動読み込み動作
|
### 重要: フック自動読み込み動作
|
||||||
|
|
||||||
> ⚠️ **貢献者向け:** `.claude-plugin/plugin.json`に`"hooks"`フィールドを追加しないでください。これは回帰テストで強制されます。
|
> WARNING: **貢献者向け:** `.claude-plugin/plugin.json`に`"hooks"`フィールドを追加しないでください。これは回帰テストで強制されます。
|
||||||
|
|
||||||
Claude Code v2.1+は、インストール済みプラグインの`hooks/hooks.json`(規約)を自動読み込みします。`plugin.json`で明示的に宣言するとエラーが発生します:
|
Claude Code v2.1+は、インストール済みプラグインの`hooks/hooks.json`(規約)を自動読み込みします。`plugin.json`で明示的に宣言するとエラーが発生します:
|
||||||
|
|
||||||
@@ -416,7 +416,7 @@ Duplicate hook file detected: ./hooks/hooks.json is already resolved to a loaded
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📥 インストール
|
## インストール
|
||||||
|
|
||||||
### オプション1:プラグインとしてインストール(推奨)
|
### オプション1:プラグインとしてインストール(推奨)
|
||||||
|
|
||||||
@@ -471,7 +471,7 @@ Duplicate hook file detected: ./hooks/hooks.json is already resolved to a loaded
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 🔧 オプション2:手動インストール
|
### オプション2:手動インストール
|
||||||
|
|
||||||
インストール内容を手動で制御したい場合:
|
インストール内容を手動で制御したい場合:
|
||||||
|
|
||||||
@@ -507,7 +507,7 @@ cp -r everything-claude-code/skills/* ~/.claude/skills/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎯 主要概念
|
## 主要概念
|
||||||
|
|
||||||
### エージェント
|
### エージェント
|
||||||
|
|
||||||
@@ -569,7 +569,7 @@ rules/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🧪 テストを実行
|
## テストを実行
|
||||||
|
|
||||||
プラグインには包括的なテストスイートが含まれています:
|
プラグインには包括的なテストスイートが含まれています:
|
||||||
|
|
||||||
@@ -585,7 +585,7 @@ node tests/hooks/hooks.test.js
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🤝 貢献
|
## 貢献
|
||||||
|
|
||||||
**貢献は大歓迎で、奨励されています。**
|
**貢献は大歓迎で、奨励されています。**
|
||||||
|
|
||||||
@@ -637,7 +637,7 @@ npm install ecc-universal
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔌 OpenCodeサポート
|
## OpenCodeサポート
|
||||||
|
|
||||||
ECCは**フルOpenCodeサポート**をプラグインとフック含めて提供。
|
ECCは**フルOpenCodeサポート**をプラグインとフック含めて提供。
|
||||||
|
|
||||||
@@ -657,13 +657,13 @@ opencode
|
|||||||
|
|
||||||
| 機能 | Claude Code | OpenCode | ステータス |
|
| 機能 | Claude Code | OpenCode | ステータス |
|
||||||
|---------|-------------|----------|--------|
|
|---------|-------------|----------|--------|
|
||||||
| Agents | ✅ 14 エージェント | ✅ 12 エージェント | **Claude Code がリード** |
|
| Agents | PASS: 14 エージェント | PASS: 12 エージェント | **Claude Code がリード** |
|
||||||
| Commands | ✅ 30 コマンド | ✅ 24 コマンド | **Claude Code がリード** |
|
| Commands | PASS: 30 コマンド | PASS: 24 コマンド | **Claude Code がリード** |
|
||||||
| Skills | ✅ 28 スキル | ✅ 16 スキル | **Claude Code がリード** |
|
| Skills | PASS: 28 スキル | PASS: 16 スキル | **Claude Code がリード** |
|
||||||
| Hooks | ✅ 3 フェーズ | ✅ 20+ イベント | **OpenCode が多い!** |
|
| Hooks | PASS: 3 フェーズ | PASS: 20+ イベント | **OpenCode が多い!** |
|
||||||
| Rules | ✅ 8 ルール | ✅ 8 ルール | **完全パリティ** |
|
| Rules | PASS: 8 ルール | PASS: 8 ルール | **完全パリティ** |
|
||||||
| MCP Servers | ✅ 完全 | ✅ 完全 | **完全パリティ** |
|
| MCP Servers | PASS: 完全 | PASS: 完全 | **完全パリティ** |
|
||||||
| Custom Tools | ✅ フック経由 | ✅ ネイティブサポート | **OpenCode がより良い** |
|
| Custom Tools | PASS: フック経由 | PASS: ネイティブサポート | **OpenCode がより良い** |
|
||||||
|
|
||||||
### プラグイン経由のフックサポート
|
### プラグイン経由のフックサポート
|
||||||
|
|
||||||
@@ -737,7 +737,7 @@ npm install ecc-universal
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📖 背景
|
## 背景
|
||||||
|
|
||||||
実験的なリリース以来、Claude Codeを使用してきました。2025年9月、[@DRodriguezFX](https://x.com/DRodriguezFX)と一緒にClaude Codeで[zenith.chat](https://zenith.chat)を構築し、Anthropic x Forum Venturesハッカソンで優勝しました。
|
実験的なリリース以来、Claude Codeを使用してきました。2025年9月、[@DRodriguezFX](https://x.com/DRodriguezFX)と一緒にClaude Codeで[zenith.chat](https://zenith.chat)を構築し、Anthropic x Forum Venturesハッカソンで優勝しました。
|
||||||
|
|
||||||
@@ -745,7 +745,7 @@ npm install ecc-universal
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚠️ 重要な注記
|
## WARNING: 重要な注記
|
||||||
|
|
||||||
### コンテキストウィンドウ管理
|
### コンテキストウィンドウ管理
|
||||||
|
|
||||||
@@ -768,13 +768,13 @@ npm install ecc-universal
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌟 Star 履歴
|
## Star 履歴
|
||||||
|
|
||||||
[](https://star-history.com/#affaan-m/everything-claude-code&Date)
|
[](https://star-history.com/#affaan-m/everything-claude-code&Date)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔗 リンク
|
## リンク
|
||||||
|
|
||||||
- **簡潔ガイド(まずはこれ):** [Everything Claude Code 簡潔ガイド](https://x.com/affaanmustafa/status/2012378465664745795)
|
- **簡潔ガイド(まずはこれ):** [Everything Claude Code 簡潔ガイド](https://x.com/affaanmustafa/status/2012378465664745795)
|
||||||
- **詳細ガイド(高度):** [Everything Claude Code 詳細ガイド](https://x.com/affaanmustafa/status/2014040193557471352)
|
- **詳細ガイド(高度):** [Everything Claude Code 詳細ガイド](https://x.com/affaanmustafa/status/2014040193557471352)
|
||||||
@@ -784,7 +784,7 @@ npm install ecc-universal
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📄 ライセンス
|
## ライセンス
|
||||||
|
|
||||||
MIT - 自由に使用、必要に応じて修正、可能であれば貢献してください。
|
MIT - 自由に使用、必要に応じて修正、可能であれば貢献してください。
|
||||||
|
|
||||||
|
|||||||
@@ -103,12 +103,12 @@ c) 影響度別に優先順位付け
|
|||||||
|
|
||||||
**パターン 1: 型推論の失敗**
|
**パターン 1: 型推論の失敗**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: Parameter 'x' implicitly has an 'any' type
|
// FAIL: エラー: Parameter 'x' implicitly has an 'any' type
|
||||||
function add(x, y) {
|
function add(x, y) {
|
||||||
return x + y
|
return x + y
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 修正: 型アノテーションを追加
|
// PASS: 修正: 型アノテーションを追加
|
||||||
function add(x: number, y: number): number {
|
function add(x: number, y: number): number {
|
||||||
return x + y
|
return x + y
|
||||||
}
|
}
|
||||||
@@ -116,25 +116,25 @@ function add(x: number, y: number): number {
|
|||||||
|
|
||||||
**パターン 2: Null/Undefinedエラー**
|
**パターン 2: Null/Undefinedエラー**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: Object is possibly 'undefined'
|
// FAIL: エラー: Object is possibly 'undefined'
|
||||||
const name = user.name.toUpperCase()
|
const name = user.name.toUpperCase()
|
||||||
|
|
||||||
// ✅ 修正: オプショナルチェーン
|
// PASS: 修正: オプショナルチェーン
|
||||||
const name = user?.name?.toUpperCase()
|
const name = user?.name?.toUpperCase()
|
||||||
|
|
||||||
// ✅ または: Nullチェック
|
// PASS: または: Nullチェック
|
||||||
const name = user && user.name ? user.name.toUpperCase() : ''
|
const name = user && user.name ? user.name.toUpperCase() : ''
|
||||||
```
|
```
|
||||||
|
|
||||||
**パターン 3: プロパティの欠落**
|
**パターン 3: プロパティの欠落**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: Property 'age' does not exist on type 'User'
|
// FAIL: エラー: Property 'age' does not exist on type 'User'
|
||||||
interface User {
|
interface User {
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
const user: User = { name: 'John', age: 30 }
|
const user: User = { name: 'John', age: 30 }
|
||||||
|
|
||||||
// ✅ 修正: インターフェースにプロパティを追加
|
// PASS: 修正: インターフェースにプロパティを追加
|
||||||
interface User {
|
interface User {
|
||||||
name: string
|
name: string
|
||||||
age?: number // 常に存在しない場合はオプショナル
|
age?: number // 常に存在しない場合はオプショナル
|
||||||
@@ -143,10 +143,10 @@ interface User {
|
|||||||
|
|
||||||
**パターン 4: インポートエラー**
|
**パターン 4: インポートエラー**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: Cannot find module '@/lib/utils'
|
// FAIL: エラー: Cannot find module '@/lib/utils'
|
||||||
import { formatDate } from '@/lib/utils'
|
import { formatDate } from '@/lib/utils'
|
||||||
|
|
||||||
// ✅ 修正1: tsconfigのパスが正しいか確認
|
// PASS: 修正1: tsconfigのパスが正しいか確認
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"paths": {
|
"paths": {
|
||||||
@@ -155,38 +155,38 @@ import { formatDate } from '@/lib/utils'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 修正2: 相対インポートを使用
|
// PASS: 修正2: 相対インポートを使用
|
||||||
import { formatDate } from '../lib/utils'
|
import { formatDate } from '../lib/utils'
|
||||||
|
|
||||||
// ✅ 修正3: 欠落しているパッケージをインストール
|
// PASS: 修正3: 欠落しているパッケージをインストール
|
||||||
npm install @/lib/utils
|
npm install @/lib/utils
|
||||||
```
|
```
|
||||||
|
|
||||||
**パターン 5: 型の不一致**
|
**パターン 5: 型の不一致**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: Type 'string' is not assignable to type 'number'
|
// FAIL: エラー: Type 'string' is not assignable to type 'number'
|
||||||
const age: number = "30"
|
const age: number = "30"
|
||||||
|
|
||||||
// ✅ 修正: 文字列を数値にパース
|
// PASS: 修正: 文字列を数値にパース
|
||||||
const age: number = parseInt("30", 10)
|
const age: number = parseInt("30", 10)
|
||||||
|
|
||||||
// ✅ または: 型を変更
|
// PASS: または: 型を変更
|
||||||
const age: string = "30"
|
const age: string = "30"
|
||||||
```
|
```
|
||||||
|
|
||||||
**パターン 6: ジェネリック制約**
|
**パターン 6: ジェネリック制約**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: Type 'T' is not assignable to type 'string'
|
// FAIL: エラー: Type 'T' is not assignable to type 'string'
|
||||||
function getLength<T>(item: T): number {
|
function getLength<T>(item: T): number {
|
||||||
return item.length
|
return item.length
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 修正: 制約を追加
|
// PASS: 修正: 制約を追加
|
||||||
function getLength<T extends { length: number }>(item: T): number {
|
function getLength<T extends { length: number }>(item: T): number {
|
||||||
return item.length
|
return item.length
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ または: より具体的な制約
|
// PASS: または: より具体的な制約
|
||||||
function getLength<T extends string | any[]>(item: T): number {
|
function getLength<T extends string | any[]>(item: T): number {
|
||||||
return item.length
|
return item.length
|
||||||
}
|
}
|
||||||
@@ -194,14 +194,14 @@ function getLength<T extends string | any[]>(item: T): number {
|
|||||||
|
|
||||||
**パターン 7: React Hookエラー**
|
**パターン 7: React Hookエラー**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: React Hook "useState" cannot be called in a function
|
// FAIL: エラー: React Hook "useState" cannot be called in a function
|
||||||
function MyComponent() {
|
function MyComponent() {
|
||||||
if (condition) {
|
if (condition) {
|
||||||
const [state, setState] = useState(0) // エラー!
|
const [state, setState] = useState(0) // エラー!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 修正: フックをトップレベルに移動
|
// PASS: 修正: フックをトップレベルに移動
|
||||||
function MyComponent() {
|
function MyComponent() {
|
||||||
const [state, setState] = useState(0)
|
const [state, setState] = useState(0)
|
||||||
|
|
||||||
@@ -215,12 +215,12 @@ function MyComponent() {
|
|||||||
|
|
||||||
**パターン 8: Async/Awaitエラー**
|
**パターン 8: Async/Awaitエラー**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: 'await' expressions are only allowed within async functions
|
// FAIL: エラー: 'await' expressions are only allowed within async functions
|
||||||
function fetchData() {
|
function fetchData() {
|
||||||
const data = await fetch('/api/data')
|
const data = await fetch('/api/data')
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 修正: asyncキーワードを追加
|
// PASS: 修正: asyncキーワードを追加
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
const data = await fetch('/api/data')
|
const data = await fetch('/api/data')
|
||||||
}
|
}
|
||||||
@@ -228,14 +228,14 @@ async function fetchData() {
|
|||||||
|
|
||||||
**パターン 9: モジュールが見つからない**
|
**パターン 9: モジュールが見つからない**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: Cannot find module 'react' or its corresponding type declarations
|
// FAIL: エラー: Cannot find module 'react' or its corresponding type declarations
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
// ✅ 修正: 依存関係をインストール
|
// PASS: 修正: 依存関係をインストール
|
||||||
npm install react
|
npm install react
|
||||||
npm install --save-dev @types/react
|
npm install --save-dev @types/react
|
||||||
|
|
||||||
// ✅ 確認: package.jsonに依存関係があることを確認
|
// PASS: 確認: package.jsonに依存関係があることを確認
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^19.0.0"
|
"react": "^19.0.0"
|
||||||
@@ -248,18 +248,18 @@ npm install --save-dev @types/react
|
|||||||
|
|
||||||
**パターン 10: Next.js固有のエラー**
|
**パターン 10: Next.js固有のエラー**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: Fast Refresh had to perform a full reload
|
// FAIL: エラー: Fast Refresh had to perform a full reload
|
||||||
// 通常、コンポーネント以外のエクスポートが原因
|
// 通常、コンポーネント以外のエクスポートが原因
|
||||||
|
|
||||||
// ✅ 修正: エクスポートを分離
|
// PASS: 修正: エクスポートを分離
|
||||||
// ❌ 間違い: file.tsx
|
// FAIL: 間違い: file.tsx
|
||||||
export const MyComponent = () => <div />
|
export const MyComponent = () => <div />
|
||||||
export const someConstant = 42 // フルリロードの原因
|
export const someConstant = 42 // フルリロードの原因
|
||||||
|
|
||||||
// ✅ 正しい: component.tsx
|
// PASS: 正しい: component.tsx
|
||||||
export const MyComponent = () => <div />
|
export const MyComponent = () => <div />
|
||||||
|
|
||||||
// ✅ 正しい: constants.ts
|
// PASS: 正しい: constants.ts
|
||||||
export const someConstant = 42
|
export const someConstant = 42
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ export const someConstant = 42
|
|||||||
|
|
||||||
### Next.js 15 + React 19の互換性
|
### Next.js 15 + React 19の互換性
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: React 19の型変更
|
// FAIL: エラー: React 19の型変更
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -278,7 +278,7 @@ const Component: FC<Props> = ({ children }) => {
|
|||||||
return <div>{children}</div>
|
return <div>{children}</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 修正: React 19ではFCは不要
|
// PASS: 修正: React 19ではFCは不要
|
||||||
interface Props {
|
interface Props {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
@@ -290,12 +290,12 @@ const Component = ({ children }: Props) => {
|
|||||||
|
|
||||||
### Supabaseクライアントの型
|
### Supabaseクライアントの型
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: Type 'any' not assignable
|
// FAIL: エラー: Type 'any' not assignable
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('*')
|
.select('*')
|
||||||
|
|
||||||
// ✅ 修正: 型アノテーションを追加
|
// PASS: 修正: 型アノテーションを追加
|
||||||
interface Market {
|
interface Market {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@@ -310,10 +310,10 @@ const { data } = await supabase
|
|||||||
|
|
||||||
### Redis Stackの型
|
### Redis Stackの型
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: Property 'ft' does not exist on type 'RedisClientType'
|
// FAIL: エラー: Property 'ft' does not exist on type 'RedisClientType'
|
||||||
const results = await client.ft.search('idx:markets', query)
|
const results = await client.ft.search('idx:markets', query)
|
||||||
|
|
||||||
// ✅ 修正: 適切なRedis Stackの型を使用
|
// PASS: 修正: 適切なRedis Stackの型を使用
|
||||||
import { createClient } from 'redis'
|
import { createClient } from 'redis'
|
||||||
|
|
||||||
const client = createClient({
|
const client = createClient({
|
||||||
@@ -328,10 +328,10 @@ const results = await client.ft.search('idx:markets', query)
|
|||||||
|
|
||||||
### Solana Web3.jsの型
|
### Solana Web3.jsの型
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ エラー: Argument of type 'string' not assignable to 'PublicKey'
|
// FAIL: エラー: Argument of type 'string' not assignable to 'PublicKey'
|
||||||
const publicKey = wallet.address
|
const publicKey = wallet.address
|
||||||
|
|
||||||
// ✅ 修正: PublicKeyコンストラクタを使用
|
// PASS: 修正: PublicKeyコンストラクタを使用
|
||||||
import { PublicKey } from '@solana/web3.js'
|
import { PublicKey } from '@solana/web3.js'
|
||||||
const publicKey = new PublicKey(wallet.address)
|
const publicKey = new PublicKey(wallet.address)
|
||||||
```
|
```
|
||||||
@@ -341,34 +341,34 @@ const publicKey = new PublicKey(wallet.address)
|
|||||||
**重要: できる限り最小限の変更を行う**
|
**重要: できる限り最小限の変更を行う**
|
||||||
|
|
||||||
### すべきこと:
|
### すべきこと:
|
||||||
✅ 欠落している型アノテーションを追加
|
PASS: 欠落している型アノテーションを追加
|
||||||
✅ 必要な箇所にnullチェックを追加
|
PASS: 必要な箇所にnullチェックを追加
|
||||||
✅ インポート/エクスポートを修正
|
PASS: インポート/エクスポートを修正
|
||||||
✅ 欠落している依存関係を追加
|
PASS: 欠落している依存関係を追加
|
||||||
✅ 型定義を更新
|
PASS: 型定義を更新
|
||||||
✅ 設定ファイルを修正
|
PASS: 設定ファイルを修正
|
||||||
|
|
||||||
### してはいけないこと:
|
### してはいけないこと:
|
||||||
❌ 関連のないコードをリファクタリング
|
FAIL: 関連のないコードをリファクタリング
|
||||||
❌ アーキテクチャを変更
|
FAIL: アーキテクチャを変更
|
||||||
❌ 変数/関数の名前を変更(エラーの原因でない限り)
|
FAIL: 変数/関数の名前を変更(エラーの原因でない限り)
|
||||||
❌ 新機能を追加
|
FAIL: 新機能を追加
|
||||||
❌ ロジックフローを変更(エラー修正以外)
|
FAIL: ロジックフローを変更(エラー修正以外)
|
||||||
❌ パフォーマンスを最適化
|
FAIL: パフォーマンスを最適化
|
||||||
❌ コードスタイルを改善
|
FAIL: コードスタイルを改善
|
||||||
|
|
||||||
**最小差分の例:**
|
**最小差分の例:**
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ファイルは200行あり、45行目にエラーがある
|
// ファイルは200行あり、45行目にエラーがある
|
||||||
|
|
||||||
// ❌ 間違い: ファイル全体をリファクタリング
|
// FAIL: 間違い: ファイル全体をリファクタリング
|
||||||
// - 変数の名前変更
|
// - 変数の名前変更
|
||||||
// - 関数の抽出
|
// - 関数の抽出
|
||||||
// - パターンの変更
|
// - パターンの変更
|
||||||
// 結果: 50行変更
|
// 結果: 50行変更
|
||||||
|
|
||||||
// ✅ 正しい: エラーのみを修正
|
// PASS: 正しい: エラーのみを修正
|
||||||
// - 45行目に型アノテーションを追加
|
// - 45行目に型アノテーションを追加
|
||||||
// 結果: 1行変更
|
// 結果: 1行変更
|
||||||
|
|
||||||
@@ -376,12 +376,12 @@ function processData(data) { // 45行目 - エラー: 'data' implicitly has 'any
|
|||||||
return data.map(item => item.value)
|
return data.map(item => item.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 最小限の修正:
|
// PASS: 最小限の修正:
|
||||||
function processData(data: any[]) { // この行のみを変更
|
function processData(data: any[]) { // この行のみを変更
|
||||||
return data.map(item => item.value)
|
return data.map(item => item.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ より良い最小限の修正(型が既知の場合):
|
// PASS: より良い最小限の修正(型が既知の場合):
|
||||||
function processData(data: Array<{ value: number }>) {
|
function processData(data: Array<{ value: number }>) {
|
||||||
return data.map(item => item.value)
|
return data.map(item => item.value)
|
||||||
}
|
}
|
||||||
@@ -396,7 +396,7 @@ function processData(data: Array<{ value: number }>) {
|
|||||||
**ビルド対象:** Next.jsプロダクション / TypeScriptチェック / ESLint
|
**ビルド対象:** Next.jsプロダクション / TypeScriptチェック / ESLint
|
||||||
**初期エラー数:** X
|
**初期エラー数:** X
|
||||||
**修正済みエラー数:** Y
|
**修正済みエラー数:** Y
|
||||||
**ビルドステータス:** ✅ 成功 / ❌ 失敗
|
**ビルドステータス:** PASS: 成功 / FAIL: 失敗
|
||||||
|
|
||||||
## 修正済みエラー
|
## 修正済みエラー
|
||||||
|
|
||||||
@@ -430,17 +430,17 @@ Parameter 'market' implicitly has an 'any' type.
|
|||||||
|
|
||||||
## 検証手順
|
## 検証手順
|
||||||
|
|
||||||
1. ✅ TypeScriptチェック成功: `npx tsc --noEmit`
|
1. PASS: TypeScriptチェック成功: `npx tsc --noEmit`
|
||||||
2. ✅ Next.jsビルド成功: `npm run build`
|
2. PASS: Next.jsビルド成功: `npm run build`
|
||||||
3. ✅ ESLintチェック成功: `npx eslint .`
|
3. PASS: ESLintチェック成功: `npx eslint .`
|
||||||
4. ✅ 新しいエラーが導入されていない
|
4. PASS: 新しいエラーが導入されていない
|
||||||
5. ✅ 開発サーバー起動: `npm run dev`
|
5. PASS: 開発サーバー起動: `npm run dev`
|
||||||
|
|
||||||
## まとめ
|
## まとめ
|
||||||
|
|
||||||
- 解決されたエラー総数: X
|
- 解決されたエラー総数: X
|
||||||
- 変更行数総数: Y
|
- 変更行数総数: Y
|
||||||
- ビルドステータス: ✅ 成功
|
- ビルドステータス: PASS: 成功
|
||||||
- 修正時間: Z 分
|
- 修正時間: Z 分
|
||||||
- ブロッキング問題: 0 件残存
|
- ブロッキング問題: 0 件残存
|
||||||
|
|
||||||
@@ -470,19 +470,19 @@ Parameter 'market' implicitly has an 'any' type.
|
|||||||
|
|
||||||
## ビルドエラーの優先度レベル
|
## ビルドエラーの優先度レベル
|
||||||
|
|
||||||
### 🔴 クリティカル(即座に修正)
|
### クリティカル(即座に修正)
|
||||||
- ビルドが完全に壊れている
|
- ビルドが完全に壊れている
|
||||||
- 開発サーバーが起動しない
|
- 開発サーバーが起動しない
|
||||||
- プロダクションデプロイがブロックされている
|
- プロダクションデプロイがブロックされている
|
||||||
- 複数のファイルが失敗している
|
- 複数のファイルが失敗している
|
||||||
|
|
||||||
### 🟡 高(早急に修正)
|
### 高(早急に修正)
|
||||||
- 単一ファイルの失敗
|
- 単一ファイルの失敗
|
||||||
- 新しいコードの型エラー
|
- 新しいコードの型エラー
|
||||||
- インポートエラー
|
- インポートエラー
|
||||||
- 重要でないビルド警告
|
- 重要でないビルド警告
|
||||||
|
|
||||||
### 🟢 中(可能な時に修正)
|
### 中(可能な時に修正)
|
||||||
- リンター警告
|
- リンター警告
|
||||||
- 非推奨APIの使用
|
- 非推奨APIの使用
|
||||||
- 非厳格な型の問題
|
- 非厳格な型の問題
|
||||||
@@ -521,13 +521,13 @@ npm install
|
|||||||
## 成功指標
|
## 成功指標
|
||||||
|
|
||||||
ビルドエラー解決後:
|
ビルドエラー解決後:
|
||||||
- ✅ `npx tsc --noEmit` が終了コード0で終了
|
- PASS: `npx tsc --noEmit` が終了コード0で終了
|
||||||
- ✅ `npm run build` が正常に完了
|
- PASS: `npm run build` が正常に完了
|
||||||
- ✅ 新しいエラーが導入されていない
|
- PASS: 新しいエラーが導入されていない
|
||||||
- ✅ 最小限の行数変更(影響を受けたファイルの5%未満)
|
- PASS: 最小限の行数変更(影響を受けたファイルの5%未満)
|
||||||
- ✅ ビルド時間が大幅に増加していない
|
- PASS: ビルド時間が大幅に増加していない
|
||||||
- ✅ 開発サーバーがエラーなく動作
|
- PASS: 開発サーバーがエラーなく動作
|
||||||
- ✅ テストが依然として成功
|
- PASS: テストが依然として成功
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -81,15 +81,15 @@ model: opus
|
|||||||
問題: APIキーがソースコードに公開されている
|
問題: APIキーがソースコードに公開されている
|
||||||
修正: 環境変数に移動
|
修正: 環境変数に移動
|
||||||
|
|
||||||
const apiKey = "sk-abc123"; // ❌ Bad
|
const apiKey = "sk-abc123"; // FAIL: Bad
|
||||||
const apiKey = process.env.API_KEY; // ✓ Good
|
const apiKey = process.env.API_KEY; // ✓ Good
|
||||||
```
|
```
|
||||||
|
|
||||||
## 承認基準
|
## 承認基準
|
||||||
|
|
||||||
- ✅ 承認: CRITICALまたはHIGH問題なし
|
- PASS: 承認: CRITICALまたはHIGH問題なし
|
||||||
- ⚠️ 警告: MEDIUM問題のみ(注意してマージ可能)
|
- WARNING: 警告: MEDIUM問題のみ(注意してマージ可能)
|
||||||
- ❌ ブロック: CRITICALまたはHIGH問題が見つかった
|
- FAIL: ブロック: CRITICALまたはHIGH問題が見つかった
|
||||||
|
|
||||||
## プロジェクト固有のガイドライン(例)
|
## プロジェクト固有のガイドライン(例)
|
||||||
|
|
||||||
|
|||||||
@@ -112,14 +112,14 @@ c) データ保護
|
|||||||
**影響:** 大きなテーブルで100〜1000倍高速なクエリ
|
**影響:** 大きなテーブルで100〜1000倍高速なクエリ
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: 外部キーにインデックスがない
|
-- FAIL: 悪い: 外部キーにインデックスがない
|
||||||
CREATE TABLE orders (
|
CREATE TABLE orders (
|
||||||
id bigint PRIMARY KEY,
|
id bigint PRIMARY KEY,
|
||||||
customer_id bigint REFERENCES customers(id)
|
customer_id bigint REFERENCES customers(id)
|
||||||
-- インデックスが欠落!
|
-- インデックスが欠落!
|
||||||
);
|
);
|
||||||
|
|
||||||
-- ✅ 良い: 外部キーにインデックス
|
-- PASS: 良い: 外部キーにインデックス
|
||||||
CREATE TABLE orders (
|
CREATE TABLE orders (
|
||||||
id bigint PRIMARY KEY,
|
id bigint PRIMARY KEY,
|
||||||
customer_id bigint REFERENCES customers(id)
|
customer_id bigint REFERENCES customers(id)
|
||||||
@@ -137,11 +137,11 @@ CREATE INDEX orders_customer_id_idx ON orders (customer_id);
|
|||||||
| **Hash** | 等価のみ | `=`(B-treeより若干高速) |
|
| **Hash** | 等価のみ | `=`(B-treeより若干高速) |
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: JSONB包含のためのB-tree
|
-- FAIL: 悪い: JSONB包含のためのB-tree
|
||||||
CREATE INDEX products_attrs_idx ON products (attributes);
|
CREATE INDEX products_attrs_idx ON products (attributes);
|
||||||
SELECT * FROM products WHERE attributes @> '{"color": "red"}';
|
SELECT * FROM products WHERE attributes @> '{"color": "red"}';
|
||||||
|
|
||||||
-- ✅ 良い: JSONBのためのGIN
|
-- PASS: 良い: JSONBのためのGIN
|
||||||
CREATE INDEX products_attrs_idx ON products USING gin (attributes);
|
CREATE INDEX products_attrs_idx ON products USING gin (attributes);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -150,11 +150,11 @@ CREATE INDEX products_attrs_idx ON products USING gin (attributes);
|
|||||||
**影響:** 複数列クエリで5〜10倍高速
|
**影響:** 複数列クエリで5〜10倍高速
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: 個別のインデックス
|
-- FAIL: 悪い: 個別のインデックス
|
||||||
CREATE INDEX orders_status_idx ON orders (status);
|
CREATE INDEX orders_status_idx ON orders (status);
|
||||||
CREATE INDEX orders_created_idx ON orders (created_at);
|
CREATE INDEX orders_created_idx ON orders (created_at);
|
||||||
|
|
||||||
-- ✅ 良い: 複合インデックス(等価列を最初に、次に範囲)
|
-- PASS: 良い: 複合インデックス(等価列を最初に、次に範囲)
|
||||||
CREATE INDEX orders_status_created_idx ON orders (status, created_at);
|
CREATE INDEX orders_status_created_idx ON orders (status, created_at);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -170,11 +170,11 @@ CREATE INDEX orders_status_created_idx ON orders (status, created_at);
|
|||||||
**影響:** テーブルルックアップを回避することで2〜5倍高速なクエリ
|
**影響:** テーブルルックアップを回避することで2〜5倍高速なクエリ
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: テーブルからnameを取得する必要がある
|
-- FAIL: 悪い: テーブルからnameを取得する必要がある
|
||||||
CREATE INDEX users_email_idx ON users (email);
|
CREATE INDEX users_email_idx ON users (email);
|
||||||
SELECT email, name FROM users WHERE email = 'user@example.com';
|
SELECT email, name FROM users WHERE email = 'user@example.com';
|
||||||
|
|
||||||
-- ✅ 良い: すべての列がインデックスに含まれる
|
-- PASS: 良い: すべての列がインデックスに含まれる
|
||||||
CREATE INDEX users_email_idx ON users (email) INCLUDE (name, created_at);
|
CREATE INDEX users_email_idx ON users (email) INCLUDE (name, created_at);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -183,10 +183,10 @@ CREATE INDEX users_email_idx ON users (email) INCLUDE (name, created_at);
|
|||||||
**影響:** 5〜20倍小さいインデックス、高速な書き込みとクエリ
|
**影響:** 5〜20倍小さいインデックス、高速な書き込みとクエリ
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: 完全なインデックスには削除された行が含まれる
|
-- FAIL: 悪い: 完全なインデックスには削除された行が含まれる
|
||||||
CREATE INDEX users_email_idx ON users (email);
|
CREATE INDEX users_email_idx ON users (email);
|
||||||
|
|
||||||
-- ✅ 良い: 部分インデックスは削除された行を除外
|
-- PASS: 良い: 部分インデックスは削除された行を除外
|
||||||
CREATE INDEX users_active_email_idx ON users (email) WHERE deleted_at IS NULL;
|
CREATE INDEX users_active_email_idx ON users (email) WHERE deleted_at IS NULL;
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ CREATE INDEX users_active_email_idx ON users (email) WHERE deleted_at IS NULL;
|
|||||||
### 1. データ型の選択
|
### 1. データ型の選択
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: 不適切な型選択
|
-- FAIL: 悪い: 不適切な型選択
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id int, -- 21億でオーバーフロー
|
id int, -- 21億でオーバーフロー
|
||||||
email varchar(255), -- 人為的な制限
|
email varchar(255), -- 人為的な制限
|
||||||
@@ -211,7 +211,7 @@ CREATE TABLE users (
|
|||||||
balance float -- 精度の損失
|
balance float -- 精度の損失
|
||||||
);
|
);
|
||||||
|
|
||||||
-- ✅ 良い: 適切な型
|
-- PASS: 良い: 適切な型
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||||
email text NOT NULL,
|
email text NOT NULL,
|
||||||
@@ -224,18 +224,18 @@ CREATE TABLE users (
|
|||||||
### 2. 主キー戦略
|
### 2. 主キー戦略
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ✅ 単一データベース: IDENTITY(デフォルト、推奨)
|
-- PASS: 単一データベース: IDENTITY(デフォルト、推奨)
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY
|
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY
|
||||||
);
|
);
|
||||||
|
|
||||||
-- ✅ 分散システム: UUIDv7(時間順)
|
-- PASS: 分散システム: UUIDv7(時間順)
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_uuidv7;
|
CREATE EXTENSION IF NOT EXISTS pg_uuidv7;
|
||||||
CREATE TABLE orders (
|
CREATE TABLE orders (
|
||||||
id uuid DEFAULT uuid_generate_v7() PRIMARY KEY
|
id uuid DEFAULT uuid_generate_v7() PRIMARY KEY
|
||||||
);
|
);
|
||||||
|
|
||||||
-- ❌ 避ける: ランダムUUIDはインデックスの断片化を引き起こす
|
-- FAIL: 避ける: ランダムUUIDはインデックスの断片化を引き起こす
|
||||||
CREATE TABLE events (
|
CREATE TABLE events (
|
||||||
id uuid DEFAULT gen_random_uuid() PRIMARY KEY -- 断片化した挿入!
|
id uuid DEFAULT gen_random_uuid() PRIMARY KEY -- 断片化した挿入!
|
||||||
);
|
);
|
||||||
@@ -246,7 +246,7 @@ CREATE TABLE events (
|
|||||||
**使用する場合:** テーブル > 1億行、時系列データ、古いデータを削除する必要がある
|
**使用する場合:** テーブル > 1億行、時系列データ、古いデータを削除する必要がある
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ✅ 良い: 月ごとにパーティション化
|
-- PASS: 良い: 月ごとにパーティション化
|
||||||
CREATE TABLE events (
|
CREATE TABLE events (
|
||||||
id bigint GENERATED ALWAYS AS IDENTITY,
|
id bigint GENERATED ALWAYS AS IDENTITY,
|
||||||
created_at timestamptz NOT NULL,
|
created_at timestamptz NOT NULL,
|
||||||
@@ -266,11 +266,11 @@ DROP TABLE events_2023_01; -- 数時間かかるDELETEではなく即座に
|
|||||||
### 4. 小文字の識別子を使用
|
### 4. 小文字の識別子を使用
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: 引用符付きの混合ケースは至る所で引用符が必要
|
-- FAIL: 悪い: 引用符付きの混合ケースは至る所で引用符が必要
|
||||||
CREATE TABLE "Users" ("userId" bigint, "firstName" text);
|
CREATE TABLE "Users" ("userId" bigint, "firstName" text);
|
||||||
SELECT "firstName" FROM "Users"; -- 引用符が必須!
|
SELECT "firstName" FROM "Users"; -- 引用符が必須!
|
||||||
|
|
||||||
-- ✅ 良い: 小文字は引用符なしで機能
|
-- PASS: 良い: 小文字は引用符なしで機能
|
||||||
CREATE TABLE users (user_id bigint, first_name text);
|
CREATE TABLE users (user_id bigint, first_name text);
|
||||||
SELECT first_name FROM users;
|
SELECT first_name FROM users;
|
||||||
```
|
```
|
||||||
@@ -284,11 +284,11 @@ SELECT first_name FROM users;
|
|||||||
**影響:** 重要 - データベースで強制されるテナント分離
|
**影響:** 重要 - データベースで強制されるテナント分離
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: アプリケーションのみのフィルタリング
|
-- FAIL: 悪い: アプリケーションのみのフィルタリング
|
||||||
SELECT * FROM orders WHERE user_id = $current_user_id;
|
SELECT * FROM orders WHERE user_id = $current_user_id;
|
||||||
-- バグはすべての注文が露出することを意味する!
|
-- バグはすべての注文が露出することを意味する!
|
||||||
|
|
||||||
-- ✅ 良い: データベースで強制されるRLS
|
-- PASS: 良い: データベースで強制されるRLS
|
||||||
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE orders FORCE ROW LEVEL SECURITY;
|
ALTER TABLE orders FORCE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
@@ -308,11 +308,11 @@ CREATE POLICY orders_user_policy ON orders
|
|||||||
**影響:** 5〜10倍高速なRLSクエリ
|
**影響:** 5〜10倍高速なRLSクエリ
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: 関数が行ごとに呼び出される
|
-- FAIL: 悪い: 関数が行ごとに呼び出される
|
||||||
CREATE POLICY orders_policy ON orders
|
CREATE POLICY orders_policy ON orders
|
||||||
USING (auth.uid() = user_id); -- 100万行に対して100万回呼び出される!
|
USING (auth.uid() = user_id); -- 100万行に対して100万回呼び出される!
|
||||||
|
|
||||||
-- ✅ 良い: SELECTでラップ(キャッシュされ、一度だけ呼び出される)
|
-- PASS: 良い: SELECTでラップ(キャッシュされ、一度だけ呼び出される)
|
||||||
CREATE POLICY orders_policy ON orders
|
CREATE POLICY orders_policy ON orders
|
||||||
USING ((SELECT auth.uid()) = user_id); -- 100倍高速
|
USING ((SELECT auth.uid()) = user_id); -- 100倍高速
|
||||||
|
|
||||||
@@ -323,10 +323,10 @@ CREATE INDEX orders_user_id_idx ON orders (user_id);
|
|||||||
### 3. 最小権限アクセス
|
### 3. 最小権限アクセス
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: 過度に許可的
|
-- FAIL: 悪い: 過度に許可的
|
||||||
GRANT ALL PRIVILEGES ON ALL TABLES TO app_user;
|
GRANT ALL PRIVILEGES ON ALL TABLES TO app_user;
|
||||||
|
|
||||||
-- ✅ 良い: 最小限の権限
|
-- PASS: 良い: 最小限の権限
|
||||||
CREATE ROLE app_readonly NOLOGIN;
|
CREATE ROLE app_readonly NOLOGIN;
|
||||||
GRANT USAGE ON SCHEMA public TO app_readonly;
|
GRANT USAGE ON SCHEMA public TO app_readonly;
|
||||||
GRANT SELECT ON public.products, public.categories TO app_readonly;
|
GRANT SELECT ON public.products, public.categories TO app_readonly;
|
||||||
@@ -378,14 +378,14 @@ SELECT pg_reload_conf();
|
|||||||
### 1. トランザクションを短く保つ
|
### 1. トランザクションを短く保つ
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: 外部APIコール中にロックを保持
|
-- FAIL: 悪い: 外部APIコール中にロックを保持
|
||||||
BEGIN;
|
BEGIN;
|
||||||
SELECT * FROM orders WHERE id = 1 FOR UPDATE;
|
SELECT * FROM orders WHERE id = 1 FOR UPDATE;
|
||||||
-- HTTPコールに5秒かかる...
|
-- HTTPコールに5秒かかる...
|
||||||
UPDATE orders SET status = 'paid' WHERE id = 1;
|
UPDATE orders SET status = 'paid' WHERE id = 1;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ✅ 良い: 最小限のロック期間
|
-- PASS: 良い: 最小限のロック期間
|
||||||
-- トランザクション外で最初にAPIコールを実行
|
-- トランザクション外で最初にAPIコールを実行
|
||||||
BEGIN;
|
BEGIN;
|
||||||
UPDATE orders SET status = 'paid', payment_id = $1
|
UPDATE orders SET status = 'paid', payment_id = $1
|
||||||
@@ -397,12 +397,12 @@ COMMIT; -- ミリ秒でロックを保持
|
|||||||
### 2. デッドロックを防ぐ
|
### 2. デッドロックを防ぐ
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: 一貫性のないロック順序がデッドロックを引き起こす
|
-- FAIL: 悪い: 一貫性のないロック順序がデッドロックを引き起こす
|
||||||
-- トランザクションA: 行1をロック、次に行2
|
-- トランザクションA: 行1をロック、次に行2
|
||||||
-- トランザクションB: 行2をロック、次に行1
|
-- トランザクションB: 行2をロック、次に行1
|
||||||
-- デッドロック!
|
-- デッドロック!
|
||||||
|
|
||||||
-- ✅ 良い: 一貫したロック順序
|
-- PASS: 良い: 一貫したロック順序
|
||||||
BEGIN;
|
BEGIN;
|
||||||
SELECT * FROM accounts WHERE id IN (1, 2) ORDER BY id FOR UPDATE;
|
SELECT * FROM accounts WHERE id IN (1, 2) ORDER BY id FOR UPDATE;
|
||||||
-- これで両方の行がロックされ、任意の順序で更新可能
|
-- これで両方の行がロックされ、任意の順序で更新可能
|
||||||
@@ -416,10 +416,10 @@ COMMIT;
|
|||||||
**影響:** ワーカーキューで10倍のスループット
|
**影響:** ワーカーキューで10倍のスループット
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: ワーカーが互いを待つ
|
-- FAIL: 悪い: ワーカーが互いを待つ
|
||||||
SELECT * FROM jobs WHERE status = 'pending' LIMIT 1 FOR UPDATE;
|
SELECT * FROM jobs WHERE status = 'pending' LIMIT 1 FOR UPDATE;
|
||||||
|
|
||||||
-- ✅ 良い: ワーカーはロックされた行をスキップ
|
-- PASS: 良い: ワーカーはロックされた行をスキップ
|
||||||
UPDATE jobs
|
UPDATE jobs
|
||||||
SET status = 'processing', worker_id = $1, started_at = now()
|
SET status = 'processing', worker_id = $1, started_at = now()
|
||||||
WHERE id = (
|
WHERE id = (
|
||||||
@@ -441,36 +441,36 @@ RETURNING *;
|
|||||||
**影響:** バルク挿入が10〜50倍高速
|
**影響:** バルク挿入が10〜50倍高速
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: 個別の挿入
|
-- FAIL: 悪い: 個別の挿入
|
||||||
INSERT INTO events (user_id, action) VALUES (1, 'click');
|
INSERT INTO events (user_id, action) VALUES (1, 'click');
|
||||||
INSERT INTO events (user_id, action) VALUES (2, 'view');
|
INSERT INTO events (user_id, action) VALUES (2, 'view');
|
||||||
-- 1000回のラウンドトリップ
|
-- 1000回のラウンドトリップ
|
||||||
|
|
||||||
-- ✅ 良い: バッチ挿入
|
-- PASS: 良い: バッチ挿入
|
||||||
INSERT INTO events (user_id, action) VALUES
|
INSERT INTO events (user_id, action) VALUES
|
||||||
(1, 'click'),
|
(1, 'click'),
|
||||||
(2, 'view'),
|
(2, 'view'),
|
||||||
(3, 'click');
|
(3, 'click');
|
||||||
-- 1回のラウンドトリップ
|
-- 1回のラウンドトリップ
|
||||||
|
|
||||||
-- ✅ 最良: 大きなデータセットにはCOPY
|
-- PASS: 最良: 大きなデータセットにはCOPY
|
||||||
COPY events (user_id, action) FROM '/path/to/data.csv' WITH (FORMAT csv);
|
COPY events (user_id, action) FROM '/path/to/data.csv' WITH (FORMAT csv);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. N+1クエリの排除
|
### 2. N+1クエリの排除
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: N+1パターン
|
-- FAIL: 悪い: N+1パターン
|
||||||
SELECT id FROM users WHERE active = true; -- 100件のIDを返す
|
SELECT id FROM users WHERE active = true; -- 100件のIDを返す
|
||||||
-- 次に100回のクエリ:
|
-- 次に100回のクエリ:
|
||||||
SELECT * FROM orders WHERE user_id = 1;
|
SELECT * FROM orders WHERE user_id = 1;
|
||||||
SELECT * FROM orders WHERE user_id = 2;
|
SELECT * FROM orders WHERE user_id = 2;
|
||||||
-- ... 98回以上
|
-- ... 98回以上
|
||||||
|
|
||||||
-- ✅ 良い: ANYを使用した単一クエリ
|
-- PASS: 良い: ANYを使用した単一クエリ
|
||||||
SELECT * FROM orders WHERE user_id = ANY(ARRAY[1, 2, 3, ...]);
|
SELECT * FROM orders WHERE user_id = ANY(ARRAY[1, 2, 3, ...]);
|
||||||
|
|
||||||
-- ✅ 良い: JOIN
|
-- PASS: 良い: JOIN
|
||||||
SELECT u.id, u.name, o.*
|
SELECT u.id, u.name, o.*
|
||||||
FROM users u
|
FROM users u
|
||||||
LEFT JOIN orders o ON o.user_id = u.id
|
LEFT JOIN orders o ON o.user_id = u.id
|
||||||
@@ -482,11 +482,11 @@ WHERE u.active = true;
|
|||||||
**影響:** ページの深さに関係なく一貫したO(1)パフォーマンス
|
**影響:** ページの深さに関係なく一貫したO(1)パフォーマンス
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: OFFSETは深さとともに遅くなる
|
-- FAIL: 悪い: OFFSETは深さとともに遅くなる
|
||||||
SELECT * FROM products ORDER BY id LIMIT 20 OFFSET 199980;
|
SELECT * FROM products ORDER BY id LIMIT 20 OFFSET 199980;
|
||||||
-- 200,000行をスキャン!
|
-- 200,000行をスキャン!
|
||||||
|
|
||||||
-- ✅ 良い: カーソルベース(常に高速)
|
-- PASS: 良い: カーソルベース(常に高速)
|
||||||
SELECT * FROM products WHERE id > 199980 ORDER BY id LIMIT 20;
|
SELECT * FROM products WHERE id > 199980 ORDER BY id LIMIT 20;
|
||||||
-- インデックスを使用、O(1)
|
-- インデックスを使用、O(1)
|
||||||
```
|
```
|
||||||
@@ -494,11 +494,11 @@ SELECT * FROM products WHERE id > 199980 ORDER BY id LIMIT 20;
|
|||||||
### 4. 挿入または更新のためのUPSERT
|
### 4. 挿入または更新のためのUPSERT
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ❌ 悪い: 競合状態
|
-- FAIL: 悪い: 競合状態
|
||||||
SELECT * FROM settings WHERE user_id = 123 AND key = 'theme';
|
SELECT * FROM settings WHERE user_id = 123 AND key = 'theme';
|
||||||
-- 両方のスレッドが何も見つけず、両方が挿入、一方が失敗
|
-- 両方のスレッドが何も見つけず、両方が挿入、一方が失敗
|
||||||
|
|
||||||
-- ✅ 良い: アトミックなUPSERT
|
-- PASS: 良い: アトミックなUPSERT
|
||||||
INSERT INTO settings (user_id, key, value)
|
INSERT INTO settings (user_id, key, value)
|
||||||
VALUES (123, 'theme', 'dark')
|
VALUES (123, 'theme', 'dark')
|
||||||
ON CONFLICT (user_id, key)
|
ON CONFLICT (user_id, key)
|
||||||
@@ -605,27 +605,27 @@ ORDER BY rank DESC;
|
|||||||
|
|
||||||
## フラグを立てるべきアンチパターン
|
## フラグを立てるべきアンチパターン
|
||||||
|
|
||||||
### ❌ クエリアンチパターン
|
### FAIL: クエリアンチパターン
|
||||||
- 本番コードでの`SELECT *`
|
- 本番コードでの`SELECT *`
|
||||||
- WHERE/JOIN列にインデックスがない
|
- WHERE/JOIN列にインデックスがない
|
||||||
- 大きなテーブルでのOFFSETページネーション
|
- 大きなテーブルでのOFFSETページネーション
|
||||||
- N+1クエリパターン
|
- N+1クエリパターン
|
||||||
- パラメータ化されていないクエリ(SQLインジェクションリスク)
|
- パラメータ化されていないクエリ(SQLインジェクションリスク)
|
||||||
|
|
||||||
### ❌ スキーマアンチパターン
|
### FAIL: スキーマアンチパターン
|
||||||
- IDに`int`(`bigint`を使用)
|
- IDに`int`(`bigint`を使用)
|
||||||
- 理由なく`varchar(255)`(`text`を使用)
|
- 理由なく`varchar(255)`(`text`を使用)
|
||||||
- タイムゾーンなしの`timestamp`(`timestamptz`を使用)
|
- タイムゾーンなしの`timestamp`(`timestamptz`を使用)
|
||||||
- 主キーとしてのランダムUUID(UUIDv7またはIDENTITYを使用)
|
- 主キーとしてのランダムUUID(UUIDv7またはIDENTITYを使用)
|
||||||
- 引用符を必要とする混合ケースの識別子
|
- 引用符を必要とする混合ケースの識別子
|
||||||
|
|
||||||
### ❌ セキュリティアンチパターン
|
### FAIL: セキュリティアンチパターン
|
||||||
- アプリケーションユーザーへの`GRANT ALL`
|
- アプリケーションユーザーへの`GRANT ALL`
|
||||||
- マルチテナントテーブルでRLSが欠落
|
- マルチテナントテーブルでRLSが欠落
|
||||||
- 行ごとに関数を呼び出すRLSポリシー(SELECTでラップされていない)
|
- 行ごとに関数を呼び出すRLSポリシー(SELECTでラップされていない)
|
||||||
- RLSポリシー列にインデックスがない
|
- RLSポリシー列にインデックスがない
|
||||||
|
|
||||||
### ❌ 接続アンチパターン
|
### FAIL: 接続アンチパターン
|
||||||
- 接続プーリングなし
|
- 接続プーリングなし
|
||||||
- アイドルタイムアウトなし
|
- アイドルタイムアウトなし
|
||||||
- トランザクションモードプーリングでのプリペアドステートメント
|
- トランザクションモードプーリングでのプリペアドステートメント
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ function extractJSDoc(pattern: string) {
|
|||||||
- [x] 古い参照なし
|
- [x] 古い参照なし
|
||||||
|
|
||||||
### 影響
|
### 影響
|
||||||
🟢 低 - ドキュメントのみ、コード変更なし
|
低 - ドキュメントのみ、コード変更なし
|
||||||
|
|
||||||
完全なアーキテクチャ概要についてはdocs/CODEMAPS/INDEX.mdを参照してください。
|
完全なアーキテクチャ概要についてはdocs/CODEMAPS/INDEX.mdを参照してください。
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -428,28 +428,28 @@ test('market search with complex query', async ({ page }) => {
|
|||||||
|
|
||||||
**1. 競合状態**
|
**1. 競合状態**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ 不安定: 要素が準備完了であると仮定しない
|
// FAIL: 不安定: 要素が準備完了であると仮定しない
|
||||||
await page.click('[data-testid="button"]')
|
await page.click('[data-testid="button"]')
|
||||||
|
|
||||||
// ✅ 安定: 要素が準備完了になるのを待つ
|
// PASS: 安定: 要素が準備完了になるのを待つ
|
||||||
await page.locator('[data-testid="button"]').click() // 組み込みの自動待機
|
await page.locator('[data-testid="button"]').click() // 組み込みの自動待機
|
||||||
```
|
```
|
||||||
|
|
||||||
**2. ネットワークタイミング**
|
**2. ネットワークタイミング**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ 不安定: 任意のタイムアウト
|
// FAIL: 不安定: 任意のタイムアウト
|
||||||
await page.waitForTimeout(5000)
|
await page.waitForTimeout(5000)
|
||||||
|
|
||||||
// ✅ 安定: 特定の条件を待つ
|
// PASS: 安定: 特定の条件を待つ
|
||||||
await page.waitForResponse(resp => resp.url().includes('/api/markets'))
|
await page.waitForResponse(resp => resp.url().includes('/api/markets'))
|
||||||
```
|
```
|
||||||
|
|
||||||
**3. アニメーションタイミング**
|
**3. アニメーションタイミング**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ 不安定: アニメーション中にクリック
|
// FAIL: 不安定: アニメーション中にクリック
|
||||||
await page.click('[data-testid="menu-item"]')
|
await page.click('[data-testid="menu-item"]')
|
||||||
|
|
||||||
// ✅ 安定: アニメーションが完了するのを待つ
|
// PASS: 安定: アニメーションが完了するのを待つ
|
||||||
await page.locator('[data-testid="menu-item"]').waitFor({ state: 'visible' })
|
await page.locator('[data-testid="menu-item"]').waitFor({ state: 'visible' })
|
||||||
await page.waitForLoadState('networkidle')
|
await page.waitForLoadState('networkidle')
|
||||||
await page.click('[data-testid="menu-item"]')
|
await page.click('[data-testid="menu-item"]')
|
||||||
@@ -548,7 +548,7 @@ jobs:
|
|||||||
|
|
||||||
**日付:** YYYY-MM-DD HH:MM
|
**日付:** YYYY-MM-DD HH:MM
|
||||||
**期間:** Xm Ys
|
**期間:** Xm Ys
|
||||||
**ステータス:** ✅ 成功 / ❌ 失敗
|
**ステータス:** PASS: 成功 / FAIL: 失敗
|
||||||
|
|
||||||
## まとめ
|
## まとめ
|
||||||
|
|
||||||
@@ -561,20 +561,20 @@ jobs:
|
|||||||
## スイート別テスト結果
|
## スイート別テスト結果
|
||||||
|
|
||||||
### Markets - ブラウズと検索
|
### Markets - ブラウズと検索
|
||||||
- ✅ user can browse markets (2.3s)
|
- PASS: user can browse markets (2.3s)
|
||||||
- ✅ semantic search returns relevant results (1.8s)
|
- PASS: semantic search returns relevant results (1.8s)
|
||||||
- ✅ search handles no results (1.2s)
|
- PASS: search handles no results (1.2s)
|
||||||
- ❌ search with special characters (0.9s)
|
- FAIL: search with special characters (0.9s)
|
||||||
|
|
||||||
### Wallet - 接続
|
### Wallet - 接続
|
||||||
- ✅ user can connect MetaMask (3.1s)
|
- PASS: user can connect MetaMask (3.1s)
|
||||||
- ⚠️ user can connect Phantom (2.8s) - 不安定
|
- WARNING: user can connect Phantom (2.8s) - 不安定
|
||||||
- ✅ user can disconnect wallet (1.5s)
|
- PASS: user can disconnect wallet (1.5s)
|
||||||
|
|
||||||
### Trading - コアフロー
|
### Trading - コアフロー
|
||||||
- ✅ user can place buy order (5.2s)
|
- PASS: user can place buy order (5.2s)
|
||||||
- ❌ user can place sell order (4.8s)
|
- FAIL: user can place sell order (4.8s)
|
||||||
- ✅ insufficient balance shows error (1.9s)
|
- PASS: insufficient balance shows error (1.9s)
|
||||||
|
|
||||||
## 失敗したテスト
|
## 失敗したテスト
|
||||||
|
|
||||||
@@ -623,13 +623,13 @@ jobs:
|
|||||||
## 成功指標
|
## 成功指標
|
||||||
|
|
||||||
E2Eテスト実行後:
|
E2Eテスト実行後:
|
||||||
- ✅ すべての重要なジャーニーが成功(100%)
|
- PASS: すべての重要なジャーニーが成功(100%)
|
||||||
- ✅ 全体の成功率 > 95%
|
- PASS: 全体の成功率 > 95%
|
||||||
- ✅ 不安定率 < 5%
|
- PASS: 不安定率 < 5%
|
||||||
- ✅ デプロイをブロックする失敗したテストなし
|
- PASS: デプロイをブロックする失敗したテストなし
|
||||||
- ✅ アーティファクトがアップロードされアクセス可能
|
- PASS: アーティファクトがアップロードされアクセス可能
|
||||||
- ✅ テスト時間 < 10分
|
- PASS: テスト時間 < 10分
|
||||||
- ✅ HTMLレポートが生成された
|
- PASS: HTMLレポートが生成された
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -146,22 +146,22 @@ e) テストがまだ合格することを確認
|
|||||||
|
|
||||||
### 1. 未使用のインポート
|
### 1. 未使用のインポート
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ 未使用のインポートを削除
|
// FAIL: 未使用のインポートを削除
|
||||||
import { useState, useEffect, useMemo } from 'react' // useStateのみ使用
|
import { useState, useEffect, useMemo } from 'react' // useStateのみ使用
|
||||||
|
|
||||||
// ✅ 使用されているもののみを保持
|
// PASS: 使用されているもののみを保持
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. デッドコードブランチ
|
### 2. デッドコードブランチ
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ 到達不可能なコードを削除
|
// FAIL: 到達不可能なコードを削除
|
||||||
if (false) {
|
if (false) {
|
||||||
// これは決して実行されない
|
// これは決して実行されない
|
||||||
doSomething()
|
doSomething()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ 未使用の関数を削除
|
// FAIL: 未使用の関数を削除
|
||||||
export function unusedHelper() {
|
export function unusedHelper() {
|
||||||
// コードベースに参照なし
|
// コードベースに参照なし
|
||||||
}
|
}
|
||||||
@@ -169,18 +169,18 @@ export function unusedHelper() {
|
|||||||
|
|
||||||
### 3. 重複コンポーネント
|
### 3. 重複コンポーネント
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ 複数の類似コンポーネント
|
// FAIL: 複数の類似コンポーネント
|
||||||
components/Button.tsx
|
components/Button.tsx
|
||||||
components/PrimaryButton.tsx
|
components/PrimaryButton.tsx
|
||||||
components/NewButton.tsx
|
components/NewButton.tsx
|
||||||
|
|
||||||
// ✅ 1つに統合
|
// PASS: 1つに統合
|
||||||
components/Button.tsx (variantプロップ付き)
|
components/Button.tsx (variantプロップ付き)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 未使用の依存関係
|
### 4. 未使用の依存関係
|
||||||
```json
|
```json
|
||||||
// ❌ インストールされているがインポートされていないパッケージ
|
// FAIL: インストールされているがインポートされていないパッケージ
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "^4.17.21", // どこでも使用されていない
|
"lodash": "^4.17.21", // どこでも使用されていない
|
||||||
@@ -240,7 +240,7 @@ components/Button.tsx (variantプロップ付き)
|
|||||||
- 依存関係: -Xパッケージ
|
- 依存関係: -Xパッケージ
|
||||||
|
|
||||||
### リスクレベル
|
### リスクレベル
|
||||||
🟢 低 - 検証可能な未使用コードのみを削除
|
低 - 検証可能な未使用コードのみを削除
|
||||||
|
|
||||||
詳細はDELETION_LOG.mdを参照してください。
|
詳細はDELETION_LOG.mdを参照してください。
|
||||||
```
|
```
|
||||||
@@ -294,12 +294,12 @@ components/Button.tsx (variantプロップ付き)
|
|||||||
## 成功指標
|
## 成功指標
|
||||||
|
|
||||||
クリーンアップセッション後:
|
クリーンアップセッション後:
|
||||||
- ✅ すべてのテストが合格
|
- PASS: すべてのテストが合格
|
||||||
- ✅ ビルドが成功
|
- PASS: ビルドが成功
|
||||||
- ✅ コンソールエラーなし
|
- PASS: コンソールエラーなし
|
||||||
- ✅ DELETION_LOG.mdが更新された
|
- PASS: DELETION_LOG.mdが更新された
|
||||||
- ✅ バンドルサイズが削減された
|
- PASS: バンドルサイズが削減された
|
||||||
- ✅ 本番環境で回帰なし
|
- PASS: 本番環境で回帰なし
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -184,12 +184,12 @@ APIセキュリティ:
|
|||||||
### 1. ハードコードされたシークレット(重要)
|
### 1. ハードコードされたシークレット(重要)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// ❌ 重要: ハードコードされたシークレット
|
// FAIL: 重要: ハードコードされたシークレット
|
||||||
const apiKey = "sk-proj-xxxxx"
|
const apiKey = "sk-proj-xxxxx"
|
||||||
const password = "admin123"
|
const password = "admin123"
|
||||||
const token = "ghp_xxxxxxxxxxxx"
|
const token = "ghp_xxxxxxxxxxxx"
|
||||||
|
|
||||||
// ✅ 正しい: 環境変数
|
// PASS: 正しい: 環境変数
|
||||||
const apiKey = process.env.OPENAI_API_KEY
|
const apiKey = process.env.OPENAI_API_KEY
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
throw new Error('OPENAI_API_KEY not configured')
|
throw new Error('OPENAI_API_KEY not configured')
|
||||||
@@ -199,11 +199,11 @@ if (!apiKey) {
|
|||||||
### 2. SQLインジェクション(重要)
|
### 2. SQLインジェクション(重要)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// ❌ 重要: SQLインジェクションの脆弱性
|
// FAIL: 重要: SQLインジェクションの脆弱性
|
||||||
const query = `SELECT * FROM users WHERE id = ${userId}`
|
const query = `SELECT * FROM users WHERE id = ${userId}`
|
||||||
await db.query(query)
|
await db.query(query)
|
||||||
|
|
||||||
// ✅ 正しい: パラメータ化されたクエリ
|
// PASS: 正しい: パラメータ化されたクエリ
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('users')
|
.from('users')
|
||||||
.select('*')
|
.select('*')
|
||||||
@@ -213,11 +213,11 @@ const { data } = await supabase
|
|||||||
### 3. コマンドインジェクション(重要)
|
### 3. コマンドインジェクション(重要)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// ❌ 重要: コマンドインジェクション
|
// FAIL: 重要: コマンドインジェクション
|
||||||
const { exec } = require('child_process')
|
const { exec } = require('child_process')
|
||||||
exec(`ping ${userInput}`, callback)
|
exec(`ping ${userInput}`, callback)
|
||||||
|
|
||||||
// ✅ 正しい: シェルコマンドではなくライブラリを使用
|
// PASS: 正しい: シェルコマンドではなくライブラリを使用
|
||||||
const dns = require('dns')
|
const dns = require('dns')
|
||||||
dns.lookup(userInput, callback)
|
dns.lookup(userInput, callback)
|
||||||
```
|
```
|
||||||
@@ -225,10 +225,10 @@ dns.lookup(userInput, callback)
|
|||||||
### 4. クロスサイトスクリプティング(XSS)(高)
|
### 4. クロスサイトスクリプティング(XSS)(高)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// ❌ 高: XSS脆弱性
|
// FAIL: 高: XSS脆弱性
|
||||||
element.innerHTML = userInput
|
element.innerHTML = userInput
|
||||||
|
|
||||||
// ✅ 正しい: textContentを使用またはサニタイズ
|
// PASS: 正しい: textContentを使用またはサニタイズ
|
||||||
element.textContent = userInput
|
element.textContent = userInput
|
||||||
// または
|
// または
|
||||||
import DOMPurify from 'dompurify'
|
import DOMPurify from 'dompurify'
|
||||||
@@ -238,10 +238,10 @@ element.innerHTML = DOMPurify.sanitize(userInput)
|
|||||||
### 5. サーバーサイドリクエストフォージェリ(SSRF)(高)
|
### 5. サーバーサイドリクエストフォージェリ(SSRF)(高)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// ❌ 高: SSRF脆弱性
|
// FAIL: 高: SSRF脆弱性
|
||||||
const response = await fetch(userProvidedUrl)
|
const response = await fetch(userProvidedUrl)
|
||||||
|
|
||||||
// ✅ 正しい: URLを検証してホワイトリスト
|
// PASS: 正しい: URLを検証してホワイトリスト
|
||||||
const allowedDomains = ['api.example.com', 'cdn.example.com']
|
const allowedDomains = ['api.example.com', 'cdn.example.com']
|
||||||
const url = new URL(userProvidedUrl)
|
const url = new URL(userProvidedUrl)
|
||||||
if (!allowedDomains.includes(url.hostname)) {
|
if (!allowedDomains.includes(url.hostname)) {
|
||||||
@@ -253,10 +253,10 @@ const response = await fetch(url.toString())
|
|||||||
### 6. 安全でない認証(重要)
|
### 6. 安全でない認証(重要)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// ❌ 重要: 平文パスワード比較
|
// FAIL: 重要: 平文パスワード比較
|
||||||
if (password === storedPassword) { /* ログイン */ }
|
if (password === storedPassword) { /* ログイン */ }
|
||||||
|
|
||||||
// ✅ 正しい: ハッシュ化されたパスワード比較
|
// PASS: 正しい: ハッシュ化されたパスワード比較
|
||||||
import bcrypt from 'bcrypt'
|
import bcrypt from 'bcrypt'
|
||||||
const isValid = await bcrypt.compare(password, hashedPassword)
|
const isValid = await bcrypt.compare(password, hashedPassword)
|
||||||
```
|
```
|
||||||
@@ -264,13 +264,13 @@ const isValid = await bcrypt.compare(password, hashedPassword)
|
|||||||
### 7. 不十分な認可(重要)
|
### 7. 不十分な認可(重要)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// ❌ 重要: 認可チェックなし
|
// FAIL: 重要: 認可チェックなし
|
||||||
app.get('/api/user/:id', async (req, res) => {
|
app.get('/api/user/:id', async (req, res) => {
|
||||||
const user = await getUser(req.params.id)
|
const user = await getUser(req.params.id)
|
||||||
res.json(user)
|
res.json(user)
|
||||||
})
|
})
|
||||||
|
|
||||||
// ✅ 正しい: ユーザーがリソースにアクセスできることを確認
|
// PASS: 正しい: ユーザーがリソースにアクセスできることを確認
|
||||||
app.get('/api/user/:id', authenticateUser, async (req, res) => {
|
app.get('/api/user/:id', authenticateUser, async (req, res) => {
|
||||||
if (req.user.id !== req.params.id && !req.user.isAdmin) {
|
if (req.user.id !== req.params.id && !req.user.isAdmin) {
|
||||||
return res.status(403).json({ error: 'Forbidden' })
|
return res.status(403).json({ error: 'Forbidden' })
|
||||||
@@ -283,13 +283,13 @@ app.get('/api/user/:id', authenticateUser, async (req, res) => {
|
|||||||
### 8. 金融操作の競合状態(重要)
|
### 8. 金融操作の競合状態(重要)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// ❌ 重要: 残高チェックの競合状態
|
// FAIL: 重要: 残高チェックの競合状態
|
||||||
const balance = await getBalance(userId)
|
const balance = await getBalance(userId)
|
||||||
if (balance >= amount) {
|
if (balance >= amount) {
|
||||||
await withdraw(userId, amount) // 別のリクエストが並行して出金できる!
|
await withdraw(userId, amount) // 別のリクエストが並行して出金できる!
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 正しい: ロック付きアトミックトランザクション
|
// PASS: 正しい: ロック付きアトミックトランザクション
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
const balance = await trx('balances')
|
const balance = await trx('balances')
|
||||||
.where({ user_id: userId })
|
.where({ user_id: userId })
|
||||||
@@ -309,13 +309,13 @@ await db.transaction(async (trx) => {
|
|||||||
### 9. 不十分なレート制限(高)
|
### 9. 不十分なレート制限(高)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// ❌ 高: レート制限なし
|
// FAIL: 高: レート制限なし
|
||||||
app.post('/api/trade', async (req, res) => {
|
app.post('/api/trade', async (req, res) => {
|
||||||
await executeTrade(req.body)
|
await executeTrade(req.body)
|
||||||
res.json({ success: true })
|
res.json({ success: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
// ✅ 正しい: レート制限
|
// PASS: 正しい: レート制限
|
||||||
import rateLimit from 'express-rate-limit'
|
import rateLimit from 'express-rate-limit'
|
||||||
|
|
||||||
const tradeLimiter = rateLimit({
|
const tradeLimiter = rateLimit({
|
||||||
@@ -333,10 +333,10 @@ app.post('/api/trade', tradeLimiter, async (req, res) => {
|
|||||||
### 10. 機密データのロギング(中)
|
### 10. 機密データのロギング(中)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// ❌ 中: 機密データのロギング
|
// FAIL: 中: 機密データのロギング
|
||||||
console.log('User login:', { email, password, apiKey })
|
console.log('User login:', { email, password, apiKey })
|
||||||
|
|
||||||
// ✅ 正しい: ログをサニタイズ
|
// PASS: 正しい: ログをサニタイズ
|
||||||
console.log('User login:', {
|
console.log('User login:', {
|
||||||
email: email.replace(/(?<=.).(?=.*@)/g, '*'),
|
email: email.replace(/(?<=.).(?=.*@)/g, '*'),
|
||||||
passwordProvided: !!password
|
passwordProvided: !!password
|
||||||
@@ -358,7 +358,7 @@ console.log('User login:', {
|
|||||||
- **高い問題:** Y
|
- **高い問題:** Y
|
||||||
- **中程度の問題:** Z
|
- **中程度の問題:** Z
|
||||||
- **低い問題:** W
|
- **低い問題:** W
|
||||||
- **リスクレベル:** 🔴 高 / 🟡 中 / 🟢 低
|
- **リスクレベル:** 高 / 中 / 低
|
||||||
|
|
||||||
## 重要な問題(即座に修正)
|
## 重要な問題(即座に修正)
|
||||||
|
|
||||||
@@ -380,7 +380,7 @@ console.log('User login:', {
|
|||||||
|
|
||||||
**修復:**
|
**修復:**
|
||||||
```javascript
|
```javascript
|
||||||
// ✅ 安全な実装
|
// PASS: 安全な実装
|
||||||
```
|
```
|
||||||
|
|
||||||
**参考資料:**
|
**参考資料:**
|
||||||
@@ -433,7 +433,7 @@ PRをレビューする際、インラインコメントを投稿:
|
|||||||
## セキュリティレビュー
|
## セキュリティレビュー
|
||||||
|
|
||||||
**レビューアー:** security-reviewer agent
|
**レビューアー:** security-reviewer agent
|
||||||
**リスクレベル:** 🔴 高 / 🟡 中 / 🟢 低
|
**リスクレベル:** 高 / 中 / 低
|
||||||
|
|
||||||
### ブロッキング問題
|
### ブロッキング問題
|
||||||
- [ ] **重要**: [説明] @ `file:line`
|
- [ ] **重要**: [説明] @ `file:line`
|
||||||
@@ -532,13 +532,13 @@ npm install --save-dev audit-ci
|
|||||||
## 成功指標
|
## 成功指標
|
||||||
|
|
||||||
セキュリティレビュー後:
|
セキュリティレビュー後:
|
||||||
- ✅ 重要な問題が見つからない
|
- PASS: 重要な問題が見つからない
|
||||||
- ✅ すべての高い問題が対処されている
|
- PASS: すべての高い問題が対処されている
|
||||||
- ✅ セキュリティチェックリストが完了
|
- PASS: セキュリティチェックリストが完了
|
||||||
- ✅ コードにシークレットがない
|
- PASS: コードにシークレットがない
|
||||||
- ✅ 依存関係が最新
|
- PASS: 依存関係が最新
|
||||||
- ✅ テストにセキュリティシナリオが含まれている
|
- PASS: テストにセキュリティシナリオが含まれている
|
||||||
- ✅ ドキュメントが更新されている
|
- PASS: ドキュメントが更新されている
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -220,26 +220,26 @@ jest.mock('@/lib/openai', () => ({
|
|||||||
|
|
||||||
## テストの悪臭(アンチパターン)
|
## テストの悪臭(アンチパターン)
|
||||||
|
|
||||||
### ❌ 実装の詳細をテスト
|
### FAIL: 実装の詳細をテスト
|
||||||
```typescript
|
```typescript
|
||||||
// 内部状態をテストしない
|
// 内部状態をテストしない
|
||||||
expect(component.state.count).toBe(5)
|
expect(component.state.count).toBe(5)
|
||||||
```
|
```
|
||||||
|
|
||||||
### ✅ ユーザーに見える動作をテスト
|
### PASS: ユーザーに見える動作をテスト
|
||||||
```typescript
|
```typescript
|
||||||
// ユーザーが見るものをテストする
|
// ユーザーが見るものをテストする
|
||||||
expect(screen.getByText('Count: 5')).toBeInTheDocument()
|
expect(screen.getByText('Count: 5')).toBeInTheDocument()
|
||||||
```
|
```
|
||||||
|
|
||||||
### ❌ テストが互いに依存
|
### FAIL: テストが互いに依存
|
||||||
```typescript
|
```typescript
|
||||||
// 前のテストに依存しない
|
// 前のテストに依存しない
|
||||||
test('creates user', () => { /* ... */ })
|
test('creates user', () => { /* ... */ })
|
||||||
test('updates same user', () => { /* 前のテストが必要 */ })
|
test('updates same user', () => { /* 前のテストが必要 */ })
|
||||||
```
|
```
|
||||||
|
|
||||||
### ✅ 独立したテスト
|
### PASS: 独立したテスト
|
||||||
```typescript
|
```typescript
|
||||||
// 各テストでデータをセットアップ
|
// 各テストでデータをセットアップ
|
||||||
test('updates user', () => {
|
test('updates user', () => {
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ Artifacts generated:
|
|||||||
╔══════════════════════════════════════════════════════════════╗
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
║ E2Eテスト結果 ║
|
║ E2Eテスト結果 ║
|
||||||
╠══════════════════════════════════════════════════════════════╣
|
╠══════════════════════════════════════════════════════════════╣
|
||||||
║ ステータス: ✅ 全テスト合格 ║
|
║ ステータス: PASS: 全テスト合格 ║
|
||||||
║ 合計: 3テスト ║
|
║ 合計: 3テスト ║
|
||||||
║ 合格: 3 (100%) ║
|
║ 合格: 3 (100%) ║
|
||||||
║ 失敗: 0 ║
|
║ 失敗: 0 ║
|
||||||
@@ -192,15 +192,15 @@ Artifacts generated:
|
|||||||
╚══════════════════════════════════════════════════════════════╝
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
アーティファクト:
|
アーティファクト:
|
||||||
📸 スクリーンショット: 2ファイル
|
スクリーンショット: 2ファイル
|
||||||
📹 ビデオ: 0ファイル (失敗時のみ)
|
ビデオ: 0ファイル (失敗時のみ)
|
||||||
🔍 トレース: 0ファイル (失敗時のみ)
|
トレース: 0ファイル (失敗時のみ)
|
||||||
📊 HTMLレポート: playwright-report/index.html
|
HTMLレポート: playwright-report/index.html
|
||||||
|
|
||||||
レポート表示: npx playwright show-report
|
レポート表示: npx playwright show-report
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ E2E テストスイートは CI/CD 統合の準備ができました!
|
PASS: E2E テストスイートは CI/CD 統合の準備ができました!
|
||||||
|
|
||||||
````
|
````
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ open artifacts/search-results.png
|
|||||||
テストが断続的に失敗する場合:
|
テストが断続的に失敗する場合:
|
||||||
|
|
||||||
```
|
```
|
||||||
⚠️ FLAKY TEST DETECTED: tests/e2e/markets/trade.spec.ts
|
WARNING: FLAKY TEST DETECTED: tests/e2e/markets/trade.spec.ts
|
||||||
|
|
||||||
テストは10回中7回合格 (合格率70%)
|
テストは10回中7回合格 (合格率70%)
|
||||||
|
|
||||||
@@ -257,10 +257,10 @@ open artifacts/search-results.png
|
|||||||
|
|
||||||
デフォルトでは、テストは複数のブラウザで実行されます:
|
デフォルトでは、テストは複数のブラウザで実行されます:
|
||||||
|
|
||||||
* ✅ Chromium(デスクトップ Chrome)
|
* PASS: Chromium(デスクトップ Chrome)
|
||||||
* ✅ Firefox(デスクトップ)
|
* PASS: Firefox(デスクトップ)
|
||||||
* ✅ WebKit(デスクトップ Safari)
|
* PASS: WebKit(デスクトップ Safari)
|
||||||
* ✅ Mobile Chrome(オプション)
|
* PASS: Mobile Chrome(オプション)
|
||||||
|
|
||||||
`playwright.config.ts` で設定してブラウザを調整します。
|
`playwright.config.ts` で設定してブラウザを調整します。
|
||||||
|
|
||||||
@@ -288,7 +288,7 @@ CI パイプラインに追加:
|
|||||||
|
|
||||||
PMX の場合、以下の E2E テストを優先:
|
PMX の場合、以下の E2E テストを優先:
|
||||||
|
|
||||||
**🔴 重大(常に成功する必要):**
|
**重大(常に成功する必要):**
|
||||||
|
|
||||||
1. ユーザーがウォレットを接続できる
|
1. ユーザーがウォレットを接続できる
|
||||||
2. ユーザーが市場をブラウズできる
|
2. ユーザーが市場をブラウズできる
|
||||||
@@ -298,7 +298,7 @@ PMX の場合、以下の E2E テストを優先:
|
|||||||
6. 市場が正しく決済される
|
6. 市場が正しく決済される
|
||||||
7. ユーザーが資金を引き出せる
|
7. ユーザーが資金を引き出せる
|
||||||
|
|
||||||
**🟡 重要:**
|
**重要:**
|
||||||
|
|
||||||
1. 市場作成フロー
|
1. 市場作成フロー
|
||||||
2. ユーザープロフィール更新
|
2. ユーザープロフィール更新
|
||||||
@@ -311,21 +311,21 @@ PMX の場合、以下の E2E テストを優先:
|
|||||||
|
|
||||||
**すべき事:**
|
**すべき事:**
|
||||||
|
|
||||||
* ✅ 保守性を高めるためページオブジェクトモデルを使用します
|
* PASS: 保守性を高めるためページオブジェクトモデルを使用します
|
||||||
* ✅ セレクタとして data-testid 属性を使用します
|
* PASS: セレクタとして data-testid 属性を使用します
|
||||||
* ✅ 任意のタイムアウトではなく API レスポンスを待機
|
* PASS: 任意のタイムアウトではなく API レスポンスを待機
|
||||||
* ✅ 重要なユーザージャーニーのエンドツーエンドテスト
|
* PASS: 重要なユーザージャーニーのエンドツーエンドテスト
|
||||||
* ✅ main にマージする前にテストを実行
|
* PASS: main にマージする前にテストを実行
|
||||||
* ✅ テスト失敗時にアーティファクトをレビュー
|
* PASS: テスト失敗時にアーティファクトをレビュー
|
||||||
|
|
||||||
**すべきでない事:**
|
**すべきでない事:**
|
||||||
|
|
||||||
* ❌ 不安定なセレクタを使用します(CSS クラスは変わる可能性)
|
* FAIL: 不安定なセレクタを使用します(CSS クラスは変わる可能性)
|
||||||
* ❌ 実装の詳細をテスト
|
* FAIL: 実装の詳細をテスト
|
||||||
* ❌ 本番環境に対してテストを実行
|
* FAIL: 本番環境に対してテストを実行
|
||||||
* ❌ 不安定なテストを無視
|
* FAIL: 不安定なテストを無視
|
||||||
* ❌ 失敗時にアーティファクトレビューをスキップ
|
* FAIL: 失敗時にアーティファクトレビューをスキップ
|
||||||
* ❌ E2E テストですべてのエッジケースをテスト(単体テストを使用します)
|
* FAIL: E2E テストですべてのエッジケースをテスト(単体テストを使用します)
|
||||||
|
|
||||||
## 重要な注意事項
|
## 重要な注意事項
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ instinctsが分離の恩恵を受ける複雑な複数ステップのプロセ
|
|||||||
## 出力フォーマット
|
## 出力フォーマット
|
||||||
|
|
||||||
```
|
```
|
||||||
🧬 進化分析
|
進化分析
|
||||||
==================
|
==================
|
||||||
|
|
||||||
進化の準備ができた3つのクラスターを発見:
|
進化の準備ができた3つのクラスターを発見:
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ ok project/internal/handler 0.023s
|
|||||||
| 変更されたファイル | 2 |
|
| 変更されたファイル | 2 |
|
||||||
| 残存問題 | 0 |
|
| 残存問題 | 0 |
|
||||||
|
|
||||||
ビルドステータス: ✅ 成功
|
ビルドステータス: PASS: 成功
|
||||||
```
|
```
|
||||||
|
|
||||||
## 修正される一般的なエラー
|
## 修正される一般的なエラー
|
||||||
|
|||||||
@@ -124,16 +124,16 @@ return fmt.Errorf("get user %s: %w", userID, err)
|
|||||||
- HIGH: 1
|
- HIGH: 1
|
||||||
- MEDIUM: 0
|
- MEDIUM: 0
|
||||||
|
|
||||||
推奨: ❌ CRITICAL問題が修正されるまでマージをブロック
|
推奨: FAIL: CRITICAL問題が修正されるまでマージをブロック
|
||||||
```
|
```
|
||||||
|
|
||||||
## 承認基準
|
## 承認基準
|
||||||
|
|
||||||
| ステータス | 条件 |
|
| ステータス | 条件 |
|
||||||
|--------|-----------|
|
|--------|-----------|
|
||||||
| ✅ 承認 | CRITICALまたはHIGH問題なし |
|
| PASS: 承認 | CRITICALまたはHIGH問題なし |
|
||||||
| ⚠️ 警告 | MEDIUM問題のみ(注意してマージ) |
|
| WARNING: 警告 | MEDIUM問題のみ(注意してマージ) |
|
||||||
| ❌ ブロック | CRITICALまたはHIGH問題が発見された |
|
| FAIL: ブロック | CRITICALまたはHIGH問題が発見された |
|
||||||
|
|
||||||
## 他のコマンドとの統合
|
## 他のコマンドとの統合
|
||||||
|
|
||||||
|
|||||||
@@ -70,17 +70,17 @@ instincts:
|
|||||||
## プライバシーに関する考慮事項
|
## プライバシーに関する考慮事項
|
||||||
|
|
||||||
エクスポートに含まれる内容:
|
エクスポートに含まれる内容:
|
||||||
- ✅ トリガーパターン
|
- PASS: トリガーパターン
|
||||||
- ✅ アクション
|
- PASS: アクション
|
||||||
- ✅ 信頼度スコア
|
- PASS: 信頼度スコア
|
||||||
- ✅ ドメイン
|
- PASS: ドメイン
|
||||||
- ✅ 観察回数
|
- PASS: 観察回数
|
||||||
|
|
||||||
エクスポートに含まれない内容:
|
エクスポートに含まれない内容:
|
||||||
- ❌ 実際のコードスニペット
|
- FAIL: 実際のコードスニペット
|
||||||
- ❌ ファイルパス
|
- FAIL: ファイルパス
|
||||||
- ❌ セッション記録
|
- FAIL: セッション記録
|
||||||
- ❌ 個人識別情報
|
- FAIL: 個人識別情報
|
||||||
|
|
||||||
## フラグ
|
## フラグ
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py import <
|
|||||||
## インポートプロセス
|
## インポートプロセス
|
||||||
|
|
||||||
```
|
```
|
||||||
📥 instinctsをインポート中: team-instincts.yaml
|
instinctsをインポート中: team-instincts.yaml
|
||||||
================================================
|
================================================
|
||||||
|
|
||||||
12件のinstinctsが見つかりました。
|
12件のinstinctsが見つかりました。
|
||||||
@@ -61,19 +61,19 @@ python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py import <
|
|||||||
|
|
||||||
## 重複instincts (3)
|
## 重複instincts (3)
|
||||||
類似のinstinctsが既に存在:
|
類似のinstinctsが既に存在:
|
||||||
⚠️ prefer-functional-style
|
WARNING: prefer-functional-style
|
||||||
ローカル: 信頼度0.8, 12回の観測
|
ローカル: 信頼度0.8, 12回の観測
|
||||||
インポート: 信頼度0.7
|
インポート: 信頼度0.7
|
||||||
→ ローカルを保持 (信頼度が高い)
|
→ ローカルを保持 (信頼度が高い)
|
||||||
|
|
||||||
⚠️ test-first-workflow
|
WARNING: test-first-workflow
|
||||||
ローカル: 信頼度0.75
|
ローカル: 信頼度0.75
|
||||||
インポート: 信頼度0.9
|
インポート: 信頼度0.9
|
||||||
→ インポートに更新 (信頼度が高い)
|
→ インポートに更新 (信頼度が高い)
|
||||||
|
|
||||||
## 競合instincts (1)
|
## 競合instincts (1)
|
||||||
ローカルのinstinctsと矛盾:
|
ローカルのinstinctsと矛盾:
|
||||||
❌ use-classes-for-services
|
FAIL: use-classes-for-services
|
||||||
競合: avoid-classes
|
競合: avoid-classes
|
||||||
→ スキップ (手動解決が必要)
|
→ スキップ (手動解決が必要)
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ Skill Creatorからインポートする場合:
|
|||||||
|
|
||||||
インポート後:
|
インポート後:
|
||||||
```
|
```
|
||||||
✅ インポート完了!
|
PASS: インポート完了!
|
||||||
|
|
||||||
追加: 8件のinstincts
|
追加: 8件のinstincts
|
||||||
更新: 1件のinstinct
|
更新: 1件のinstinct
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ python3 ~/.claude/skills/continuous-learning-v2/scripts/instinct-cli.py status
|
|||||||
## 出力形式
|
## 出力形式
|
||||||
|
|
||||||
```
|
```
|
||||||
📊 instinctステータス
|
instinctステータス
|
||||||
==================
|
==================
|
||||||
|
|
||||||
## コードスタイル (4 instincts)
|
## コードスタイル (4 instincts)
|
||||||
|
|||||||
@@ -204,9 +204,9 @@ Claudeの統合計画での欠落リスクを減らすために、両方のモ
|
|||||||
3. **太字テキスト**でプロンプトを出力(**保存された実際のファイルパスを使用する必要があります**):
|
3. **太字テキスト**でプロンプトを出力(**保存された実際のファイルパスを使用する必要があります**):
|
||||||
|
|
||||||
---
|
---
|
||||||
**計画が生成され、`.claude/plan/actual-feature-name.md`に保存されました**
|
**計画が生成され、`.claude/plan/actual-feature-name.md`に保存されました**
|
||||||
|
|
||||||
**上記の計画をレビューしてください。以下のことができます:**
|
**上記の計画をレビューしてください。以下のことができます:**
|
||||||
- **計画を変更**: 調整が必要なことを教えてください、計画を更新します
|
- **計画を変更**: 調整が必要なことを教えてください、計画を更新します
|
||||||
- **計画を実行**: 以下のコマンドを新しいセッションにコピー
|
- **計画を実行**: 以下のコマンドを新しいセッションにコピー
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ Claudeの統合計画での欠落リスクを減らすために、両方のモ
|
|||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
|
||||||
**注意**: 上記の`actual-feature-name.md`は実際に保存されたファイル名で置き換える必要があります!
|
**注意**: 上記の`actual-feature-name.md`は実際に保存されたファイル名で置き換える必要があります!
|
||||||
|
|
||||||
4. **現在のレスポンスを直ちに終了**(ここで停止。これ以上のツール呼び出しはありません。)
|
4. **現在のレスポンスを直ちに終了**(ここで停止。これ以上のツール呼び出しはありません。)
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ Agent:
|
|||||||
## 静的解析結果
|
## 静的解析結果
|
||||||
✓ ruff: 問題なし
|
✓ ruff: 問題なし
|
||||||
✓ mypy: エラーなし
|
✓ mypy: エラーなし
|
||||||
⚠️ black: 2ファイルが再フォーマット必要
|
WARNING: black: 2ファイルが再フォーマット必要
|
||||||
✓ bandit: セキュリティ問題なし
|
✓ bandit: セキュリティ問題なし
|
||||||
|
|
||||||
## 発見された問題
|
## 発見された問題
|
||||||
@@ -155,7 +155,7 @@ with open("config.json") as f: # 良い
|
|||||||
- HIGH: 1
|
- HIGH: 1
|
||||||
- MEDIUM: 2
|
- MEDIUM: 2
|
||||||
|
|
||||||
推奨: ❌ CRITICAL問題が修正されるまでマージをブロック
|
推奨: FAIL: CRITICAL問題が修正されるまでマージをブロック
|
||||||
|
|
||||||
## フォーマット必要
|
## フォーマット必要
|
||||||
実行: `black app/routes/user.py app/services/auth.py`
|
実行: `black app/routes/user.py app/services/auth.py`
|
||||||
@@ -165,9 +165,9 @@ with open("config.json") as f: # 良い
|
|||||||
|
|
||||||
| ステータス | 条件 |
|
| ステータス | 条件 |
|
||||||
|--------|-----------|
|
|--------|-----------|
|
||||||
| ✅ 承認 | CRITICALまたはHIGH問題なし |
|
| PASS: 承認 | CRITICALまたはHIGH問題なし |
|
||||||
| ⚠️ 警告 | MEDIUM問題のみ(注意してマージ) |
|
| WARNING: 警告 | MEDIUM問題のみ(注意してマージ) |
|
||||||
| ❌ ブロック | CRITICALまたはHIGH問題が発見された |
|
| FAIL: ブロック | CRITICALまたはHIGH問題が発見された |
|
||||||
|
|
||||||
## 他のコマンドとの統合
|
## 他のコマンドとの統合
|
||||||
|
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ FAIL lib/liquidity.test.ts
|
|||||||
1 test failed, 0 passed
|
1 test failed, 0 passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ テストは期待通りに失敗しました。実装の準備ができました。
|
PASS: テストは期待通りに失敗しました。実装の準備ができました。
|
||||||
|
|
||||||
## ステップ4: 最小限のコードを実装(GREEN)
|
## ステップ4: 最小限のコードを実装(GREEN)
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ PASS lib/liquidity.test.ts
|
|||||||
3 tests passed
|
3 tests passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ すべてのテストが合格しました!
|
PASS: すべてのテストが合格しました!
|
||||||
|
|
||||||
## ステップ6: リファクタリング(IMPROVE)
|
## ステップ6: リファクタリング(IMPROVE)
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ PASS lib/liquidity.test.ts
|
|||||||
3 tests passed
|
3 tests passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ リファクタリング完了、テストはまだ合格しています!
|
PASS: リファクタリング完了、テストはまだ合格しています!
|
||||||
|
|
||||||
## ステップ8: カバレッジの確認
|
## ステップ8: カバレッジの確認
|
||||||
|
|
||||||
@@ -247,29 +247,29 @@ File | % Stmts | % Branch | % Funcs | % Lines
|
|||||||
---------------|---------|----------|---------|--------
|
---------------|---------|----------|---------|--------
|
||||||
liquidity.ts | 100 | 100 | 100 | 100
|
liquidity.ts | 100 | 100 | 100 | 100
|
||||||
|
|
||||||
Coverage: 100% ✅ (Target: 80%)
|
Coverage: 100% PASS: (Target: 80%)
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ TDDセッション完了!
|
PASS: TDDセッション完了!
|
||||||
```
|
```
|
||||||
|
|
||||||
## TDDベストプラクティス
|
## TDDベストプラクティス
|
||||||
|
|
||||||
**すべきこと:**
|
**すべきこと:**
|
||||||
- ✅ 実装の前にまずテストを書く
|
- PASS: 実装の前にまずテストを書く
|
||||||
- ✅ テストを実行し、実装前に失敗することを確認
|
- PASS: テストを実行し、実装前に失敗することを確認
|
||||||
- ✅ テストに合格するための最小限のコードを書く
|
- PASS: テストに合格するための最小限のコードを書く
|
||||||
- ✅ テストが緑色になってからのみリファクタリング
|
- PASS: テストが緑色になってからのみリファクタリング
|
||||||
- ✅ エッジケースとエラーシナリオを追加
|
- PASS: エッジケースとエラーシナリオを追加
|
||||||
- ✅ 80%以上のカバレッジを目指す(重要なコードは100%)
|
- PASS: 80%以上のカバレッジを目指す(重要なコードは100%)
|
||||||
|
|
||||||
**してはいけないこと:**
|
**してはいけないこと:**
|
||||||
- ❌ テストの前に実装を書く
|
- FAIL: テストの前に実装を書く
|
||||||
- ❌ 各変更後のテスト実行をスキップ
|
- FAIL: 各変更後のテスト実行をスキップ
|
||||||
- ❌ 一度に多くのコードを書く
|
- FAIL: 一度に多くのコードを書く
|
||||||
- ❌ 失敗するテストを無視
|
- FAIL: 失敗するテストを無視
|
||||||
- ❌ 実装の詳細をテスト(動作をテスト)
|
- FAIL: 実装の詳細をテスト(動作をテスト)
|
||||||
- ❌ すべてをモック化(統合テストを優先)
|
- FAIL: すべてをモック化(統合テストを優先)
|
||||||
|
|
||||||
## 含めるべきテストタイプ
|
## 含めるべきテストタイプ
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ description: Backend architecture patterns, API design, database optimization, a
|
|||||||
### RESTful API構造
|
### RESTful API構造
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ リソースベースのURL
|
// PASS: リソースベースのURL
|
||||||
GET /api/markets # リソースのリスト
|
GET /api/markets # リソースのリスト
|
||||||
GET /api/markets/:id # 単一リソースの取得
|
GET /api/markets/:id # 単一リソースの取得
|
||||||
POST /api/markets # リソースの作成
|
POST /api/markets # リソースの作成
|
||||||
@@ -20,7 +20,7 @@ PUT /api/markets/:id # リソースの置換
|
|||||||
PATCH /api/markets/:id # リソースの更新
|
PATCH /api/markets/:id # リソースの更新
|
||||||
DELETE /api/markets/:id # リソースの削除
|
DELETE /api/markets/:id # リソースの削除
|
||||||
|
|
||||||
// ✅ フィルタリング、ソート、ページネーション用のクエリパラメータ
|
// PASS: フィルタリング、ソート、ページネーション用のクエリパラメータ
|
||||||
GET /api/markets?status=active&sort=volume&limit=20&offset=0
|
GET /api/markets?status=active&sort=volume&limit=20&offset=0
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ export default withAuth(async (req, res) => {
|
|||||||
### クエリ最適化
|
### クエリ最適化
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ 良い: 必要な列のみを選択
|
// PASS: 良い: 必要な列のみを選択
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('id, name, status, volume')
|
.select('id, name, status, volume')
|
||||||
@@ -128,7 +128,7 @@ const { data } = await supabase
|
|||||||
.order('volume', { ascending: false })
|
.order('volume', { ascending: false })
|
||||||
.limit(10)
|
.limit(10)
|
||||||
|
|
||||||
// ❌ 悪い: すべてを選択
|
// FAIL: 悪い: すべてを選択
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('*')
|
.select('*')
|
||||||
@@ -137,13 +137,13 @@ const { data } = await supabase
|
|||||||
### N+1クエリ防止
|
### N+1クエリ防止
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ 悪い: N+1クエリ問題
|
// FAIL: 悪い: N+1クエリ問題
|
||||||
const markets = await getMarkets()
|
const markets = await getMarkets()
|
||||||
for (const market of markets) {
|
for (const market of markets) {
|
||||||
market.creator = await getUser(market.creator_id) // Nクエリ
|
market.creator = await getUser(market.creator_id) // Nクエリ
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 良い: バッチフェッチ
|
// PASS: 良い: バッチフェッチ
|
||||||
const markets = await getMarkets()
|
const markets = await getMarkets()
|
||||||
const creatorIds = markets.map(m => m.creator_id)
|
const creatorIds = markets.map(m => m.creator_id)
|
||||||
const creators = await getUsers(creatorIds) // 1クエリ
|
const creators = await getUsers(creatorIds) // 1クエリ
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ ORDER BY hour DESC;
|
|||||||
### 効率的なフィルタリング
|
### 効率的なフィルタリング
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ✅ 良い: インデックス列を最初に使用
|
-- PASS: 良い: インデックス列を最初に使用
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM markets_analytics
|
FROM markets_analytics
|
||||||
WHERE date >= '2025-01-01'
|
WHERE date >= '2025-01-01'
|
||||||
@@ -95,7 +95,7 @@ WHERE date >= '2025-01-01'
|
|||||||
ORDER BY date DESC
|
ORDER BY date DESC
|
||||||
LIMIT 100;
|
LIMIT 100;
|
||||||
|
|
||||||
-- ❌ 悪い: インデックスのない列を最初にフィルタリング
|
-- FAIL: 悪い: インデックスのない列を最初にフィルタリング
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM markets_analytics
|
FROM markets_analytics
|
||||||
WHERE volume > 1000
|
WHERE volume > 1000
|
||||||
@@ -106,7 +106,7 @@ WHERE volume > 1000
|
|||||||
### 集計
|
### 集計
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ✅ 良い: ClickHouse固有の集計関数を使用
|
-- PASS: 良い: ClickHouse固有の集計関数を使用
|
||||||
SELECT
|
SELECT
|
||||||
toStartOfDay(created_at) AS day,
|
toStartOfDay(created_at) AS day,
|
||||||
market_id,
|
market_id,
|
||||||
@@ -119,7 +119,7 @@ WHERE created_at >= today() - INTERVAL 7 DAY
|
|||||||
GROUP BY day, market_id
|
GROUP BY day, market_id
|
||||||
ORDER BY day DESC, total_volume DESC;
|
ORDER BY day DESC, total_volume DESC;
|
||||||
|
|
||||||
-- ✅ パーセンタイルにはquantileを使用(percentileより効率的)
|
-- PASS: パーセンタイルにはquantileを使用(percentileより効率的)
|
||||||
SELECT
|
SELECT
|
||||||
quantile(0.50)(trade_size) AS median,
|
quantile(0.50)(trade_size) AS median,
|
||||||
quantile(0.95)(trade_size) AS p95,
|
quantile(0.95)(trade_size) AS p95,
|
||||||
@@ -162,7 +162,7 @@ const clickhouse = new ClickHouse({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// ✅ バッチ挿入(効率的)
|
// PASS: バッチ挿入(効率的)
|
||||||
async function bulkInsertTrades(trades: Trade[]) {
|
async function bulkInsertTrades(trades: Trade[]) {
|
||||||
const values = trades.map(trade => `(
|
const values = trades.map(trade => `(
|
||||||
'${trade.id}',
|
'${trade.id}',
|
||||||
@@ -178,7 +178,7 @@ async function bulkInsertTrades(trades: Trade[]) {
|
|||||||
`).toPromise()
|
`).toPromise()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ 個別挿入(低速)
|
// FAIL: 個別挿入(低速)
|
||||||
async function insertTrade(trade: Trade) {
|
async function insertTrade(trade: Trade) {
|
||||||
// ループ内でこれをしないでください!
|
// ループ内でこれをしないでください!
|
||||||
await clickhouse.query(`
|
await clickhouse.query(`
|
||||||
|
|||||||
@@ -42,12 +42,12 @@ description: TypeScript、JavaScript、React、Node.js開発のための汎用
|
|||||||
### 変数の命名
|
### 変数の命名
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Descriptive names
|
// PASS: GOOD: Descriptive names
|
||||||
const marketSearchQuery = 'election'
|
const marketSearchQuery = 'election'
|
||||||
const isUserAuthenticated = true
|
const isUserAuthenticated = true
|
||||||
const totalRevenue = 1000
|
const totalRevenue = 1000
|
||||||
|
|
||||||
// ❌ BAD: Unclear names
|
// FAIL: BAD: Unclear names
|
||||||
const q = 'election'
|
const q = 'election'
|
||||||
const flag = true
|
const flag = true
|
||||||
const x = 1000
|
const x = 1000
|
||||||
@@ -56,12 +56,12 @@ const x = 1000
|
|||||||
### 関数の命名
|
### 関数の命名
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Verb-noun pattern
|
// PASS: GOOD: Verb-noun pattern
|
||||||
async function fetchMarketData(marketId: string) { }
|
async function fetchMarketData(marketId: string) { }
|
||||||
function calculateSimilarity(a: number[], b: number[]) { }
|
function calculateSimilarity(a: number[], b: number[]) { }
|
||||||
function isValidEmail(email: string): boolean { }
|
function isValidEmail(email: string): boolean { }
|
||||||
|
|
||||||
// ❌ BAD: Unclear or noun-only
|
// FAIL: BAD: Unclear or noun-only
|
||||||
async function market(id: string) { }
|
async function market(id: string) { }
|
||||||
function similarity(a, b) { }
|
function similarity(a, b) { }
|
||||||
function email(e) { }
|
function email(e) { }
|
||||||
@@ -70,7 +70,7 @@ function email(e) { }
|
|||||||
### 不変性パターン(重要)
|
### 不変性パターン(重要)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ ALWAYS use spread operator
|
// PASS: ALWAYS use spread operator
|
||||||
const updatedUser = {
|
const updatedUser = {
|
||||||
...user,
|
...user,
|
||||||
name: 'New Name'
|
name: 'New Name'
|
||||||
@@ -78,7 +78,7 @@ const updatedUser = {
|
|||||||
|
|
||||||
const updatedArray = [...items, newItem]
|
const updatedArray = [...items, newItem]
|
||||||
|
|
||||||
// ❌ NEVER mutate directly
|
// FAIL: NEVER mutate directly
|
||||||
user.name = 'New Name' // BAD
|
user.name = 'New Name' // BAD
|
||||||
items.push(newItem) // BAD
|
items.push(newItem) // BAD
|
||||||
```
|
```
|
||||||
@@ -86,7 +86,7 @@ items.push(newItem) // BAD
|
|||||||
### エラーハンドリング
|
### エラーハンドリング
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Comprehensive error handling
|
// PASS: GOOD: Comprehensive error handling
|
||||||
async function fetchData(url: string) {
|
async function fetchData(url: string) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
@@ -102,7 +102,7 @@ async function fetchData(url: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: No error handling
|
// FAIL: BAD: No error handling
|
||||||
async function fetchData(url) {
|
async function fetchData(url) {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
return response.json()
|
return response.json()
|
||||||
@@ -112,14 +112,14 @@ async function fetchData(url) {
|
|||||||
### Async/Awaitベストプラクティス
|
### Async/Awaitベストプラクティス
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Parallel execution when possible
|
// PASS: GOOD: Parallel execution when possible
|
||||||
const [users, markets, stats] = await Promise.all([
|
const [users, markets, stats] = await Promise.all([
|
||||||
fetchUsers(),
|
fetchUsers(),
|
||||||
fetchMarkets(),
|
fetchMarkets(),
|
||||||
fetchStats()
|
fetchStats()
|
||||||
])
|
])
|
||||||
|
|
||||||
// ❌ BAD: Sequential when unnecessary
|
// FAIL: BAD: Sequential when unnecessary
|
||||||
const users = await fetchUsers()
|
const users = await fetchUsers()
|
||||||
const markets = await fetchMarkets()
|
const markets = await fetchMarkets()
|
||||||
const stats = await fetchStats()
|
const stats = await fetchStats()
|
||||||
@@ -128,7 +128,7 @@ const stats = await fetchStats()
|
|||||||
### 型安全性
|
### 型安全性
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Proper types
|
// PASS: GOOD: Proper types
|
||||||
interface Market {
|
interface Market {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@@ -140,7 +140,7 @@ function getMarket(id: string): Promise<Market> {
|
|||||||
// Implementation
|
// Implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: Using 'any'
|
// FAIL: BAD: Using 'any'
|
||||||
function getMarket(id: any): Promise<any> {
|
function getMarket(id: any): Promise<any> {
|
||||||
// Implementation
|
// Implementation
|
||||||
}
|
}
|
||||||
@@ -151,7 +151,7 @@ function getMarket(id: any): Promise<any> {
|
|||||||
### コンポーネント構造
|
### コンポーネント構造
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Functional component with types
|
// PASS: GOOD: Functional component with types
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
@@ -176,7 +176,7 @@ export function Button({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: No types, unclear structure
|
// FAIL: BAD: No types, unclear structure
|
||||||
export function Button(props) {
|
export function Button(props) {
|
||||||
return <button onClick={props.onClick}>{props.children}</button>
|
return <button onClick={props.onClick}>{props.children}</button>
|
||||||
}
|
}
|
||||||
@@ -185,7 +185,7 @@ export function Button(props) {
|
|||||||
### カスタムフック
|
### カスタムフック
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Reusable custom hook
|
// PASS: GOOD: Reusable custom hook
|
||||||
export function useDebounce<T>(value: T, delay: number): T {
|
export function useDebounce<T>(value: T, delay: number): T {
|
||||||
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
||||||
|
|
||||||
@@ -207,25 +207,25 @@ const debouncedQuery = useDebounce(searchQuery, 500)
|
|||||||
### 状態管理
|
### 状態管理
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Proper state updates
|
// PASS: GOOD: Proper state updates
|
||||||
const [count, setCount] = useState(0)
|
const [count, setCount] = useState(0)
|
||||||
|
|
||||||
// Functional update for state based on previous state
|
// Functional update for state based on previous state
|
||||||
setCount(prev => prev + 1)
|
setCount(prev => prev + 1)
|
||||||
|
|
||||||
// ❌ BAD: Direct state reference
|
// FAIL: BAD: Direct state reference
|
||||||
setCount(count + 1) // Can be stale in async scenarios
|
setCount(count + 1) // Can be stale in async scenarios
|
||||||
```
|
```
|
||||||
|
|
||||||
### 条件付きレンダリング
|
### 条件付きレンダリング
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Clear conditional rendering
|
// PASS: GOOD: Clear conditional rendering
|
||||||
{isLoading && <Spinner />}
|
{isLoading && <Spinner />}
|
||||||
{error && <ErrorMessage error={error} />}
|
{error && <ErrorMessage error={error} />}
|
||||||
{data && <DataDisplay data={data} />}
|
{data && <DataDisplay data={data} />}
|
||||||
|
|
||||||
// ❌ BAD: Ternary hell
|
// FAIL: BAD: Ternary hell
|
||||||
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
|
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ GET /api/markets?status=active&limit=10&offset=0
|
|||||||
### レスポンス形式
|
### レスポンス形式
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Consistent response structure
|
// PASS: GOOD: Consistent response structure
|
||||||
interface ApiResponse<T> {
|
interface ApiResponse<T> {
|
||||||
success: boolean
|
success: boolean
|
||||||
data?: T
|
data?: T
|
||||||
@@ -279,7 +279,7 @@ return NextResponse.json({
|
|||||||
```typescript
|
```typescript
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
// ✅ GOOD: Schema validation
|
// PASS: GOOD: Schema validation
|
||||||
const CreateMarketSchema = z.object({
|
const CreateMarketSchema = z.object({
|
||||||
name: z.string().min(1).max(200),
|
name: z.string().min(1).max(200),
|
||||||
description: z.string().min(1).max(2000),
|
description: z.string().min(1).max(2000),
|
||||||
@@ -342,14 +342,14 @@ types/market.types.ts # 型定義は .types サフィックス付き cam
|
|||||||
### コメントを追加するタイミング
|
### コメントを追加するタイミング
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Explain WHY, not WHAT
|
// PASS: GOOD: Explain WHY, not WHAT
|
||||||
// Use exponential backoff to avoid overwhelming the API during outages
|
// Use exponential backoff to avoid overwhelming the API during outages
|
||||||
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
|
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
|
||||||
|
|
||||||
// Deliberately using mutation here for performance with large arrays
|
// Deliberately using mutation here for performance with large arrays
|
||||||
items.push(newItem)
|
items.push(newItem)
|
||||||
|
|
||||||
// ❌ BAD: Stating the obvious
|
// FAIL: BAD: Stating the obvious
|
||||||
// Increment counter by 1
|
// Increment counter by 1
|
||||||
count++
|
count++
|
||||||
|
|
||||||
@@ -389,12 +389,12 @@ export async function searchMarkets(
|
|||||||
```typescript
|
```typescript
|
||||||
import { useMemo, useCallback } from 'react'
|
import { useMemo, useCallback } from 'react'
|
||||||
|
|
||||||
// ✅ GOOD: Memoize expensive computations
|
// PASS: GOOD: Memoize expensive computations
|
||||||
const sortedMarkets = useMemo(() => {
|
const sortedMarkets = useMemo(() => {
|
||||||
return markets.sort((a, b) => b.volume - a.volume)
|
return markets.sort((a, b) => b.volume - a.volume)
|
||||||
}, [markets])
|
}, [markets])
|
||||||
|
|
||||||
// ✅ GOOD: Memoize callbacks
|
// PASS: GOOD: Memoize callbacks
|
||||||
const handleSearch = useCallback((query: string) => {
|
const handleSearch = useCallback((query: string) => {
|
||||||
setSearchQuery(query)
|
setSearchQuery(query)
|
||||||
}, [])
|
}, [])
|
||||||
@@ -405,7 +405,7 @@ const handleSearch = useCallback((query: string) => {
|
|||||||
```typescript
|
```typescript
|
||||||
import { lazy, Suspense } from 'react'
|
import { lazy, Suspense } from 'react'
|
||||||
|
|
||||||
// ✅ GOOD: Lazy load heavy components
|
// PASS: GOOD: Lazy load heavy components
|
||||||
const HeavyChart = lazy(() => import('./HeavyChart'))
|
const HeavyChart = lazy(() => import('./HeavyChart'))
|
||||||
|
|
||||||
export function Dashboard() {
|
export function Dashboard() {
|
||||||
@@ -420,13 +420,13 @@ export function Dashboard() {
|
|||||||
### データベースクエリ
|
### データベースクエリ
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Select only needed columns
|
// PASS: GOOD: Select only needed columns
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('id, name, status')
|
.select('id, name, status')
|
||||||
.limit(10)
|
.limit(10)
|
||||||
|
|
||||||
// ❌ BAD: Select everything
|
// FAIL: BAD: Select everything
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('*')
|
.select('*')
|
||||||
@@ -453,12 +453,12 @@ test('calculates similarity correctly', () => {
|
|||||||
### テストの命名
|
### テストの命名
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Descriptive test names
|
// PASS: GOOD: Descriptive test names
|
||||||
test('returns empty array when no markets match query', () => { })
|
test('returns empty array when no markets match query', () => { })
|
||||||
test('throws error when OpenAI API key is missing', () => { })
|
test('throws error when OpenAI API key is missing', () => { })
|
||||||
test('falls back to substring search when Redis unavailable', () => { })
|
test('falls back to substring search when Redis unavailable', () => { })
|
||||||
|
|
||||||
// ❌ BAD: Vague test names
|
// FAIL: BAD: Vague test names
|
||||||
test('works', () => { })
|
test('works', () => { })
|
||||||
test('test search', () => { })
|
test('test search', () => { })
|
||||||
```
|
```
|
||||||
@@ -470,12 +470,12 @@ test('test search', () => { })
|
|||||||
### 1. 長い関数
|
### 1. 長い関数
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: Function > 50 lines
|
// FAIL: BAD: Function > 50 lines
|
||||||
function processMarketData() {
|
function processMarketData() {
|
||||||
// 100 lines of code
|
// 100 lines of code
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ GOOD: Split into smaller functions
|
// PASS: GOOD: Split into smaller functions
|
||||||
function processMarketData() {
|
function processMarketData() {
|
||||||
const validated = validateData()
|
const validated = validateData()
|
||||||
const transformed = transformData(validated)
|
const transformed = transformData(validated)
|
||||||
@@ -486,7 +486,7 @@ function processMarketData() {
|
|||||||
### 2. 深いネスト
|
### 2. 深いネスト
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: 5+ levels of nesting
|
// FAIL: BAD: 5+ levels of nesting
|
||||||
if (user) {
|
if (user) {
|
||||||
if (user.isAdmin) {
|
if (user.isAdmin) {
|
||||||
if (market) {
|
if (market) {
|
||||||
@@ -499,7 +499,7 @@ if (user) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ GOOD: Early returns
|
// PASS: GOOD: Early returns
|
||||||
if (!user) return
|
if (!user) return
|
||||||
if (!user.isAdmin) return
|
if (!user.isAdmin) return
|
||||||
if (!market) return
|
if (!market) return
|
||||||
@@ -512,11 +512,11 @@ if (!hasPermission) return
|
|||||||
### 3. マジックナンバー
|
### 3. マジックナンバー
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: Unexplained numbers
|
// FAIL: BAD: Unexplained numbers
|
||||||
if (retryCount > 3) { }
|
if (retryCount > 3) { }
|
||||||
setTimeout(callback, 500)
|
setTimeout(callback, 500)
|
||||||
|
|
||||||
// ✅ GOOD: Named constants
|
// PASS: GOOD: Named constants
|
||||||
const MAX_RETRIES = 3
|
const MAX_RETRIES = 3
|
||||||
const DEBOUNCE_DELAY_MS = 500
|
const DEBOUNCE_DELAY_MS = 500
|
||||||
|
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ DJANGO 検証レポート
|
|||||||
✓ ハードコードされたシークレットなし
|
✓ ハードコードされたシークレットなし
|
||||||
✓ マイグレーションが含まれる
|
✓ マイグレーションが含まれる
|
||||||
|
|
||||||
推奨: ⚠️ デプロイ前にpip-auditの脆弱性を修正してください
|
推奨: WARNING: デプロイ前にpip-auditの脆弱性を修正してください
|
||||||
|
|
||||||
次のステップ:
|
次のステップ:
|
||||||
1. 脆弱な依存関係を更新
|
1. 脆弱な依存関係を更新
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ React、Next.js、高性能ユーザーインターフェースのためのモ
|
|||||||
### 継承よりコンポジション
|
### 継承よりコンポジション
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Component composition
|
// PASS: GOOD: Component composition
|
||||||
interface CardProps {
|
interface CardProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
variant?: 'default' | 'outlined'
|
variant?: 'default' | 'outlined'
|
||||||
@@ -283,17 +283,17 @@ export function useMarkets() {
|
|||||||
### メモ化
|
### メモ化
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ useMemo for expensive computations
|
// PASS: useMemo for expensive computations
|
||||||
const sortedMarkets = useMemo(() => {
|
const sortedMarkets = useMemo(() => {
|
||||||
return markets.sort((a, b) => b.volume - a.volume)
|
return markets.sort((a, b) => b.volume - a.volume)
|
||||||
}, [markets])
|
}, [markets])
|
||||||
|
|
||||||
// ✅ useCallback for functions passed to children
|
// PASS: useCallback for functions passed to children
|
||||||
const handleSearch = useCallback((query: string) => {
|
const handleSearch = useCallback((query: string) => {
|
||||||
setSearchQuery(query)
|
setSearchQuery(query)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// ✅ React.memo for pure components
|
// PASS: React.memo for pure components
|
||||||
export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
||||||
return (
|
return (
|
||||||
<div className="market-card">
|
<div className="market-card">
|
||||||
@@ -309,7 +309,7 @@ export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
|||||||
```typescript
|
```typescript
|
||||||
import { lazy, Suspense } from 'react'
|
import { lazy, Suspense } from 'react'
|
||||||
|
|
||||||
// ✅ Lazy load heavy components
|
// PASS: Lazy load heavy components
|
||||||
const HeavyChart = lazy(() => import('./HeavyChart'))
|
const HeavyChart = lazy(() => import('./HeavyChart'))
|
||||||
const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
|
const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
|
||||||
|
|
||||||
@@ -504,7 +504,7 @@ export class ErrorBoundary extends React.Component<
|
|||||||
```typescript
|
```typescript
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
|
|
||||||
// ✅ List animations
|
// PASS: List animations
|
||||||
export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
@@ -523,7 +523,7 @@ export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Modal animations
|
// PASS: Modal animations
|
||||||
export function Modal({ isOpen, onClose, children }: ModalProps) {
|
export function Modal({ isOpen, onClose, children }: ModalProps) {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ description: サブエージェントのコンテキスト問題を解決する
|
|||||||
┌─────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────┐
|
||||||
│ │
|
│ │
|
||||||
│ ┌──────────┐ ┌──────────┐ │
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
│ │ DISPATCH │─────▶│ EVALUATE │ │
|
│ │ DISPATCH │─────│ EVALUATE │ │
|
||||||
│ └──────────┘ └──────────┘ │
|
│ └──────────┘ └──────────┘ │
|
||||||
│ ▲ │ │
|
│ ▲ │ │
|
||||||
│ │ ▼ │
|
│ │ ▼ │
|
||||||
│ ┌──────────┐ ┌──────────┐ │
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
│ │ LOOP │◀─────│ REFINE │ │
|
│ │ LOOP │─────│ REFINE │ │
|
||||||
│ └──────────┘ └──────────┘ │
|
│ └──────────┘ └──────────┘ │
|
||||||
│ │
|
│ │
|
||||||
│ 最大3サイクル、その後続行 │
|
│ 最大3サイクル、その後続行 │
|
||||||
|
|||||||
@@ -17,22 +17,22 @@ Spring Bootサービスにおける読みやすく保守可能なJava(17+)コー
|
|||||||
## 命名
|
## 命名
|
||||||
|
|
||||||
```java
|
```java
|
||||||
// ✅ クラス/レコード: PascalCase
|
// PASS: クラス/レコード: PascalCase
|
||||||
public class MarketService {}
|
public class MarketService {}
|
||||||
public record Money(BigDecimal amount, Currency currency) {}
|
public record Money(BigDecimal amount, Currency currency) {}
|
||||||
|
|
||||||
// ✅ メソッド/フィールド: camelCase
|
// PASS: メソッド/フィールド: camelCase
|
||||||
private final MarketRepository marketRepository;
|
private final MarketRepository marketRepository;
|
||||||
public Market findBySlug(String slug) {}
|
public Market findBySlug(String slug) {}
|
||||||
|
|
||||||
// ✅ 定数: UPPER_SNAKE_CASE
|
// PASS: 定数: UPPER_SNAKE_CASE
|
||||||
private static final int MAX_PAGE_SIZE = 100;
|
private static final int MAX_PAGE_SIZE = 100;
|
||||||
```
|
```
|
||||||
|
|
||||||
## 不変性
|
## 不変性
|
||||||
|
|
||||||
```java
|
```java
|
||||||
// ✅ recordとfinalフィールドを優先
|
// PASS: recordとfinalフィールドを優先
|
||||||
public record MarketDto(Long id, String name, MarketStatus status) {}
|
public record MarketDto(Long id, String name, MarketStatus status) {}
|
||||||
|
|
||||||
public class Market {
|
public class Market {
|
||||||
@@ -45,10 +45,10 @@ public class Market {
|
|||||||
## Optionalの使用
|
## Optionalの使用
|
||||||
|
|
||||||
```java
|
```java
|
||||||
// ✅ find*メソッドからOptionalを返す
|
// PASS: find*メソッドからOptionalを返す
|
||||||
Optional<Market> market = marketRepository.findBySlug(slug);
|
Optional<Market> market = marketRepository.findBySlug(slug);
|
||||||
|
|
||||||
// ✅ get()の代わりにmap/flatMapを使用
|
// PASS: get()の代わりにmap/flatMapを使用
|
||||||
return market
|
return market
|
||||||
.map(MarketResponse::from)
|
.map(MarketResponse::from)
|
||||||
.orElseThrow(() -> new EntityNotFoundException("Market not found"));
|
.orElseThrow(() -> new EntityNotFoundException("Market not found"));
|
||||||
@@ -57,13 +57,13 @@ return market
|
|||||||
## ストリームのベストプラクティス
|
## ストリームのベストプラクティス
|
||||||
|
|
||||||
```java
|
```java
|
||||||
// ✅ 変換にストリームを使用し、パイプラインを短く保つ
|
// PASS: 変換にストリームを使用し、パイプラインを短く保つ
|
||||||
List<String> names = markets.stream()
|
List<String> names = markets.stream()
|
||||||
.map(Market::name)
|
.map(Market::name)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
// ❌ 複雑なネストされたストリームを避ける; 明確性のためにループを優先
|
// FAIL: 複雑なネストされたストリームを避ける; 明確性のためにループを優先
|
||||||
```
|
```
|
||||||
|
|
||||||
## 例外
|
## 例外
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ description: 認証の追加、ユーザー入力の処理、シークレット
|
|||||||
|
|
||||||
### 1. シークレット管理
|
### 1. シークレット管理
|
||||||
|
|
||||||
#### ❌ 絶対にしないこと
|
#### FAIL: 絶対にしないこと
|
||||||
```typescript
|
```typescript
|
||||||
const apiKey = "sk-proj-xxxxx" // ハードコードされたシークレット
|
const apiKey = "sk-proj-xxxxx" // ハードコードされたシークレット
|
||||||
const dbPassword = "password123" // ソースコード内
|
const dbPassword = "password123" // ソースコード内
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ✅ 常にすること
|
#### PASS: 常にすること
|
||||||
```typescript
|
```typescript
|
||||||
const apiKey = process.env.OPENAI_API_KEY
|
const apiKey = process.env.OPENAI_API_KEY
|
||||||
const dbUrl = process.env.DATABASE_URL
|
const dbUrl = process.env.DATABASE_URL
|
||||||
@@ -107,14 +107,14 @@ function validateFileUpload(file: File) {
|
|||||||
|
|
||||||
### 3. SQLインジェクション防止
|
### 3. SQLインジェクション防止
|
||||||
|
|
||||||
#### ❌ 絶対にSQLを連結しない
|
#### FAIL: 絶対にSQLを連結しない
|
||||||
```typescript
|
```typescript
|
||||||
// 危険 - SQLインジェクションの脆弱性
|
// 危険 - SQLインジェクションの脆弱性
|
||||||
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
|
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
|
||||||
await db.query(query)
|
await db.query(query)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ✅ 常にパラメータ化されたクエリを使用
|
#### PASS: 常にパラメータ化されたクエリを使用
|
||||||
```typescript
|
```typescript
|
||||||
// 安全 - パラメータ化されたクエリ
|
// 安全 - パラメータ化されたクエリ
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
@@ -139,10 +139,10 @@ await db.query(
|
|||||||
|
|
||||||
#### JWTトークン処理
|
#### JWTトークン処理
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ 誤り:localStorage(XSSに脆弱)
|
// FAIL: 誤り:localStorage(XSSに脆弱)
|
||||||
localStorage.setItem('token', token)
|
localStorage.setItem('token', token)
|
||||||
|
|
||||||
// ✅ 正解:httpOnly Cookie
|
// PASS: 正解:httpOnly Cookie
|
||||||
res.setHeader('Set-Cookie',
|
res.setHeader('Set-Cookie',
|
||||||
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
|
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
|
||||||
```
|
```
|
||||||
@@ -299,18 +299,18 @@ app.use('/api/search', searchLimiter)
|
|||||||
|
|
||||||
#### ロギング
|
#### ロギング
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ 誤り:機密データをログに記録
|
// FAIL: 誤り:機密データをログに記録
|
||||||
console.log('User login:', { email, password })
|
console.log('User login:', { email, password })
|
||||||
console.log('Payment:', { cardNumber, cvv })
|
console.log('Payment:', { cardNumber, cvv })
|
||||||
|
|
||||||
// ✅ 正解:機密データを編集
|
// PASS: 正解:機密データを編集
|
||||||
console.log('User login:', { email, userId })
|
console.log('User login:', { email, userId })
|
||||||
console.log('Payment:', { last4: card.last4, userId })
|
console.log('Payment:', { last4: card.last4, userId })
|
||||||
```
|
```
|
||||||
|
|
||||||
#### エラーメッセージ
|
#### エラーメッセージ
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ 誤り:内部詳細を露出
|
// FAIL: 誤り:内部詳細を露出
|
||||||
catch (error) {
|
catch (error) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: error.message, stack: error.stack },
|
{ error: error.message, stack: error.stack },
|
||||||
@@ -318,7 +318,7 @@ catch (error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 正解:一般的なエラーメッセージ
|
// PASS: 正解:一般的なエラーメッセージ
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('Internal error:', error)
|
console.error('Internal error:', error)
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#### 最小権限の原則
|
#### 最小権限の原則
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# ✅ 正解:最小限の権限
|
# PASS: 正解:最小限の権限
|
||||||
iam_role:
|
iam_role:
|
||||||
permissions:
|
permissions:
|
||||||
- s3:GetObject # 読み取りアクセスのみ
|
- s3:GetObject # 読み取りアクセスのみ
|
||||||
@@ -32,7 +32,7 @@ iam_role:
|
|||||||
resources:
|
resources:
|
||||||
- arn:aws:s3:::my-bucket/* # 特定のバケットのみ
|
- arn:aws:s3:::my-bucket/* # 特定のバケットのみ
|
||||||
|
|
||||||
# ❌ 誤り:過度に広範な権限
|
# FAIL: 誤り:過度に広範な権限
|
||||||
iam_role:
|
iam_role:
|
||||||
permissions:
|
permissions:
|
||||||
- s3:* # すべてのS3アクション
|
- s3:* # すべてのS3アクション
|
||||||
@@ -65,14 +65,14 @@ aws iam enable-mfa-device \
|
|||||||
#### クラウドシークレットマネージャー
|
#### クラウドシークレットマネージャー
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ 正解:クラウドシークレットマネージャーを使用
|
// PASS: 正解:クラウドシークレットマネージャーを使用
|
||||||
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
|
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
|
||||||
|
|
||||||
const client = new SecretsManager({ region: 'us-east-1' });
|
const client = new SecretsManager({ region: 'us-east-1' });
|
||||||
const secret = await client.getSecretValue({ SecretId: 'prod/api-key' });
|
const secret = await client.getSecretValue({ SecretId: 'prod/api-key' });
|
||||||
const apiKey = JSON.parse(secret.SecretString).key;
|
const apiKey = JSON.parse(secret.SecretString).key;
|
||||||
|
|
||||||
// ❌ 誤り:ハードコードまたは環境変数のみ
|
// FAIL: 誤り:ハードコードまたは環境変数のみ
|
||||||
const apiKey = process.env.API_KEY; // ローテーションされず、監査されない
|
const apiKey = process.env.API_KEY; // ローテーションされず、監査されない
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ aws secretsmanager rotate-secret \
|
|||||||
#### VPCとファイアウォール設定
|
#### VPCとファイアウォール設定
|
||||||
|
|
||||||
```terraform
|
```terraform
|
||||||
# ✅ 正解:制限されたセキュリティグループ
|
# PASS: 正解:制限されたセキュリティグループ
|
||||||
resource "aws_security_group" "app" {
|
resource "aws_security_group" "app" {
|
||||||
name = "app-sg"
|
name = "app-sg"
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ resource "aws_security_group" "app" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# ❌ 誤り:インターネットに公開
|
# FAIL: 誤り:インターネットに公開
|
||||||
resource "aws_security_group" "bad" {
|
resource "aws_security_group" "bad" {
|
||||||
ingress {
|
ingress {
|
||||||
from_port = 0
|
from_port = 0
|
||||||
@@ -142,7 +142,7 @@ resource "aws_security_group" "bad" {
|
|||||||
#### CloudWatch/ロギング設定
|
#### CloudWatch/ロギング設定
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ 正解:包括的なロギング
|
// PASS: 正解:包括的なロギング
|
||||||
import { CloudWatchLogsClient, CreateLogStreamCommand } from '@aws-sdk/client-cloudwatch-logs';
|
import { CloudWatchLogsClient, CreateLogStreamCommand } from '@aws-sdk/client-cloudwatch-logs';
|
||||||
|
|
||||||
const logSecurityEvent = async (event: SecurityEvent) => {
|
const logSecurityEvent = async (event: SecurityEvent) => {
|
||||||
@@ -177,7 +177,7 @@ const logSecurityEvent = async (event: SecurityEvent) => {
|
|||||||
#### 安全なパイプライン設定
|
#### 安全なパイプライン設定
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# ✅ 正解:安全なGitHub Actionsワークフロー
|
# PASS: 正解:安全なGitHub Actionsワークフロー
|
||||||
name: Deploy
|
name: Deploy
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -237,7 +237,7 @@ jobs:
|
|||||||
#### Cloudflareセキュリティ設定
|
#### Cloudflareセキュリティ設定
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ 正解:セキュリティヘッダー付きCloudflare Workers
|
// PASS: 正解:セキュリティヘッダー付きCloudflare Workers
|
||||||
export default {
|
export default {
|
||||||
async fetch(request: Request): Promise<Response> {
|
async fetch(request: Request): Promise<Response> {
|
||||||
const response = await fetch(request);
|
const response = await fetch(request);
|
||||||
@@ -281,7 +281,7 @@ export default {
|
|||||||
#### 自動バックアップ
|
#### 自動バックアップ
|
||||||
|
|
||||||
```terraform
|
```terraform
|
||||||
# ✅ 正解:自動RDSバックアップ
|
# PASS: 正解:自動RDSバックアップ
|
||||||
resource "aws_db_instance" "main" {
|
resource "aws_db_instance" "main" {
|
||||||
allocated_storage = 20
|
allocated_storage = 20
|
||||||
engine = "postgres"
|
engine = "postgres"
|
||||||
@@ -327,10 +327,10 @@ resource "aws_db_instance" "main" {
|
|||||||
### S3バケットの露出
|
### S3バケットの露出
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# ❌ 誤り:公開バケット
|
# FAIL: 誤り:公開バケット
|
||||||
aws s3api put-bucket-acl --bucket my-bucket --acl public-read
|
aws s3api put-bucket-acl --bucket my-bucket --acl public-read
|
||||||
|
|
||||||
# ✅ 正解:特定のアクセス付きプライベートバケット
|
# PASS: 正解:特定のアクセス付きプライベートバケット
|
||||||
aws s3api put-bucket-acl --bucket my-bucket --acl private
|
aws s3api put-bucket-acl --bucket my-bucket --acl private
|
||||||
aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json
|
aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json
|
||||||
```
|
```
|
||||||
@@ -338,12 +338,12 @@ aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json
|
|||||||
### RDS公開アクセス
|
### RDS公開アクセス
|
||||||
|
|
||||||
```terraform
|
```terraform
|
||||||
# ❌ 誤り
|
# FAIL: 誤り
|
||||||
resource "aws_db_instance" "bad" {
|
resource "aws_db_instance" "bad" {
|
||||||
publicly_accessible = true # 絶対にこれをしない!
|
publicly_accessible = true # 絶対にこれをしない!
|
||||||
}
|
}
|
||||||
|
|
||||||
# ✅ 正解
|
# PASS: 正解
|
||||||
resource "aws_db_instance" "good" {
|
resource "aws_db_instance" "good" {
|
||||||
publicly_accessible = false
|
publicly_accessible = false
|
||||||
vpc_security_group_ids = [aws_security_group.db.id]
|
vpc_security_group_ids = [aws_security_group.db.id]
|
||||||
|
|||||||
@@ -313,39 +313,39 @@ npm run test:coverage
|
|||||||
|
|
||||||
## 避けるべき一般的なテストの誤り
|
## 避けるべき一般的なテストの誤り
|
||||||
|
|
||||||
### ❌ 誤り:実装の詳細をテスト
|
### FAIL: 誤り:実装の詳細をテスト
|
||||||
```typescript
|
```typescript
|
||||||
// 内部状態をテストしない
|
// 内部状態をテストしない
|
||||||
expect(component.state.count).toBe(5)
|
expect(component.state.count).toBe(5)
|
||||||
```
|
```
|
||||||
|
|
||||||
### ✅ 正解:ユーザーに見える動作をテスト
|
### PASS: 正解:ユーザーに見える動作をテスト
|
||||||
```typescript
|
```typescript
|
||||||
// ユーザーが見るものをテスト
|
// ユーザーが見るものをテスト
|
||||||
expect(screen.getByText('Count: 5')).toBeInTheDocument()
|
expect(screen.getByText('Count: 5')).toBeInTheDocument()
|
||||||
```
|
```
|
||||||
|
|
||||||
### ❌ 誤り:脆弱なセレクタ
|
### FAIL: 誤り:脆弱なセレクタ
|
||||||
```typescript
|
```typescript
|
||||||
// 簡単に壊れる
|
// 簡単に壊れる
|
||||||
await page.click('.css-class-xyz')
|
await page.click('.css-class-xyz')
|
||||||
```
|
```
|
||||||
|
|
||||||
### ✅ 正解:セマンティックセレクタ
|
### PASS: 正解:セマンティックセレクタ
|
||||||
```typescript
|
```typescript
|
||||||
// 変更に強い
|
// 変更に強い
|
||||||
await page.click('button:has-text("Submit")')
|
await page.click('button:has-text("Submit")')
|
||||||
await page.click('[data-testid="submit-button"]')
|
await page.click('[data-testid="submit-button"]')
|
||||||
```
|
```
|
||||||
|
|
||||||
### ❌ 誤り:テストの分離なし
|
### FAIL: 誤り:テストの分離なし
|
||||||
```typescript
|
```typescript
|
||||||
// テストが互いに依存
|
// テストが互いに依存
|
||||||
test('creates user', () => { /* ... */ })
|
test('creates user', () => { /* ... */ })
|
||||||
test('updates same user', () => { /* 前のテストに依存 */ })
|
test('updates same user', () => { /* 前のテストに依存 */ })
|
||||||
```
|
```
|
||||||
|
|
||||||
### ✅ 正解:独立したテスト
|
### PASS: 正解:独立したテスト
|
||||||
```typescript
|
```typescript
|
||||||
// 各テストが独自のデータをセットアップ
|
// 各テストが独自のデータをセットアップ
|
||||||
test('creates user', () => {
|
test('creates user', () => {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
**🌐 Language / 语言 / 語言 / 언어**
|
**Language / 语言 / 語言 / 언어**
|
||||||
|
|
||||||
[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](README.md)
|
[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](README.md)
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 빠른 시작
|
## 빠른 시작
|
||||||
|
|
||||||
2분 안에 설정 완료:
|
2분 안에 설정 완료:
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
|
|
||||||
### 2단계: 룰 설치 (필수)
|
### 2단계: 룰 설치 (필수)
|
||||||
|
|
||||||
> ⚠️ **중요:** Claude Code 플러그인은 `rules`를 자동으로 배포할 수 없습니다. 수동으로 설치해야 합니다:
|
> WARNING: **중요:** Claude Code 플러그인은 `rules`를 자동으로 배포할 수 없습니다. 수동으로 설치해야 합니다:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 먼저 저장소 클론
|
# 먼저 저장소 클론
|
||||||
@@ -150,11 +150,11 @@ cd everything-claude-code
|
|||||||
/plugin list everything-claude-code@everything-claude-code
|
/plugin list everything-claude-code@everything-claude-code
|
||||||
```
|
```
|
||||||
|
|
||||||
✨ **끝!** 이제 16개 에이전트, 65개 스킬, 40개 커맨드를 사용할 수 있습니다.
|
**끝!** 이제 16개 에이전트, 65개 스킬, 40개 커맨드를 사용할 수 있습니다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌐 크로스 플랫폼 지원
|
## 크로스 플랫폼 지원
|
||||||
|
|
||||||
이 플러그인은 **Windows, macOS, Linux**를 완벽하게 지원하며, 주요 IDE(Cursor, OpenCode, Antigravity) 및 CLI 하네스와 긴밀하게 통합됩니다. 모든 훅과 스크립트는 최대 호환성을 위해 Node.js로 작성되었습니다.
|
이 플러그인은 **Windows, macOS, Linux**를 완벽하게 지원하며, 주요 IDE(Cursor, OpenCode, Antigravity) 및 CLI 하네스와 긴밀하게 통합됩니다. 모든 훅과 스크립트는 최대 호환성을 위해 Node.js로 작성되었습니다.
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ export ECC_DISABLED_HOOKS="pre:bash:tmux-reminder,post:edit:typecheck"
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 구성 요소
|
## 구성 요소
|
||||||
|
|
||||||
이 저장소는 **Claude Code 플러그인**입니다 - 직접 설치하거나 컴포넌트를 수동으로 복사할 수 있습니다.
|
이 저장소는 **Claude Code 플러그인**입니다 - 직접 설치하거나 컴포넌트를 수동으로 복사할 수 있습니다.
|
||||||
|
|
||||||
@@ -263,7 +263,7 @@ everything-claude-code/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠️ 에코시스템 도구
|
## 에코시스템 도구
|
||||||
|
|
||||||
### Skill Creator
|
### Skill Creator
|
||||||
|
|
||||||
@@ -314,7 +314,7 @@ Claude Code에서 `/security-scan`을 사용하거나, [GitHub Action](https://g
|
|||||||
|
|
||||||
[GitHub](https://github.com/affaan-m/agentshield) | [npm](https://www.npmjs.com/package/ecc-agentshield)
|
[GitHub](https://github.com/affaan-m/agentshield) | [npm](https://www.npmjs.com/package/ecc-agentshield)
|
||||||
|
|
||||||
### 🧠 지속적 학습 v2
|
### 지속적 학습 v2
|
||||||
|
|
||||||
직관(Instinct) 기반 학습 시스템이 여러분의 패턴을 자동으로 학습합니다:
|
직관(Instinct) 기반 학습 시스템이 여러분의 패턴을 자동으로 학습합니다:
|
||||||
|
|
||||||
@@ -329,7 +329,7 @@ Claude Code에서 `/security-scan`을 사용하거나, [GitHub Action](https://g
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 요구 사항
|
## 요구 사항
|
||||||
|
|
||||||
### Claude Code CLI 버전
|
### Claude Code CLI 버전
|
||||||
|
|
||||||
@@ -344,13 +344,13 @@ claude --version
|
|||||||
|
|
||||||
### 중요: 훅 자동 로딩 동작
|
### 중요: 훅 자동 로딩 동작
|
||||||
|
|
||||||
> ⚠️ **기여자 참고:** `.claude-plugin/plugin.json`에 `"hooks"` 필드를 추가하지 **마세요**. 회귀 테스트로 이를 강제합니다.
|
> WARNING: **기여자 참고:** `.claude-plugin/plugin.json`에 `"hooks"` 필드를 추가하지 **마세요**. 회귀 테스트로 이를 강제합니다.
|
||||||
|
|
||||||
Claude Code v2.1+는 설치된 플러그인의 `hooks/hooks.json`을 **자동으로 로드**합니다. 명시적으로 선언하면 중복 감지 오류가 발생합니다.
|
Claude Code v2.1+는 설치된 플러그인의 `hooks/hooks.json`을 **자동으로 로드**합니다. 명시적으로 선언하면 중복 감지 오류가 발생합니다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📥 설치
|
## 설치
|
||||||
|
|
||||||
### 옵션 1: 플러그인으로 설치 (권장)
|
### 옵션 1: 플러그인으로 설치 (권장)
|
||||||
|
|
||||||
@@ -397,7 +397,7 @@ Claude Code v2.1+는 설치된 플러그인의 `hooks/hooks.json`을 **자동으
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 🔧 옵션 2: 수동 설치
|
### 옵션 2: 수동 설치
|
||||||
|
|
||||||
설치할 항목을 직접 선택하고 싶다면:
|
설치할 항목을 직접 선택하고 싶다면:
|
||||||
|
|
||||||
@@ -422,7 +422,7 @@ cp -r everything-claude-code/skills/search-first ~/.claude/skills/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎯 핵심 개념
|
## 핵심 개념
|
||||||
|
|
||||||
### 에이전트
|
### 에이전트
|
||||||
|
|
||||||
@@ -483,7 +483,7 @@ rules/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🗺️ 어떤 에이전트를 사용해야 할까?
|
## 어떤 에이전트를 사용해야 할까?
|
||||||
|
|
||||||
어디서 시작해야 할지 모르겠다면 이 참고표를 보세요:
|
어디서 시작해야 할지 모르겠다면 이 참고표를 보세요:
|
||||||
|
|
||||||
@@ -529,7 +529,7 @@ rules/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ❓ FAQ
|
## FAQ
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>설치된 에이전트/커맨드 확인은 어떻게 하나요?</b></summary>
|
<summary><b>설치된 에이전트/커맨드 확인은 어떻게 하나요?</b></summary>
|
||||||
@@ -602,7 +602,7 @@ cp -r everything-claude-code/rules/common/* ~/.claude/rules/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🧪 테스트 실행
|
## 테스트 실행
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 모든 테스트 실행
|
# 모든 테스트 실행
|
||||||
@@ -616,7 +616,7 @@ node tests/hooks/hooks.test.js
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🤝 기여하기
|
## 기여하기
|
||||||
|
|
||||||
**기여를 환영합니다.**
|
**기여를 환영합니다.**
|
||||||
|
|
||||||
@@ -687,7 +687,7 @@ Claude Code 사용 비용이 부담된다면 토큰 소비를 관리해야 합
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚠️ 중요 참고 사항
|
## WARNING: 중요 참고 사항
|
||||||
|
|
||||||
### 커스터마이징
|
### 커스터마이징
|
||||||
|
|
||||||
@@ -699,7 +699,7 @@ Claude Code 사용 비용이 부담된다면 토큰 소비를 관리해야 합
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 💜 스폰서
|
## 스폰서
|
||||||
|
|
||||||
이 프로젝트는 무료 오픈소스입니다. 스폰서의 지원으로 유지보수와 성장이 이루어집니다.
|
이 프로젝트는 무료 오픈소스입니다. 스폰서의 지원으로 유지보수와 성장이 이루어집니다.
|
||||||
|
|
||||||
@@ -707,13 +707,13 @@ Claude Code 사용 비용이 부담된다면 토큰 소비를 관리해야 합
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌟 Star 히스토리
|
## Star 히스토리
|
||||||
|
|
||||||
[](https://star-history.com/#affaan-m/everything-claude-code&Date)
|
[](https://star-history.com/#affaan-m/everything-claude-code&Date)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔗 링크
|
## 링크
|
||||||
|
|
||||||
- **요약 가이드 (여기서 시작):** [The Shorthand Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2012378465664745795)
|
- **요약 가이드 (여기서 시작):** [The Shorthand Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2012378465664745795)
|
||||||
- **상세 가이드 (고급):** [The Longform Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2014040193557471352)
|
- **상세 가이드 (고급):** [The Longform Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2014040193557471352)
|
||||||
@@ -722,7 +722,7 @@ Claude Code 사용 비용이 부담된다면 토큰 소비를 관리해야 합
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📄 라이선스
|
## 라이선스
|
||||||
|
|
||||||
MIT - 자유롭게 사용하고, 필요에 따라 수정하고, 가능하다면 기여해 주세요.
|
MIT - 자유롭게 사용하고, 필요에 따라 수정하고, 가능하다면 기여해 주세요.
|
||||||
|
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ Running 3 tests using 3 workers
|
|||||||
╔══════════════════════════════════════════════════════════════╗
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
║ E2E 테스트 결과 ║
|
║ E2E 테스트 결과 ║
|
||||||
╠══════════════════════════════════════════════════════════════╣
|
╠══════════════════════════════════════════════════════════════╣
|
||||||
║ 상태: ✅ 모든 테스트 통과 ║
|
║ 상태: PASS: 모든 테스트 통과 ║
|
||||||
║ 전체: 3개 테스트 ║
|
║ 전체: 3개 테스트 ║
|
||||||
║ 통과: 3 (100%) ║
|
║ 통과: 3 (100%) ║
|
||||||
║ 실패: 0 ║
|
║ 실패: 0 ║
|
||||||
@@ -191,15 +191,15 @@ Running 3 tests using 3 workers
|
|||||||
╚══════════════════════════════════════════════════════════════╝
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
아티팩트:
|
아티팩트:
|
||||||
📸 스크린샷: 2개 파일
|
스크린샷: 2개 파일
|
||||||
📹 비디오: 0개 파일 (실패 시에만)
|
비디오: 0개 파일 (실패 시에만)
|
||||||
🔍 트레이스: 0개 파일 (실패 시에만)
|
트레이스: 0개 파일 (실패 시에만)
|
||||||
📊 HTML 보고서: playwright-report/index.html
|
HTML 보고서: playwright-report/index.html
|
||||||
|
|
||||||
보고서 확인: npx playwright show-report
|
보고서 확인: npx playwright show-report
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ CI/CD 통합 준비가 완료된 E2E 테스트 모음!
|
PASS: CI/CD 통합 준비가 완료된 E2E 테스트 모음!
|
||||||
````
|
````
|
||||||
|
|
||||||
## 테스트 아티팩트
|
## 테스트 아티팩트
|
||||||
@@ -235,7 +235,7 @@ open artifacts/search-results.png
|
|||||||
테스트가 간헐적으로 실패하는 경우:
|
테스트가 간헐적으로 실패하는 경우:
|
||||||
|
|
||||||
```
|
```
|
||||||
⚠️ 불안정한 테스트 감지됨: tests/e2e/markets/trade.spec.ts
|
WARNING: 불안정한 테스트 감지됨: tests/e2e/markets/trade.spec.ts
|
||||||
|
|
||||||
테스트가 10회 중 7회 통과 (70% 통과율)
|
테스트가 10회 중 7회 통과 (70% 통과율)
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ ok project/internal/handler 0.023s
|
|||||||
| 수정된 파일 | 2 |
|
| 수정된 파일 | 2 |
|
||||||
| 남은 이슈 | 0 |
|
| 남은 이슈 | 0 |
|
||||||
|
|
||||||
Build 상태: ✅ 성공
|
Build 상태: PASS: 성공
|
||||||
````
|
````
|
||||||
|
|
||||||
## 자주 발생하는 에러
|
## 자주 발생하는 에러
|
||||||
|
|||||||
@@ -124,16 +124,16 @@ return fmt.Errorf("get user %s: %w", userID, err)
|
|||||||
- HIGH: 1
|
- HIGH: 1
|
||||||
- MEDIUM: 0
|
- MEDIUM: 0
|
||||||
|
|
||||||
권장: ❌ CRITICAL 이슈가 수정될 때까지 merge 차단
|
권장: FAIL: CRITICAL 이슈가 수정될 때까지 merge 차단
|
||||||
````
|
````
|
||||||
|
|
||||||
## 승인 기준
|
## 승인 기준
|
||||||
|
|
||||||
| 상태 | 조건 |
|
| 상태 | 조건 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| ✅ 승인 | CRITICAL 또는 HIGH 이슈 없음 |
|
| PASS: 승인 | CRITICAL 또는 HIGH 이슈 없음 |
|
||||||
| ⚠️ 경고 | MEDIUM 이슈만 있음 (주의하여 merge) |
|
| WARNING: 경고 | MEDIUM 이슈만 있음 (주의하여 merge) |
|
||||||
| ❌ 차단 | CRITICAL 또는 HIGH 이슈 발견 |
|
| FAIL: 차단 | CRITICAL 또는 HIGH 이슈 발견 |
|
||||||
|
|
||||||
## 다른 커맨드와의 연동
|
## 다른 커맨드와의 연동
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ Dead Code Cleanup
|
|||||||
건너뜀: 항목 2개 (테스트 실패)
|
건너뜀: 항목 2개 (테스트 실패)
|
||||||
절감: 약 450줄 제거
|
절감: 약 450줄 제거
|
||||||
──────────────────────────────
|
──────────────────────────────
|
||||||
모든 테스트 통과 ✅
|
모든 테스트 통과 PASS:
|
||||||
```
|
```
|
||||||
|
|
||||||
## 규칙
|
## 규칙
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ FAIL lib/liquidity.test.ts
|
|||||||
1 test failed, 0 passed
|
1 test failed, 0 passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ 예상대로 테스트 실패. 구현 준비 완료.
|
PASS: 예상대로 테스트 실패. 구현 준비 완료.
|
||||||
|
|
||||||
## 4단계: 최소한의 코드 구현 (GREEN)
|
## 4단계: 최소한의 코드 구현 (GREEN)
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ PASS lib/liquidity.test.ts
|
|||||||
3 tests passed
|
3 tests passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ 모든 테스트 통과!
|
PASS: 모든 테스트 통과!
|
||||||
|
|
||||||
## 6단계: 리팩토링 (IMPROVE)
|
## 6단계: 리팩토링 (IMPROVE)
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ PASS lib/liquidity.test.ts
|
|||||||
3 tests passed
|
3 tests passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ 리팩토링 완료, 테스트 여전히 통과!
|
PASS: 리팩토링 완료, 테스트 여전히 통과!
|
||||||
|
|
||||||
## 8단계: 커버리지 확인
|
## 8단계: 커버리지 확인
|
||||||
|
|
||||||
@@ -247,10 +247,10 @@ File | % Stmts | % Branch | % Funcs | % Lines
|
|||||||
---------------|---------|----------|---------|--------
|
---------------|---------|----------|---------|--------
|
||||||
liquidity.ts | 100 | 100 | 100 | 100
|
liquidity.ts | 100 | 100 | 100 | 100
|
||||||
|
|
||||||
Coverage: 100% ✅ (목표: 80%)
|
Coverage: 100% PASS: (목표: 80%)
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ TDD 세션 완료!
|
PASS: TDD 세션 완료!
|
||||||
````
|
````
|
||||||
|
|
||||||
## TDD 모범 사례
|
## TDD 모범 사례
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ description: 테스트 커버리지를 분석하고, 80% 이상을 목표로 누
|
|||||||
src/services/auth.ts 45% 88%
|
src/services/auth.ts 45% 88%
|
||||||
src/utils/validation.ts 32% 82%
|
src/utils/validation.ts 32% 82%
|
||||||
──────────────────────────────
|
──────────────────────────────
|
||||||
전체: 67% 84% ✅
|
전체: 67% 84% PASS:
|
||||||
```
|
```
|
||||||
|
|
||||||
## 집중 영역
|
## 집중 영역
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ origin: ECC
|
|||||||
### RESTful API 구조
|
### RESTful API 구조
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ Resource-based URLs
|
// PASS: Resource-based URLs
|
||||||
GET /api/markets # List resources
|
GET /api/markets # List resources
|
||||||
GET /api/markets/:id # Get single resource
|
GET /api/markets/:id # Get single resource
|
||||||
POST /api/markets # Create resource
|
POST /api/markets # Create resource
|
||||||
@@ -31,7 +31,7 @@ PUT /api/markets/:id # Replace resource
|
|||||||
PATCH /api/markets/:id # Update resource
|
PATCH /api/markets/:id # Update resource
|
||||||
DELETE /api/markets/:id # Delete resource
|
DELETE /api/markets/:id # Delete resource
|
||||||
|
|
||||||
// ✅ Query parameters for filtering, sorting, pagination
|
// PASS: Query parameters for filtering, sorting, pagination
|
||||||
GET /api/markets?status=active&sort=volume&limit=20&offset=0
|
GET /api/markets?status=active&sort=volume&limit=20&offset=0
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ export default withAuth(async (req, res) => {
|
|||||||
### 쿼리 최적화
|
### 쿼리 최적화
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Select only needed columns
|
// PASS: GOOD: Select only needed columns
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('id, name, status, volume')
|
.select('id, name, status, volume')
|
||||||
@@ -140,7 +140,7 @@ const { data } = await supabase
|
|||||||
.order('volume', { ascending: false })
|
.order('volume', { ascending: false })
|
||||||
.limit(10)
|
.limit(10)
|
||||||
|
|
||||||
// ❌ BAD: Select everything
|
// FAIL: BAD: Select everything
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('*')
|
.select('*')
|
||||||
@@ -149,13 +149,13 @@ const { data } = await supabase
|
|||||||
### N+1 쿼리 방지
|
### N+1 쿼리 방지
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: N+1 query problem
|
// FAIL: BAD: N+1 query problem
|
||||||
const markets = await getMarkets()
|
const markets = await getMarkets()
|
||||||
for (const market of markets) {
|
for (const market of markets) {
|
||||||
market.creator = await getUser(market.creator_id) // N queries
|
market.creator = await getUser(market.creator_id) // N queries
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ GOOD: Batch fetch
|
// PASS: GOOD: Batch fetch
|
||||||
const markets = await getMarkets()
|
const markets = await getMarkets()
|
||||||
const creatorIds = markets.map(m => m.creator_id)
|
const creatorIds = markets.map(m => m.creator_id)
|
||||||
const creators = await getUsers(creatorIds) // 1 query
|
const creators = await getUsers(creatorIds) // 1 query
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ ORDER BY hour DESC;
|
|||||||
### 효율적인 필터링
|
### 효율적인 필터링
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ✅ 좋음: 인덱스된 컬럼을 먼저 사용
|
-- PASS: 좋음: 인덱스된 컬럼을 먼저 사용
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM markets_analytics
|
FROM markets_analytics
|
||||||
WHERE date >= '2025-01-01'
|
WHERE date >= '2025-01-01'
|
||||||
@@ -105,7 +105,7 @@ WHERE date >= '2025-01-01'
|
|||||||
ORDER BY date DESC
|
ORDER BY date DESC
|
||||||
LIMIT 100;
|
LIMIT 100;
|
||||||
|
|
||||||
-- ❌ 나쁨: 비인덱스 컬럼을 먼저 필터링
|
-- FAIL: 나쁨: 비인덱스 컬럼을 먼저 필터링
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM markets_analytics
|
FROM markets_analytics
|
||||||
WHERE volume > 1000
|
WHERE volume > 1000
|
||||||
@@ -116,7 +116,7 @@ WHERE volume > 1000
|
|||||||
### 집계
|
### 집계
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ✅ 좋음: ClickHouse 전용 집계 함수를 사용
|
-- PASS: 좋음: ClickHouse 전용 집계 함수를 사용
|
||||||
SELECT
|
SELECT
|
||||||
toStartOfDay(created_at) AS day,
|
toStartOfDay(created_at) AS day,
|
||||||
market_id,
|
market_id,
|
||||||
@@ -129,7 +129,7 @@ WHERE created_at >= today() - INTERVAL 7 DAY
|
|||||||
GROUP BY day, market_id
|
GROUP BY day, market_id
|
||||||
ORDER BY day DESC, total_volume DESC;
|
ORDER BY day DESC, total_volume DESC;
|
||||||
|
|
||||||
-- ✅ 백분위수에는 quantile 사용 (percentile보다 효율적)
|
-- PASS: 백분위수에는 quantile 사용 (percentile보다 효율적)
|
||||||
SELECT
|
SELECT
|
||||||
quantile(0.50)(trade_size) AS median,
|
quantile(0.50)(trade_size) AS median,
|
||||||
quantile(0.95)(trade_size) AS p95,
|
quantile(0.95)(trade_size) AS p95,
|
||||||
@@ -172,7 +172,7 @@ const clickhouse = new ClickHouse({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// ✅ 배치 삽입 (효율적)
|
// PASS: 배치 삽입 (효율적)
|
||||||
async function bulkInsertTrades(trades: Trade[]) {
|
async function bulkInsertTrades(trades: Trade[]) {
|
||||||
const rows = trades.map(trade => ({
|
const rows = trades.map(trade => ({
|
||||||
id: trade.id,
|
id: trade.id,
|
||||||
@@ -185,7 +185,7 @@ async function bulkInsertTrades(trades: Trade[]) {
|
|||||||
await clickhouse.insert('trades', rows)
|
await clickhouse.insert('trades', rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ 개별 삽입 (느림)
|
// FAIL: 개별 삽입 (느림)
|
||||||
async function insertTrade(trade: Trade) {
|
async function insertTrade(trade: Trade) {
|
||||||
// 루프 안에서 이렇게 하지 마세요!
|
// 루프 안에서 이렇게 하지 마세요!
|
||||||
await clickhouse.query(`
|
await clickhouse.query(`
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ origin: ECC
|
|||||||
### 변수 네이밍
|
### 변수 네이밍
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Descriptive names
|
// PASS: GOOD: Descriptive names
|
||||||
const marketSearchQuery = 'election'
|
const marketSearchQuery = 'election'
|
||||||
const isUserAuthenticated = true
|
const isUserAuthenticated = true
|
||||||
const totalRevenue = 1000
|
const totalRevenue = 1000
|
||||||
|
|
||||||
// ❌ BAD: Unclear names
|
// FAIL: BAD: Unclear names
|
||||||
const q = 'election'
|
const q = 'election'
|
||||||
const flag = true
|
const flag = true
|
||||||
const x = 1000
|
const x = 1000
|
||||||
@@ -62,12 +62,12 @@ const x = 1000
|
|||||||
### 함수 네이밍
|
### 함수 네이밍
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Verb-noun pattern
|
// PASS: GOOD: Verb-noun pattern
|
||||||
async function fetchMarketData(marketId: string) { }
|
async function fetchMarketData(marketId: string) { }
|
||||||
function calculateSimilarity(a: number[], b: number[]) { }
|
function calculateSimilarity(a: number[], b: number[]) { }
|
||||||
function isValidEmail(email: string): boolean { }
|
function isValidEmail(email: string): boolean { }
|
||||||
|
|
||||||
// ❌ BAD: Unclear or noun-only
|
// FAIL: BAD: Unclear or noun-only
|
||||||
async function market(id: string) { }
|
async function market(id: string) { }
|
||||||
function similarity(a, b) { }
|
function similarity(a, b) { }
|
||||||
function email(e) { }
|
function email(e) { }
|
||||||
@@ -76,7 +76,7 @@ function email(e) { }
|
|||||||
### 불변성 패턴 (필수)
|
### 불변성 패턴 (필수)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ ALWAYS use spread operator
|
// PASS: ALWAYS use spread operator
|
||||||
const updatedUser = {
|
const updatedUser = {
|
||||||
...user,
|
...user,
|
||||||
name: 'New Name'
|
name: 'New Name'
|
||||||
@@ -84,7 +84,7 @@ const updatedUser = {
|
|||||||
|
|
||||||
const updatedArray = [...items, newItem]
|
const updatedArray = [...items, newItem]
|
||||||
|
|
||||||
// ❌ NEVER mutate directly
|
// FAIL: NEVER mutate directly
|
||||||
user.name = 'New Name' // BAD
|
user.name = 'New Name' // BAD
|
||||||
items.push(newItem) // BAD
|
items.push(newItem) // BAD
|
||||||
```
|
```
|
||||||
@@ -92,7 +92,7 @@ items.push(newItem) // BAD
|
|||||||
### 에러 처리
|
### 에러 처리
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Comprehensive error handling
|
// PASS: GOOD: Comprehensive error handling
|
||||||
async function fetchData(url: string) {
|
async function fetchData(url: string) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
@@ -108,7 +108,7 @@ async function fetchData(url: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: No error handling
|
// FAIL: BAD: No error handling
|
||||||
async function fetchData(url) {
|
async function fetchData(url) {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
return response.json()
|
return response.json()
|
||||||
@@ -118,14 +118,14 @@ async function fetchData(url) {
|
|||||||
### Async/Await 모범 사례
|
### Async/Await 모범 사례
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Parallel execution when possible
|
// PASS: GOOD: Parallel execution when possible
|
||||||
const [users, markets, stats] = await Promise.all([
|
const [users, markets, stats] = await Promise.all([
|
||||||
fetchUsers(),
|
fetchUsers(),
|
||||||
fetchMarkets(),
|
fetchMarkets(),
|
||||||
fetchStats()
|
fetchStats()
|
||||||
])
|
])
|
||||||
|
|
||||||
// ❌ BAD: Sequential when unnecessary
|
// FAIL: BAD: Sequential when unnecessary
|
||||||
const users = await fetchUsers()
|
const users = await fetchUsers()
|
||||||
const markets = await fetchMarkets()
|
const markets = await fetchMarkets()
|
||||||
const stats = await fetchStats()
|
const stats = await fetchStats()
|
||||||
@@ -134,7 +134,7 @@ const stats = await fetchStats()
|
|||||||
### 타입 안전성
|
### 타입 안전성
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Proper types
|
// PASS: GOOD: Proper types
|
||||||
interface Market {
|
interface Market {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@@ -146,7 +146,7 @@ function getMarket(id: string): Promise<Market> {
|
|||||||
// Implementation
|
// Implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: Using 'any'
|
// FAIL: BAD: Using 'any'
|
||||||
function getMarket(id: any): Promise<any> {
|
function getMarket(id: any): Promise<any> {
|
||||||
// Implementation
|
// Implementation
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,7 @@ function getMarket(id: any): Promise<any> {
|
|||||||
### 컴포넌트 구조
|
### 컴포넌트 구조
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Functional component with types
|
// PASS: GOOD: Functional component with types
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
@@ -182,7 +182,7 @@ export function Button({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ BAD: No types, unclear structure
|
// FAIL: BAD: No types, unclear structure
|
||||||
export function Button(props) {
|
export function Button(props) {
|
||||||
return <button onClick={props.onClick}>{props.children}</button>
|
return <button onClick={props.onClick}>{props.children}</button>
|
||||||
}
|
}
|
||||||
@@ -191,7 +191,7 @@ export function Button(props) {
|
|||||||
### 커스텀 Hook
|
### 커스텀 Hook
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Reusable custom hook
|
// PASS: GOOD: Reusable custom hook
|
||||||
export function useDebounce<T>(value: T, delay: number): T {
|
export function useDebounce<T>(value: T, delay: number): T {
|
||||||
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
||||||
|
|
||||||
@@ -213,25 +213,25 @@ const debouncedQuery = useDebounce(searchQuery, 500)
|
|||||||
### 상태 관리
|
### 상태 관리
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Proper state updates
|
// PASS: GOOD: Proper state updates
|
||||||
const [count, setCount] = useState(0)
|
const [count, setCount] = useState(0)
|
||||||
|
|
||||||
// Functional update for state based on previous state
|
// Functional update for state based on previous state
|
||||||
setCount(prev => prev + 1)
|
setCount(prev => prev + 1)
|
||||||
|
|
||||||
// ❌ BAD: Direct state reference
|
// FAIL: BAD: Direct state reference
|
||||||
setCount(count + 1) // Can be stale in async scenarios
|
setCount(count + 1) // Can be stale in async scenarios
|
||||||
```
|
```
|
||||||
|
|
||||||
### 조건부 렌더링
|
### 조건부 렌더링
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Clear conditional rendering
|
// PASS: GOOD: Clear conditional rendering
|
||||||
{isLoading && <Spinner />}
|
{isLoading && <Spinner />}
|
||||||
{error && <ErrorMessage error={error} />}
|
{error && <ErrorMessage error={error} />}
|
||||||
{data && <DataDisplay data={data} />}
|
{data && <DataDisplay data={data} />}
|
||||||
|
|
||||||
// ❌ BAD: Ternary hell
|
// FAIL: BAD: Ternary hell
|
||||||
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
|
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ GET /api/markets?status=active&limit=10&offset=0
|
|||||||
### 응답 형식
|
### 응답 형식
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Consistent response structure
|
// PASS: GOOD: Consistent response structure
|
||||||
interface ApiResponse<T> {
|
interface ApiResponse<T> {
|
||||||
success: boolean
|
success: boolean
|
||||||
data?: T
|
data?: T
|
||||||
@@ -285,7 +285,7 @@ return NextResponse.json({
|
|||||||
```typescript
|
```typescript
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
// ✅ GOOD: Schema validation
|
// PASS: GOOD: Schema validation
|
||||||
const CreateMarketSchema = z.object({
|
const CreateMarketSchema = z.object({
|
||||||
name: z.string().min(1).max(200),
|
name: z.string().min(1).max(200),
|
||||||
description: z.string().min(1).max(2000),
|
description: z.string().min(1).max(2000),
|
||||||
@@ -348,14 +348,14 @@ types/market.types.ts # camelCase with .types suffix
|
|||||||
### 주석을 작성해야 하는 경우
|
### 주석을 작성해야 하는 경우
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Explain WHY, not WHAT
|
// PASS: GOOD: Explain WHY, not WHAT
|
||||||
// Use exponential backoff to avoid overwhelming the API during outages
|
// Use exponential backoff to avoid overwhelming the API during outages
|
||||||
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
|
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
|
||||||
|
|
||||||
// Deliberately using mutation here for performance with large arrays
|
// Deliberately using mutation here for performance with large arrays
|
||||||
items.push(newItem)
|
items.push(newItem)
|
||||||
|
|
||||||
// ❌ BAD: Stating the obvious
|
// FAIL: BAD: Stating the obvious
|
||||||
// Increment counter by 1
|
// Increment counter by 1
|
||||||
count++
|
count++
|
||||||
|
|
||||||
@@ -395,12 +395,12 @@ export async function searchMarkets(
|
|||||||
```typescript
|
```typescript
|
||||||
import { useMemo, useCallback } from 'react'
|
import { useMemo, useCallback } from 'react'
|
||||||
|
|
||||||
// ✅ GOOD: Memoize expensive computations
|
// PASS: GOOD: Memoize expensive computations
|
||||||
const sortedMarkets = useMemo(() => {
|
const sortedMarkets = useMemo(() => {
|
||||||
return [...markets].sort((a, b) => b.volume - a.volume)
|
return [...markets].sort((a, b) => b.volume - a.volume)
|
||||||
}, [markets])
|
}, [markets])
|
||||||
|
|
||||||
// ✅ GOOD: Memoize callbacks
|
// PASS: GOOD: Memoize callbacks
|
||||||
const handleSearch = useCallback((query: string) => {
|
const handleSearch = useCallback((query: string) => {
|
||||||
setSearchQuery(query)
|
setSearchQuery(query)
|
||||||
}, [])
|
}, [])
|
||||||
@@ -411,7 +411,7 @@ const handleSearch = useCallback((query: string) => {
|
|||||||
```typescript
|
```typescript
|
||||||
import { lazy, Suspense } from 'react'
|
import { lazy, Suspense } from 'react'
|
||||||
|
|
||||||
// ✅ GOOD: Lazy load heavy components
|
// PASS: GOOD: Lazy load heavy components
|
||||||
const HeavyChart = lazy(() => import('./HeavyChart'))
|
const HeavyChart = lazy(() => import('./HeavyChart'))
|
||||||
|
|
||||||
export function Dashboard() {
|
export function Dashboard() {
|
||||||
@@ -426,13 +426,13 @@ export function Dashboard() {
|
|||||||
### 데이터베이스 쿼리
|
### 데이터베이스 쿼리
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Select only needed columns
|
// PASS: GOOD: Select only needed columns
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('id, name, status')
|
.select('id, name, status')
|
||||||
.limit(10)
|
.limit(10)
|
||||||
|
|
||||||
// ❌ BAD: Select everything
|
// FAIL: BAD: Select everything
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('markets')
|
.from('markets')
|
||||||
.select('*')
|
.select('*')
|
||||||
@@ -459,12 +459,12 @@ test('calculates similarity correctly', () => {
|
|||||||
### 테스트 네이밍
|
### 테스트 네이밍
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Descriptive test names
|
// PASS: GOOD: Descriptive test names
|
||||||
test('returns empty array when no markets match query', () => { })
|
test('returns empty array when no markets match query', () => { })
|
||||||
test('throws error when OpenAI API key is missing', () => { })
|
test('throws error when OpenAI API key is missing', () => { })
|
||||||
test('falls back to substring search when Redis unavailable', () => { })
|
test('falls back to substring search when Redis unavailable', () => { })
|
||||||
|
|
||||||
// ❌ BAD: Vague test names
|
// FAIL: BAD: Vague test names
|
||||||
test('works', () => { })
|
test('works', () => { })
|
||||||
test('test search', () => { })
|
test('test search', () => { })
|
||||||
```
|
```
|
||||||
@@ -475,12 +475,12 @@ test('test search', () => { })
|
|||||||
|
|
||||||
### 1. 긴 함수
|
### 1. 긴 함수
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: Function > 50 lines
|
// FAIL: BAD: Function > 50 lines
|
||||||
function processMarketData() {
|
function processMarketData() {
|
||||||
// 100 lines of code
|
// 100 lines of code
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ GOOD: Split into smaller functions
|
// PASS: GOOD: Split into smaller functions
|
||||||
function processMarketData() {
|
function processMarketData() {
|
||||||
const validated = validateData()
|
const validated = validateData()
|
||||||
const transformed = transformData(validated)
|
const transformed = transformData(validated)
|
||||||
@@ -490,7 +490,7 @@ function processMarketData() {
|
|||||||
|
|
||||||
### 2. 깊은 중첩
|
### 2. 깊은 중첩
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: 5+ levels of nesting
|
// FAIL: BAD: 5+ levels of nesting
|
||||||
if (user) {
|
if (user) {
|
||||||
if (user.isAdmin) {
|
if (user.isAdmin) {
|
||||||
if (market) {
|
if (market) {
|
||||||
@@ -503,7 +503,7 @@ if (user) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ GOOD: Early returns
|
// PASS: GOOD: Early returns
|
||||||
if (!user) return
|
if (!user) return
|
||||||
if (!user.isAdmin) return
|
if (!user.isAdmin) return
|
||||||
if (!market) return
|
if (!market) return
|
||||||
@@ -515,11 +515,11 @@ if (!hasPermission) return
|
|||||||
|
|
||||||
### 3. 매직 넘버
|
### 3. 매직 넘버
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ BAD: Unexplained numbers
|
// FAIL: BAD: Unexplained numbers
|
||||||
if (retryCount > 3) { }
|
if (retryCount > 3) { }
|
||||||
setTimeout(callback, 500)
|
setTimeout(callback, 500)
|
||||||
|
|
||||||
// ✅ GOOD: Named constants
|
// PASS: GOOD: Named constants
|
||||||
const MAX_RETRIES = 3
|
const MAX_RETRIES = 3
|
||||||
const DEBOUNCE_DELAY_MS = 500
|
const DEBOUNCE_DELAY_MS = 500
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ React, Next.js 및 고성능 사용자 인터페이스를 위한 모던 프론
|
|||||||
### 상속보다 합성
|
### 상속보다 합성
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ GOOD: Component composition
|
// PASS: GOOD: Component composition
|
||||||
interface CardProps {
|
interface CardProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
variant?: 'default' | 'outlined'
|
variant?: 'default' | 'outlined'
|
||||||
@@ -304,17 +304,17 @@ export function useMarkets() {
|
|||||||
### 메모이제이션
|
### 메모이제이션
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ useMemo for expensive computations
|
// PASS: useMemo for expensive computations
|
||||||
const sortedMarkets = useMemo(() => {
|
const sortedMarkets = useMemo(() => {
|
||||||
return [...markets].sort((a, b) => b.volume - a.volume)
|
return [...markets].sort((a, b) => b.volume - a.volume)
|
||||||
}, [markets])
|
}, [markets])
|
||||||
|
|
||||||
// ✅ useCallback for functions passed to children
|
// PASS: useCallback for functions passed to children
|
||||||
const handleSearch = useCallback((query: string) => {
|
const handleSearch = useCallback((query: string) => {
|
||||||
setSearchQuery(query)
|
setSearchQuery(query)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// ✅ React.memo for pure components
|
// PASS: React.memo for pure components
|
||||||
export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
||||||
return (
|
return (
|
||||||
<div className="market-card">
|
<div className="market-card">
|
||||||
@@ -330,7 +330,7 @@ export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
|||||||
```typescript
|
```typescript
|
||||||
import { lazy, Suspense } from 'react'
|
import { lazy, Suspense } from 'react'
|
||||||
|
|
||||||
// ✅ Lazy load heavy components
|
// PASS: Lazy load heavy components
|
||||||
const HeavyChart = lazy(() => import('./HeavyChart'))
|
const HeavyChart = lazy(() => import('./HeavyChart'))
|
||||||
const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
|
const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
|
||||||
|
|
||||||
@@ -525,7 +525,7 @@ export class ErrorBoundary extends React.Component<
|
|||||||
```typescript
|
```typescript
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
|
|
||||||
// ✅ List animations
|
// PASS: List animations
|
||||||
export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
@@ -544,7 +544,7 @@ export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Modal animations
|
// PASS: Modal animations
|
||||||
export function Modal({ isOpen, onClose, children }: ModalProps) {
|
export function Modal({ isOpen, onClose, children }: ModalProps) {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
|
|||||||
@@ -36,12 +36,12 @@ origin: ECC
|
|||||||
┌─────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────┐
|
||||||
│ │
|
│ │
|
||||||
│ ┌──────────┐ ┌──────────┐ │
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
│ │ DISPATCH │─────▶│ EVALUATE │ │
|
│ │ DISPATCH │─────│ EVALUATE │ │
|
||||||
│ └──────────┘ └──────────┘ │
|
│ └──────────┘ └──────────┘ │
|
||||||
│ ▲ │ │
|
│ ▲ │ │
|
||||||
│ │ ▼ │
|
│ │ ▼ │
|
||||||
│ ┌──────────┐ ┌──────────┐ │
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
│ │ LOOP │◀─────│ REFINE │ │
|
│ │ LOOP │─────│ REFINE │ │
|
||||||
│ └──────────┘ └──────────┘ │
|
│ └──────────┘ └──────────┘ │
|
||||||
│ │
|
│ │
|
||||||
│ Max 3 cycles, then proceed │
|
│ Max 3 cycles, then proceed │
|
||||||
|
|||||||
@@ -140,10 +140,10 @@ await db.query(
|
|||||||
|
|
||||||
#### JWT 토큰 처리
|
#### JWT 토큰 처리
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ WRONG: localStorage (vulnerable to XSS)
|
// FAIL: WRONG: localStorage (vulnerable to XSS)
|
||||||
localStorage.setItem('token', token)
|
localStorage.setItem('token', token)
|
||||||
|
|
||||||
// ✅ CORRECT: httpOnly cookies
|
// PASS: CORRECT: httpOnly cookies
|
||||||
res.setHeader('Set-Cookie',
|
res.setHeader('Set-Cookie',
|
||||||
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
|
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
|
||||||
```
|
```
|
||||||
@@ -302,18 +302,18 @@ app.use('/api/search', searchLimiter)
|
|||||||
|
|
||||||
#### 로깅
|
#### 로깅
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ WRONG: Logging sensitive data
|
// FAIL: WRONG: Logging sensitive data
|
||||||
console.log('User login:', { email, password })
|
console.log('User login:', { email, password })
|
||||||
console.log('Payment:', { cardNumber, cvv })
|
console.log('Payment:', { cardNumber, cvv })
|
||||||
|
|
||||||
// ✅ CORRECT: Redact sensitive data
|
// PASS: CORRECT: Redact sensitive data
|
||||||
console.log('User login:', { email, userId })
|
console.log('User login:', { email, userId })
|
||||||
console.log('Payment:', { last4: card.last4, userId })
|
console.log('Payment:', { last4: card.last4, userId })
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 에러 메시지
|
#### 에러 메시지
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ WRONG: Exposing internal details
|
// FAIL: WRONG: Exposing internal details
|
||||||
catch (error) {
|
catch (error) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: error.message, stack: error.stack },
|
{ error: error.message, stack: error.stack },
|
||||||
@@ -321,7 +321,7 @@ catch (error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ CORRECT: Generic error messages
|
// PASS: CORRECT: Generic error messages
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('Internal error:', error)
|
console.error('Internal error:', error)
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#### 최소 권한 원칙
|
#### 최소 권한 원칙
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# ✅ CORRECT: Minimal permissions
|
# PASS: CORRECT: Minimal permissions
|
||||||
iam_role:
|
iam_role:
|
||||||
permissions:
|
permissions:
|
||||||
- s3:GetObject # Only read access
|
- s3:GetObject # Only read access
|
||||||
@@ -32,7 +32,7 @@ iam_role:
|
|||||||
resources:
|
resources:
|
||||||
- arn:aws:s3:::my-bucket/* # Specific bucket only
|
- arn:aws:s3:::my-bucket/* # Specific bucket only
|
||||||
|
|
||||||
# ❌ WRONG: Overly broad permissions
|
# FAIL: WRONG: Overly broad permissions
|
||||||
iam_role:
|
iam_role:
|
||||||
permissions:
|
permissions:
|
||||||
- s3:* # All S3 actions
|
- s3:* # All S3 actions
|
||||||
@@ -65,14 +65,14 @@ aws iam enable-mfa-device \
|
|||||||
#### 클라우드 시크릿 매니저
|
#### 클라우드 시크릿 매니저
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ CORRECT: Use cloud secrets manager
|
// PASS: CORRECT: Use cloud secrets manager
|
||||||
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
|
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
|
||||||
|
|
||||||
const client = new SecretsManager({ region: 'us-east-1' });
|
const client = new SecretsManager({ region: 'us-east-1' });
|
||||||
const secret = await client.getSecretValue({ SecretId: 'prod/api-key' });
|
const secret = await client.getSecretValue({ SecretId: 'prod/api-key' });
|
||||||
const apiKey = JSON.parse(secret.SecretString).key;
|
const apiKey = JSON.parse(secret.SecretString).key;
|
||||||
|
|
||||||
// ❌ WRONG: Hardcoded or in environment variables only
|
// FAIL: WRONG: Hardcoded or in environment variables only
|
||||||
const apiKey = process.env.API_KEY; // Not rotated, not audited
|
const apiKey = process.env.API_KEY; // Not rotated, not audited
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ aws secretsmanager rotate-secret \
|
|||||||
#### VPC 및 방화벽 구성
|
#### VPC 및 방화벽 구성
|
||||||
|
|
||||||
```terraform
|
```terraform
|
||||||
# ✅ CORRECT: Restricted security group
|
# PASS: CORRECT: Restricted security group
|
||||||
resource "aws_security_group" "app" {
|
resource "aws_security_group" "app" {
|
||||||
name = "app-sg"
|
name = "app-sg"
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ resource "aws_security_group" "app" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# ❌ WRONG: Open to the internet
|
# FAIL: WRONG: Open to the internet
|
||||||
resource "aws_security_group" "bad" {
|
resource "aws_security_group" "bad" {
|
||||||
ingress {
|
ingress {
|
||||||
from_port = 0
|
from_port = 0
|
||||||
@@ -142,7 +142,7 @@ resource "aws_security_group" "bad" {
|
|||||||
#### CloudWatch/로깅 구성
|
#### CloudWatch/로깅 구성
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ CORRECT: Comprehensive logging
|
// PASS: CORRECT: Comprehensive logging
|
||||||
import { CloudWatchLogsClient, CreateLogStreamCommand } from '@aws-sdk/client-cloudwatch-logs';
|
import { CloudWatchLogsClient, CreateLogStreamCommand } from '@aws-sdk/client-cloudwatch-logs';
|
||||||
|
|
||||||
const logSecurityEvent = async (event: SecurityEvent) => {
|
const logSecurityEvent = async (event: SecurityEvent) => {
|
||||||
@@ -177,7 +177,7 @@ const logSecurityEvent = async (event: SecurityEvent) => {
|
|||||||
#### 보안 파이프라인 구성
|
#### 보안 파이프라인 구성
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# ✅ CORRECT: Secure GitHub Actions workflow
|
# PASS: CORRECT: Secure GitHub Actions workflow
|
||||||
name: Deploy
|
name: Deploy
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -237,7 +237,7 @@ jobs:
|
|||||||
#### Cloudflare 보안 구성
|
#### Cloudflare 보안 구성
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ CORRECT: Cloudflare Workers with security headers
|
// PASS: CORRECT: Cloudflare Workers with security headers
|
||||||
export default {
|
export default {
|
||||||
async fetch(request: Request): Promise<Response> {
|
async fetch(request: Request): Promise<Response> {
|
||||||
const response = await fetch(request);
|
const response = await fetch(request);
|
||||||
@@ -281,7 +281,7 @@ export default {
|
|||||||
#### 자동 백업
|
#### 자동 백업
|
||||||
|
|
||||||
```terraform
|
```terraform
|
||||||
# ✅ CORRECT: Automated RDS backups
|
# PASS: CORRECT: Automated RDS backups
|
||||||
resource "aws_db_instance" "main" {
|
resource "aws_db_instance" "main" {
|
||||||
allocated_storage = 20
|
allocated_storage = 20
|
||||||
engine = "postgres"
|
engine = "postgres"
|
||||||
@@ -327,10 +327,10 @@ resource "aws_db_instance" "main" {
|
|||||||
### S3 버킷 노출
|
### S3 버킷 노출
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# ❌ WRONG: Public bucket
|
# FAIL: WRONG: Public bucket
|
||||||
aws s3api put-bucket-acl --bucket my-bucket --acl public-read
|
aws s3api put-bucket-acl --bucket my-bucket --acl public-read
|
||||||
|
|
||||||
# ✅ CORRECT: Private bucket with specific access
|
# PASS: CORRECT: Private bucket with specific access
|
||||||
aws s3api put-bucket-acl --bucket my-bucket --acl private
|
aws s3api put-bucket-acl --bucket my-bucket --acl private
|
||||||
aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json
|
aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json
|
||||||
```
|
```
|
||||||
@@ -338,12 +338,12 @@ aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json
|
|||||||
### RDS 공개 접근
|
### RDS 공개 접근
|
||||||
|
|
||||||
```terraform
|
```terraform
|
||||||
# ❌ WRONG
|
# FAIL: WRONG
|
||||||
resource "aws_db_instance" "bad" {
|
resource "aws_db_instance" "bad" {
|
||||||
publicly_accessible = true # NEVER do this!
|
publicly_accessible = true # NEVER do this!
|
||||||
}
|
}
|
||||||
|
|
||||||
# ✅ CORRECT
|
# PASS: CORRECT
|
||||||
resource "aws_db_instance" "good" {
|
resource "aws_db_instance" "good" {
|
||||||
publicly_accessible = false
|
publicly_accessible = false
|
||||||
vpc_security_group_ids = [aws_security_group.db.id]
|
vpc_security_group_ids = [aws_security_group.db.id]
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
**🌐 Idioma / Language / 语言**
|
**Idioma / Language / 语言**
|
||||||
|
|
||||||
[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Português (BR)](README.md)
|
[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [Português (BR)](README.md)
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ Este repositório contém apenas o código. Os guias explicam tudo.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Início Rápido
|
## Início Rápido
|
||||||
|
|
||||||
Comece em menos de 2 minutos:
|
Comece em menos de 2 minutos:
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ Comece em menos de 2 minutos:
|
|||||||
|
|
||||||
### Passo 2: Instalar as Regras (Obrigatório)
|
### Passo 2: Instalar as Regras (Obrigatório)
|
||||||
|
|
||||||
> ⚠️ **Importante:** Plugins do Claude Code não podem distribuir `rules` automaticamente. Instale-as manualmente:
|
> WARNING: **Importante:** Plugins do Claude Code não podem distribuir `rules` automaticamente. Instale-as manualmente:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone o repositório primeiro
|
# Clone o repositório primeiro
|
||||||
@@ -161,11 +161,11 @@ npx ecc-install typescript
|
|||||||
/plugin list everything-claude-code@everything-claude-code
|
/plugin list everything-claude-code@everything-claude-code
|
||||||
```
|
```
|
||||||
|
|
||||||
✨ **Pronto!** Você agora tem acesso a 28 agentes, 116 skills e 59 comandos.
|
**Pronto!** Você agora tem acesso a 28 agentes, 116 skills e 59 comandos.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌐 Suporte Multiplataforma
|
## Suporte Multiplataforma
|
||||||
|
|
||||||
Este plugin agora suporta totalmente **Windows, macOS e Linux**, com integração estreita em principais IDEs (Cursor, OpenCode, Antigravity) e harnesses CLI. Todos os hooks e scripts foram reescritos em Node.js para máxima compatibilidade.
|
Este plugin agora suporta totalmente **Windows, macOS e Linux**, com integração estreita em principais IDEs (Cursor, OpenCode, Antigravity) e harnesses CLI. Todos os hooks e scripts foram reescritos em Node.js para máxima compatibilidade.
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ export ECC_DISABLED_HOOKS="pre:bash:tmux-reminder,post:edit:typecheck"
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 O Que Está Incluído
|
## O Que Está Incluído
|
||||||
|
|
||||||
```
|
```
|
||||||
everything-claude-code/
|
everything-claude-code/
|
||||||
@@ -230,7 +230,7 @@ everything-claude-code/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠️ Ferramentas do Ecossistema
|
## Ferramentas do Ecossistema
|
||||||
|
|
||||||
### Criador de Skills
|
### Criador de Skills
|
||||||
|
|
||||||
@@ -269,7 +269,7 @@ npx ecc-agentshield scan --opus --stream
|
|||||||
npx ecc-agentshield init
|
npx ecc-agentshield init
|
||||||
```
|
```
|
||||||
|
|
||||||
### 🧠 Aprendizado Contínuo v2
|
### Aprendizado Contínuo v2
|
||||||
|
|
||||||
O sistema de aprendizado baseado em instincts aprende automaticamente seus padrões:
|
O sistema de aprendizado baseado em instincts aprende automaticamente seus padrões:
|
||||||
|
|
||||||
@@ -282,7 +282,7 @@ O sistema de aprendizado baseado em instincts aprende automaticamente seus padr
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 Requisitos
|
## Requisitos
|
||||||
|
|
||||||
### Versão do Claude Code CLI
|
### Versão do Claude Code CLI
|
||||||
|
|
||||||
@@ -295,7 +295,7 @@ claude --version
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📥 Instalação
|
## Instalação
|
||||||
|
|
||||||
### Opção 1: Instalar como Plugin (Recomendado)
|
### Opção 1: Instalar como Plugin (Recomendado)
|
||||||
|
|
||||||
@@ -343,7 +343,7 @@ Ou adicione diretamente ao seu `~/.claude/settings.json`:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 🔧 Opção 2: Instalação Manual
|
### Opção 2: Instalação Manual
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clonar o repositório
|
# Clonar o repositório
|
||||||
@@ -365,7 +365,7 @@ cp -r everything-claude-code/.agents/skills/* ~/.claude/skills/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎯 Conceitos-Chave
|
## Conceitos-Chave
|
||||||
|
|
||||||
### Agentes
|
### Agentes
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ Regras são diretrizes sempre seguidas, organizadas em `common/` (agnóstico à
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🗺️ Qual Agente Devo Usar?
|
## Qual Agente Devo Usar?
|
||||||
|
|
||||||
| Quero... | Use este comando | Agente usado |
|
| Quero... | Use este comando | Agente usado |
|
||||||
|----------|-----------------|--------------|
|
|----------|-----------------|--------------|
|
||||||
@@ -437,7 +437,7 @@ Regras são diretrizes sempre seguidas, organizadas em `common/` (agnóstico à
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ❓ FAQ
|
## FAQ
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>Como verificar quais agentes/comandos estão instalados?</b></summary>
|
<summary><b>Como verificar quais agentes/comandos estão instalados?</b></summary>
|
||||||
@@ -476,7 +476,7 @@ Veja [CONTRIBUTING.md](CONTRIBUTING.md). Em resumo:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🧪 Executando Testes
|
## Executando Testes
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Executar todos os testes
|
# Executar todos os testes
|
||||||
@@ -490,7 +490,7 @@ node tests/hooks/hooks.test.js
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🤝 Contribuindo
|
## Contribuindo
|
||||||
|
|
||||||
**Contribuições são bem-vindas e incentivadas.**
|
**Contribuições são bem-vindas e incentivadas.**
|
||||||
|
|
||||||
@@ -504,6 +504,6 @@ Por favor contribua! Veja [CONTRIBUTING.md](CONTRIBUTING.md) para diretrizes.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📄 Licença
|
## Licença
|
||||||
|
|
||||||
MIT — consulte o [arquivo LICENSE](../../LICENSE) para detalhes.
|
MIT — consulte o [arquivo LICENSE](../../LICENSE) para detalhes.
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ Artifacts generated:
|
|||||||
╔══════════════════════════════════════════════════════════════╗
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
║ E2E Test Results ║
|
║ E2E Test Results ║
|
||||||
╠══════════════════════════════════════════════════════════════╣
|
╠══════════════════════════════════════════════════════════════╣
|
||||||
║ Status: ✅ ALL TESTS PASSED ║
|
║ Status: PASS: ALL TESTS PASSED ║
|
||||||
║ Total: 3 tests ║
|
║ Total: 3 tests ║
|
||||||
║ Passed: 3 (100%) ║
|
║ Passed: 3 (100%) ║
|
||||||
║ Failed: 0 ║
|
║ Failed: 0 ║
|
||||||
@@ -191,15 +191,15 @@ Artifacts generated:
|
|||||||
╚══════════════════════════════════════════════════════════════╝
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
Artifacts:
|
Artifacts:
|
||||||
📸 Screenshots: 2 files
|
Screenshots: 2 files
|
||||||
📹 Videos: 0 files (only on failure)
|
Videos: 0 files (only on failure)
|
||||||
🔍 Traces: 0 files (only on failure)
|
Traces: 0 files (only on failure)
|
||||||
📊 HTML Report: playwright-report/index.html
|
HTML Report: playwright-report/index.html
|
||||||
|
|
||||||
View report: npx playwright show-report
|
View report: npx playwright show-report
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ E2E test suite ready for CI/CD integration!
|
PASS: E2E test suite ready for CI/CD integration!
|
||||||
```
|
```
|
||||||
|
|
||||||
## Artefatos de Teste
|
## Artefatos de Teste
|
||||||
@@ -235,7 +235,7 @@ open artifacts/search-results.png
|
|||||||
Se um teste falhar de forma intermitente:
|
Se um teste falhar de forma intermitente:
|
||||||
|
|
||||||
```
|
```
|
||||||
⚠️ FLAKY TEST DETECTED: tests/e2e/markets/trade.spec.ts
|
WARNING: FLAKY TEST DETECTED: tests/e2e/markets/trade.spec.ts
|
||||||
|
|
||||||
Test passed 7/10 runs (70% pass rate)
|
Test passed 7/10 runs (70% pass rate)
|
||||||
|
|
||||||
@@ -254,10 +254,10 @@ Quarantine recommendation: Mark as test.fixme() until fixed
|
|||||||
## Configuração de Navegador
|
## Configuração de Navegador
|
||||||
|
|
||||||
Os testes rodam em múltiplos navegadores por padrão:
|
Os testes rodam em múltiplos navegadores por padrão:
|
||||||
- ✅ Chromium (Desktop Chrome)
|
- PASS: Chromium (Desktop Chrome)
|
||||||
- ✅ Firefox (Desktop)
|
- PASS: Firefox (Desktop)
|
||||||
- ✅ WebKit (Desktop Safari)
|
- PASS: WebKit (Desktop Safari)
|
||||||
- ✅ Mobile Chrome (optional)
|
- PASS: Mobile Chrome (optional)
|
||||||
|
|
||||||
Configure em `playwright.config.ts` para ajustar navegadores.
|
Configure em `playwright.config.ts` para ajustar navegadores.
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ Adicione ao seu pipeline de CI:
|
|||||||
|
|
||||||
Para PMX, priorize estes testes E2E:
|
Para PMX, priorize estes testes E2E:
|
||||||
|
|
||||||
**🔴 CRITICAL (Must Always Pass):**
|
**CRITICAL (Must Always Pass):**
|
||||||
1. User can connect wallet
|
1. User can connect wallet
|
||||||
2. User can browse markets
|
2. User can browse markets
|
||||||
3. User can search markets (semantic search)
|
3. User can search markets (semantic search)
|
||||||
@@ -294,7 +294,7 @@ Para PMX, priorize estes testes E2E:
|
|||||||
6. Market resolves correctly
|
6. Market resolves correctly
|
||||||
7. User can withdraw funds
|
7. User can withdraw funds
|
||||||
|
|
||||||
**🟡 IMPORTANT:**
|
**IMPORTANT:**
|
||||||
1. Market creation flow
|
1. Market creation flow
|
||||||
2. User profile updates
|
2. User profile updates
|
||||||
3. Real-time price updates
|
3. Real-time price updates
|
||||||
@@ -305,20 +305,20 @@ Para PMX, priorize estes testes E2E:
|
|||||||
## Boas Práticas
|
## Boas Práticas
|
||||||
|
|
||||||
**DO:**
|
**DO:**
|
||||||
- ✅ Use Page Object Model para manutenção
|
- PASS: Use Page Object Model para manutenção
|
||||||
- ✅ Use atributos data-testid para seletores
|
- PASS: Use atributos data-testid para seletores
|
||||||
- ✅ Aguarde respostas de API, não timeouts arbitrários
|
- PASS: Aguarde respostas de API, não timeouts arbitrários
|
||||||
- ✅ Teste jornadas críticas de usuário end-to-end
|
- PASS: Teste jornadas críticas de usuário end-to-end
|
||||||
- ✅ Rode testes antes de mergear em main
|
- PASS: Rode testes antes de mergear em main
|
||||||
- ✅ Revise artefatos quando testes falharem
|
- PASS: Revise artefatos quando testes falharem
|
||||||
|
|
||||||
**DON'T:**
|
**DON'T:**
|
||||||
- ❌ Use seletores frágeis (classes CSS podem mudar)
|
- FAIL: Use seletores frágeis (classes CSS podem mudar)
|
||||||
- ❌ Teste detalhes de implementação
|
- FAIL: Teste detalhes de implementação
|
||||||
- ❌ Rode testes contra produção
|
- FAIL: Rode testes contra produção
|
||||||
- ❌ Ignore testes flaky
|
- FAIL: Ignore testes flaky
|
||||||
- ❌ Pule revisão de artefatos em falhas
|
- FAIL: Pule revisão de artefatos em falhas
|
||||||
- ❌ Teste todo edge case com E2E (use testes unitários)
|
- FAIL: Teste todo edge case com E2E (use testes unitários)
|
||||||
|
|
||||||
## Notas Importantes
|
## Notas Importantes
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ ok project/internal/handler 0.023s
|
|||||||
| Files modified | 2 |
|
| Files modified | 2 |
|
||||||
| Remaining issues | 0 |
|
| Remaining issues | 0 |
|
||||||
|
|
||||||
Build Status: ✅ SUCCESS
|
Build Status: PASS: SUCCESS
|
||||||
```
|
```
|
||||||
|
|
||||||
## Erros Comuns Corrigidos
|
## Erros Comuns Corrigidos
|
||||||
|
|||||||
@@ -124,16 +124,16 @@ return fmt.Errorf("get user %s: %w", userID, err)
|
|||||||
- HIGH: 1
|
- HIGH: 1
|
||||||
- MEDIUM: 0
|
- MEDIUM: 0
|
||||||
|
|
||||||
Recommendation: ❌ Block merge until CRITICAL issue is fixed
|
Recommendation: FAIL: Block merge until CRITICAL issue is fixed
|
||||||
```
|
```
|
||||||
|
|
||||||
## Critérios de Aprovação
|
## Critérios de Aprovação
|
||||||
|
|
||||||
| Status | Condição |
|
| Status | Condição |
|
||||||
|--------|----------|
|
|--------|----------|
|
||||||
| ✅ Aprovado | Sem problemas CRÍTICO ou ALTO |
|
| PASS: Aprovado | Sem problemas CRÍTICO ou ALTO |
|
||||||
| ⚠️ Aviso | Apenas problemas MÉDIOS (merge com cautela) |
|
| WARNING: Aviso | Apenas problemas MÉDIOS (merge com cautela) |
|
||||||
| ❌ Bloqueado | Problemas CRÍTICO ou ALTO encontrados |
|
| FAIL: Bloqueado | Problemas CRÍTICO ou ALTO encontrados |
|
||||||
## Integração com Outros Comandos
|
## Integração com Outros Comandos
|
||||||
|
|
||||||
- Use `/go-test` primeiro para garantir que os testes passam
|
- Use `/go-test` primeiro para garantir que os testes passam
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ Deleted: 12 unused functions
|
|||||||
Skipped: 2 items (tests failed)
|
Skipped: 2 items (tests failed)
|
||||||
Saved: ~450 lines removed
|
Saved: ~450 lines removed
|
||||||
──────────────────────────────
|
──────────────────────────────
|
||||||
All tests passing ✅
|
All tests passing PASS:
|
||||||
```
|
```
|
||||||
|
|
||||||
## Regras
|
## Regras
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ FAIL lib/liquidity.test.ts
|
|||||||
1 test failed, 0 passed
|
1 test failed, 0 passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ Os testes falharam conforme o esperado. Pronto para implementação.
|
PASS: Os testes falharam conforme o esperado. Pronto para implementação.
|
||||||
|
|
||||||
## Etapa 4: Implementar o código mínimo (VERDE)
|
## Etapa 4: Implementar o código mínimo (VERDE)
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ PASS lib/liquidity.test.ts
|
|||||||
3 tests passed
|
3 tests passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ Todos os testes passando
|
PASS: Todos os testes passando
|
||||||
|
|
||||||
## Etapa 6: Refatorar (MELHORAR)
|
## Etapa 6: Refatorar (MELHORAR)
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ PASS lib/liquidity.test.ts
|
|||||||
3 tests passed
|
3 tests passed
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ Refatoração concluída, testes ainda passando!
|
PASS: Refatoração concluída, testes ainda passando!
|
||||||
|
|
||||||
## Etapa 8: Verificar a cobertura
|
## Etapa 8: Verificar a cobertura
|
||||||
|
|
||||||
@@ -247,29 +247,29 @@ File | % Stmts | % Branch | % Funcs | % Lines
|
|||||||
---------------|---------|----------|---------|--------
|
---------------|---------|----------|---------|--------
|
||||||
liquidity.ts | 100 | 100 | 100 | 100
|
liquidity.ts | 100 | 100 | 100 | 100
|
||||||
|
|
||||||
Coverage: 100% ✅ (Target: 80%)
|
Coverage: 100% PASS: (Target: 80%)
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ TDD sessão completa!
|
PASS: TDD sessão completa!
|
||||||
```
|
```
|
||||||
|
|
||||||
## Boas Práticas de TDD
|
## Boas Práticas de TDD
|
||||||
|
|
||||||
**Fazer:**
|
**Fazer:**
|
||||||
- ✅ Escreva o teste PRIMEIRO, antes da implementação
|
- PASS: Escreva o teste PRIMEIRO, antes da implementação
|
||||||
- ✅ Rode testes e confirme que FALHAM antes de implementar
|
- PASS: Rode testes e confirme que FALHAM antes de implementar
|
||||||
- ✅ Escreva código mínimo para fazer passar
|
- PASS: Escreva código mínimo para fazer passar
|
||||||
- ✅ Refatore só depois que os testes estiverem verdes
|
- PASS: Refatore só depois que os testes estiverem verdes
|
||||||
- ✅ Adicione casos de borda e cenários de erro
|
- PASS: Adicione casos de borda e cenários de erro
|
||||||
- ✅ Mire 80%+ de cobertura (100% para código crítico)
|
- PASS: Mire 80%+ de cobertura (100% para código crítico)
|
||||||
|
|
||||||
**Não fazer:**
|
**Não fazer:**
|
||||||
- ❌ Escrever implementação antes de testes
|
- FAIL: Escrever implementação antes de testes
|
||||||
- ❌ Pular execução de testes após cada mudança
|
- FAIL: Pular execução de testes após cada mudança
|
||||||
- ❌ Escrever código demais de uma vez
|
- FAIL: Escrever código demais de uma vez
|
||||||
- ❌ Ignorar testes falhando
|
- FAIL: Ignorar testes falhando
|
||||||
- ❌ Testar detalhes de implementação (teste comportamento)
|
- FAIL: Testar detalhes de implementação (teste comportamento)
|
||||||
- ❌ Fazer mock de tudo (prefira testes de integração)
|
- FAIL: Fazer mock de tudo (prefira testes de integração)
|
||||||
|
|
||||||
## Tipos de Teste a Incluir
|
## Tipos de Teste a Incluir
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ File Before After
|
|||||||
src/services/auth.ts 45% 88%
|
src/services/auth.ts 45% 88%
|
||||||
src/utils/validation.ts 32% 82%
|
src/utils/validation.ts 32% 82%
|
||||||
──────────────────────────────
|
──────────────────────────────
|
||||||
Overall: 67% 84% ✅
|
Overall: 67% 84% PASS:
|
||||||
```
|
```
|
||||||
|
|
||||||
## Áreas de Foco
|
## Áreas de Foco
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
**🌐 Dil / Language / 语言 / 語言**
|
**Dil / Language / 语言 / 語言**
|
||||||
|
|
||||||
[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [**Türkçe**](README.md)
|
[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md) | [**Türkçe**](README.md)
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ Bu repository yalnızca ham kodu içerir. Rehberler her şeyi açıklıyor.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Hızlı Başlangıç
|
## Hızlı Başlangıç
|
||||||
|
|
||||||
2 dakikadan kısa sürede başlayın:
|
2 dakikadan kısa sürede başlayın:
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ Bu repository yalnızca ham kodu içerir. Rehberler her şeyi açıklıyor.
|
|||||||
|
|
||||||
### Adım 2: Rule'ları Kurun (Gerekli)
|
### Adım 2: Rule'ları Kurun (Gerekli)
|
||||||
|
|
||||||
> ⚠️ **Önemli:** Claude Code plugin'leri `rule`'ları otomatik olarak dağıtamaz. Manuel olarak kurmalısınız:
|
> WARNING: **Önemli:** Claude Code plugin'leri `rule`'ları otomatik olarak dağıtamaz. Manuel olarak kurmalısınız:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Önce repo'yu klonlayın
|
# Önce repo'yu klonlayın
|
||||||
@@ -164,11 +164,11 @@ Manuel kurulum talimatları için `rules/` klasöründeki README'ye bakın.
|
|||||||
/plugin list everything-claude-code@everything-claude-code
|
/plugin list everything-claude-code@everything-claude-code
|
||||||
```
|
```
|
||||||
|
|
||||||
✨ **Bu kadar!** Artık 28 agent, 116 skill ve 59 command'a erişiminiz var.
|
**Bu kadar!** Artık 28 agent, 116 skill ve 59 command'a erişiminiz var.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌐 Çapraz Platform Desteği
|
## Çapraz Platform Desteği
|
||||||
|
|
||||||
Bu plugin artık **Windows, macOS ve Linux**'u tam olarak destekliyor, ana IDE'ler (Cursor, OpenCode, Antigravity) ve CLI harness'lar arasında sıkı entegrasyon ile birlikte. Tüm hook'lar ve script'ler maksimum uyumluluk için Node.js ile yeniden yazıldı.
|
Bu plugin artık **Windows, macOS ve Linux**'u tam olarak destekliyor, ana IDE'ler (Cursor, OpenCode, Antigravity) ve CLI harness'lar arasında sıkı entegrasyon ile birlikte. Tüm hook'lar ve script'ler maksimum uyumluluk için Node.js ile yeniden yazıldı.
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ export ECC_DISABLED_HOOKS="pre:bash:tmux-reminder,post:edit:typecheck"
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 İçindekiler
|
## İçindekiler
|
||||||
|
|
||||||
Bu repo bir **Claude Code plugin'i** - doğrudan kurun veya component'leri manuel olarak kopyalayın.
|
Bu repo bir **Claude Code plugin'i** - doğrudan kurun veya component'leri manuel olarak kopyalayın.
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ everything-claude-code/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🗺️ Hangi Agent'ı Kullanmalıyım?
|
## Hangi Agent'ı Kullanmalıyım?
|
||||||
|
|
||||||
Nereden başlayacağınızdan emin değil misiniz? Bu hızlı referansı kullanın:
|
Nereden başlayacağınızdan emin değil misiniz? Bu hızlı referansı kullanın:
|
||||||
|
|
||||||
@@ -337,7 +337,7 @@ Nereden başlayacağınızdan emin değil misiniz? Bu hızlı referansı kullan
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ❓ SSS
|
## SSS
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>Hangi agent/command'ların kurulu olduğunu nasıl kontrol ederim?</b></summary>
|
<summary><b>Hangi agent/command'ların kurulu olduğunu nasıl kontrol ederim?</b></summary>
|
||||||
@@ -410,7 +410,7 @@ Evet. ECC çapraz platformdur:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🧪 Testleri Çalıştırma
|
## Testleri Çalıştırma
|
||||||
|
|
||||||
Plugin kapsamlı bir test suite içerir:
|
Plugin kapsamlı bir test suite içerir:
|
||||||
|
|
||||||
@@ -426,7 +426,7 @@ node tests/hooks/hooks.test.js
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🤝 Katkıda Bulunma
|
## Katkıda Bulunma
|
||||||
|
|
||||||
**Katkılar beklenir ve teşvik edilir.**
|
**Katkılar beklenir ve teşvik edilir.**
|
||||||
|
|
||||||
@@ -448,7 +448,7 @@ Lütfen katkıda bulunun! Rehber için [CONTRIBUTING.md](../../CONTRIBUTING.md)'
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📄 Lisans
|
## Lisans
|
||||||
|
|
||||||
MIT - Özgürce kullanın, ihtiyaç duyduğunuz gibi değiştirin, yapabiliyorsanız geri katkıda bulunun.
|
MIT - Özgürce kullanın, ihtiyaç duyduğunuz gibi değiştirin, yapabiliyorsanız geri katkıda bulunun.
|
||||||
|
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ Oluşturulan artifact'lar:
|
|||||||
╔══════════════════════════════════════════════════════════════╗
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
║ E2E Test Sonuçları ║
|
║ E2E Test Sonuçları ║
|
||||||
╠══════════════════════════════════════════════════════════════╣
|
╠══════════════════════════════════════════════════════════════╣
|
||||||
║ Durum: ✅ TÜM TESTLER GEÇTİ ║
|
║ Durum: PASS: TÜM TESTLER GEÇTİ ║
|
||||||
║ Toplam: 3 test ║
|
║ Toplam: 3 test ║
|
||||||
║ Geçti: 3 (%100) ║
|
║ Geçti: 3 (%100) ║
|
||||||
║ Başarısız: 0 ║
|
║ Başarısız: 0 ║
|
||||||
@@ -191,15 +191,15 @@ Oluşturulan artifact'lar:
|
|||||||
╚══════════════════════════════════════════════════════════════╝
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
Artifact'lar:
|
Artifact'lar:
|
||||||
📸 Ekran Görüntüleri: 2 dosya
|
Ekran Görüntüleri: 2 dosya
|
||||||
📹 Videolar: 0 dosya (sadece hatada)
|
Videolar: 0 dosya (sadece hatada)
|
||||||
🔍 İzlemeler: 0 dosya (sadece hatada)
|
İzlemeler: 0 dosya (sadece hatada)
|
||||||
📊 HTML Rapor: playwright-report/index.html
|
HTML Rapor: playwright-report/index.html
|
||||||
|
|
||||||
Raporu görüntüle: npx playwright show-report
|
Raporu görüntüle: npx playwright show-report
|
||||||
```
|
```
|
||||||
|
|
||||||
✅ E2E test paketi CI/CD entegrasyonuna hazır!
|
PASS: E2E test paketi CI/CD entegrasyonuna hazır!
|
||||||
```
|
```
|
||||||
|
|
||||||
## Test Artifact'ları
|
## Test Artifact'ları
|
||||||
@@ -235,7 +235,7 @@ open artifacts/search-results.png
|
|||||||
Bir test aralıklı olarak başarısız olursa:
|
Bir test aralıklı olarak başarısız olursa:
|
||||||
|
|
||||||
```
|
```
|
||||||
⚠️ DENGESİZ TEST TESPİT EDİLDİ: tests/e2e/markets/trade.spec.ts
|
WARNING: DENGESİZ TEST TESPİT EDİLDİ: tests/e2e/markets/trade.spec.ts
|
||||||
|
|
||||||
Test 10 çalıştırmadan 7'sinde geçti (%70 geçme oranı)
|
Test 10 çalıştırmadan 7'sinde geçti (%70 geçme oranı)
|
||||||
|
|
||||||
@@ -254,10 +254,10 @@ Karantina önerisi: Düzeltilene kadar test.fixme() olarak işaretle
|
|||||||
## Tarayıcı Yapılandırması
|
## Tarayıcı Yapılandırması
|
||||||
|
|
||||||
Testler varsayılan olarak birden fazla tarayıcıda çalışır:
|
Testler varsayılan olarak birden fazla tarayıcıda çalışır:
|
||||||
- ✅ Chromium (Desktop Chrome)
|
- PASS: Chromium (Desktop Chrome)
|
||||||
- ✅ Firefox (Desktop)
|
- PASS: Firefox (Desktop)
|
||||||
- ✅ WebKit (Desktop Safari)
|
- PASS: WebKit (Desktop Safari)
|
||||||
- ✅ Mobile Chrome (opsiyonel)
|
- PASS: Mobile Chrome (opsiyonel)
|
||||||
|
|
||||||
Tarayıcıları ayarlamak için `playwright.config.ts`'yi yapılandırın.
|
Tarayıcıları ayarlamak için `playwright.config.ts`'yi yapılandırın.
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ CI pipeline'ınıza ekleyin:
|
|||||||
|
|
||||||
PMX için bu E2E testlerine öncelik verin:
|
PMX için bu E2E testlerine öncelik verin:
|
||||||
|
|
||||||
**🔴 KRİTİK (Her Zaman Geçmeli):**
|
**KRİTİK (Her Zaman Geçmeli):**
|
||||||
1. Kullanıcı cüzdan bağlayabilir
|
1. Kullanıcı cüzdan bağlayabilir
|
||||||
2. Kullanıcı piyasalara göz atabilir
|
2. Kullanıcı piyasalara göz atabilir
|
||||||
3. Kullanıcı piyasa arayabilir (semantik arama)
|
3. Kullanıcı piyasa arayabilir (semantik arama)
|
||||||
@@ -294,7 +294,7 @@ PMX için bu E2E testlerine öncelik verin:
|
|||||||
6. Piyasa doğru çözülür
|
6. Piyasa doğru çözülür
|
||||||
7. Kullanıcı fon çekebilir
|
7. Kullanıcı fon çekebilir
|
||||||
|
|
||||||
**🟡 ÖNEMLİ:**
|
**ÖNEMLİ:**
|
||||||
1. Piyasa oluşturma akışı
|
1. Piyasa oluşturma akışı
|
||||||
2. Kullanıcı profil güncellemeleri
|
2. Kullanıcı profil güncellemeleri
|
||||||
3. Gerçek zamanlı fiyat güncellemeleri
|
3. Gerçek zamanlı fiyat güncellemeleri
|
||||||
@@ -305,20 +305,20 @@ PMX için bu E2E testlerine öncelik verin:
|
|||||||
## En İyi Uygulamalar
|
## En İyi Uygulamalar
|
||||||
|
|
||||||
**YAPIN:**
|
**YAPIN:**
|
||||||
- ✅ Sürdürülebilirlik için Page Object Model kullanın
|
- PASS: Sürdürülebilirlik için Page Object Model kullanın
|
||||||
- ✅ Selector'lar için data-testid nitelikleri kullanın
|
- PASS: Selector'lar için data-testid nitelikleri kullanın
|
||||||
- ✅ Rastgele timeout'lar değil, API yanıtlarını bekleyin
|
- PASS: Rastgele timeout'lar değil, API yanıtlarını bekleyin
|
||||||
- ✅ Kritik kullanıcı yolculuklarını uçtan uca test edin
|
- PASS: Kritik kullanıcı yolculuklarını uçtan uca test edin
|
||||||
- ✅ Main'e merge etmeden önce testleri çalıştırın
|
- PASS: Main'e merge etmeden önce testleri çalıştırın
|
||||||
- ✅ Testler başarısız olduğunda artifact'ları inceleyin
|
- PASS: Testler başarısız olduğunda artifact'ları inceleyin
|
||||||
|
|
||||||
**YAPMAYIN:**
|
**YAPMAYIN:**
|
||||||
- ❌ Kırılgan selector'lar kullanmayın (CSS sınıfları değişebilir)
|
- FAIL: Kırılgan selector'lar kullanmayın (CSS sınıfları değişebilir)
|
||||||
- ❌ Uygulama detaylarını test etmeyin
|
- FAIL: Uygulama detaylarını test etmeyin
|
||||||
- ❌ Production'a karşı testler çalıştırmayın
|
- FAIL: Production'a karşı testler çalıştırmayın
|
||||||
- ❌ Dengesiz testleri görmezden gelmeyin
|
- FAIL: Dengesiz testleri görmezden gelmeyin
|
||||||
- ❌ Başarısızlıklarda artifact incelemesini atlamayın
|
- FAIL: Başarısızlıklarda artifact incelemesini atlamayın
|
||||||
- ❌ Her edge case'i E2E ile test etmeyin (unit testler kullanın)
|
- FAIL: Her edge case'i E2E ile test etmeyin (unit testler kullanın)
|
||||||
|
|
||||||
## Önemli Notlar
|
## Önemli Notlar
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ ok project/internal/handler 0.023s
|
|||||||
| Değiştirilen dosyalar | 2 |
|
| Değiştirilen dosyalar | 2 |
|
||||||
| Kalan sorunlar | 0 |
|
| Kalan sorunlar | 0 |
|
||||||
|
|
||||||
Build Durumu: ✅ BAŞARILI
|
Build Durumu: PASS: BAŞARILI
|
||||||
```
|
```
|
||||||
|
|
||||||
## Düzeltilen Yaygın Hatalar
|
## Düzeltilen Yaygın Hatalar
|
||||||
|
|||||||
@@ -124,16 +124,16 @@ return fmt.Errorf("get user %s: %w", userID, err)
|
|||||||
- YÜKSEK: 1
|
- YÜKSEK: 1
|
||||||
- ORTA: 0
|
- ORTA: 0
|
||||||
|
|
||||||
Öneri: ❌ KRİTİK sorun düzeltilene kadar merge'i engelle
|
Öneri: FAIL: KRİTİK sorun düzeltilene kadar merge'i engelle
|
||||||
```
|
```
|
||||||
|
|
||||||
## Onay Kriterleri
|
## Onay Kriterleri
|
||||||
|
|
||||||
| Durum | Koşul |
|
| Durum | Koşul |
|
||||||
|--------|-----------|
|
|--------|-----------|
|
||||||
| ✅ Onayla | KRİTİK veya YÜKSEK sorun yok |
|
| PASS: Onayla | KRİTİK veya YÜKSEK sorun yok |
|
||||||
| ⚠️ Uyarı | Sadece ORTA sorunlar (dikkatle merge et) |
|
| WARNING: Uyarı | Sadece ORTA sorunlar (dikkatle merge et) |
|
||||||
| ❌ Engelle | KRİTİK veya YÜKSEK sorun bulundu |
|
| FAIL: Engelle | KRİTİK veya YÜKSEK sorun bulundu |
|
||||||
|
|
||||||
## Diğer Komutlarla Entegrasyon
|
## Diğer Komutlarla Entegrasyon
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ Yerel dosya yollarından veya HTTP(S) URL'lerinden içgüdüleri içe aktar.
|
|||||||
## İçe Aktarma İşlemi
|
## İçe Aktarma İşlemi
|
||||||
|
|
||||||
```
|
```
|
||||||
📥 Importing instincts from: team-instincts.yaml
|
Importing instincts from: team-instincts.yaml
|
||||||
================================================
|
================================================
|
||||||
|
|
||||||
Found 12 instincts to import.
|
Found 12 instincts to import.
|
||||||
@@ -60,12 +60,12 @@ These will be added:
|
|||||||
|
|
||||||
## Duplicate Instincts (3)
|
## Duplicate Instincts (3)
|
||||||
Already have similar instincts:
|
Already have similar instincts:
|
||||||
⚠️ prefer-functional-style
|
WARNING: prefer-functional-style
|
||||||
Local: 0.8 confidence, 12 observations
|
Local: 0.8 confidence, 12 observations
|
||||||
Import: 0.7 confidence
|
Import: 0.7 confidence
|
||||||
→ Keep local (higher confidence)
|
→ Keep local (higher confidence)
|
||||||
|
|
||||||
⚠️ test-first-workflow
|
WARNING: test-first-workflow
|
||||||
Local: 0.75 confidence
|
Local: 0.75 confidence
|
||||||
Import: 0.9 confidence
|
Import: 0.9 confidence
|
||||||
→ Update to import (higher confidence)
|
→ Update to import (higher confidence)
|
||||||
@@ -102,7 +102,7 @@ project_name: "my-project"
|
|||||||
|
|
||||||
İçe aktarma sonrası:
|
İçe aktarma sonrası:
|
||||||
```
|
```
|
||||||
✅ Import complete!
|
PASS: Import complete!
|
||||||
|
|
||||||
Added: 8 instincts
|
Added: 8 instincts
|
||||||
Updated: 1 instinct
|
Updated: 1 instinct
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user