@kood/claude-code 0.5.10 → 0.6.0

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.
Files changed (36) hide show
  1. package/dist/index.js +117 -67
  2. package/package.json +1 -1
  3. package/templates/.claude/agents/build-fixer.md +371 -0
  4. package/templates/.claude/agents/critic.md +223 -0
  5. package/templates/.claude/agents/deep-executor.md +320 -0
  6. package/templates/.claude/agents/git-operator.md +15 -0
  7. package/templates/.claude/agents/planner.md +11 -7
  8. package/templates/.claude/agents/qa-tester.md +488 -0
  9. package/templates/.claude/agents/researcher.md +189 -0
  10. package/templates/.claude/agents/scientist.md +544 -0
  11. package/templates/.claude/agents/security-reviewer.md +549 -0
  12. package/templates/.claude/agents/tdd-guide.md +413 -0
  13. package/templates/.claude/agents/vision.md +165 -0
  14. package/templates/.claude/commands/pre-deploy.md +79 -2
  15. package/templates/.claude/instructions/agent-patterns/model-routing.md +2 -2
  16. package/templates/.claude/skills/brainstorm/SKILL.md +889 -0
  17. package/templates/.claude/skills/bug-fix/SKILL.md +69 -0
  18. package/templates/.claude/skills/crawler/SKILL.md +156 -0
  19. package/templates/.claude/skills/crawler/references/anti-bot-checklist.md +162 -0
  20. package/templates/.claude/skills/crawler/references/code-templates.md +119 -0
  21. package/templates/.claude/skills/crawler/references/crawling-patterns.md +167 -0
  22. package/templates/.claude/skills/crawler/references/document-templates.md +147 -0
  23. package/templates/.claude/skills/crawler/references/network-crawling.md +141 -0
  24. package/templates/.claude/skills/crawler/references/playwriter-commands.md +172 -0
  25. package/templates/.claude/skills/crawler/references/pre-crawl-checklist.md +221 -0
  26. package/templates/.claude/skills/crawler/references/selector-strategies.md +140 -0
  27. package/templates/.claude/skills/execute/SKILL.md +5 -0
  28. package/templates/.claude/skills/feedback/SKILL.md +570 -0
  29. package/templates/.claude/skills/figma-to-code/SKILL.md +1 -0
  30. package/templates/.claude/skills/global-uiux-design/SKILL.md +1 -0
  31. package/templates/.claude/skills/korea-uiux-design/SKILL.md +1 -0
  32. package/templates/.claude/skills/nextjs-react-best-practices/SKILL.md +1 -0
  33. package/templates/.claude/skills/plan/SKILL.md +44 -0
  34. package/templates/.claude/skills/ralph/SKILL.md +16 -18
  35. package/templates/.claude/skills/refactor/SKILL.md +19 -0
  36. package/templates/.claude/skills/tanstack-start-react-best-practices/SKILL.md +1 -0
@@ -0,0 +1,147 @@
1
+ # 분석 결과 문서 템플릿
2
+
3
+ > `.claude/crawler/[사이트명]/` 폴더 구조
4
+
5
+ ---
6
+
7
+ ## 폴더 구조
8
+
9
+ ```
10
+ .claude/crawler/example-com/
11
+ ├── ANALYSIS.md # 사이트 구조
12
+ ├── SELECTORS.md # DOM selector
13
+ ├── API.md # API endpoint
14
+ ├── NETWORK.md # 인증 정보
15
+ └── CRAWLER.ts # 생성 코드
16
+ ```
17
+
18
+ ---
19
+
20
+ ## ANALYSIS.md
21
+
22
+ ```markdown
23
+ # [사이트명] 크롤링 분석
24
+
25
+ 생성: {{TIMESTAMP}}
26
+ URL: {{BASE_URL}}
27
+
28
+ ## 페이지 타입
29
+
30
+ | 타입 | URL | 비고 |
31
+ |------|-----|------|
32
+ | 목록 | /items | |
33
+ | 상세 | /items/[id] | |
34
+ | 검색 | /search?q= | |
35
+
36
+ ## 데이터 로딩
37
+
38
+ - [ ] SSR
39
+ - [ ] CSR (API 기반)
40
+ - [ ] 무한 스크롤
41
+ - [ ] 페이지네이션
42
+
43
+ ## 인증
44
+
45
+ - [ ] 공개
46
+ - [ ] 로그인 필요
47
+ - [ ] API 키
48
+
49
+ ## 발견사항
50
+
51
+ [특이사항, 제약, 주의점]
52
+ ```
53
+
54
+ ---
55
+
56
+ ## SELECTORS.md
57
+
58
+ ```markdown
59
+ # [사이트명] Selector
60
+
61
+ ## 목록 페이지
62
+
63
+ | 요소 | Selector | 비고 |
64
+ |------|----------|------|
65
+ | 컨테이너 | `.item-list` | |
66
+ | 카드 | `.item-card` | |
67
+ | 제목 | `.item-card h2` | |
68
+ | 링크 | `.item-card a` | |
69
+ | 다음 | `button[aria-label="Next"]` | |
70
+
71
+ ## aria-ref 매핑
72
+
73
+ | ref | Selector | 설명 |
74
+ |-----|----------|------|
75
+ | e14 | `getByRole('button', { name: 'Load more' })` | 더보기 |
76
+ ```
77
+
78
+ ---
79
+
80
+ ## API.md
81
+
82
+ ```markdown
83
+ # [사이트명] API
84
+
85
+ ## GET /api/items
86
+
87
+ **Parameters:**
88
+
89
+ | 파라미터 | 타입 | 설명 |
90
+ |----------|------|------|
91
+ | page | number | 페이지 |
92
+ | limit | number | 개수 |
93
+
94
+ **Headers:**
95
+
96
+ ```json
97
+ { "Authorization": "Bearer ..." }
98
+ ```
99
+
100
+ **Response:**
101
+
102
+ ```typescript
103
+ interface Response {
104
+ data: Item[];
105
+ pagination: { page: number; total: number; hasNext: boolean };
106
+ }
107
+ ```
108
+
109
+ ## Rate Limiting
110
+
111
+ [제한사항]
112
+ ```
113
+
114
+ ---
115
+
116
+ ## NETWORK.md
117
+
118
+ ```markdown
119
+ # [사이트명] Network
120
+
121
+ ## 인증 정보
122
+
123
+ | 항목 | 값 | 만료 |
124
+ |------|-----|------|
125
+ | Cookie | `session=...` | 24h |
126
+ | Token | `Bearer ...` | 1h |
127
+
128
+ ## 필수 헤더
129
+
130
+ ```json
131
+ {
132
+ "Cookie": "...",
133
+ "Authorization": "Bearer ...",
134
+ "User-Agent": "Mozilla/5.0 ..."
135
+ }
136
+ ```
137
+
138
+ ## Rate Limit
139
+
140
+ - 제한: 60 req/min
141
+ - 딜레이: 1000ms
142
+
143
+ ## 봇 탐지
144
+
145
+ - [ ] Cloudflare
146
+ - [ ] reCAPTCHA
147
+ ```
@@ -0,0 +1,141 @@
1
+ # Network 분석
2
+
3
+ > Playwriter로 인증 정보 추출 → NETWORK.md 문서화
4
+
5
+ ---
6
+
7
+ <workflow>
8
+
9
+ ```text
10
+ 1. Playwriter로 페이지 탐방
11
+ 2. API 인터셉트 → 엔드포인트/헤더/쿠키/토큰 추출
12
+ 3. NETWORK.md에 문서화
13
+ 4. 크롤러 코드 작성 시 활용
14
+ ```
15
+
16
+ </workflow>
17
+
18
+ ---
19
+
20
+ <cookie>
21
+
22
+ ## 쿠키 추출
23
+
24
+ ```bash
25
+ # 모든 쿠키
26
+ playwriter -s 1 -e "console.log(JSON.stringify(await context.cookies(), null, 2))"
27
+
28
+ # 인증 쿠키만
29
+ playwriter -s 1 -e $'
30
+ const cookies = await context.cookies();
31
+ console.log(cookies.filter(c =>
32
+ ["session","token","auth","sid"].some(n => c.name.toLowerCase().includes(n))
33
+ ));
34
+ '
35
+ ```
36
+
37
+ </cookie>
38
+
39
+ ---
40
+
41
+ <token>
42
+
43
+ ## 토큰 추출
44
+
45
+ ```bash
46
+ # localStorage
47
+ playwriter -s 1 -e "console.log(await state.page.evaluate(() => localStorage.getItem('token')))"
48
+
49
+ # sessionStorage
50
+ playwriter -s 1 -e "console.log(await state.page.evaluate(() => sessionStorage.getItem('accessToken')))"
51
+
52
+ # Authorization 헤더
53
+ playwriter -s 1 -e $'
54
+ state.page.on("request", req => {
55
+ const auth = req.headers()["authorization"];
56
+ if (auth) console.log("Auth:", auth);
57
+ });
58
+ '
59
+ ```
60
+
61
+ </token>
62
+
63
+ ---
64
+
65
+ <headers>
66
+
67
+ ## 헤더 캡처
68
+
69
+ ```bash
70
+ playwriter -s 1 -e $'
71
+ state.page.on("request", req => {
72
+ if (req.url().includes("/api/"))
73
+ console.log(JSON.stringify(req.headers(), null, 2));
74
+ });
75
+ '
76
+ ```
77
+
78
+ </headers>
79
+
80
+ ---
81
+
82
+ <bot_check>
83
+
84
+ ## 봇 탐지 확인
85
+
86
+ ```bash
87
+ playwriter -s 1 -e "console.log(await state.page.content().then(c => c.includes('cf-')))"
88
+
89
+ playwriter -s 1 -e $'
90
+ state.page.on("response", res => {
91
+ if ([403, 429].includes(res.status()))
92
+ console.log("차단:", res.status(), res.url());
93
+ });
94
+ '
95
+ ```
96
+
97
+ </bot_check>
98
+
99
+ ---
100
+
101
+ <template>
102
+
103
+ ## NETWORK.md 템플릿
104
+
105
+ ```markdown
106
+ # [사이트명] Network
107
+
108
+ ## 인증
109
+
110
+ | 항목 | 값 | 만료 |
111
+ |------|-----|------|
112
+ | Cookie | `session=...` | 24h |
113
+ | Token | `Bearer ...` | 1h |
114
+
115
+ ## 필수 헤더
116
+
117
+ \`\`\`json
118
+ {
119
+ "Cookie": "...",
120
+ "Authorization": "Bearer ...",
121
+ "User-Agent": "Mozilla/5.0 ..."
122
+ }
123
+ \`\`\`
124
+
125
+ ## Rate Limit
126
+
127
+ - 제한: 60 req/min
128
+ - 딜레이: 1000ms
129
+
130
+ ## 봇 탐지
131
+
132
+ - [ ] Cloudflare
133
+ - [ ] reCAPTCHA
134
+
135
+ ## 참고
136
+
137
+ - 봇 탐지 강함 → Nstbrowser
138
+ - 쿠키 만료 짧음 → 갱신 로직
139
+ ```
140
+
141
+ </template>
@@ -0,0 +1,172 @@
1
+ # Playwriter 명령어
2
+
3
+ > 크롤링 분석용 핵심 명령어
4
+
5
+ ---
6
+
7
+ <session>
8
+
9
+ ## 세션
10
+
11
+ ```bash
12
+ playwriter session new # 생성 → ID 반환
13
+ playwriter session list # 목록
14
+ playwriter session reset 1 # 리셋
15
+ ```
16
+
17
+ </session>
18
+
19
+ ---
20
+
21
+ <execution>
22
+
23
+ ## 실행
24
+
25
+ ```bash
26
+ playwriter -s 1 -e "<code>"
27
+ playwriter -s 1 --timeout 20000 -e "<code>" # 타임아웃 증가
28
+ ```
29
+
30
+ </execution>
31
+
32
+ ---
33
+
34
+ <page>
35
+
36
+ ## 페이지
37
+
38
+ ```javascript
39
+ // 생성 + 이동
40
+ state.page = await context.newPage();
41
+ await state.page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
42
+
43
+ // 기존 페이지 찾기
44
+ state.page = context.pages().find(p => p.url().includes('example.com'));
45
+ ```
46
+
47
+ </page>
48
+
49
+ ---
50
+
51
+ <structure>
52
+
53
+ ## 구조 파악
54
+
55
+ ```javascript
56
+ // 접근성 트리 (권장)
57
+ await accessibilitySnapshot({ page: state.page })
58
+ await accessibilitySnapshot({ page: state.page, search: /button|link/ })
59
+
60
+ // 시각적 확인
61
+ await screenshotWithAccessibilityLabels({ page: state.page })
62
+
63
+ // HTML
64
+ await getCleanHTML({ locator: state.page.locator('body') })
65
+ ```
66
+
67
+ </structure>
68
+
69
+ ---
70
+
71
+ <interaction>
72
+
73
+ ## 상호작용
74
+
75
+ ```javascript
76
+ // 클릭
77
+ await page.locator('aria-ref=e14').click()
78
+ await page.getByRole('button', { name: 'Submit' }).click()
79
+
80
+ // 입력
81
+ await page.locator('input[name="email"]').fill('test@example.com')
82
+
83
+ // 스크롤
84
+ await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight))
85
+ ```
86
+
87
+ </interaction>
88
+
89
+ ---
90
+
91
+ <selector>
92
+
93
+ ## Selector 추출
94
+
95
+ ```javascript
96
+ // aria-ref → Playwright selector
97
+ const sel = await getLocatorStringForElement(page.locator('aria-ref=e14'));
98
+ // => "getByRole('button', { name: 'Save' })"
99
+ ```
100
+
101
+ </selector>
102
+
103
+ ---
104
+
105
+ <network>
106
+
107
+ ## 네트워크 인터셉트
108
+
109
+ ```javascript
110
+ state.requests = []; state.responses = [];
111
+ page.on('request', req => {
112
+ if (req.url().includes('/api/'))
113
+ state.requests.push({ url: req.url(), method: req.method(), headers: req.headers() });
114
+ });
115
+ page.on('response', async res => {
116
+ if (res.url().includes('/api/'))
117
+ try { state.responses.push({ url: res.url(), body: await res.json() }); } catch {}
118
+ });
119
+
120
+ // 분석
121
+ state.responses.forEach(r => console.log(r.url));
122
+
123
+ // 정리
124
+ page.removeAllListeners('request');
125
+ page.removeAllListeners('response');
126
+ ```
127
+
128
+ </network>
129
+
130
+ ---
131
+
132
+ <screenshot>
133
+
134
+ ## 스크린샷
135
+
136
+ ```javascript
137
+ await page.screenshot({ path: 'shot.png', scale: 'css' })
138
+ await page.screenshot({ path: 'full.png', scale: 'css', fullPage: true })
139
+ await page.locator('.card').screenshot({ path: 'card.png', scale: 'css' })
140
+ ```
141
+
142
+ </screenshot>
143
+
144
+ ---
145
+
146
+ <wait>
147
+
148
+ ## 대기
149
+
150
+ ```javascript
151
+ await page.waitForLoadState('domcontentloaded')
152
+ await page.waitForSelector('.loaded')
153
+ await waitForPageLoad({ page: state.page, timeout: 5000 })
154
+ ```
155
+
156
+ </wait>
157
+
158
+ ---
159
+
160
+ <utils>
161
+
162
+ ## 유틸리티
163
+
164
+ | 함수 | 용도 |
165
+ |------|------|
166
+ | `accessibilitySnapshot({ page })` | 접근성 트리 |
167
+ | `screenshotWithAccessibilityLabels({ page })` | 레이블 스크린샷 |
168
+ | `getCleanHTML({ locator })` | 정제된 HTML |
169
+ | `getLocatorStringForElement(locator)` | Selector 추출 |
170
+ | `waitForPageLoad({ page })` | 로드 대기 |
171
+
172
+ </utils>
@@ -0,0 +1,221 @@
1
+ # 크롤링 전 분석 체크리스트
2
+
3
+ > Playwriter 사이트 분석 시 확인 항목
4
+
5
+ ---
6
+
7
+ <rendering_check>
8
+
9
+ ## 렌더링 방식
10
+
11
+ ```bash
12
+ playwriter -s 1 -e $'
13
+ const html = await state.page.content();
14
+ console.log("HTML:", html.length, "| SSR:", html.length > 5000 ? "가능" : "CSR");
15
+ '
16
+ ```
17
+
18
+ | 방식 | 특징 | 전략 |
19
+ |------|------|------|
20
+ | SSR | HTML에 데이터 포함 | DOM 파싱 |
21
+ | CSR | JS로 로딩 | API 인터셉트 |
22
+ | Hybrid | 초기 SSR + 추가 CSR | 혼합 |
23
+
24
+ </rendering_check>
25
+
26
+ ---
27
+
28
+ <bot_detection>
29
+
30
+ ## 봇 탐지 확인
31
+
32
+ ```bash
33
+ playwriter -s 1 -e $'
34
+ const html = await state.page.content();
35
+ console.log({
36
+ cloudflare: html.includes("cf-ray") || html.includes("cf-challenge"),
37
+ recaptcha: html.includes("recaptcha"),
38
+ hcaptcha: html.includes("hcaptcha"),
39
+ datadome: html.includes("datadome"),
40
+ akamai: html.includes("_abck"),
41
+ });
42
+ '
43
+ ```
44
+
45
+ ```bash
46
+ # 차단 응답 모니터링
47
+ playwriter -s 1 -e $'
48
+ state.page.on("response", res => {
49
+ if ([403, 429, 503].includes(res.status()))
50
+ console.log("차단:", res.status(), res.url());
51
+ });
52
+ '
53
+ ```
54
+
55
+ | 코드 | 의미 | 대응 |
56
+ |------|------|------|
57
+ | 403 | 접근 차단 | Anti-Detect |
58
+ | 429 | Rate Limit | 딜레이 증가 |
59
+ | 503 | 일시 차단 | 대기 후 재시도 |
60
+
61
+ </bot_detection>
62
+
63
+ ---
64
+
65
+ <honeypot>
66
+
67
+ ## 허니팟 탐지
68
+
69
+ ```bash
70
+ playwriter -s 1 -e $'
71
+ const hidden = await state.page.$$eval("a", links =>
72
+ links.filter(a => {
73
+ const s = getComputedStyle(a);
74
+ return s.display==="none" || s.visibility==="hidden" || s.opacity==="0" || a.offsetWidth===0;
75
+ }).map(a => a.href)
76
+ );
77
+ console.log("숨겨진 링크:", hidden.length);
78
+ '
79
+ ```
80
+
81
+ | 유형 | 탐지 | 대응 |
82
+ |------|------|------|
83
+ | 숨김 링크 | `display:none`, `visibility:hidden` | 클릭 금지 |
84
+ | 함정 필드 | `name*=honeypot`, `name*=trap` | 입력 금지 |
85
+ | 0x0 요소 | `offsetWidth===0` | 무시 |
86
+
87
+ </honeypot>
88
+
89
+ ---
90
+
91
+ <rate_limit>
92
+
93
+ ## Rate Limiting
94
+
95
+ ```bash
96
+ playwriter -s 1 -e $'
97
+ state.page.on("response", res => {
98
+ const h = res.headers();
99
+ if (h["x-ratelimit-limit"]) console.log("Limit:", h["x-ratelimit-limit"]);
100
+ if (h["retry-after"]) console.log("Retry:", h["retry-after"]);
101
+ });
102
+ '
103
+ ```
104
+
105
+ | 헤더 | 설명 |
106
+ |------|------|
107
+ | `X-RateLimit-Limit` | 최대 요청 수 |
108
+ | `X-RateLimit-Remaining` | 남은 요청 수 |
109
+ | `Retry-After` | 대기 시간 (초) |
110
+
111
+ </rate_limit>
112
+
113
+ ---
114
+
115
+ <auth_check>
116
+
117
+ ## 인증 분석
118
+
119
+ ```bash
120
+ # 쿠키
121
+ playwriter -s 1 -e $'
122
+ const cookies = await context.cookies();
123
+ console.log(cookies.filter(c =>
124
+ ["session","token","auth","jwt"].some(k => c.name.toLowerCase().includes(k))
125
+ ));
126
+ '
127
+
128
+ # 토큰
129
+ playwriter -s 1 -e $'
130
+ const storage = await state.page.evaluate(() => ({
131
+ local: Object.keys(localStorage).filter(k => k.match(/token|auth|jwt/i)),
132
+ session: Object.keys(sessionStorage).filter(k => k.match(/token|auth|jwt/i))
133
+ }));
134
+ console.log(storage);
135
+ '
136
+
137
+ # Authorization 헤더
138
+ playwriter -s 1 -e $'
139
+ state.page.on("request", req => {
140
+ const auth = req.headers()["authorization"];
141
+ if (auth) console.log("Auth:", auth.slice(0, 50));
142
+ });
143
+ '
144
+ ```
145
+
146
+ </auth_check>
147
+
148
+ ---
149
+
150
+ <api_discovery>
151
+
152
+ ## API 발견
153
+
154
+ ```bash
155
+ playwriter -s 1 -e $'
156
+ state.apis = [];
157
+ state.page.on("request", req => {
158
+ if (req.url().includes("/api/") || req.resourceType()==="fetch")
159
+ state.apis.push({ method: req.method(), url: req.url().split("?")[0] });
160
+ });
161
+ '
162
+ # 페이지 탐색 후
163
+ playwriter -s 1 -e $'
164
+ const unique = [...new Map(state.apis.map(a => [a.url, a])).values()];
165
+ console.log(unique);
166
+ '
167
+ ```
168
+
169
+ </api_discovery>
170
+
171
+ ---
172
+
173
+ <dynamic_content>
174
+
175
+ ## 동적 콘텐츠
176
+
177
+ ```bash
178
+ # Lazy loading
179
+ playwriter -s 1 -e $'
180
+ const lazy = await state.page.$$eval("img", imgs =>
181
+ imgs.filter(i => i.dataset.src || i.loading==="lazy").length
182
+ );
183
+ console.log("Lazy 이미지:", lazy);
184
+ '
185
+
186
+ # Shadow DOM / iframe
187
+ playwriter -s 1 -e $'
188
+ const shadow = await state.page.$$eval("*", els => els.filter(e => e.shadowRoot).length);
189
+ const iframes = await state.page.$$eval("iframe", f => f.length);
190
+ console.log("Shadow:", shadow, "| iframe:", iframes);
191
+ '
192
+ ```
193
+
194
+ </dynamic_content>
195
+
196
+ ---
197
+
198
+ <decision_table>
199
+
200
+ ## 전략 결정
201
+
202
+ | 항목 | 결과 | 권장 |
203
+ |------|------|------|
204
+ | 렌더링 | SSR / CSR | DOM / API |
205
+ | 봇 탐지 | 없음 / 있음 | 일반 / Anti-Detect |
206
+ | 인증 | 공개 / 필요 | 직접 / 쿠키·토큰 |
207
+ | Rate Limit | 없음 / 있음 | 병렬 / 딜레이 |
208
+
209
+ </decision_table>
210
+
211
+ ---
212
+
213
+ <warnings>
214
+
215
+ ```text
216
+ ⚠️ 허니팟 클릭 금지
217
+ ⚠️ Rate Limit 초과 시 중단
218
+ ⚠️ robots.txt 확인
219
+ ```
220
+
221
+ </warnings>