docs: address Korean translation review feedback

This commit is contained in:
Affaan Mustafa
2026-03-13 00:17:54 -07:00
parent 526a9070e6
commit fb7b73a962
36 changed files with 383 additions and 132 deletions

View File

@@ -1,4 +1,4 @@
**Language:** English | [繁體中文](docs/zh-TW/README.md) | [한국어](docs/ko-KR/README.md)
**Language:** English | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md)
# Everything Claude Code

View File

@@ -195,6 +195,15 @@ model: sonnet
| `tools` | 필요한 것만 포함 | `Read, Write, Edit, Bash, Grep, Glob, WebFetch, Task` |
| `model` | 복잡도 수준 | `haiku` (단순), `sonnet` (코딩), `opus` (복잡) |
### 예시 에이전트
| 에이전트 | 용도 |
|----------|------|
| `tdd-guide.md` | 테스트 주도 개발 |
| `code-reviewer.md` | 코드 리뷰 |
| `security-reviewer.md` | 보안 점검 |
| `build-error-resolver.md` | 빌드 오류 수정 |
---
## 훅 기여하기
@@ -216,6 +225,68 @@ hooks/hooks.json
| `SessionStart` | 세션 시작 시 | 컨텍스트 로딩 |
| `Stop` | 세션 종료 시 | 정리, 감사 |
### 훅 형식
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "tool == \"Bash\" && tool_input.command matches \"rm -rf /\"",
"hooks": [
{
"type": "command",
"command": "echo '[Hook] BLOCKED: Dangerous command' && exit 1"
}
],
"description": "위험한 rm 명령 차단"
}
]
}
}
```
### Matcher 문법
```javascript
// 특정 도구 매칭
tool == "Bash"
tool == "Edit"
tool == "Write"
// 입력 패턴 매칭
tool_input.command matches "npm install"
tool_input.file_path matches "\\.tsx?$"
// 조건 결합
tool == "Bash" && tool_input.command matches "git push"
```
### 훅 예시
```json
// tmux 밖 dev 서버 차단
{
"matcher": "tool == \"Bash\" && tool_input.command matches \"npm run dev\"",
"hooks": [{"type": "command", "command": "echo '개발 서버는 tmux에서 실행하세요' && exit 1"}],
"description": "dev 서버를 tmux에서 실행하도록 강제"
}
// TypeScript 편집 후 자동 포맷
{
"matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\.tsx?$\"",
"hooks": [{"type": "command", "command": "npx prettier --write \"$file_path\""}],
"description": "TypeScript 파일 편집 후 포맷"
}
// git push 전 경고
{
"matcher": "tool == \"Bash\" && tool_input.command matches \"git push\"",
"hooks": [{"type": "command", "command": "echo '[Hook] push 전에 변경사항을 다시 검토하세요'"}],
"description": "push 전 검토 리마인더"
}
```
### 훅 체크리스트
- [ ] Matcher가 구체적 (너무 광범위하지 않게)
@@ -236,6 +307,36 @@ hooks/hooks.json
commands/your-command.md
```
### 커맨드 템플릿
```markdown
---
description: /help에 표시되는 간단한 설명
---
# 커맨드 이름
## 목적
이 커맨드가 수행하는 작업.
## 사용법
\`\`\`
/your-command [args]
\`\`\`
## 워크플로우
1. 첫 번째 단계
2. 두 번째 단계
3. 마지막 단계
## 출력
사용자가 받는 결과.
```
### 커맨드 예시
| 커맨드 | 용도 |
@@ -247,6 +348,34 @@ commands/your-command.md
---
## 크로스-하네스 및 번역
### 스킬 서브셋 (Codex 및 Cursor)
ECC는 다른 하네스를 위한 스킬 서브셋도 제공합니다:
- **Codex:** `.agents/skills/``agents/openai.yaml`에 나열된 스킬이 Codex에서 로드됩니다.
- **Cursor:** `.cursor/skills/` — Cursor용 스킬 서브셋이 별도로 포함됩니다.
Codex 또는 Cursor에서도 제공해야 하는 **새 스킬**을 추가한다면:
1. 먼저 `skills/your-skill-name/` 아래에 일반적인 ECC 스킬로 추가합니다.
2. **Codex**에서도 제공해야 하면 `.agents/skills/`에 반영하고, 필요하면 `agents/openai.yaml`에도 참조를 추가합니다.
3. **Cursor**에서도 제공해야 하면 Cursor 레이아웃에 맞게 `.cursor/skills/` 아래에 추가합니다.
기존 디렉터리의 구조를 확인한 뒤 같은 패턴을 따르세요. 이 서브셋 동기화는 수동이므로 PR 설명에 반영 여부를 적어 두는 것이 좋습니다.
### 번역
번역 문서는 `docs/` 아래에 있습니다. 예: `docs/zh-CN`, `docs/zh-TW`, `docs/ja-JP`.
번역된 에이전트, 커맨드, 스킬을 변경한다면:
- 대응하는 번역 파일도 함께 업데이트하거나
- 유지보수자/번역자가 후속 작업을 할 수 있도록 이슈를 열어 주세요.
---
## Pull Request 프로세스
### 1. PR 제목 형식

View File

@@ -167,7 +167,7 @@ cd everything-claude-code
3. **package.json**: `packageManager` 필드
4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb에서 감지
5. **글로벌 설정**: `~/.claude/package-manager.json`
6. **폴백**: 사용 가능한 첫 번째 패키지 매니저
6. **폴백**: `npm`
패키지 매니저 설정 방법:
@@ -416,7 +416,7 @@ cp -r everything-claude-code/rules/typescript/* ~/.claude/rules/ # 사용하
cp everything-claude-code/commands/*.md ~/.claude/commands/
# 스킬 복사
cp -r everything-claude-code/.agents/skills/* ~/.claude/skills/
cp -r everything-claude-code/skills/* ~/.claude/skills/
cp -r everything-claude-code/skills/search-first ~/.claude/skills/
```
@@ -498,7 +498,9 @@ rules/
| 보안 취약점 찾기 | `/security-scan` | security-reviewer |
| 사용하지 않는 코드 제거 | `/refactor-clean` | refactor-cleaner |
| 문서 업데이트 | `/update-docs` | doc-updater |
| Go 빌드 실패 수정 | `/go-build` | go-build-resolver |
| Go 코드 리뷰 | `/go-review` | go-reviewer |
| 데이터베이스 스키마/쿼리 리뷰 | `/code-review` + database-reviewer 에이전트 | database-reviewer |
| Python 코드 리뷰 | `/python-review` | python-reviewer |
### 일반적인 워크플로우

View File

@@ -38,20 +38,20 @@
| Merge | merge | 확정 | 영문 유지 |
| Repository | 저장소 | 확정 | |
| Fork | Fork | 확정 | 영문 유지 |
| Supabase | Supabase | - | 제품명 유지 |
| Redis | Redis | - | 제품명 유지 |
| Playwright | Playwright | - | 제품명 유지 |
| TypeScript | TypeScript | - | 언어명 유지 |
| JavaScript | JavaScript | - | 언어명 유지 |
| Go/Golang | Go | - | 언어명 유지 |
| React | React | - | 프레임워크명 유지 |
| Next.js | Next.js | - | 프레임워크명 유지 |
| PostgreSQL | PostgreSQL | - | 제품명 유지 |
| Supabase | Supabase | 확정 | 제품명 유지 |
| Redis | Redis | 확정 | 제품명 유지 |
| Playwright | Playwright | 확정 | 제품명 유지 |
| TypeScript | TypeScript | 확정 | 언어명 유지 |
| JavaScript | JavaScript | 확정 | 언어명 유지 |
| Go/Golang | Go | 확정 | 언어명 유지 |
| React | React | 확정 | 프레임워크명 유지 |
| Next.js | Next.js | 확정 | 프레임워크명 유지 |
| PostgreSQL | PostgreSQL | 확정 | 제품명 유지 |
| RLS (Row Level Security) | RLS(행 수준 보안) | 확정 | 최초 사용 시 전개 |
| OWASP | OWASP | - | 영문 유지 |
| XSS | XSS | - | 영문 유지 |
| OWASP | OWASP | 확정 | 영문 유지 |
| XSS | XSS | 확정 | 영문 유지 |
| SQL Injection | SQL 인젝션 | 확정 | |
| CSRF | CSRF | - | 영문 유지 |
| CSRF | CSRF | 확정 | 영문 유지 |
| Refactor | 리팩토링 | 확정 | |
| Dead Code | 데드 코드 | 확정 | |
| Lint/Linter | Lint | 확정 | 영문 유지 |
@@ -70,11 +70,11 @@
| Migration | 마이그레이션 | 확정 | |
| Transaction | 트랜잭션 | 확정 | |
| Concurrency | 동시성 | 확정 | |
| Goroutine | Goroutine | - | Go 용어 유지 |
| Goroutine | Goroutine | 확정 | Go 용어 유지 |
| Channel | Channel | 확정 | Go 컨텍스트에서 유지 |
| Mutex | Mutex | - | 영문 유지 |
| Mutex | Mutex | 확정 | 영문 유지 |
| Interface | 인터페이스 | 확정 | |
| Struct | Struct | - | Go 용어 유지 |
| Struct | Struct | 확정 | Go 용어 유지 |
| Mock | Mock | 확정 | 테스트 용어 유지 |
| Stub | Stub | 확정 | 테스트 용어 유지 |
| Fixture | Fixture | 확정 | 테스트 용어 유지 |
@@ -82,7 +82,7 @@
| Snapshot | 스냅샷 | 확정 | |
| Trace | 트레이스 | 확정 | |
| Artifact | 아티팩트 | 확정 | |
| CI/CD | CI/CD | - | 영문 유지 |
| CI/CD | CI/CD | 확정 | 영문 유지 |
| Pipeline | 파이프라인 | 확정 | |
---

View File

@@ -87,6 +87,15 @@ docs/CODEMAPS/
4. **실행 가능** — 실제로 작동하는 설정 커맨드 포함
5. **상호 참조** — 관련 문서 링크
## 품질 체크리스트
- [ ] 실제 코드에서 코드맵 생성
- [ ] 모든 파일 경로 존재 확인
- [ ] 코드 예제가 컴파일 또는 실행됨
- [ ] 링크 검증 완료
- [ ] 최신 타임스탬프 업데이트
- [ ] 오래된 참조 없음
## 업데이트 시점
**항상:** 새 주요 기능, API 라우트 변경, 의존성 추가/제거, 아키텍처 변경, 설정 프로세스 수정.

View File

@@ -71,7 +71,7 @@ npx playwright show-report # HTML 보고서 보기
- **시맨틱 로케이터 사용**: `[data-testid="..."]` > CSS 셀렉터 > XPath
- **시간이 아닌 조건 대기**: `waitForResponse()` > `waitForTimeout()`
- **자동 대기 내장**: `page.locator().click()`은 자동 대기; `page.click()`은 아님
- **자동 대기 내장**: `locator.click()` `page.click()` 모두 자동 대기를 제공하지만, 더 안정적인 `locator` 기반 API를 선호
- **테스트 격리**: 각 테스트는 독립적; 공유 상태 없음
- **빠른 실패**: 모든 핵심 단계에서 `expect()` 어설션 사용
- **재시도 시 트레이스**: 실패 디버깅을 위해 `trace: 'on-first-retry'` 설정

View File

@@ -21,10 +21,16 @@ model: sonnet
기대 동작을 설명하는 실패하는 테스트 작성.
### 2. 테스트 실행 -- 실패 확인
Node.js (npm):
```bash
npm test
```
언어 중립:
- 프로젝트의 기본 테스트 명령을 실행하세요.
- Python: `pytest`
- Go: `go test ./...`
### 3. 최소한의 구현 작성 (GREEN)
테스트를 통과하기에 충분한 코드만.
@@ -34,11 +40,17 @@ npm test
중복 제거, 이름 개선, 최적화 -- 테스트는 그린 유지.
### 6. 커버리지 확인
Node.js (npm):
```bash
npm run test:coverage
# 필수: branches, functions, lines, statements 80% 이상
```
언어 중립:
- 프로젝트의 기본 커버리지 명령을 실행하세요.
- Python: `pytest --cov`
- Go: `go test ./... -cover`
## 필수 테스트 유형
| 유형 | 테스트 대상 | 시점 |

View File

@@ -1,3 +1,8 @@
---
name: build-fix
description: 최소한의 안전한 변경으로 build 및 타입 오류를 점진적으로 수정합니다.
---
# Build 오류 수정
최소한의 안전한 변경으로 build 및 타입 오류를 점진적으로 수정합니다.
@@ -14,7 +19,7 @@
| `pom.xml` | `mvn compile` |
| `build.gradle` | `./gradlew compileJava` |
| `go.mod` | `go build ./...` |
| `pyproject.toml` | `python -m py_compile` 또는 `mypy .` |
| `pyproject.toml` | `python -m compileall .` 또는 `mypy .` |
## 2단계: 오류 파싱 및 그룹화

View File

@@ -1,10 +1,15 @@
---
name: checkpoint
description: 워크플로우에서 checkpoint를 생성, 검증, 조회 또는 정리합니다.
---
# Checkpoint 명령어
워크플로우에서 checkpoint를 생성하거나 검증합니다.
## 사용법
`/checkpoint [create|verify|list] [name]`
`/checkpoint [create|verify|list|clear] [name]`
## Checkpoint 생성

View File

@@ -19,9 +19,9 @@
- 800줄 초과 파일
- 4단계 초과 중첩 깊이
- 누락된 에러 처리
- console.log 문
- 디버그 로깅 문구(예: 개발용 로그/print 등)
- TODO/FIXME 주석
- 공개 API에 대한 JSDoc 누락
- 활성 언어에 대한 공개 API 문서 누락(예: JSDoc/Go doc/Docstring 등)
**모범 사례 (MEDIUM):**
- 변이(Mutation) 패턴 (불변 패턴을 사용하세요)

View File

@@ -36,7 +36,7 @@ e2e-runner 에이전트가 수행하는 작업:
## 사용 예시
```
````
User: /e2e 마켓 검색 및 조회 흐름 테스트
Agent (e2e-runner):
@@ -200,7 +200,7 @@ Running 3 tests using 3 workers
```
✅ CI/CD 통합 준비가 완료된 E2E 테스트 모음!
```
````
## 테스트 아티팩트

View File

@@ -4,7 +4,7 @@
## 사용법
`/eval [define|check|report|list] [feature-name]`
`/eval [define|check|report|list|clean] [feature-name]`
## 평가 정의

View File

@@ -43,7 +43,7 @@ go mod tidy -v
## 예시 세션
```text
````text
User: /go-build
Agent:
@@ -141,7 +141,7 @@ ok project/internal/handler 0.023s
| 남은 이슈 | 0 |
Build 상태: ✅ 성공
```
````
## 자주 발생하는 에러

View File

@@ -68,7 +68,7 @@ govulncheck ./...
## 사용 예시
```text
````text
User: /go-review
Agent:
@@ -125,7 +125,7 @@ return fmt.Errorf("get user %s: %w", userID, err)
- MEDIUM: 0
권장: ❌ CRITICAL 이슈가 수정될 때까지 merge 차단
```
````
## 승인 기준

View File

@@ -32,7 +32,7 @@ node scripts/setup-package-manager.js --list
3. **package.json**: `packageManager` 필드
4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb의 존재 여부
5. **전역 설정**: `~/.claude/package-manager.json`
6. **폴백**: 사용 가능한 첫 번째 패키지 매니저 (pnpm > bun > yarn > npm)
6. **폴백**: `npm`
## 설정 파일

View File

@@ -48,7 +48,7 @@ REPEAT: 다음 기능/시나리오
## 사용 예시
```
````
User: /tdd 마켓 유동성 점수를 계산하는 함수가 필요합니다
Agent (tdd-guide):
@@ -251,7 +251,7 @@ Coverage: 100% ✅ (목표: 80%)
```
✅ TDD 세션 완료!
```
````
## TDD 모범 사례

View File

@@ -1,3 +1,8 @@
---
name: test-coverage
description: 테스트 커버리지를 분석하고, 80% 이상을 목표로 누락된 테스트를 식별하고 생성합니다.
---
# 테스트 커버리지
테스트 커버리지를 분석하고, 갭을 식별하며, 80% 이상 커버리지 달성을 위해 누락된 테스트를 생성합니다.

View File

@@ -10,15 +10,16 @@
## 2단계: 코드맵 생성
`docs/CODEMAPS/` (또는 `.reports/codemaps/`)에 코드맵 생성 또는 업데이트:
`docs/CODEMAPS/`에 코드맵 생성 또는 업데이트:
| 파일 | 내용 |
|------|------|
| `architecture.md` | 상위 시스템 다이어그램, 서비스 경계, 데이터 흐름 |
| `INDEX.md` | 전체 코드베이스 개요와 영역별 링크 |
| `backend.md` | API 라우트, 미들웨어 체인, 서비스 → 리포지토리 매핑 |
| `frontend.md` | 페이지 트리, 컴포넌트 계층, 상태 관리 흐름 |
| `data.md` | 데이터베이스 테이블, 관계, 마이그레이션 히스토리 |
| `dependencies.md` | 외부 서비스, 서드파티 통합, 공유 라이브러리 |
| `database.md` | 데이터베이스 스키마, 마이그레이션, 저장소 계층 |
| `integrations.md` | 외부 서비스, 서드파티 통합, 어댑터 |
| `workers.md` | 백그라운드 작업, 큐, 스케줄러 |
### 코드맵 형식
@@ -41,27 +42,33 @@ src/repos/user.ts (데이터베이스 접근, 80줄)
- Stripe (결제 처리)
```
## 3단계: 변경 감지
## 3단계: 영역 분류
1. 이전 코드맵이 있는 경우 변경 비율 계산
2. 변경이 30%를 초과하면 diff를 표시하고 덮어쓰기 전에 사용자 승인 요청
3. 변경이 30% 이하이면 기존 파일에 바로 업데이트
생성기는 파일 경로 패턴을 기반으로 영역을 자동 분류합니다:
1. 프론트엔드: `app/`, `pages/`, `components/`, `hooks/`, `.tsx`, `.jsx`
2. 백엔드: `api/`, `routes/`, `controllers/`, `services/`, `.route.ts`
3. 데이터베이스: `db/`, `migrations/`, `prisma/`, `repositories/`
4. 통합: `integrations/`, `adapters/`, `connectors/`, `plugins/`
5. 워커: `workers/`, `jobs/`, `queues/`, `tasks/`, `cron/`
## 4단계: 메타데이터 추가
각 코드맵에 최신 정보 헤더를 추가합니다:
```markdown
<!-- Generated: 2026-02-11 | Files scanned: 142 | Token estimate: ~800 -->
**Last Updated:** 2026-03-12
**Total Files:** 42
**Total Lines:** 1875
```
## 5단계: 분석 보고서 저장
## 5단계: 인덱스와 영역 문서 동기화
`.reports/codemap-diff.txt`에 요약을 작성합니다:
- 마지막 스캔 이후 추가/제거/수정된 파일
- 새로 감지된 의존성
- 아키텍처 변경 사항 (새 라우트, 새 서비스 등)
- 90일 이상 업데이트되지 않은 문서에 대한 오래된 항목 경고
`INDEX.md`는 생성된 영역 문서를 링크하고 요약해야 합니다:
- 각 영역의 파일 수와 총 라인 수
- 감지된 엔트리 포인트
- 저장소 트리의 간단한 ASCII 개요
- 영역별 세부 문서 링크
## 팁
@@ -69,4 +76,4 @@ src/repos/user.ts (데이터베이스 접근, 80줄)
- 전체 코드 블록 대신 **파일 경로와 함수 시그니처** 사용
- 효율적인 컨텍스트 로딩을 위해 각 코드맵을 **1000 토큰 미만**으로 유지
- 장황한 설명 대신 데이터 흐름에 ASCII 다이어그램 사용
- 주요 기능 추가 또는 리팩토링 세션 후 실행
- 주요 기능 추가 또는 리팩토링 세션 후 `npx tsx scripts/codemaps/generate.ts` 실행

View File

@@ -1,3 +1,8 @@
---
name: update-docs
description: 코드베이스를 기준으로 문서를 동기화하고 생성된 섹션을 갱신합니다.
---
# 문서 업데이트
문서를 코드베이스와 동기화하고, 원본 소스 파일에서 생성합니다.

View File

@@ -23,11 +23,15 @@
- 통과/실패 수 보고
- 커버리지 비율 보고
5. **Console.log 감사**
5. **시크릿 스캔**
- 소스 파일에서 API 키, 토큰, 비밀값 패턴 검색
- 발견 위치 보고
6. **Console.log 감사**
- 소스 파일에서 console.log 검색
- 위치 보고
6. **Git 상태**
7. **Git 상태**
- 커밋되지 않은 변경사항 표시
- 마지막 커밋 이후 수정된 파일 표시

View File

@@ -142,12 +142,12 @@ from django.db import transaction
def create_order(*, customer, product_id: uuid.UUID, quantity: int) -> Order:
"""재고 검증과 결제 보류를 포함한 주문 생성."""
product = Product.objects.select_for_update().get(id=product_id)
if product.stock < quantity:
raise InsufficientStockError()
with transaction.atomic():
product = Product.objects.select_for_update().get(id=product_id)
if product.stock < quantity:
raise InsufficientStockError()
order = Order.objects.create(
customer=customer,
product=product,

View File

@@ -55,7 +55,9 @@ pub enum AppError {
#[error("Unauthorized")]
Unauthorized,
#[error(transparent)]
Internal(#[from] anyhow::Error),
Database(#[from] sqlx::Error),
#[error(transparent)]
Io(#[from] std::io::Error),
}
impl IntoResponse for AppError {
@@ -64,7 +66,11 @@ impl IntoResponse for AppError {
Self::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
Self::Validation(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
Self::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
Self::Internal(err) => {
Self::Database(err) => {
tracing::error!(?err, "database error");
(StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into())
}
Self::Io(err) => {
tracing::error!(?err, "internal error");
(StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into())
}

View File

@@ -1,7 +1,7 @@
{
"statusLine": {
"type": "command",
"command": "input=$(cat); user=$(whoami); cwd=$(echo \"$input\" | jq -r '.workspace.current_dir' | sed \"s|$HOME|~|g\"); model=$(echo \"$input\" | jq -r '.model.display_name'); time=$(date +%H:%M); remaining=$(echo \"$input\" | jq -r '.context_window.remaining_percentage // empty'); transcript=$(echo \"$input\" | jq -r '.transcript_path'); todo_count=$([ -f \"$transcript\" ] && grep -c '\"type\":\"todo\"' \"$transcript\" 2>/dev/null || echo 0); cd \"$(echo \"$input\" | jq -r '.workspace.current_dir')\" 2>/dev/null; branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo ''); status=''; [ -n \"$branch\" ] && { [ -n \"$(git status --porcelain 2>/dev/null)\" ] && status='*'; }; B='\\033[38;2;30;102;245m'; G='\\033[38;2;64;160;43m'; Y='\\033[38;2;223;142;29m'; M='\\033[38;2;136;57;239m'; C='\\033[38;2;23;146;153m'; R='\\033[0m'; T='\\033[38;2;76;79;105m'; printf \"${C}${user}${R}:${B}${cwd}${R}\"; [ -n \"$branch\" ] && printf \" ${G}${branch}${Y}${status}${R}\"; [ -n \"$remaining\" ] && printf \" ${M}ctx:${remaining}%%${R}\"; printf \" ${T}${model}${R} ${Y}${time}${R}\"; [ \"$todo_count\" -gt 0 ] && printf \" ${C}todos:${todo_count}${R}\"; echo",
"command": "input=$(cat); user=$(whoami); cwd=$(echo \"$input\" | jq -r '.workspace.current_dir' | sed \"s|$HOME|~|g\"); model=$(echo \"$input\" | jq -r '.model.display_name'); time=$(date +%H:%M); remaining=$(echo \"$input\" | jq -r '.context_window.remaining_percentage // empty'); transcript=$(echo \"$input\" | jq -r '.transcript_path'); todo_count=$([ -f \"$transcript\" ] && { grep -c '\"type\":\"todo\"' \"$transcript\" 2>/dev/null || true; } || echo 0); cd \"$(echo \"$input\" | jq -r '.workspace.current_dir')\" 2>/dev/null; branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo ''); status=''; [ -n \"$branch\" ] && { [ -n \"$(git status --porcelain 2>/dev/null)\" ] && status='*'; }; B='\\033[38;2;30;102;245m'; G='\\033[38;2;64;160;43m'; Y='\\033[38;2;223;142;29m'; M='\\033[38;2;136;57;239m'; C='\\033[38;2;23;146;153m'; R='\\033[0m'; T='\\033[38;2;76;79;105m'; printf \"${C}${user}${R}:${B}${cwd}${R}\"; [ -n \"$branch\" ] && printf \" ${G}${branch}${Y}${status}${R}\"; [ -n \"$remaining\" ] && printf \" ${M}ctx:${remaining}%%${R}\"; printf \" ${T}${model}${R} ${Y}${time}${R}\"; [ \"$todo_count\" -gt 0 ] && printf \" ${C}todos:${todo_count}${R}\"; echo",
"description": "Custom status line showing: user:path branch* ctx:% model time todos:N"
},
"_comments": {

View File

@@ -13,6 +13,9 @@
| security-reviewer | 보안 분석 | 커밋 전 |
| build-error-resolver | 빌드 에러 수정 | 빌드 실패 시 |
| e2e-runner | E2E 테스팅 | 핵심 사용자 흐름 |
| database-reviewer | 데이터베이스 스키마/쿼리 리뷰 | 스키마 설계, 쿼리 최적화 |
| go-reviewer | Go 코드 리뷰 | Go 코드 작성 또는 수정 후 |
| go-build-resolver | Go 빌드 에러 수정 | `go build` 또는 `go vet` 실패 시 |
| refactor-cleaner | 사용하지 않는 코드 정리 | 코드 유지보수 |
| doc-updater | 문서 관리 | 문서 업데이트 |

View File

@@ -9,7 +9,7 @@
타입: feat, fix, refactor, docs, test, chore, perf, ci
참고: 어트리뷰션 ~/.claude/settings.json에서 전역적으로 비활성화되어 있습니다.
참고: 어트리뷰션 비활성화 여부는 각자의 `~/.claude/settings.json` 로컬 설정에 따라 달라질 수 있습니다.
## Pull Request 워크플로우

View File

@@ -42,6 +42,7 @@ GET /api/markets?status=active&sort=volume&limit=20&offset=0
interface MarketRepository {
findAll(filters?: MarketFilters): Promise<Market[]>
findById(id: string): Promise<Market | null>
findByIds(ids: string[]): Promise<Market[]>
create(data: CreateMarketDto): Promise<Market>
update(id: string, data: UpdateMarketDto): Promise<Market>
delete(id: string): Promise<void>
@@ -85,7 +86,7 @@ class MarketService {
const markets = await this.marketRepo.findByIds(results.map(r => r.id))
// Sort by similarity
return markets.sort((a, b) => {
return [...markets].sort((a, b) => {
const scoreA = results.find(r => r.id === a.id)?.score || 0
const scoreB = results.find(r => r.id === b.id)?.score || 0
return scoreA - scoreB
@@ -320,7 +321,7 @@ async function fetchWithRetry<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
let lastError: Error
let lastError: Error = new Error('Retry attempts exhausted')
for (let i = 0; i < maxRetries; i++) {
try {

View File

@@ -51,7 +51,7 @@ SETTINGS index_granularity = 8192;
### ReplacingMergeTree (중복 제거)
```sql
-- For data that may have duplicates (e.g., from multiple sources)
-- 중복이 있을 수 있는 데이터용 (예: 여러 소스에서 수집된 경우)
CREATE TABLE user_events (
event_id String,
user_id String,
@@ -67,7 +67,7 @@ PRIMARY KEY (user_id, event_id);
### AggregatingMergeTree (사전 집계)
```sql
-- For maintaining aggregated metrics
-- 집계 메트릭을 유지하기 위한 용도
CREATE TABLE market_stats_hourly (
hour DateTime,
market_id String,
@@ -78,7 +78,7 @@ CREATE TABLE market_stats_hourly (
PARTITION BY toYYYYMM(hour)
ORDER BY (hour, market_id);
-- Query aggregated data
-- 집계된 데이터 조회
SELECT
hour,
market_id,
@@ -96,7 +96,7 @@ ORDER BY hour DESC;
### 효율적인 필터링
```sql
-- ✅ GOOD: Use indexed columns first
-- ✅ 좋음: 인덱스된 컬럼을 먼저 사용
SELECT *
FROM markets_analytics
WHERE date >= '2025-01-01'
@@ -105,7 +105,7 @@ WHERE date >= '2025-01-01'
ORDER BY date DESC
LIMIT 100;
-- ❌ BAD: Filter on non-indexed columns first
-- ❌ 나쁨: 비인덱스 컬럼을 먼저 필터링
SELECT *
FROM markets_analytics
WHERE volume > 1000
@@ -116,7 +116,7 @@ WHERE volume > 1000
### 집계
```sql
-- ✅ GOOD: Use ClickHouse-specific aggregation functions
-- ✅ 좋음: ClickHouse 전용 집계 함수를 사용
SELECT
toStartOfDay(created_at) AS day,
market_id,
@@ -129,7 +129,7 @@ WHERE created_at >= today() - INTERVAL 7 DAY
GROUP BY day, market_id
ORDER BY day DESC, total_volume DESC;
-- ✅ Use quantile for percentiles (more efficient than percentile)
-- ✅ 백분위수에는 quantile 사용 (percentile보다 효율적)
SELECT
quantile(0.50)(trade_size) AS median,
quantile(0.95)(trade_size) AS p95,
@@ -141,7 +141,7 @@ WHERE created_at >= now() - INTERVAL 1 HOUR;
### 윈도우 함수
```sql
-- Calculate running totals
-- 누적 합계 계산
SELECT
date,
market_id,
@@ -172,25 +172,22 @@ const clickhouse = new ClickHouse({
}
})
// ✅ Batch insert (efficient)
// ✅ 배치 삽입 (효율적)
async function bulkInsertTrades(trades: Trade[]) {
const values = trades.map(trade => `(
'${trade.id}',
'${trade.market_id}',
'${trade.user_id}',
${trade.amount},
'${trade.timestamp.toISOString()}'
)`).join(',')
const rows = trades.map(trade => ({
id: trade.id,
market_id: trade.market_id,
user_id: trade.user_id,
amount: trade.amount,
timestamp: trade.timestamp.toISOString()
}))
await clickhouse.query(`
INSERT INTO trades (id, market_id, user_id, amount, timestamp)
VALUES ${values}
`).toPromise()
await clickhouse.insert('trades', rows)
}
// ❌ Individual inserts (slow)
// ❌ 개별 삽입 (느림)
async function insertTrade(trade: Trade) {
// Don't do this in a loop!
// 루프 안에서 이렇게 하지 마세요!
await clickhouse.query(`
INSERT INTO trades VALUES ('${trade.id}', ...)
`).toPromise()
@@ -200,7 +197,7 @@ async function insertTrade(trade: Trade) {
### 스트리밍 삽입
```typescript
// For continuous data ingestion
// 연속적인 데이터 수집용
import { createWriteStream } from 'fs'
import { pipeline } from 'stream/promises'
@@ -220,7 +217,7 @@ async function streamInserts() {
### 실시간 집계
```sql
-- Create materialized view for hourly stats
-- 시간별 통계를 위한 materialized view 생성
CREATE MATERIALIZED VIEW market_stats_hourly_mv
TO market_stats_hourly
AS SELECT
@@ -232,7 +229,7 @@ AS SELECT
FROM trades
GROUP BY hour, market_id;
-- Query the materialized view
-- materialized view 조회
SELECT
hour,
market_id,
@@ -249,7 +246,7 @@ GROUP BY hour, market_id;
### 쿼리 성능
```sql
-- Check slow queries
-- 느린 쿼리 확인
SELECT
query_id,
user,
@@ -269,7 +266,7 @@ LIMIT 10;
### 테이블 통계
```sql
-- Check table sizes
-- 테이블 크기 확인
SELECT
database,
table,
@@ -287,7 +284,7 @@ ORDER BY sum(bytes) DESC;
### 시계열 분석
```sql
-- Daily active users
-- 일간 활성 사용자
SELECT
toDate(timestamp) AS date,
uniq(user_id) AS daily_active_users
@@ -296,7 +293,7 @@ WHERE timestamp >= today() - INTERVAL 30 DAY
GROUP BY date
ORDER BY date;
-- Retention analysis
-- 리텐션 분석
SELECT
signup_date,
countIf(days_since_signup = 0) AS day_0,
@@ -319,7 +316,7 @@ ORDER BY signup_date DESC;
### 퍼널 분석
```sql
-- Conversion funnel
-- 전환 퍼널
SELECT
countIf(step = 'viewed_market') AS viewed,
countIf(step = 'clicked_trade') AS clicked,
@@ -340,7 +337,7 @@ GROUP BY session_id;
### 코호트 분석
```sql
-- User cohorts by signup month
-- 가입 월별 사용자 코호트
SELECT
toStartOfMonth(signup_date) AS cohort,
toStartOfMonth(activity_date) AS month,
@@ -362,12 +359,12 @@ ORDER BY cohort, months_since_signup;
### ETL 패턴
```typescript
// Extract, Transform, Load
// 추출, 변환, 적재(ETL)
async function etlPipeline() {
// 1. Extract from source
// 1. 소스에서 추출
const rawData = await extractFromPostgres()
// 2. Transform
// 2. 변환
const transformed = rawData.map(row => ({
date: new Date(row.created_at).toISOString().split('T')[0],
market_id: row.market_slug,
@@ -375,18 +372,29 @@ async function etlPipeline() {
trades: parseInt(row.trade_count)
}))
// 3. Load to ClickHouse
// 3. ClickHouse에 적재
await bulkInsertToClickHouse(transformed)
}
// Run periodically
setInterval(etlPipeline, 60 * 60 * 1000) // Every hour
// 주기적으로 실행
let etlRunning = false
setInterval(async () => {
if (etlRunning) return
etlRunning = true
try {
await etlPipeline()
} finally {
etlRunning = false
}
}, 60 * 60 * 1000) // Every hour
```
### 변경 데이터 캡처 (CDC)
```typescript
// Listen to PostgreSQL changes and sync to ClickHouse
// PostgreSQL 변경을 수신하고 ClickHouse와 동기화
import { Client } from 'pg'
const pgClient = new Client({ connectionString: process.env.DATABASE_URL })

View File

@@ -397,7 +397,7 @@ import { useMemo, useCallback } from 'react'
// ✅ GOOD: Memoize expensive computations
const sortedMarkets = useMemo(() => {
return markets.sort((a, b) => b.volume - a.volume)
return [...markets].sort((a, b) => b.volume - a.volume)
}, [markets])
// ✅ GOOD: Memoize callbacks

View File

@@ -77,6 +77,35 @@ Claude Code 세션 종료 시 자동으로 평가하여 학습된 스킬로 저
}
```
## 예시
### 자동 패턴 추출 설정 예시
```json
{
"min_session_length": 10,
"extraction_threshold": "medium",
"auto_approve": false,
"learned_skills_path": "~/.claude/skills/learned/"
}
```
### Stop Hook 연결 예시
```json
{
"hooks": {
"Stop": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "~/.claude/skills/continuous-learning/evaluate-session.sh"
}]
}]
}
}
```
## Stop Hook을 사용하는 이유
- **경량**: 세션 종료 시 한 번만 실행
@@ -116,4 +145,4 @@ Homunculus v2는 더 정교한 접근법을 취합니다:
4. **도메인 태깅** - code-style, testing, git, debugging 등
5. **진화 경로** - 관련 본능을 스킬/명령어로 클러스터링
자세한 사양은 `docs/continuous-learning-v2-spec.md`를 참조하세요.
자세한 사양은 [`continuous-learning-v2-spec.md`](../../../continuous-learning-v2-spec.md)를 참조하세요.

View File

@@ -154,6 +154,8 @@ const [isOpen, toggleOpen] = useToggle()
### 비동기 데이터 페칭 Hook
```typescript
import { useCallback, useEffect, useRef, useState } from 'react'
interface UseQueryOptions<T> {
onSuccess?: (data: T) => void
onError?: (error: Error) => void
@@ -168,6 +170,14 @@ export function useQuery<T>(
const [data, setData] = useState<T | null>(null)
const [error, setError] = useState<Error | null>(null)
const [loading, setLoading] = useState(false)
const successRef = useRef(options?.onSuccess)
const errorRef = useRef(options?.onError)
const enabled = options?.enabled !== false
useEffect(() => {
successRef.current = options?.onSuccess
errorRef.current = options?.onError
}, [options?.onSuccess, options?.onError])
const refetch = useCallback(async () => {
setLoading(true)
@@ -176,21 +186,21 @@ export function useQuery<T>(
try {
const result = await fetcher()
setData(result)
options?.onSuccess?.(result)
successRef.current?.(result)
} catch (err) {
const error = err as Error
setError(error)
options?.onError?.(error)
errorRef.current?.(error)
} finally {
setLoading(false)
}
}, [fetcher, options])
}, [fetcher])
useEffect(() => {
if (options?.enabled !== false) {
if (enabled) {
refetch()
}
}, [key, refetch, options?.enabled])
}, [key, enabled, refetch])
return { data, error, loading, refetch }
}
@@ -296,7 +306,7 @@ export function useMarkets() {
```typescript
// ✅ useMemo for expensive computations
const sortedMarkets = useMemo(() => {
return markets.sort((a, b) => b.volume - a.volume)
return [...markets].sort((a, b) => b.volume - a.volume)
}, [markets])
// ✅ useCallback for functions passed to children

View File

@@ -533,7 +533,8 @@ func ProcessRequest(data []byte) []byte {
buf.Write(data)
// Process...
return buf.Bytes()
out := append([]byte(nil), buf.Bytes()...)
return out
}
```

View File

@@ -215,8 +215,8 @@ const securityHeaders = [
key: 'Content-Security-Policy',
value: `
default-src 'self';
script-src 'self' 'unsafe-eval' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
script-src 'self' 'nonce-{nonce}';
style-src 'self' 'nonce-{nonce}';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
@@ -225,6 +225,8 @@ const securityHeaders = [
]
```
`{nonce}`는 요청마다 새로 생성하고, 헤더와 인라인 `<script>`/`<style>` 태그에 동일하게 주입해야 합니다.
#### 확인 단계
- [ ] 사용자 제공 HTML이 새니타이징됨
- [ ] CSP 헤더가 구성됨
@@ -339,7 +341,9 @@ catch (error) {
#### 지갑 검증
```typescript
import { verify } from '@solana/web3.js'
import nacl from 'tweetnacl'
import bs58 from 'bs58'
import { PublicKey } from '@solana/web3.js'
async function verifyWalletOwnership(
publicKey: string,
@@ -347,18 +351,23 @@ async function verifyWalletOwnership(
message: string
) {
try {
const isValid = verify(
Buffer.from(message),
Buffer.from(signature, 'base64'),
Buffer.from(publicKey, 'base64')
const publicKeyBytes = new PublicKey(publicKey).toBytes()
const signatureBytes = bs58.decode(signature)
const messageBytes = new TextEncoder().encode(message)
return nacl.sign.detached.verify(
messageBytes,
signatureBytes,
publicKeyBytes
)
return isValid
} catch (error) {
return false
}
}
```
참고: Solana 공개 키와 서명은 일반적으로 base64가 아니라 base58로 인코딩됩니다.
#### 트랜잭션 검증
```typescript
async function verifyTransaction(transaction: Transaction) {

View File

@@ -195,7 +195,7 @@ jobs:
# Scan for secrets
- name: Secret scanning
uses: trufflesecurity/trufflehog@main
uses: trufflesecurity/trufflehog@6c05c4a00b91aa542267d8e32a8254774799d68d
# Dependency audit
- name: Audit dependencies
@@ -215,7 +215,7 @@ jobs:
// package.json - Use lock files and integrity checks
{
"scripts": {
"install": "npm ci", // Use ci for reproducible builds
"deps:install": "npm ci", // Use ci for reproducible builds
"audit": "npm audit --audit-level=moderate",
"check": "npm outdated"
}

View File

@@ -45,12 +45,14 @@ origin: ECC
"hooks": {
"PreToolUse": [
{
"matcher": "Edit",
"hooks": [{ "type": "command", "command": "node ~/.claude/skills/strategic-compact/suggest-compact.js" }]
},
{
"matcher": "Write",
"hooks": [{ "type": "command", "command": "node ~/.claude/skills/strategic-compact/suggest-compact.js" }]
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/run-with-flags.js\" \"pre:edit-write:suggest-compact\" \"scripts/hooks/suggest-compact.js\" \"standard,strict\""
}
],
"description": "Suggest manual compaction at logical intervals"
}
]
}

View File

@@ -191,11 +191,9 @@ test('user can search and filter markets', async ({ page }) => {
// Search for markets
await page.fill('input[placeholder="Search markets"]', 'election')
// Wait for debounce and results
await page.waitForTimeout(600)
// Verify search results displayed
// Wait for stable search results instead of sleeping
const results = page.locator('[data-testid="market-card"]')
await expect(results.first()).toBeVisible({ timeout: 5000 })
await expect(results).toHaveCount(5, { timeout: 5000 })
// Verify results contain search term
@@ -300,7 +298,7 @@ npm run test:coverage
```json
{
"jest": {
"coverageThresholds": {
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,

View File

@@ -77,7 +77,8 @@ grep -rn "console.log" --include="*.ts" --include="*.tsx" src/ 2>/dev/null | hea
```bash
# Show what changed
git diff --stat
git diff HEAD~1 --name-only
git diff --name-only
git diff --cached --name-only
```
각 변경된 파일에서 다음을 검토합니다: