@choblue/claude-code-toolkit 1.1.2 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/.project-map-cache +1 -0
- package/.claude/CLAUDE.md +12 -0
- package/.claude/PROJECT_MAP.md +43 -0
- package/.claude/agents/explore.md +7 -0
- package/.claude/hooks/project-map-detector.sh +71 -0
- package/.claude/hooks/skill-detector.sh +89 -0
- package/.claude/hooks/skill-keywords.conf +19 -0
- package/.claude/scripts/generate-project-map.sh +264 -0
- package/.claude/settings.json +8 -0
- package/.claude/skills/TailwindCSS/SKILL.md +23 -5
- package/README.md +35 -4
- package/install.sh +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
03a6f698d5246e33e211ef5f209fc9471c01cf50
|
package/.claude/CLAUDE.md
CHANGED
|
@@ -9,6 +9,12 @@ Claude는 작업 시작 전 반드시 이 규칙을 따라야 한다.
|
|
|
9
9
|
|
|
10
10
|
Main Agent의 Context Window는 제한적이다. 반드시 서브에이전트에 위임하라.
|
|
11
11
|
|
|
12
|
+
### PROJECT_MAP.md 활용
|
|
13
|
+
- `.claude/PROJECT_MAP.md`가 있으면 explore 전에 반드시 먼저 Read하라
|
|
14
|
+
- 단순 구조 확인은 explore 생략 가능
|
|
15
|
+
- 세부 코드 탐색이 필요할 때만 explore 위임
|
|
16
|
+
- 갱신: `.claude/scripts/generate-project-map.sh` 실행
|
|
17
|
+
|
|
12
18
|
### 위임 규칙
|
|
13
19
|
- **탐색/검색 작업** → `explore` 에이전트 (haiku) 위임
|
|
14
20
|
- **코드 구현** → `code-writer-fe` 또는 `code-writer-be` 에이전트 (opus) 위임
|
|
@@ -85,8 +91,14 @@ Main Agent의 Context Window는 제한적이다. 반드시 서브에이전트에
|
|
|
85
91
|
- `backend.md` - NestJS 테스트 규칙
|
|
86
92
|
- `.claude/skills/Git/SKILL.md` - Git 커밋/PR/브랜치 규칙
|
|
87
93
|
|
|
94
|
+
### Scripts
|
|
95
|
+
- `.claude/scripts/generate-project-map.sh` - PROJECT_MAP.md 자동 생성
|
|
96
|
+
|
|
88
97
|
### Hooks
|
|
89
98
|
- `.claude/hooks/quality-gate.sh` - 품질 체크 프로토콜
|
|
99
|
+
- `.claude/hooks/skill-detector.sh` - 프롬프트 기반 스킬 자동 추천
|
|
100
|
+
- `.claude/hooks/skill-keywords.conf` - 스킬별 키워드 매핑 설정
|
|
101
|
+
- `.claude/hooks/project-map-detector.sh` - 프로젝트 구조 변경 감지
|
|
90
102
|
|
|
91
103
|
---
|
|
92
104
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# PROJECT MAP
|
|
2
|
+
|
|
3
|
+
> Auto-generated by `generate-project-map.sh`
|
|
4
|
+
> 이 파일은 explore 에이전트의 사전 캐시로 사용된다. 직접 수정하지 말 것.
|
|
5
|
+
|
|
6
|
+
## 프로젝트 정보
|
|
7
|
+
|
|
8
|
+
- **이름**: @choblue/claude-code-toolkit
|
|
9
|
+
- **브랜치**: main
|
|
10
|
+
- **최근 커밋**: 03a6f69 DOCS: skill-detector hook 문서 반영 및 CHANGELOG 추가
|
|
11
|
+
- **생성 시간**: 2026-02-25 15:12:39
|
|
12
|
+
- **총 파일 수**: 41
|
|
13
|
+
|
|
14
|
+
## 디렉토리 구조
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
.
|
|
18
|
+
./.claude
|
|
19
|
+
./.claude/agents
|
|
20
|
+
./.claude/hooks
|
|
21
|
+
./.claude/scripts
|
|
22
|
+
./.claude/skills
|
|
23
|
+
./.claude/skills/Coding
|
|
24
|
+
./.claude/skills/Git
|
|
25
|
+
./.claude/skills/NextJS
|
|
26
|
+
./.claude/skills/React
|
|
27
|
+
./.claude/skills/ReactHookForm
|
|
28
|
+
./.claude/skills/TDD
|
|
29
|
+
./.claude/skills/TailwindCSS
|
|
30
|
+
./.claude/skills/TanStackQuery
|
|
31
|
+
./.claude/skills/TypeORM
|
|
32
|
+
./.claude/skills/TypeScript
|
|
33
|
+
./.claude/skills/Zustand
|
|
34
|
+
./bin
|
|
35
|
+
./skills
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 주요 파일
|
|
39
|
+
|
|
40
|
+
### 설정 파일
|
|
41
|
+
|
|
42
|
+
- `package.json`
|
|
43
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# project-map-detector.sh - PROJECT_MAP.md 구조 변경 감지 hook
|
|
3
|
+
# UserPromptSubmit hook으로 등록하여 매 프롬프트마다 실행된다.
|
|
4
|
+
# 구조 변경 감지 시 PROJECT_MAP.md 갱신을 안내한다.
|
|
5
|
+
|
|
6
|
+
# stdin으로 전달된 JSON을 읽어서 버린다 (hook 프로토콜 준수)
|
|
7
|
+
read -r INPUT 2>/dev/null || true
|
|
8
|
+
|
|
9
|
+
CLAUDE_DIR=".claude"
|
|
10
|
+
MAP_FILE="$CLAUDE_DIR/PROJECT_MAP.md"
|
|
11
|
+
CACHE_FILE="$CLAUDE_DIR/.project-map-cache"
|
|
12
|
+
|
|
13
|
+
# PROJECT_MAP.md 없으면 생성 안내
|
|
14
|
+
if [ ! -f "$MAP_FILE" ]; then
|
|
15
|
+
echo "[PROJECT_MAP] PROJECT_MAP.md가 없습니다. 다음 명령으로 생성하세요:"
|
|
16
|
+
echo " .claude/scripts/generate-project-map.sh"
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Git repo 아니면 조용히 종료
|
|
21
|
+
if ! git rev-parse --is-inside-work-tree &>/dev/null 2>&1; then
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# 현재 HEAD 해시
|
|
26
|
+
CURRENT_HEAD="$(git rev-parse HEAD 2>/dev/null)" || exit 0
|
|
27
|
+
|
|
28
|
+
# 캐시 비교
|
|
29
|
+
if [ -f "$CACHE_FILE" ]; then
|
|
30
|
+
CACHED_HEAD="$(cat "$CACHE_FILE" 2>/dev/null)"
|
|
31
|
+
if [ "$CURRENT_HEAD" = "$CACHED_HEAD" ]; then
|
|
32
|
+
# 캐시 히트 → 변경 없음
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# 캐시 미스 → git diff로 구조 변경 확인
|
|
38
|
+
# 이전 캐시가 없으면 현재 HEAD만 캐시하고 종료 (첫 실행)
|
|
39
|
+
if [ ! -f "$CACHE_FILE" ]; then
|
|
40
|
+
printf '%s' "$CURRENT_HEAD" > "$CACHE_FILE"
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
CACHED_HEAD="$(cat "$CACHE_FILE" 2>/dev/null)"
|
|
45
|
+
|
|
46
|
+
# 구조 변경 패턴 필터링
|
|
47
|
+
STRUCTURE_CHANGED=false
|
|
48
|
+
|
|
49
|
+
# 파일 추가/삭제/이동 감지 (A, D, R)
|
|
50
|
+
ADD_DEL="$(git diff --name-status "$CACHED_HEAD" "$CURRENT_HEAD" 2>/dev/null | grep -E '^[ADR]' | head -20)" || true
|
|
51
|
+
|
|
52
|
+
if [ -n "$ADD_DEL" ]; then
|
|
53
|
+
STRUCTURE_CHANGED=true
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# 설정 파일 변경 감지
|
|
57
|
+
CONFIG_PATTERN="package\.json$|tsconfig\.json$|\.eslintrc|next\.config|vite\.config|nest-cli\.json|docker-compose"
|
|
58
|
+
CONFIG_CHANGED="$(git diff --name-only "$CACHED_HEAD" "$CURRENT_HEAD" 2>/dev/null | grep -E "$CONFIG_PATTERN" | head -5)" || true
|
|
59
|
+
|
|
60
|
+
if [ -n "$CONFIG_CHANGED" ]; then
|
|
61
|
+
STRUCTURE_CHANGED=true
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# 캐시 업데이트
|
|
65
|
+
printf '%s' "$CURRENT_HEAD" > "$CACHE_FILE"
|
|
66
|
+
|
|
67
|
+
# 변경 감지 시 안내
|
|
68
|
+
if [ "$STRUCTURE_CHANGED" = true ]; then
|
|
69
|
+
echo "[PROJECT_MAP] 프로젝트 구조 변경이 감지되었습니다. PROJECT_MAP.md 갱신을 권장합니다:"
|
|
70
|
+
echo " .claude/scripts/generate-project-map.sh"
|
|
71
|
+
fi
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Skill Detector Hook
|
|
3
|
+
# 사용자 프롬프트를 분석하여 관련 스킬을 자동 추천한다.
|
|
4
|
+
# UserPromptSubmit hook으로 등록하여 사용한다.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
CONF_FILE="${SCRIPT_DIR}/skill-keywords.conf"
|
|
10
|
+
|
|
11
|
+
if [[ ! -f "$CONF_FILE" ]]; then
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# stdin에서 프롬프트 읽기 (JSON 형태: {"prompt": "..."})
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
PROMPT=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('prompt',''))" 2>/dev/null || echo "$INPUT")
|
|
18
|
+
|
|
19
|
+
if [[ -z "$PROMPT" ]]; then
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# 소문자 변환
|
|
24
|
+
PROMPT_LOWER=$(echo "$PROMPT" | tr '[:upper:]' '[:lower:]')
|
|
25
|
+
|
|
26
|
+
# 스킬별 점수 계산
|
|
27
|
+
declare -a SKILL_NAMES=()
|
|
28
|
+
declare -a SKILL_COMMANDS=()
|
|
29
|
+
declare -a SKILL_SCORES=()
|
|
30
|
+
|
|
31
|
+
while IFS= read -r line; do
|
|
32
|
+
# 빈 줄, 주석 건너뛰기
|
|
33
|
+
[[ -z "$line" || "$line" == \#* ]] && continue
|
|
34
|
+
|
|
35
|
+
SKILL_NAME=$(echo "$line" | cut -d'|' -f1)
|
|
36
|
+
SKILL_CMD=$(echo "$line" | cut -d'|' -f2)
|
|
37
|
+
KEYWORDS_STR=$(echo "$line" | cut -d'|' -f3)
|
|
38
|
+
|
|
39
|
+
SCORE=0
|
|
40
|
+
for keyword in $KEYWORDS_STR; do
|
|
41
|
+
if [[ "$PROMPT_LOWER" == *"$keyword"* ]]; then
|
|
42
|
+
SCORE=$((SCORE + 1))
|
|
43
|
+
fi
|
|
44
|
+
done
|
|
45
|
+
|
|
46
|
+
if ((SCORE > 0)); then
|
|
47
|
+
SKILL_NAMES+=("$SKILL_NAME")
|
|
48
|
+
SKILL_COMMANDS+=("$SKILL_CMD")
|
|
49
|
+
SKILL_SCORES+=("$SCORE")
|
|
50
|
+
fi
|
|
51
|
+
done < "$CONF_FILE"
|
|
52
|
+
|
|
53
|
+
# 매칭된 스킬이 없으면 침묵
|
|
54
|
+
if ((${#SKILL_NAMES[@]} == 0)); then
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# 점수 기준 내림차순 정렬 (상위 5개)
|
|
59
|
+
SORTED_INDICES=()
|
|
60
|
+
for i in "${!SKILL_SCORES[@]}"; do
|
|
61
|
+
SORTED_INDICES+=("$i")
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
# 버블 정렬 (점수 내림차순)
|
|
65
|
+
for ((i = 0; i < ${#SORTED_INDICES[@]}; i++)); do
|
|
66
|
+
for ((j = i + 1; j < ${#SORTED_INDICES[@]}; j++)); do
|
|
67
|
+
idx_i=${SORTED_INDICES[$i]}
|
|
68
|
+
idx_j=${SORTED_INDICES[$j]}
|
|
69
|
+
if ((SKILL_SCORES[idx_j] > SKILL_SCORES[idx_i])); then
|
|
70
|
+
SORTED_INDICES[$i]=$idx_j
|
|
71
|
+
SORTED_INDICES[$j]=$idx_i
|
|
72
|
+
fi
|
|
73
|
+
done
|
|
74
|
+
done
|
|
75
|
+
|
|
76
|
+
# 상위 5개만
|
|
77
|
+
MAX=5
|
|
78
|
+
if ((${#SORTED_INDICES[@]} < MAX)); then
|
|
79
|
+
MAX=${#SORTED_INDICES[@]}
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# 출력
|
|
83
|
+
echo "[Skill Detector]"
|
|
84
|
+
echo "프롬프트 분석 결과, 다음 스킬을 반드시 참조하라:"
|
|
85
|
+
for ((i = 0; i < MAX; i++)); do
|
|
86
|
+
idx=${SORTED_INDICES[$i]}
|
|
87
|
+
echo "- ${SKILL_NAMES[$idx]}: /skill ${SKILL_COMMANDS[$idx]}"
|
|
88
|
+
done
|
|
89
|
+
echo "구현 전에 위 스킬을 로드하여 패턴을 따르라."
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Skill Detector 키워드 매핑 설정
|
|
2
|
+
# FORMAT: SKILL_NAME|SKILL_COMMAND|KEYWORD1 KEYWORD2 ...
|
|
3
|
+
# - SKILL_NAME: 표시용 이름
|
|
4
|
+
# - SKILL_COMMAND: /skill 명령에 사용할 이름
|
|
5
|
+
# - KEYWORDS: 공백으로 구분된 매칭 키워드 (소문자)
|
|
6
|
+
#
|
|
7
|
+
# 스킬 추가 시 이 파일만 수정하면 됩니다.
|
|
8
|
+
|
|
9
|
+
React|React|react 리액트 컴포넌트 component hook usestate useeffect usememo usecallback useref props jsx tsx 훅 커스텀훅
|
|
10
|
+
NextJS|NextJS|next.js nextjs 넥스트 app router server component route handler server action ssr ssg isr middleware layout page
|
|
11
|
+
TailwindCSS|TailwindCSS|tailwind tailwindcss 테일윈드 스타일 className cn cva variant 반응형 다크모드 dark mode
|
|
12
|
+
TanStackQuery|TanStackQuery|tanstack query react-query 리액트쿼리 usequery usemutation querykey prefetch invalidate 서버상태
|
|
13
|
+
Zustand|Zustand|zustand 주스탄드 store 스토어 전역상태 global state slice persist devtools
|
|
14
|
+
ReactHookForm|ReactHookForm|react-hook-form 리액트훅폼 useform register handlesubmit controller zod schema 폼 form validation 검증
|
|
15
|
+
TypeScript|TypeScript|typescript 타입스크립트 타입 type interface generic 제네릭 유틸리티타입 타입가드 type guard
|
|
16
|
+
TypeORM|TypeORM|typeorm 타입오알엠 entity repository querybuilder migration 마이그레이션 relation 트랜잭션 transaction
|
|
17
|
+
Coding|Coding|리팩토링 refactor 클린코드 clean code 네이밍 naming 에러처리 error handling 코드품질
|
|
18
|
+
TDD|TDD|tdd 테스트 test jest vitest 단위테스트 unit test e2e 통합테스트 integration test mock stub spy
|
|
19
|
+
Git|Git|git 커밋 commit 브랜치 branch pr pull request merge rebase
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# generate-project-map.sh - PROJECT_MAP.md를 자동 생성한다
|
|
3
|
+
# Usage: .claude/scripts/generate-project-map.sh
|
|
4
|
+
# 프로젝트 루트에서 실행하면 .claude/PROJECT_MAP.md를 생성한다.
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
# .claude 디렉토리 확인
|
|
9
|
+
CLAUDE_DIR=""
|
|
10
|
+
if [ -d ".claude" ]; then
|
|
11
|
+
CLAUDE_DIR=".claude"
|
|
12
|
+
elif [ -d "$(git rev-parse --show-toplevel 2>/dev/null)/.claude" ]; then
|
|
13
|
+
CLAUDE_DIR="$(git rev-parse --show-toplevel)/.claude"
|
|
14
|
+
else
|
|
15
|
+
echo "Error: .claude/ 디렉토리를 찾을 수 없습니다." >&2
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
OUTPUT_FILE="$CLAUDE_DIR/PROJECT_MAP.md"
|
|
20
|
+
TEMP_FILE="$(mktemp)"
|
|
21
|
+
trap 'rm -f "$TEMP_FILE"' EXIT
|
|
22
|
+
|
|
23
|
+
# === JSON 파싱 헬퍼 ===
|
|
24
|
+
# python3 사용 가능하면 python3, 아니면 순수 grep/sed 폴백
|
|
25
|
+
json_get() {
|
|
26
|
+
local file="$1"
|
|
27
|
+
local key="$2"
|
|
28
|
+
if command -v python3 &>/dev/null; then
|
|
29
|
+
python3 -c "
|
|
30
|
+
import json, sys
|
|
31
|
+
try:
|
|
32
|
+
d = json.load(open('$file'))
|
|
33
|
+
val = d.get('$key', '')
|
|
34
|
+
if isinstance(val, str):
|
|
35
|
+
print(val)
|
|
36
|
+
elif isinstance(val, dict):
|
|
37
|
+
for k in val:
|
|
38
|
+
print(k)
|
|
39
|
+
elif isinstance(val, list):
|
|
40
|
+
for item in val:
|
|
41
|
+
print(item)
|
|
42
|
+
except:
|
|
43
|
+
pass
|
|
44
|
+
" 2>/dev/null
|
|
45
|
+
else
|
|
46
|
+
grep -o "\"$key\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" "$file" 2>/dev/null | head -1 | sed 's/.*: *"\(.*\)"/\1/'
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
json_get_scripts() {
|
|
51
|
+
local file="$1"
|
|
52
|
+
if command -v python3 &>/dev/null; then
|
|
53
|
+
python3 -c "
|
|
54
|
+
import json, sys
|
|
55
|
+
try:
|
|
56
|
+
d = json.load(open('$file'))
|
|
57
|
+
scripts = d.get('scripts', {})
|
|
58
|
+
for key in ['dev', 'build', 'test', 'start', 'lint']:
|
|
59
|
+
if key in scripts:
|
|
60
|
+
print(f'{key}: {scripts[key]}')
|
|
61
|
+
except:
|
|
62
|
+
pass
|
|
63
|
+
" 2>/dev/null
|
|
64
|
+
fi
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
json_get_deps() {
|
|
68
|
+
local file="$1"
|
|
69
|
+
if command -v python3 &>/dev/null; then
|
|
70
|
+
python3 -c "
|
|
71
|
+
import json, sys
|
|
72
|
+
try:
|
|
73
|
+
d = json.load(open('$file'))
|
|
74
|
+
deps = {}
|
|
75
|
+
deps.update(d.get('dependencies', {}))
|
|
76
|
+
deps.update(d.get('devDependencies', {}))
|
|
77
|
+
# 주요 프레임워크/라이브러리만 추출
|
|
78
|
+
keywords = [
|
|
79
|
+
'react', 'next', 'vue', 'nuxt', 'angular', 'svelte',
|
|
80
|
+
'nest', 'express', 'fastify', 'koa', 'hono',
|
|
81
|
+
'typeorm', 'prisma', 'drizzle', 'mongoose', 'sequelize',
|
|
82
|
+
'tailwind', 'styled-components', 'emotion',
|
|
83
|
+
'jest', 'vitest', 'mocha', 'cypress', 'playwright',
|
|
84
|
+
'typescript', 'webpack', 'vite', 'turbo', 'esbuild',
|
|
85
|
+
'zustand', 'redux', 'recoil', 'jotai', 'mobx',
|
|
86
|
+
'tanstack', 'react-query', 'swr', 'axios',
|
|
87
|
+
'zod', 'yup', 'joi', 'class-validator',
|
|
88
|
+
'react-hook-form', 'formik',
|
|
89
|
+
'storybook', 'eslint', 'prettier',
|
|
90
|
+
]
|
|
91
|
+
found = []
|
|
92
|
+
for dep, ver in sorted(deps.items()):
|
|
93
|
+
dep_lower = dep.lower()
|
|
94
|
+
for kw in keywords:
|
|
95
|
+
if kw in dep_lower:
|
|
96
|
+
found.append(f'{dep}@{ver}')
|
|
97
|
+
break
|
|
98
|
+
for item in found:
|
|
99
|
+
print(item)
|
|
100
|
+
except:
|
|
101
|
+
pass
|
|
102
|
+
" 2>/dev/null
|
|
103
|
+
fi
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# === 생성 시작 ===
|
|
107
|
+
{
|
|
108
|
+
echo "# PROJECT MAP"
|
|
109
|
+
echo ""
|
|
110
|
+
echo "> Auto-generated by \`generate-project-map.sh\`"
|
|
111
|
+
echo "> 이 파일은 explore 에이전트의 사전 캐시로 사용된다. 직접 수정하지 말 것."
|
|
112
|
+
echo ""
|
|
113
|
+
|
|
114
|
+
# --- 프로젝트 정보 ---
|
|
115
|
+
echo "## 프로젝트 정보"
|
|
116
|
+
echo ""
|
|
117
|
+
|
|
118
|
+
if [ -f "package.json" ]; then
|
|
119
|
+
local_name="$(json_get package.json name)"
|
|
120
|
+
if [ -n "$local_name" ]; then
|
|
121
|
+
echo "- **이름**: $local_name"
|
|
122
|
+
fi
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
if command -v git &>/dev/null && git rev-parse --is-inside-work-tree &>/dev/null 2>&1; then
|
|
126
|
+
echo "- **브랜치**: $(git branch --show-current 2>/dev/null || echo 'unknown')"
|
|
127
|
+
echo "- **최근 커밋**: $(git log -1 --format='%h %s' 2>/dev/null || echo 'unknown')"
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
echo "- **생성 시간**: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
131
|
+
|
|
132
|
+
# 총 파일 수 (소스 파일만)
|
|
133
|
+
total_files=0
|
|
134
|
+
if command -v git &>/dev/null && git rev-parse --is-inside-work-tree &>/dev/null 2>&1; then
|
|
135
|
+
total_files=$(git ls-files 2>/dev/null | wc -l | tr -d ' ')
|
|
136
|
+
else
|
|
137
|
+
total_files=$(find . -type f \
|
|
138
|
+
-not -path '*/node_modules/*' \
|
|
139
|
+
-not -path '*/.git/*' \
|
|
140
|
+
-not -path '*/.next/*' \
|
|
141
|
+
-not -path '*/dist/*' \
|
|
142
|
+
-not -path '*/build/*' \
|
|
143
|
+
-not -path '*/coverage/*' \
|
|
144
|
+
2>/dev/null | wc -l | tr -d ' ')
|
|
145
|
+
fi
|
|
146
|
+
echo "- **총 파일 수**: $total_files"
|
|
147
|
+
echo ""
|
|
148
|
+
|
|
149
|
+
# --- 기술 스택 ---
|
|
150
|
+
if [ -f "package.json" ]; then
|
|
151
|
+
deps="$(json_get_deps package.json)"
|
|
152
|
+
if [ -n "$deps" ]; then
|
|
153
|
+
echo "## 기술 스택"
|
|
154
|
+
echo ""
|
|
155
|
+
echo '```'
|
|
156
|
+
echo "$deps"
|
|
157
|
+
echo '```'
|
|
158
|
+
echo ""
|
|
159
|
+
fi
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
# --- 디렉토리 구조 ---
|
|
163
|
+
echo "## 디렉토리 구조"
|
|
164
|
+
echo ""
|
|
165
|
+
echo '```'
|
|
166
|
+
|
|
167
|
+
EXCLUDE_DIRS="node_modules|.git|.next|dist|build|coverage|.cache|.turbo|__pycache__|.venv|vendor"
|
|
168
|
+
|
|
169
|
+
find_dirs() {
|
|
170
|
+
find . -maxdepth 3 -type d \
|
|
171
|
+
\( -name node_modules -o -name .git -o -name .next -o -name dist \
|
|
172
|
+
-o -name build -o -name coverage -o -name .cache -o -name .turbo \
|
|
173
|
+
-o -name __pycache__ -o -name .venv -o -name vendor \
|
|
174
|
+
-o -name .idea -o -name .vscode \) -prune \
|
|
175
|
+
-o -type d -print 2>/dev/null | sort | head -80
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if command -v tree &>/dev/null; then
|
|
179
|
+
tree -d -L 3 -I "$EXCLUDE_DIRS|.idea|.vscode" --noreport 2>/dev/null || find_dirs
|
|
180
|
+
else
|
|
181
|
+
find_dirs
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
echo '```'
|
|
185
|
+
echo ""
|
|
186
|
+
|
|
187
|
+
# --- 주요 파일 ---
|
|
188
|
+
echo "## 주요 파일"
|
|
189
|
+
echo ""
|
|
190
|
+
|
|
191
|
+
# 설정 파일
|
|
192
|
+
config_files=""
|
|
193
|
+
for f in package.json tsconfig.json tsconfig.*.json \
|
|
194
|
+
.eslintrc* eslint.config.* .prettierrc* prettier.config.* \
|
|
195
|
+
next.config.* vite.config.* webpack.config.* \
|
|
196
|
+
nest-cli.json ormconfig.* \
|
|
197
|
+
docker-compose.yml Dockerfile \
|
|
198
|
+
.env.example .env.local.example; do
|
|
199
|
+
# glob 확장이 파일과 매치하는지 확인
|
|
200
|
+
for match in $f; do
|
|
201
|
+
if [ -f "$match" ]; then
|
|
202
|
+
config_files="$config_files$match\n"
|
|
203
|
+
fi
|
|
204
|
+
done
|
|
205
|
+
done
|
|
206
|
+
|
|
207
|
+
if [ -n "$config_files" ]; then
|
|
208
|
+
echo "### 설정 파일"
|
|
209
|
+
echo ""
|
|
210
|
+
printf '%b' "$config_files" | sort -u | while read -r cf; do
|
|
211
|
+
[ -n "$cf" ] && echo "- \`$cf\`"
|
|
212
|
+
done
|
|
213
|
+
echo ""
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
# 엔트리포인트
|
|
217
|
+
entry_files=""
|
|
218
|
+
for f in src/main.ts src/main.tsx src/index.ts src/index.tsx \
|
|
219
|
+
src/app.ts src/app.tsx src/App.tsx \
|
|
220
|
+
app/layout.tsx app/page.tsx \
|
|
221
|
+
src/app/layout.tsx src/app/page.tsx \
|
|
222
|
+
pages/_app.tsx pages/index.tsx \
|
|
223
|
+
src/pages/_app.tsx src/pages/index.tsx; do
|
|
224
|
+
if [ -f "$f" ]; then
|
|
225
|
+
entry_files="$entry_files$f\n"
|
|
226
|
+
fi
|
|
227
|
+
done
|
|
228
|
+
|
|
229
|
+
if [ -n "$entry_files" ]; then
|
|
230
|
+
echo "### 엔트리포인트"
|
|
231
|
+
echo ""
|
|
232
|
+
printf '%b' "$entry_files" | while read -r ef; do
|
|
233
|
+
[ -n "$ef" ] && echo "- \`$ef\`"
|
|
234
|
+
done
|
|
235
|
+
echo ""
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
# --- 빌드 명령 ---
|
|
239
|
+
if [ -f "package.json" ]; then
|
|
240
|
+
scripts="$(json_get_scripts package.json)"
|
|
241
|
+
if [ -n "$scripts" ]; then
|
|
242
|
+
echo "## 빌드 명령"
|
|
243
|
+
echo ""
|
|
244
|
+
echo '```'
|
|
245
|
+
echo "$scripts"
|
|
246
|
+
echo '```'
|
|
247
|
+
echo ""
|
|
248
|
+
fi
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
} > "$TEMP_FILE"
|
|
252
|
+
|
|
253
|
+
# 파일 크기 확인 (20KB 제한)
|
|
254
|
+
file_size=$(wc -c < "$TEMP_FILE" | tr -d ' ')
|
|
255
|
+
if [ "$file_size" -gt 20480 ]; then
|
|
256
|
+
echo "Warning: PROJECT_MAP.md가 20KB를 초과합니다 (${file_size}B). 잘라냅니다." >&2
|
|
257
|
+
head -c 20480 "$TEMP_FILE" > "$OUTPUT_FILE"
|
|
258
|
+
echo "" >> "$OUTPUT_FILE"
|
|
259
|
+
echo "> [truncated - 20KB 제한 초과]" >> "$OUTPUT_FILE"
|
|
260
|
+
else
|
|
261
|
+
cp "$TEMP_FILE" "$OUTPUT_FILE"
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
echo "PROJECT_MAP.md 생성 완료: $OUTPUT_FILE (${file_size}B)"
|
package/.claude/settings.json
CHANGED
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
{
|
|
8
8
|
"type": "command",
|
|
9
9
|
"command": ".claude/hooks/quality-gate.sh"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"type": "command",
|
|
13
|
+
"command": ".claude/hooks/skill-detector.sh"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"type": "command",
|
|
17
|
+
"command": ".claude/hooks/project-map-detector.sh"
|
|
10
18
|
}
|
|
11
19
|
]
|
|
12
20
|
}
|
|
@@ -26,11 +26,23 @@ React 규칙은 `../React/SKILL.md`, 공통 원칙은 `../Coding/SKILL.md`를
|
|
|
26
26
|
|
|
27
27
|
### 클래스 조합
|
|
28
28
|
- 관련 있는 클래스를 논리적 그룹으로 정렬한다
|
|
29
|
-
- 권장 순서: 레이아웃
|
|
29
|
+
- 권장 순서: 레이아웃 → 크기 → 간격 → 타이포그래피 → 색상 → 효과
|
|
30
|
+
- **클래스가 길어지면 관심사별로 줄바꿈하여 가독성을 높인다**
|
|
30
31
|
|
|
31
32
|
```typescript
|
|
32
|
-
//
|
|
33
|
+
// Bad - 한 줄로 길게 나열 (화면 벗어남, 어떤 CSS가 있는지 파악 어려움)
|
|
33
34
|
<button className="flex items-center justify-center w-full h-10 px-4 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700">
|
|
35
|
+
|
|
36
|
+
// Good - 관심사별 줄바꿈
|
|
37
|
+
<button
|
|
38
|
+
className={cn(
|
|
39
|
+
'flex items-center justify-center', // 레이아웃
|
|
40
|
+
'w-full h-10 px-4', // 크기/간격
|
|
41
|
+
'text-sm font-medium text-white', // 타이포그래피
|
|
42
|
+
'bg-blue-600 rounded-lg', // 배경/모양
|
|
43
|
+
'hover:bg-blue-700', // 상태
|
|
44
|
+
)}
|
|
45
|
+
>
|
|
34
46
|
버튼
|
|
35
47
|
</button>
|
|
36
48
|
```
|
|
@@ -227,7 +239,8 @@ export function Button({ variant = 'primary', size = 'md', className, children }
|
|
|
227
239
|
<button
|
|
228
240
|
className={cn(
|
|
229
241
|
// 기본 스타일
|
|
230
|
-
'inline-flex items-center justify-center
|
|
242
|
+
'inline-flex items-center justify-center',
|
|
243
|
+
'rounded-lg font-medium transition-colors',
|
|
231
244
|
// variant
|
|
232
245
|
{
|
|
233
246
|
'bg-blue-600 text-white hover:bg-blue-700': variant === 'primary',
|
|
@@ -261,7 +274,12 @@ import { cn } from '@/lib/utils';
|
|
|
261
274
|
|
|
262
275
|
const buttonVariants = cva(
|
|
263
276
|
// 기본 스타일
|
|
264
|
-
|
|
277
|
+
[
|
|
278
|
+
'inline-flex items-center justify-center', // 레이아웃
|
|
279
|
+
'rounded-lg font-medium', // 모양/타이포
|
|
280
|
+
'transition-colors', // 효과
|
|
281
|
+
'disabled:pointer-events-none disabled:opacity-50', // 상태
|
|
282
|
+
],
|
|
265
283
|
{
|
|
266
284
|
variants: {
|
|
267
285
|
variant: {
|
|
@@ -370,4 +388,4 @@ const colorClasses = {
|
|
|
370
388
|
} as const;
|
|
371
389
|
|
|
372
390
|
<div className={colorClasses[color]}>
|
|
373
|
-
```
|
|
391
|
+
```
|
package/README.md
CHANGED
|
@@ -49,8 +49,13 @@
|
|
|
49
49
|
│ │ └── backend.md ← NestJS 테스트 규칙
|
|
50
50
|
│ └── Git/
|
|
51
51
|
│ └── SKILL.md ← 커밋/PR/브랜치 규칙
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
├── hooks/
|
|
53
|
+
│ ├── quality-gate.sh ← 품질 체크 프로토콜
|
|
54
|
+
│ ├── skill-detector.sh ← 프롬프트 기반 스킬 자동 추천
|
|
55
|
+
│ ├── skill-keywords.conf ← 스킬별 키워드 매핑 설정
|
|
56
|
+
│ └── project-map-detector.sh ← 프로젝트 구조 변경 감지
|
|
57
|
+
└── scripts/
|
|
58
|
+
└── generate-project-map.sh ← PROJECT_MAP.md 자동 생성
|
|
54
59
|
```
|
|
55
60
|
|
|
56
61
|
## 설치
|
|
@@ -131,9 +136,35 @@ Main Agent는 직접 코드를 작성하거나 탐색하지 않고, 전문 서
|
|
|
131
136
|
| test-writer | opus | TDD 테스트 작성/실행 |
|
|
132
137
|
| git-manager | sonnet | 커밋, 브랜치, PR |
|
|
133
138
|
|
|
134
|
-
###
|
|
139
|
+
### Hooks
|
|
135
140
|
|
|
136
|
-
매 프롬프트마다
|
|
141
|
+
매 프롬프트마다 UserPromptSubmit hook이 실행된다.
|
|
142
|
+
|
|
143
|
+
| Hook | 역할 |
|
|
144
|
+
|------|------|
|
|
145
|
+
| quality-gate.sh | 적절한 에이전트/스킬 활용을 상기시킨다 |
|
|
146
|
+
| skill-detector.sh | 프롬프트 키워드를 분석하여 관련 스킬을 자동 추천한다 |
|
|
147
|
+
| project-map-detector.sh | 프로젝트 구조 변경을 감지하여 PROJECT_MAP.md 갱신을 안내한다 |
|
|
148
|
+
|
|
149
|
+
`skill-keywords.conf`에서 스킬별 키워드를 관리하며, 스킬 추가 시 conf 파일만 수정하면 된다.
|
|
150
|
+
|
|
151
|
+
### PROJECT_MAP.md
|
|
152
|
+
|
|
153
|
+
explore 에이전트가 매번 코드베이스를 재탐색하는 비용을 줄이기 위해, 프로젝트 구조를 캐싱한다.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# PROJECT_MAP.md 생성
|
|
157
|
+
.claude/scripts/generate-project-map.sh
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
생성되는 내용:
|
|
161
|
+
- 프로젝트 정보 (이름, 브랜치, 최근 커밋)
|
|
162
|
+
- 기술 스택 (package.json에서 자동 감지)
|
|
163
|
+
- 디렉토리 구조 (3레벨 깊이)
|
|
164
|
+
- 주요 파일 (설정 파일, 엔트리포인트)
|
|
165
|
+
- 빌드 명령 (dev/build/test/start)
|
|
166
|
+
|
|
167
|
+
`project-map-detector.sh` hook이 구조 변경(파일 추가/삭제, 설정 파일 변경)을 감지하면 갱신을 안내한다.
|
|
137
168
|
|
|
138
169
|
## 프로젝트별 커스터마이징
|
|
139
170
|
|
package/install.sh
CHANGED
|
@@ -242,6 +242,10 @@ copy_common() {
|
|
|
242
242
|
# hooks
|
|
243
243
|
copy_dir "hooks"
|
|
244
244
|
chmod +x "$TARGET_DIR/hooks/"*.sh
|
|
245
|
+
|
|
246
|
+
# scripts
|
|
247
|
+
copy_dir "scripts"
|
|
248
|
+
chmod +x "$TARGET_DIR/scripts/"*.sh
|
|
245
249
|
}
|
|
246
250
|
|
|
247
251
|
# FE (프론트엔드)
|
|
@@ -330,6 +334,7 @@ echo " $TARGET_DIR/settings.json"
|
|
|
330
334
|
echo " $TARGET_DIR/agents/"
|
|
331
335
|
echo " $TARGET_DIR/skills/"
|
|
332
336
|
echo " $TARGET_DIR/hooks/"
|
|
337
|
+
echo " $TARGET_DIR/scripts/"
|
|
333
338
|
echo ""
|
|
334
339
|
if [ "$INSTALL_MODE" = "global" ]; then
|
|
335
340
|
echo "이제 어떤 프로젝트에서든 Claude Code를 실행하면 자동으로 적용됩니다."
|
|
@@ -337,3 +342,6 @@ else
|
|
|
337
342
|
echo "현재 프로젝트($(pwd))에서 Claude Code를 실행하면 자동으로 적용됩니다."
|
|
338
343
|
echo "다른 프로젝트에도 적용하려면 --global 옵션을 사용하세요."
|
|
339
344
|
fi
|
|
345
|
+
echo ""
|
|
346
|
+
echo "PROJECT_MAP.md를 생성하면 explore 에이전트가 더 빠르게 동작합니다:"
|
|
347
|
+
echo " $TARGET_DIR/scripts/generate-project-map.sh"
|