mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
docs: address Korean translation review feedback
This commit is contained in:
@@ -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
|
# Everything Claude Code
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,15 @@ model: sonnet
|
|||||||
| `tools` | 필요한 것만 포함 | `Read, Write, Edit, Bash, Grep, Glob, WebFetch, Task` |
|
| `tools` | 필요한 것만 포함 | `Read, Write, Edit, Bash, Grep, Glob, WebFetch, Task` |
|
||||||
| `model` | 복잡도 수준 | `haiku` (단순), `sonnet` (코딩), `opus` (복잡) |
|
| `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` | 세션 시작 시 | 컨텍스트 로딩 |
|
| `SessionStart` | 세션 시작 시 | 컨텍스트 로딩 |
|
||||||
| `Stop` | 세션 종료 시 | 정리, 감사 |
|
| `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가 구체적 (너무 광범위하지 않게)
|
- [ ] Matcher가 구체적 (너무 광범위하지 않게)
|
||||||
@@ -236,6 +307,36 @@ hooks/hooks.json
|
|||||||
commands/your-command.md
|
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 프로세스
|
## Pull Request 프로세스
|
||||||
|
|
||||||
### 1. PR 제목 형식
|
### 1. PR 제목 형식
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ cd everything-claude-code
|
|||||||
3. **package.json**: `packageManager` 필드
|
3. **package.json**: `packageManager` 필드
|
||||||
4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb에서 감지
|
4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb에서 감지
|
||||||
5. **글로벌 설정**: `~/.claude/package-manager.json`
|
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 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/
|
cp -r everything-claude-code/skills/search-first ~/.claude/skills/
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -498,7 +498,9 @@ rules/
|
|||||||
| 보안 취약점 찾기 | `/security-scan` | security-reviewer |
|
| 보안 취약점 찾기 | `/security-scan` | security-reviewer |
|
||||||
| 사용하지 않는 코드 제거 | `/refactor-clean` | refactor-cleaner |
|
| 사용하지 않는 코드 제거 | `/refactor-clean` | refactor-cleaner |
|
||||||
| 문서 업데이트 | `/update-docs` | doc-updater |
|
| 문서 업데이트 | `/update-docs` | doc-updater |
|
||||||
|
| Go 빌드 실패 수정 | `/go-build` | go-build-resolver |
|
||||||
| Go 코드 리뷰 | `/go-review` | go-reviewer |
|
| Go 코드 리뷰 | `/go-review` | go-reviewer |
|
||||||
|
| 데이터베이스 스키마/쿼리 리뷰 | `/code-review` + database-reviewer 에이전트 | database-reviewer |
|
||||||
| Python 코드 리뷰 | `/python-review` | python-reviewer |
|
| Python 코드 리뷰 | `/python-review` | python-reviewer |
|
||||||
|
|
||||||
### 일반적인 워크플로우
|
### 일반적인 워크플로우
|
||||||
|
|||||||
@@ -38,20 +38,20 @@
|
|||||||
| Merge | merge | 확정 | 영문 유지 |
|
| Merge | merge | 확정 | 영문 유지 |
|
||||||
| Repository | 저장소 | 확정 | |
|
| Repository | 저장소 | 확정 | |
|
||||||
| Fork | Fork | 확정 | 영문 유지 |
|
| Fork | Fork | 확정 | 영문 유지 |
|
||||||
| Supabase | Supabase | - | 제품명 유지 |
|
| Supabase | Supabase | 확정 | 제품명 유지 |
|
||||||
| Redis | Redis | - | 제품명 유지 |
|
| Redis | Redis | 확정 | 제품명 유지 |
|
||||||
| Playwright | Playwright | - | 제품명 유지 |
|
| Playwright | Playwright | 확정 | 제품명 유지 |
|
||||||
| TypeScript | TypeScript | - | 언어명 유지 |
|
| TypeScript | TypeScript | 확정 | 언어명 유지 |
|
||||||
| JavaScript | JavaScript | - | 언어명 유지 |
|
| JavaScript | JavaScript | 확정 | 언어명 유지 |
|
||||||
| Go/Golang | Go | - | 언어명 유지 |
|
| Go/Golang | Go | 확정 | 언어명 유지 |
|
||||||
| React | React | - | 프레임워크명 유지 |
|
| React | React | 확정 | 프레임워크명 유지 |
|
||||||
| Next.js | Next.js | - | 프레임워크명 유지 |
|
| Next.js | Next.js | 확정 | 프레임워크명 유지 |
|
||||||
| PostgreSQL | PostgreSQL | - | 제품명 유지 |
|
| PostgreSQL | PostgreSQL | 확정 | 제품명 유지 |
|
||||||
| RLS (Row Level Security) | RLS(행 수준 보안) | 확정 | 최초 사용 시 전개 |
|
| RLS (Row Level Security) | RLS(행 수준 보안) | 확정 | 최초 사용 시 전개 |
|
||||||
| OWASP | OWASP | - | 영문 유지 |
|
| OWASP | OWASP | 확정 | 영문 유지 |
|
||||||
| XSS | XSS | - | 영문 유지 |
|
| XSS | XSS | 확정 | 영문 유지 |
|
||||||
| SQL Injection | SQL 인젝션 | 확정 | |
|
| SQL Injection | SQL 인젝션 | 확정 | |
|
||||||
| CSRF | CSRF | - | 영문 유지 |
|
| CSRF | CSRF | 확정 | 영문 유지 |
|
||||||
| Refactor | 리팩토링 | 확정 | |
|
| Refactor | 리팩토링 | 확정 | |
|
||||||
| Dead Code | 데드 코드 | 확정 | |
|
| Dead Code | 데드 코드 | 확정 | |
|
||||||
| Lint/Linter | Lint | 확정 | 영문 유지 |
|
| Lint/Linter | Lint | 확정 | 영문 유지 |
|
||||||
@@ -70,11 +70,11 @@
|
|||||||
| Migration | 마이그레이션 | 확정 | |
|
| Migration | 마이그레이션 | 확정 | |
|
||||||
| Transaction | 트랜잭션 | 확정 | |
|
| Transaction | 트랜잭션 | 확정 | |
|
||||||
| Concurrency | 동시성 | 확정 | |
|
| Concurrency | 동시성 | 확정 | |
|
||||||
| Goroutine | Goroutine | - | Go 용어 유지 |
|
| Goroutine | Goroutine | 확정 | Go 용어 유지 |
|
||||||
| Channel | Channel | 확정 | Go 컨텍스트에서 유지 |
|
| Channel | Channel | 확정 | Go 컨텍스트에서 유지 |
|
||||||
| Mutex | Mutex | - | 영문 유지 |
|
| Mutex | Mutex | 확정 | 영문 유지 |
|
||||||
| Interface | 인터페이스 | 확정 | |
|
| Interface | 인터페이스 | 확정 | |
|
||||||
| Struct | Struct | - | Go 용어 유지 |
|
| Struct | Struct | 확정 | Go 용어 유지 |
|
||||||
| Mock | Mock | 확정 | 테스트 용어 유지 |
|
| Mock | Mock | 확정 | 테스트 용어 유지 |
|
||||||
| Stub | Stub | 확정 | 테스트 용어 유지 |
|
| Stub | Stub | 확정 | 테스트 용어 유지 |
|
||||||
| Fixture | Fixture | 확정 | 테스트 용어 유지 |
|
| Fixture | Fixture | 확정 | 테스트 용어 유지 |
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
| Snapshot | 스냅샷 | 확정 | |
|
| Snapshot | 스냅샷 | 확정 | |
|
||||||
| Trace | 트레이스 | 확정 | |
|
| Trace | 트레이스 | 확정 | |
|
||||||
| Artifact | 아티팩트 | 확정 | |
|
| Artifact | 아티팩트 | 확정 | |
|
||||||
| CI/CD | CI/CD | - | 영문 유지 |
|
| CI/CD | CI/CD | 확정 | 영문 유지 |
|
||||||
| Pipeline | 파이프라인 | 확정 | |
|
| Pipeline | 파이프라인 | 확정 | |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -87,6 +87,15 @@ docs/CODEMAPS/
|
|||||||
4. **실행 가능** — 실제로 작동하는 설정 커맨드 포함
|
4. **실행 가능** — 실제로 작동하는 설정 커맨드 포함
|
||||||
5. **상호 참조** — 관련 문서 링크
|
5. **상호 참조** — 관련 문서 링크
|
||||||
|
|
||||||
|
## 품질 체크리스트
|
||||||
|
|
||||||
|
- [ ] 실제 코드에서 코드맵 생성
|
||||||
|
- [ ] 모든 파일 경로 존재 확인
|
||||||
|
- [ ] 코드 예제가 컴파일 또는 실행됨
|
||||||
|
- [ ] 링크 검증 완료
|
||||||
|
- [ ] 최신 타임스탬프 업데이트
|
||||||
|
- [ ] 오래된 참조 없음
|
||||||
|
|
||||||
## 업데이트 시점
|
## 업데이트 시점
|
||||||
|
|
||||||
**항상:** 새 주요 기능, API 라우트 변경, 의존성 추가/제거, 아키텍처 변경, 설정 프로세스 수정.
|
**항상:** 새 주요 기능, API 라우트 변경, 의존성 추가/제거, 아키텍처 변경, 설정 프로세스 수정.
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ npx playwright show-report # HTML 보고서 보기
|
|||||||
|
|
||||||
- **시맨틱 로케이터 사용**: `[data-testid="..."]` > CSS 셀렉터 > XPath
|
- **시맨틱 로케이터 사용**: `[data-testid="..."]` > CSS 셀렉터 > XPath
|
||||||
- **시간이 아닌 조건 대기**: `waitForResponse()` > `waitForTimeout()`
|
- **시간이 아닌 조건 대기**: `waitForResponse()` > `waitForTimeout()`
|
||||||
- **자동 대기 내장**: `page.locator().click()`은 자동 대기; `page.click()`은 아님
|
- **자동 대기 내장**: `locator.click()`과 `page.click()` 모두 자동 대기를 제공하지만, 더 안정적인 `locator` 기반 API를 선호
|
||||||
- **테스트 격리**: 각 테스트는 독립적; 공유 상태 없음
|
- **테스트 격리**: 각 테스트는 독립적; 공유 상태 없음
|
||||||
- **빠른 실패**: 모든 핵심 단계에서 `expect()` 어설션 사용
|
- **빠른 실패**: 모든 핵심 단계에서 `expect()` 어설션 사용
|
||||||
- **재시도 시 트레이스**: 실패 디버깅을 위해 `trace: 'on-first-retry'` 설정
|
- **재시도 시 트레이스**: 실패 디버깅을 위해 `trace: 'on-first-retry'` 설정
|
||||||
|
|||||||
@@ -21,10 +21,16 @@ model: sonnet
|
|||||||
기대 동작을 설명하는 실패하는 테스트 작성.
|
기대 동작을 설명하는 실패하는 테스트 작성.
|
||||||
|
|
||||||
### 2. 테스트 실행 -- 실패 확인
|
### 2. 테스트 실행 -- 실패 확인
|
||||||
|
Node.js (npm):
|
||||||
```bash
|
```bash
|
||||||
npm test
|
npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
언어 중립:
|
||||||
|
- 프로젝트의 기본 테스트 명령을 실행하세요.
|
||||||
|
- Python: `pytest`
|
||||||
|
- Go: `go test ./...`
|
||||||
|
|
||||||
### 3. 최소한의 구현 작성 (GREEN)
|
### 3. 최소한의 구현 작성 (GREEN)
|
||||||
테스트를 통과하기에 충분한 코드만.
|
테스트를 통과하기에 충분한 코드만.
|
||||||
|
|
||||||
@@ -34,11 +40,17 @@ npm test
|
|||||||
중복 제거, 이름 개선, 최적화 -- 테스트는 그린 유지.
|
중복 제거, 이름 개선, 최적화 -- 테스트는 그린 유지.
|
||||||
|
|
||||||
### 6. 커버리지 확인
|
### 6. 커버리지 확인
|
||||||
|
Node.js (npm):
|
||||||
```bash
|
```bash
|
||||||
npm run test:coverage
|
npm run test:coverage
|
||||||
# 필수: branches, functions, lines, statements 80% 이상
|
# 필수: branches, functions, lines, statements 80% 이상
|
||||||
```
|
```
|
||||||
|
|
||||||
|
언어 중립:
|
||||||
|
- 프로젝트의 기본 커버리지 명령을 실행하세요.
|
||||||
|
- Python: `pytest --cov`
|
||||||
|
- Go: `go test ./... -cover`
|
||||||
|
|
||||||
## 필수 테스트 유형
|
## 필수 테스트 유형
|
||||||
|
|
||||||
| 유형 | 테스트 대상 | 시점 |
|
| 유형 | 테스트 대상 | 시점 |
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
name: build-fix
|
||||||
|
description: 최소한의 안전한 변경으로 build 및 타입 오류를 점진적으로 수정합니다.
|
||||||
|
---
|
||||||
|
|
||||||
# Build 오류 수정
|
# Build 오류 수정
|
||||||
|
|
||||||
최소한의 안전한 변경으로 build 및 타입 오류를 점진적으로 수정합니다.
|
최소한의 안전한 변경으로 build 및 타입 오류를 점진적으로 수정합니다.
|
||||||
@@ -14,7 +19,7 @@
|
|||||||
| `pom.xml` | `mvn compile` |
|
| `pom.xml` | `mvn compile` |
|
||||||
| `build.gradle` | `./gradlew compileJava` |
|
| `build.gradle` | `./gradlew compileJava` |
|
||||||
| `go.mod` | `go build ./...` |
|
| `go.mod` | `go build ./...` |
|
||||||
| `pyproject.toml` | `python -m py_compile` 또는 `mypy .` |
|
| `pyproject.toml` | `python -m compileall .` 또는 `mypy .` |
|
||||||
|
|
||||||
## 2단계: 오류 파싱 및 그룹화
|
## 2단계: 오류 파싱 및 그룹화
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
|
---
|
||||||
|
name: checkpoint
|
||||||
|
description: 워크플로우에서 checkpoint를 생성, 검증, 조회 또는 정리합니다.
|
||||||
|
---
|
||||||
|
|
||||||
# Checkpoint 명령어
|
# Checkpoint 명령어
|
||||||
|
|
||||||
워크플로우에서 checkpoint를 생성하거나 검증합니다.
|
워크플로우에서 checkpoint를 생성하거나 검증합니다.
|
||||||
|
|
||||||
## 사용법
|
## 사용법
|
||||||
|
|
||||||
`/checkpoint [create|verify|list] [name]`
|
`/checkpoint [create|verify|list|clear] [name]`
|
||||||
|
|
||||||
## Checkpoint 생성
|
## Checkpoint 생성
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
- 800줄 초과 파일
|
- 800줄 초과 파일
|
||||||
- 4단계 초과 중첩 깊이
|
- 4단계 초과 중첩 깊이
|
||||||
- 누락된 에러 처리
|
- 누락된 에러 처리
|
||||||
- console.log 문
|
- 디버그 로깅 문구(예: 개발용 로그/print 등)
|
||||||
- TODO/FIXME 주석
|
- TODO/FIXME 주석
|
||||||
- 공개 API에 대한 JSDoc 누락
|
- 활성 언어에 대한 공개 API 문서 누락(예: JSDoc/Go doc/Docstring 등)
|
||||||
|
|
||||||
**모범 사례 (MEDIUM):**
|
**모범 사례 (MEDIUM):**
|
||||||
- 변이(Mutation) 패턴 (불변 패턴을 사용하세요)
|
- 변이(Mutation) 패턴 (불변 패턴을 사용하세요)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ e2e-runner 에이전트가 수행하는 작업:
|
|||||||
|
|
||||||
## 사용 예시
|
## 사용 예시
|
||||||
|
|
||||||
```
|
````
|
||||||
User: /e2e 마켓 검색 및 조회 흐름 테스트
|
User: /e2e 마켓 검색 및 조회 흐름 테스트
|
||||||
|
|
||||||
Agent (e2e-runner):
|
Agent (e2e-runner):
|
||||||
@@ -200,7 +200,7 @@ Running 3 tests using 3 workers
|
|||||||
```
|
```
|
||||||
|
|
||||||
✅ CI/CD 통합 준비가 완료된 E2E 테스트 모음!
|
✅ CI/CD 통합 준비가 완료된 E2E 테스트 모음!
|
||||||
```
|
````
|
||||||
|
|
||||||
## 테스트 아티팩트
|
## 테스트 아티팩트
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
## 사용법
|
## 사용법
|
||||||
|
|
||||||
`/eval [define|check|report|list] [feature-name]`
|
`/eval [define|check|report|list|clean] [feature-name]`
|
||||||
|
|
||||||
## 평가 정의
|
## 평가 정의
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ go mod tidy -v
|
|||||||
|
|
||||||
## 예시 세션
|
## 예시 세션
|
||||||
|
|
||||||
```text
|
````text
|
||||||
User: /go-build
|
User: /go-build
|
||||||
|
|
||||||
Agent:
|
Agent:
|
||||||
@@ -141,7 +141,7 @@ ok project/internal/handler 0.023s
|
|||||||
| 남은 이슈 | 0 |
|
| 남은 이슈 | 0 |
|
||||||
|
|
||||||
Build 상태: ✅ 성공
|
Build 상태: ✅ 성공
|
||||||
```
|
````
|
||||||
|
|
||||||
## 자주 발생하는 에러
|
## 자주 발생하는 에러
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ govulncheck ./...
|
|||||||
|
|
||||||
## 사용 예시
|
## 사용 예시
|
||||||
|
|
||||||
```text
|
````text
|
||||||
User: /go-review
|
User: /go-review
|
||||||
|
|
||||||
Agent:
|
Agent:
|
||||||
@@ -125,7 +125,7 @@ return fmt.Errorf("get user %s: %w", userID, err)
|
|||||||
- MEDIUM: 0
|
- MEDIUM: 0
|
||||||
|
|
||||||
권장: ❌ CRITICAL 이슈가 수정될 때까지 merge 차단
|
권장: ❌ CRITICAL 이슈가 수정될 때까지 merge 차단
|
||||||
```
|
````
|
||||||
|
|
||||||
## 승인 기준
|
## 승인 기준
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ node scripts/setup-package-manager.js --list
|
|||||||
3. **package.json**: `packageManager` 필드
|
3. **package.json**: `packageManager` 필드
|
||||||
4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb의 존재 여부
|
4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb의 존재 여부
|
||||||
5. **전역 설정**: `~/.claude/package-manager.json`
|
5. **전역 설정**: `~/.claude/package-manager.json`
|
||||||
6. **폴백**: 사용 가능한 첫 번째 패키지 매니저 (pnpm > bun > yarn > npm)
|
6. **폴백**: `npm`
|
||||||
|
|
||||||
## 설정 파일
|
## 설정 파일
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ REPEAT: 다음 기능/시나리오
|
|||||||
|
|
||||||
## 사용 예시
|
## 사용 예시
|
||||||
|
|
||||||
```
|
````
|
||||||
User: /tdd 마켓 유동성 점수를 계산하는 함수가 필요합니다
|
User: /tdd 마켓 유동성 점수를 계산하는 함수가 필요합니다
|
||||||
|
|
||||||
Agent (tdd-guide):
|
Agent (tdd-guide):
|
||||||
@@ -251,7 +251,7 @@ Coverage: 100% ✅ (목표: 80%)
|
|||||||
```
|
```
|
||||||
|
|
||||||
✅ TDD 세션 완료!
|
✅ TDD 세션 완료!
|
||||||
```
|
````
|
||||||
|
|
||||||
## TDD 모범 사례
|
## TDD 모범 사례
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
name: test-coverage
|
||||||
|
description: 테스트 커버리지를 분석하고, 80% 이상을 목표로 누락된 테스트를 식별하고 생성합니다.
|
||||||
|
---
|
||||||
|
|
||||||
# 테스트 커버리지
|
# 테스트 커버리지
|
||||||
|
|
||||||
테스트 커버리지를 분석하고, 갭을 식별하며, 80% 이상 커버리지 달성을 위해 누락된 테스트를 생성합니다.
|
테스트 커버리지를 분석하고, 갭을 식별하며, 80% 이상 커버리지 달성을 위해 누락된 테스트를 생성합니다.
|
||||||
|
|||||||
@@ -10,15 +10,16 @@
|
|||||||
|
|
||||||
## 2단계: 코드맵 생성
|
## 2단계: 코드맵 생성
|
||||||
|
|
||||||
`docs/CODEMAPS/` (또는 `.reports/codemaps/`)에 코드맵 생성 또는 업데이트:
|
`docs/CODEMAPS/`에 코드맵 생성 또는 업데이트:
|
||||||
|
|
||||||
| 파일 | 내용 |
|
| 파일 | 내용 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `architecture.md` | 상위 시스템 다이어그램, 서비스 경계, 데이터 흐름 |
|
| `INDEX.md` | 전체 코드베이스 개요와 영역별 링크 |
|
||||||
| `backend.md` | API 라우트, 미들웨어 체인, 서비스 → 리포지토리 매핑 |
|
| `backend.md` | API 라우트, 미들웨어 체인, 서비스 → 리포지토리 매핑 |
|
||||||
| `frontend.md` | 페이지 트리, 컴포넌트 계층, 상태 관리 흐름 |
|
| `frontend.md` | 페이지 트리, 컴포넌트 계층, 상태 관리 흐름 |
|
||||||
| `data.md` | 데이터베이스 테이블, 관계, 마이그레이션 히스토리 |
|
| `database.md` | 데이터베이스 스키마, 마이그레이션, 저장소 계층 |
|
||||||
| `dependencies.md` | 외부 서비스, 서드파티 통합, 공유 라이브러리 |
|
| `integrations.md` | 외부 서비스, 서드파티 통합, 어댑터 |
|
||||||
|
| `workers.md` | 백그라운드 작업, 큐, 스케줄러 |
|
||||||
|
|
||||||
### 코드맵 형식
|
### 코드맵 형식
|
||||||
|
|
||||||
@@ -41,27 +42,33 @@ src/repos/user.ts (데이터베이스 접근, 80줄)
|
|||||||
- Stripe (결제 처리)
|
- 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단계: 메타데이터 추가
|
## 4단계: 메타데이터 추가
|
||||||
|
|
||||||
각 코드맵에 최신 정보 헤더를 추가합니다:
|
각 코드맵에 최신 정보 헤더를 추가합니다:
|
||||||
|
|
||||||
```markdown
|
```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`에 요약을 작성합니다:
|
`INDEX.md`는 생성된 영역 문서를 링크하고 요약해야 합니다:
|
||||||
- 마지막 스캔 이후 추가/제거/수정된 파일
|
- 각 영역의 파일 수와 총 라인 수
|
||||||
- 새로 감지된 의존성
|
- 감지된 엔트리 포인트
|
||||||
- 아키텍처 변경 사항 (새 라우트, 새 서비스 등)
|
- 저장소 트리의 간단한 ASCII 개요
|
||||||
- 90일 이상 업데이트되지 않은 문서에 대한 오래된 항목 경고
|
- 영역별 세부 문서 링크
|
||||||
|
|
||||||
## 팁
|
## 팁
|
||||||
|
|
||||||
@@ -69,4 +76,4 @@ src/repos/user.ts (데이터베이스 접근, 80줄)
|
|||||||
- 전체 코드 블록 대신 **파일 경로와 함수 시그니처** 사용
|
- 전체 코드 블록 대신 **파일 경로와 함수 시그니처** 사용
|
||||||
- 효율적인 컨텍스트 로딩을 위해 각 코드맵을 **1000 토큰 미만**으로 유지
|
- 효율적인 컨텍스트 로딩을 위해 각 코드맵을 **1000 토큰 미만**으로 유지
|
||||||
- 장황한 설명 대신 데이터 흐름에 ASCII 다이어그램 사용
|
- 장황한 설명 대신 데이터 흐름에 ASCII 다이어그램 사용
|
||||||
- 주요 기능 추가 또는 리팩토링 세션 후 실행
|
- 주요 기능 추가 또는 리팩토링 세션 후 `npx tsx scripts/codemaps/generate.ts` 실행
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
name: update-docs
|
||||||
|
description: 코드베이스를 기준으로 문서를 동기화하고 생성된 섹션을 갱신합니다.
|
||||||
|
---
|
||||||
|
|
||||||
# 문서 업데이트
|
# 문서 업데이트
|
||||||
|
|
||||||
문서를 코드베이스와 동기화하고, 원본 소스 파일에서 생성합니다.
|
문서를 코드베이스와 동기화하고, 원본 소스 파일에서 생성합니다.
|
||||||
|
|||||||
@@ -23,11 +23,15 @@
|
|||||||
- 통과/실패 수 보고
|
- 통과/실패 수 보고
|
||||||
- 커버리지 비율 보고
|
- 커버리지 비율 보고
|
||||||
|
|
||||||
5. **Console.log 감사**
|
5. **시크릿 스캔**
|
||||||
|
- 소스 파일에서 API 키, 토큰, 비밀값 패턴 검색
|
||||||
|
- 발견 위치 보고
|
||||||
|
|
||||||
|
6. **Console.log 감사**
|
||||||
- 소스 파일에서 console.log 검색
|
- 소스 파일에서 console.log 검색
|
||||||
- 위치 보고
|
- 위치 보고
|
||||||
|
|
||||||
6. **Git 상태**
|
7. **Git 상태**
|
||||||
- 커밋되지 않은 변경사항 표시
|
- 커밋되지 않은 변경사항 표시
|
||||||
- 마지막 커밋 이후 수정된 파일 표시
|
- 마지막 커밋 이후 수정된 파일 표시
|
||||||
|
|
||||||
|
|||||||
@@ -142,12 +142,12 @@ from django.db import transaction
|
|||||||
|
|
||||||
def create_order(*, customer, product_id: uuid.UUID, quantity: int) -> Order:
|
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():
|
with transaction.atomic():
|
||||||
|
product = Product.objects.select_for_update().get(id=product_id)
|
||||||
|
|
||||||
|
if product.stock < quantity:
|
||||||
|
raise InsufficientStockError()
|
||||||
|
|
||||||
order = Order.objects.create(
|
order = Order.objects.create(
|
||||||
customer=customer,
|
customer=customer,
|
||||||
product=product,
|
product=product,
|
||||||
|
|||||||
@@ -55,7 +55,9 @@ pub enum AppError {
|
|||||||
#[error("Unauthorized")]
|
#[error("Unauthorized")]
|
||||||
Unauthorized,
|
Unauthorized,
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Internal(#[from] anyhow::Error),
|
Database(#[from] sqlx::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoResponse for AppError {
|
impl IntoResponse for AppError {
|
||||||
@@ -64,7 +66,11 @@ impl IntoResponse for AppError {
|
|||||||
Self::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
|
Self::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
|
||||||
Self::Validation(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
|
Self::Validation(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
|
||||||
Self::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
|
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");
|
tracing::error!(?err, "internal error");
|
||||||
(StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into())
|
(StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"statusLine": {
|
"statusLine": {
|
||||||
"type": "command",
|
"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"
|
"description": "Custom status line showing: user:path branch* ctx:% model time todos:N"
|
||||||
},
|
},
|
||||||
"_comments": {
|
"_comments": {
|
||||||
|
|||||||
@@ -13,6 +13,9 @@
|
|||||||
| security-reviewer | 보안 분석 | 커밋 전 |
|
| security-reviewer | 보안 분석 | 커밋 전 |
|
||||||
| build-error-resolver | 빌드 에러 수정 | 빌드 실패 시 |
|
| build-error-resolver | 빌드 에러 수정 | 빌드 실패 시 |
|
||||||
| e2e-runner | E2E 테스팅 | 핵심 사용자 흐름 |
|
| e2e-runner | E2E 테스팅 | 핵심 사용자 흐름 |
|
||||||
|
| database-reviewer | 데이터베이스 스키마/쿼리 리뷰 | 스키마 설계, 쿼리 최적화 |
|
||||||
|
| go-reviewer | Go 코드 리뷰 | Go 코드 작성 또는 수정 후 |
|
||||||
|
| go-build-resolver | Go 빌드 에러 수정 | `go build` 또는 `go vet` 실패 시 |
|
||||||
| refactor-cleaner | 사용하지 않는 코드 정리 | 코드 유지보수 |
|
| refactor-cleaner | 사용하지 않는 코드 정리 | 코드 유지보수 |
|
||||||
| doc-updater | 문서 관리 | 문서 업데이트 |
|
| doc-updater | 문서 관리 | 문서 업데이트 |
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
타입: feat, fix, refactor, docs, test, chore, perf, ci
|
타입: feat, fix, refactor, docs, test, chore, perf, ci
|
||||||
|
|
||||||
참고: 어트리뷰션은 ~/.claude/settings.json에서 전역적으로 비활성화되어 있습니다.
|
참고: 어트리뷰션 비활성화 여부는 각자의 `~/.claude/settings.json` 로컬 설정에 따라 달라질 수 있습니다.
|
||||||
|
|
||||||
## Pull Request 워크플로우
|
## Pull Request 워크플로우
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ GET /api/markets?status=active&sort=volume&limit=20&offset=0
|
|||||||
interface MarketRepository {
|
interface MarketRepository {
|
||||||
findAll(filters?: MarketFilters): Promise<Market[]>
|
findAll(filters?: MarketFilters): Promise<Market[]>
|
||||||
findById(id: string): Promise<Market | null>
|
findById(id: string): Promise<Market | null>
|
||||||
|
findByIds(ids: string[]): Promise<Market[]>
|
||||||
create(data: CreateMarketDto): Promise<Market>
|
create(data: CreateMarketDto): Promise<Market>
|
||||||
update(id: string, data: UpdateMarketDto): Promise<Market>
|
update(id: string, data: UpdateMarketDto): Promise<Market>
|
||||||
delete(id: string): Promise<void>
|
delete(id: string): Promise<void>
|
||||||
@@ -85,7 +86,7 @@ class MarketService {
|
|||||||
const markets = await this.marketRepo.findByIds(results.map(r => r.id))
|
const markets = await this.marketRepo.findByIds(results.map(r => r.id))
|
||||||
|
|
||||||
// Sort by similarity
|
// 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 scoreA = results.find(r => r.id === a.id)?.score || 0
|
||||||
const scoreB = results.find(r => r.id === b.id)?.score || 0
|
const scoreB = results.find(r => r.id === b.id)?.score || 0
|
||||||
return scoreA - scoreB
|
return scoreA - scoreB
|
||||||
@@ -320,7 +321,7 @@ async function fetchWithRetry<T>(
|
|||||||
fn: () => Promise<T>,
|
fn: () => Promise<T>,
|
||||||
maxRetries = 3
|
maxRetries = 3
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
let lastError: Error
|
let lastError: Error = new Error('Retry attempts exhausted')
|
||||||
|
|
||||||
for (let i = 0; i < maxRetries; i++) {
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ SETTINGS index_granularity = 8192;
|
|||||||
### ReplacingMergeTree (중복 제거)
|
### ReplacingMergeTree (중복 제거)
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- For data that may have duplicates (e.g., from multiple sources)
|
-- 중복이 있을 수 있는 데이터용 (예: 여러 소스에서 수집된 경우)
|
||||||
CREATE TABLE user_events (
|
CREATE TABLE user_events (
|
||||||
event_id String,
|
event_id String,
|
||||||
user_id String,
|
user_id String,
|
||||||
@@ -67,7 +67,7 @@ PRIMARY KEY (user_id, event_id);
|
|||||||
### AggregatingMergeTree (사전 집계)
|
### AggregatingMergeTree (사전 집계)
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- For maintaining aggregated metrics
|
-- 집계 메트릭을 유지하기 위한 용도
|
||||||
CREATE TABLE market_stats_hourly (
|
CREATE TABLE market_stats_hourly (
|
||||||
hour DateTime,
|
hour DateTime,
|
||||||
market_id String,
|
market_id String,
|
||||||
@@ -78,7 +78,7 @@ CREATE TABLE market_stats_hourly (
|
|||||||
PARTITION BY toYYYYMM(hour)
|
PARTITION BY toYYYYMM(hour)
|
||||||
ORDER BY (hour, market_id);
|
ORDER BY (hour, market_id);
|
||||||
|
|
||||||
-- Query aggregated data
|
-- 집계된 데이터 조회
|
||||||
SELECT
|
SELECT
|
||||||
hour,
|
hour,
|
||||||
market_id,
|
market_id,
|
||||||
@@ -96,7 +96,7 @@ ORDER BY hour DESC;
|
|||||||
### 효율적인 필터링
|
### 효율적인 필터링
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ✅ GOOD: Use indexed columns first
|
-- ✅ 좋음: 인덱스된 컬럼을 먼저 사용
|
||||||
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;
|
||||||
|
|
||||||
-- ❌ BAD: Filter on non-indexed columns first
|
-- ❌ 나쁨: 비인덱스 컬럼을 먼저 필터링
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM markets_analytics
|
FROM markets_analytics
|
||||||
WHERE volume > 1000
|
WHERE volume > 1000
|
||||||
@@ -116,7 +116,7 @@ WHERE volume > 1000
|
|||||||
### 집계
|
### 집계
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- ✅ GOOD: Use ClickHouse-specific aggregation functions
|
-- ✅ 좋음: 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;
|
||||||
|
|
||||||
-- ✅ Use quantile for percentiles (more efficient than percentile)
|
-- ✅ 백분위수에는 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,
|
||||||
@@ -141,7 +141,7 @@ WHERE created_at >= now() - INTERVAL 1 HOUR;
|
|||||||
### 윈도우 함수
|
### 윈도우 함수
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Calculate running totals
|
-- 누적 합계 계산
|
||||||
SELECT
|
SELECT
|
||||||
date,
|
date,
|
||||||
market_id,
|
market_id,
|
||||||
@@ -172,25 +172,22 @@ const clickhouse = new ClickHouse({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// ✅ Batch insert (efficient)
|
// ✅ 배치 삽입 (효율적)
|
||||||
async function bulkInsertTrades(trades: Trade[]) {
|
async function bulkInsertTrades(trades: Trade[]) {
|
||||||
const values = trades.map(trade => `(
|
const rows = trades.map(trade => ({
|
||||||
'${trade.id}',
|
id: trade.id,
|
||||||
'${trade.market_id}',
|
market_id: trade.market_id,
|
||||||
'${trade.user_id}',
|
user_id: trade.user_id,
|
||||||
${trade.amount},
|
amount: trade.amount,
|
||||||
'${trade.timestamp.toISOString()}'
|
timestamp: trade.timestamp.toISOString()
|
||||||
)`).join(',')
|
}))
|
||||||
|
|
||||||
await clickhouse.query(`
|
await clickhouse.insert('trades', rows)
|
||||||
INSERT INTO trades (id, market_id, user_id, amount, timestamp)
|
|
||||||
VALUES ${values}
|
|
||||||
`).toPromise()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ Individual inserts (slow)
|
// ❌ 개별 삽입 (느림)
|
||||||
async function insertTrade(trade: Trade) {
|
async function insertTrade(trade: Trade) {
|
||||||
// Don't do this in a loop!
|
// 루프 안에서 이렇게 하지 마세요!
|
||||||
await clickhouse.query(`
|
await clickhouse.query(`
|
||||||
INSERT INTO trades VALUES ('${trade.id}', ...)
|
INSERT INTO trades VALUES ('${trade.id}', ...)
|
||||||
`).toPromise()
|
`).toPromise()
|
||||||
@@ -200,7 +197,7 @@ async function insertTrade(trade: Trade) {
|
|||||||
### 스트리밍 삽입
|
### 스트리밍 삽입
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// For continuous data ingestion
|
// 연속적인 데이터 수집용
|
||||||
import { createWriteStream } from 'fs'
|
import { createWriteStream } from 'fs'
|
||||||
import { pipeline } from 'stream/promises'
|
import { pipeline } from 'stream/promises'
|
||||||
|
|
||||||
@@ -220,7 +217,7 @@ async function streamInserts() {
|
|||||||
### 실시간 집계
|
### 실시간 집계
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Create materialized view for hourly stats
|
-- 시간별 통계를 위한 materialized view 생성
|
||||||
CREATE MATERIALIZED VIEW market_stats_hourly_mv
|
CREATE MATERIALIZED VIEW market_stats_hourly_mv
|
||||||
TO market_stats_hourly
|
TO market_stats_hourly
|
||||||
AS SELECT
|
AS SELECT
|
||||||
@@ -232,7 +229,7 @@ AS SELECT
|
|||||||
FROM trades
|
FROM trades
|
||||||
GROUP BY hour, market_id;
|
GROUP BY hour, market_id;
|
||||||
|
|
||||||
-- Query the materialized view
|
-- materialized view 조회
|
||||||
SELECT
|
SELECT
|
||||||
hour,
|
hour,
|
||||||
market_id,
|
market_id,
|
||||||
@@ -249,7 +246,7 @@ GROUP BY hour, market_id;
|
|||||||
### 쿼리 성능
|
### 쿼리 성능
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Check slow queries
|
-- 느린 쿼리 확인
|
||||||
SELECT
|
SELECT
|
||||||
query_id,
|
query_id,
|
||||||
user,
|
user,
|
||||||
@@ -269,7 +266,7 @@ LIMIT 10;
|
|||||||
### 테이블 통계
|
### 테이블 통계
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Check table sizes
|
-- 테이블 크기 확인
|
||||||
SELECT
|
SELECT
|
||||||
database,
|
database,
|
||||||
table,
|
table,
|
||||||
@@ -287,7 +284,7 @@ ORDER BY sum(bytes) DESC;
|
|||||||
### 시계열 분석
|
### 시계열 분석
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Daily active users
|
-- 일간 활성 사용자
|
||||||
SELECT
|
SELECT
|
||||||
toDate(timestamp) AS date,
|
toDate(timestamp) AS date,
|
||||||
uniq(user_id) AS daily_active_users
|
uniq(user_id) AS daily_active_users
|
||||||
@@ -296,7 +293,7 @@ WHERE timestamp >= today() - INTERVAL 30 DAY
|
|||||||
GROUP BY date
|
GROUP BY date
|
||||||
ORDER BY date;
|
ORDER BY date;
|
||||||
|
|
||||||
-- Retention analysis
|
-- 리텐션 분석
|
||||||
SELECT
|
SELECT
|
||||||
signup_date,
|
signup_date,
|
||||||
countIf(days_since_signup = 0) AS day_0,
|
countIf(days_since_signup = 0) AS day_0,
|
||||||
@@ -319,7 +316,7 @@ ORDER BY signup_date DESC;
|
|||||||
### 퍼널 분석
|
### 퍼널 분석
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Conversion funnel
|
-- 전환 퍼널
|
||||||
SELECT
|
SELECT
|
||||||
countIf(step = 'viewed_market') AS viewed,
|
countIf(step = 'viewed_market') AS viewed,
|
||||||
countIf(step = 'clicked_trade') AS clicked,
|
countIf(step = 'clicked_trade') AS clicked,
|
||||||
@@ -340,7 +337,7 @@ GROUP BY session_id;
|
|||||||
### 코호트 분석
|
### 코호트 분석
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- User cohorts by signup month
|
-- 가입 월별 사용자 코호트
|
||||||
SELECT
|
SELECT
|
||||||
toStartOfMonth(signup_date) AS cohort,
|
toStartOfMonth(signup_date) AS cohort,
|
||||||
toStartOfMonth(activity_date) AS month,
|
toStartOfMonth(activity_date) AS month,
|
||||||
@@ -362,12 +359,12 @@ ORDER BY cohort, months_since_signup;
|
|||||||
### ETL 패턴
|
### ETL 패턴
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Extract, Transform, Load
|
// 추출, 변환, 적재(ETL)
|
||||||
async function etlPipeline() {
|
async function etlPipeline() {
|
||||||
// 1. Extract from source
|
// 1. 소스에서 추출
|
||||||
const rawData = await extractFromPostgres()
|
const rawData = await extractFromPostgres()
|
||||||
|
|
||||||
// 2. Transform
|
// 2. 변환
|
||||||
const transformed = rawData.map(row => ({
|
const transformed = rawData.map(row => ({
|
||||||
date: new Date(row.created_at).toISOString().split('T')[0],
|
date: new Date(row.created_at).toISOString().split('T')[0],
|
||||||
market_id: row.market_slug,
|
market_id: row.market_slug,
|
||||||
@@ -375,18 +372,29 @@ async function etlPipeline() {
|
|||||||
trades: parseInt(row.trade_count)
|
trades: parseInt(row.trade_count)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 3. Load to ClickHouse
|
// 3. ClickHouse에 적재
|
||||||
await bulkInsertToClickHouse(transformed)
|
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)
|
### 변경 데이터 캡처 (CDC)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Listen to PostgreSQL changes and sync to ClickHouse
|
// PostgreSQL 변경을 수신하고 ClickHouse와 동기화
|
||||||
import { Client } from 'pg'
|
import { Client } from 'pg'
|
||||||
|
|
||||||
const pgClient = new Client({ connectionString: process.env.DATABASE_URL })
|
const pgClient = new Client({ connectionString: process.env.DATABASE_URL })
|
||||||
|
|||||||
@@ -397,7 +397,7 @@ import { useMemo, useCallback } from 'react'
|
|||||||
|
|
||||||
// ✅ GOOD: Memoize expensive computations
|
// ✅ 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
|
// ✅ GOOD: Memoize callbacks
|
||||||
|
|||||||
@@ -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을 사용하는 이유
|
## Stop Hook을 사용하는 이유
|
||||||
|
|
||||||
- **경량**: 세션 종료 시 한 번만 실행
|
- **경량**: 세션 종료 시 한 번만 실행
|
||||||
@@ -116,4 +145,4 @@ Homunculus v2는 더 정교한 접근법을 취합니다:
|
|||||||
4. **도메인 태깅** - code-style, testing, git, debugging 등
|
4. **도메인 태깅** - code-style, testing, git, debugging 등
|
||||||
5. **진화 경로** - 관련 본능을 스킬/명령어로 클러스터링
|
5. **진화 경로** - 관련 본능을 스킬/명령어로 클러스터링
|
||||||
|
|
||||||
자세한 사양은 `docs/continuous-learning-v2-spec.md`를 참조하세요.
|
자세한 사양은 [`continuous-learning-v2-spec.md`](../../../continuous-learning-v2-spec.md)를 참조하세요.
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ const [isOpen, toggleOpen] = useToggle()
|
|||||||
### 비동기 데이터 페칭 Hook
|
### 비동기 데이터 페칭 Hook
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
interface UseQueryOptions<T> {
|
interface UseQueryOptions<T> {
|
||||||
onSuccess?: (data: T) => void
|
onSuccess?: (data: T) => void
|
||||||
onError?: (error: Error) => void
|
onError?: (error: Error) => void
|
||||||
@@ -168,6 +170,14 @@ export function useQuery<T>(
|
|||||||
const [data, setData] = useState<T | null>(null)
|
const [data, setData] = useState<T | null>(null)
|
||||||
const [error, setError] = useState<Error | null>(null)
|
const [error, setError] = useState<Error | null>(null)
|
||||||
const [loading, setLoading] = useState(false)
|
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 () => {
|
const refetch = useCallback(async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
@@ -176,21 +186,21 @@ export function useQuery<T>(
|
|||||||
try {
|
try {
|
||||||
const result = await fetcher()
|
const result = await fetcher()
|
||||||
setData(result)
|
setData(result)
|
||||||
options?.onSuccess?.(result)
|
successRef.current?.(result)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = err as Error
|
const error = err as Error
|
||||||
setError(error)
|
setError(error)
|
||||||
options?.onError?.(error)
|
errorRef.current?.(error)
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [fetcher, options])
|
}, [fetcher])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (options?.enabled !== false) {
|
if (enabled) {
|
||||||
refetch()
|
refetch()
|
||||||
}
|
}
|
||||||
}, [key, refetch, options?.enabled])
|
}, [key, enabled, refetch])
|
||||||
|
|
||||||
return { data, error, loading, refetch }
|
return { data, error, loading, refetch }
|
||||||
}
|
}
|
||||||
@@ -296,7 +306,7 @@ export function useMarkets() {
|
|||||||
```typescript
|
```typescript
|
||||||
// ✅ useMemo for expensive computations
|
// ✅ 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
|
// ✅ useCallback for functions passed to children
|
||||||
|
|||||||
@@ -533,7 +533,8 @@ func ProcessRequest(data []byte) []byte {
|
|||||||
|
|
||||||
buf.Write(data)
|
buf.Write(data)
|
||||||
// Process...
|
// Process...
|
||||||
return buf.Bytes()
|
out := append([]byte(nil), buf.Bytes()...)
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -215,8 +215,8 @@ const securityHeaders = [
|
|||||||
key: 'Content-Security-Policy',
|
key: 'Content-Security-Policy',
|
||||||
value: `
|
value: `
|
||||||
default-src 'self';
|
default-src 'self';
|
||||||
script-src 'self' 'unsafe-eval' 'unsafe-inline';
|
script-src 'self' 'nonce-{nonce}';
|
||||||
style-src 'self' 'unsafe-inline';
|
style-src 'self' 'nonce-{nonce}';
|
||||||
img-src 'self' data: https:;
|
img-src 'self' data: https:;
|
||||||
font-src 'self';
|
font-src 'self';
|
||||||
connect-src 'self' https://api.example.com;
|
connect-src 'self' https://api.example.com;
|
||||||
@@ -225,6 +225,8 @@ const securityHeaders = [
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`{nonce}`는 요청마다 새로 생성하고, 헤더와 인라인 `<script>`/`<style>` 태그에 동일하게 주입해야 합니다.
|
||||||
|
|
||||||
#### 확인 단계
|
#### 확인 단계
|
||||||
- [ ] 사용자 제공 HTML이 새니타이징됨
|
- [ ] 사용자 제공 HTML이 새니타이징됨
|
||||||
- [ ] CSP 헤더가 구성됨
|
- [ ] CSP 헤더가 구성됨
|
||||||
@@ -339,7 +341,9 @@ catch (error) {
|
|||||||
|
|
||||||
#### 지갑 검증
|
#### 지갑 검증
|
||||||
```typescript
|
```typescript
|
||||||
import { verify } from '@solana/web3.js'
|
import nacl from 'tweetnacl'
|
||||||
|
import bs58 from 'bs58'
|
||||||
|
import { PublicKey } from '@solana/web3.js'
|
||||||
|
|
||||||
async function verifyWalletOwnership(
|
async function verifyWalletOwnership(
|
||||||
publicKey: string,
|
publicKey: string,
|
||||||
@@ -347,18 +351,23 @@ async function verifyWalletOwnership(
|
|||||||
message: string
|
message: string
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const isValid = verify(
|
const publicKeyBytes = new PublicKey(publicKey).toBytes()
|
||||||
Buffer.from(message),
|
const signatureBytes = bs58.decode(signature)
|
||||||
Buffer.from(signature, 'base64'),
|
const messageBytes = new TextEncoder().encode(message)
|
||||||
Buffer.from(publicKey, 'base64')
|
|
||||||
|
return nacl.sign.detached.verify(
|
||||||
|
messageBytes,
|
||||||
|
signatureBytes,
|
||||||
|
publicKeyBytes
|
||||||
)
|
)
|
||||||
return isValid
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
참고: Solana 공개 키와 서명은 일반적으로 base64가 아니라 base58로 인코딩됩니다.
|
||||||
|
|
||||||
#### 트랜잭션 검증
|
#### 트랜잭션 검증
|
||||||
```typescript
|
```typescript
|
||||||
async function verifyTransaction(transaction: Transaction) {
|
async function verifyTransaction(transaction: Transaction) {
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ jobs:
|
|||||||
|
|
||||||
# Scan for secrets
|
# Scan for secrets
|
||||||
- name: Secret scanning
|
- name: Secret scanning
|
||||||
uses: trufflesecurity/trufflehog@main
|
uses: trufflesecurity/trufflehog@6c05c4a00b91aa542267d8e32a8254774799d68d
|
||||||
|
|
||||||
# Dependency audit
|
# Dependency audit
|
||||||
- name: Audit dependencies
|
- name: Audit dependencies
|
||||||
@@ -215,7 +215,7 @@ jobs:
|
|||||||
// package.json - Use lock files and integrity checks
|
// package.json - Use lock files and integrity checks
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "npm ci", // Use ci for reproducible builds
|
"deps:install": "npm ci", // Use ci for reproducible builds
|
||||||
"audit": "npm audit --audit-level=moderate",
|
"audit": "npm audit --audit-level=moderate",
|
||||||
"check": "npm outdated"
|
"check": "npm outdated"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,12 +45,14 @@ origin: ECC
|
|||||||
"hooks": {
|
"hooks": {
|
||||||
"PreToolUse": [
|
"PreToolUse": [
|
||||||
{
|
{
|
||||||
"matcher": "Edit",
|
"matcher": "Edit|Write",
|
||||||
"hooks": [{ "type": "command", "command": "node ~/.claude/skills/strategic-compact/suggest-compact.js" }]
|
"hooks": [
|
||||||
},
|
{
|
||||||
{
|
"type": "command",
|
||||||
"matcher": "Write",
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/run-with-flags.js\" \"pre:edit-write:suggest-compact\" \"scripts/hooks/suggest-compact.js\" \"standard,strict\""
|
||||||
"hooks": [{ "type": "command", "command": "node ~/.claude/skills/strategic-compact/suggest-compact.js" }]
|
}
|
||||||
|
],
|
||||||
|
"description": "Suggest manual compaction at logical intervals"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,11 +191,9 @@ test('user can search and filter markets', async ({ page }) => {
|
|||||||
// Search for markets
|
// Search for markets
|
||||||
await page.fill('input[placeholder="Search markets"]', 'election')
|
await page.fill('input[placeholder="Search markets"]', 'election')
|
||||||
|
|
||||||
// Wait for debounce and results
|
// Wait for stable search results instead of sleeping
|
||||||
await page.waitForTimeout(600)
|
|
||||||
|
|
||||||
// Verify search results displayed
|
|
||||||
const results = page.locator('[data-testid="market-card"]')
|
const results = page.locator('[data-testid="market-card"]')
|
||||||
|
await expect(results.first()).toBeVisible({ timeout: 5000 })
|
||||||
await expect(results).toHaveCount(5, { timeout: 5000 })
|
await expect(results).toHaveCount(5, { timeout: 5000 })
|
||||||
|
|
||||||
// Verify results contain search term
|
// Verify results contain search term
|
||||||
@@ -300,7 +298,7 @@ npm run test:coverage
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"jest": {
|
"jest": {
|
||||||
"coverageThresholds": {
|
"coverageThreshold": {
|
||||||
"global": {
|
"global": {
|
||||||
"branches": 80,
|
"branches": 80,
|
||||||
"functions": 80,
|
"functions": 80,
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ grep -rn "console.log" --include="*.ts" --include="*.tsx" src/ 2>/dev/null | hea
|
|||||||
```bash
|
```bash
|
||||||
# Show what changed
|
# Show what changed
|
||||||
git diff --stat
|
git diff --stat
|
||||||
git diff HEAD~1 --name-only
|
git diff --name-only
|
||||||
|
git diff --cached --name-only
|
||||||
```
|
```
|
||||||
|
|
||||||
각 변경된 파일에서 다음을 검토합니다:
|
각 변경된 파일에서 다음을 검토합니다:
|
||||||
|
|||||||
Reference in New Issue
Block a user