clouvel 0.3.1__py3-none-any.whl → 0.6.3__py3-none-any.whl

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.
clouvel/tools/docs.py ADDED
@@ -0,0 +1,247 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Docs tools: PRD 템플릿, 가이드 등"""
3
+
4
+ from datetime import datetime
5
+ from mcp.types import TextContent
6
+
7
+
8
+ async def get_prd_template(project_name: str, output_path: str) -> list[TextContent]:
9
+ """PRD 템플릿 생성"""
10
+ template = f"""# {project_name} PRD
11
+
12
+ > 이 문서가 법. 여기 없으면 안 만듦.
13
+ > 작성일: {datetime.now().strftime('%Y-%m-%d')}
14
+
15
+ ---
16
+
17
+ ## 1. 한 줄 요약
18
+ <!-- 프로젝트가 뭔지 한 문장으로. 못 쓰면 정리 안 된 거임. -->
19
+
20
+ [여기에 작성]
21
+
22
+ ---
23
+
24
+ ## 2. 핵심 원칙
25
+
26
+ > 절대 안 변하는 것들. 이거 기준으로 기능 판단.
27
+
28
+ 1. [원칙 1]
29
+ 2. [원칙 2]
30
+ 3. [원칙 3]
31
+
32
+ ---
33
+
34
+ ## 3. 용어 정의
35
+
36
+ | 용어 | 설명 |
37
+ |------|------|
38
+ | [용어1] | [설명] |
39
+
40
+ ---
41
+
42
+ ## 4. 입력 스펙
43
+
44
+ > 외부에서 들어오는 거. 사용자 입력, API 요청 등.
45
+
46
+ ### 4.1 [입력 타입 1]
47
+ - 형식:
48
+ - 필수 필드:
49
+ - 제한사항:
50
+
51
+ ---
52
+
53
+ ## 5. 출력 스펙
54
+
55
+ > 외부로 나가는 거. UI, API 응답, 파일 등.
56
+
57
+ ### 5.1 [출력 타입 1]
58
+ - 형식:
59
+ - 필드:
60
+ - 예시:
61
+
62
+ ---
63
+
64
+ ## 6. 에러 케이스
65
+
66
+ > 터질 수 있는 모든 상황. 빠뜨리면 버그됨.
67
+
68
+ | 상황 | 처리 방법 | 에러 코드 |
69
+ |------|----------|-----------|
70
+ | [상황1] | [방법] | [코드] |
71
+
72
+ ---
73
+
74
+ ## 7. 상태 머신
75
+
76
+ > 있으면 그림으로. 상태 전이 명확하게.
77
+
78
+ ```
79
+ [상태1] --이벤트--> [상태2]
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 8. API 엔드포인트
85
+
86
+ | Method | Path | 설명 |
87
+ |--------|------|------|
88
+ | GET | /api/... | ... |
89
+
90
+ ---
91
+
92
+ ## 9. DB 스키마
93
+
94
+ | 테이블 | 컬럼 | 타입 | 설명 |
95
+ |--------|------|------|------|
96
+ | | | | |
97
+
98
+ ---
99
+
100
+ ## 10. 검증 계획
101
+
102
+ > 어떻게 테스트할 건지. 뭘 확인해야 하는지.
103
+
104
+ ### 10.1 단위 테스트
105
+ - [ ] [테스트 케이스 1]
106
+
107
+ ### 10.2 통합 테스트
108
+ - [ ] [테스트 케이스 1]
109
+
110
+ ---
111
+
112
+ ## 11. 변경 로그
113
+
114
+ | 날짜 | 변경 내용 | 작성자 |
115
+ |------|----------|--------|
116
+ | {datetime.now().strftime('%Y-%m-%d')} | 최초 작성 | |
117
+
118
+ """
119
+ return [TextContent(type="text", text=f"```markdown\n{template}\n```\n\n출력 경로: `{output_path}`에 저장하세요.")]
120
+
121
+
122
+ async def write_prd_section(section: str, content: str) -> list[TextContent]:
123
+ """PRD 섹션별 작성 가이드"""
124
+ guides = {
125
+ "summary": "한 줄 요약: 프로젝트가 뭔지 한 문장으로. 못 쓰면 정리 안 된 거임.",
126
+ "principles": "핵심 원칙: 절대 안 변하는 것들 3-5개. 기능 판단의 기준.",
127
+ "input_spec": "입력 스펙: 외부에서 들어오는 모든 것. 형식, 필수 필드, 제한사항 포함.",
128
+ "output_spec": "출력 스펙: 외부로 나가는 모든 것. 형식, 필드, 예시 포함.",
129
+ "errors": "에러 케이스: 터질 수 있는 모든 상황. 하나라도 빠뜨리면 버그됨.",
130
+ "state_machine": "상태 머신: 상태가 있으면 그림으로. 상태 전이 명확하게.",
131
+ "api_endpoints": "API 엔드포인트: Method, Path, 설명. RESTful하게.",
132
+ "db_schema": "DB 스키마: 테이블, 컬럼, 타입, 관계. 정규화 고려.",
133
+ }
134
+
135
+ guide = guides.get(section, "알 수 없는 섹션입니다.")
136
+ return [TextContent(type="text", text=f"## {section} 작성 가이드\n\n{guide}\n\n내용:\n{content if content else '(입력 필요)'}")]
137
+
138
+
139
+ async def get_prd_guide() -> list[TextContent]:
140
+ """PRD 작성 가이드"""
141
+ return [TextContent(type="text", text="""# PRD 작성 가이드
142
+
143
+ ## 왜 PRD?
144
+ - 코딩 전에 뭘 만들지 정리
145
+ - 팀원/AI 간 인식 통일
146
+ - 나중에 "이거 왜 이렇게 했지?" 방지
147
+
148
+ ## 작성 순서
149
+
150
+ ### 1단계: 핵심부터
151
+ 1. **한 줄 요약** - 못 쓰면 정리 안 된 거
152
+ 2. **핵심 원칙** - 절대 안 변하는 3가지
153
+
154
+ ### 2단계: 입출력
155
+ 3. **입력 스펙** - 뭐가 들어오나
156
+ 4. **출력 스펙** - 뭐가 나가나
157
+
158
+ ### 3단계: 예외처리
159
+ 5. **에러 케이스** - 터질 수 있는 거 다
160
+
161
+ ### 4단계: 상세
162
+ 6. **API** - 엔드포인트 목록
163
+ 7. **DB** - 테이블 구조
164
+ 8. **상태 머신** - 있으면
165
+
166
+ ### 5단계: 검증
167
+ 9. **테스트 계획** - 어떻게 확인할 건지
168
+
169
+ ## 팁
170
+ - 완벽하게 쓰려고 하지 마. 일단 써.
171
+ - 코딩하면서 업데이트해도 됨.
172
+ - 근데 PRD에 없는 기능은 절대 안 만듦.
173
+ """)]
174
+
175
+
176
+ async def get_verify_checklist() -> list[TextContent]:
177
+ """검증 체크리스트"""
178
+ return [TextContent(type="text", text="""# 검증 체크리스트
179
+
180
+ ## PRD 검증
181
+ - [ ] 한 줄 요약이 명확한가?
182
+ - [ ] 핵심 원칙이 3개 이상인가?
183
+ - [ ] 입력/출력 스펙이 구체적인가?
184
+ - [ ] 에러 케이스가 빠짐없이 있는가?
185
+
186
+ ## 코드 검증
187
+ - [ ] PRD에 명시된 기능만 구현했는가?
188
+ - [ ] 에러 케이스 처리가 되어 있는가?
189
+ - [ ] 테스트가 작성되어 있는가?
190
+
191
+ ## 배포 전 검증
192
+ - [ ] 모든 테스트가 통과하는가?
193
+ - [ ] lint 에러가 없는가?
194
+ - [ ] 빌드가 성공하는가?
195
+ """)]
196
+
197
+
198
+ async def get_setup_guide(platform: str) -> list[TextContent]:
199
+ """설정 가이드"""
200
+ guides = {
201
+ "desktop": """## Claude Desktop 설정
202
+
203
+ 1. 설정 파일 열기: `%APPDATA%\\Claude\\claude_desktop_config.json`
204
+ 2. 아래 내용 추가:
205
+
206
+ ```json
207
+ {
208
+ "mcpServers": {
209
+ "clouvel": {
210
+ "command": "uvx",
211
+ "args": ["clouvel"]
212
+ }
213
+ }
214
+ }
215
+ ```
216
+
217
+ 3. Claude Desktop 재시작
218
+ """,
219
+ "code": """## Claude Code (CLI) 설정
220
+
221
+ ```bash
222
+ # 자동 설정
223
+ clouvel init
224
+
225
+ # 또는 수동
226
+ clouvel init -p /path/to/project -l strict
227
+ ```
228
+ """,
229
+ "vscode": """## VS Code 설정
230
+
231
+ 1. 확장 탭에서 "Clouvel" 검색
232
+ 2. 설치
233
+ 3. Command Palette (Ctrl+Shift+P) → "Clouvel: Setup MCP Server"
234
+ """,
235
+ "cursor": """## Cursor 설정
236
+
237
+ VS Code와 동일합니다. "Clouvel" 확장을 설치하세요.
238
+ """,
239
+ }
240
+
241
+ if platform == "all":
242
+ result = "# Clouvel 설정 가이드\n\n"
243
+ for p, g in guides.items():
244
+ result += g + "\n---\n\n"
245
+ return [TextContent(type="text", text=result)]
246
+
247
+ return [TextContent(type="text", text=guides.get(platform, "알 수 없는 플랫폼입니다."))]
clouvel/tools/hooks.py ADDED
@@ -0,0 +1,170 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Hook tools (v0.8): hook_design, hook_verify"""
3
+
4
+ import json
5
+ from pathlib import Path
6
+ from datetime import datetime
7
+ from mcp.types import TextContent
8
+
9
+
10
+ async def hook_design(path: str, trigger: str, checks: list, block_on_fail: bool) -> list[TextContent]:
11
+ """설계 훅 - 코드 작성 전 자동 체크포인트"""
12
+ project_path = Path(path)
13
+
14
+ if not project_path.exists():
15
+ return [TextContent(type="text", text=f"❌ 경로가 존재하지 않습니다: {path}")]
16
+
17
+ # 트리거별 기본 체크 항목
18
+ default_checks = {
19
+ "pre_code": ["prd_exists", "architecture_review", "scope_defined"],
20
+ "pre_feature": ["feature_in_prd", "api_spec_exists", "db_schema_ready"],
21
+ "pre_refactor": ["test_coverage", "backup_exists", "rollback_plan"],
22
+ "pre_api": ["api_spec_complete", "auth_defined", "error_codes_listed"]
23
+ }
24
+
25
+ # 체크 항목 설정 (사용자 지정 또는 기본값)
26
+ active_checks = checks if checks else default_checks.get(trigger, [])
27
+
28
+ # 훅 설정 파일 생성
29
+ hooks_dir = project_path / ".claude" / "hooks"
30
+ hooks_dir.mkdir(parents=True, exist_ok=True)
31
+
32
+ hook_config = {
33
+ "name": f"design_{trigger}",
34
+ "trigger": trigger,
35
+ "checks": active_checks,
36
+ "block_on_fail": block_on_fail,
37
+ "created": datetime.now().isoformat()
38
+ }
39
+
40
+ hook_file = hooks_dir / f"{trigger}.json"
41
+ hook_file.write_text(json.dumps(hook_config, indent=2, ensure_ascii=False), encoding='utf-8')
42
+
43
+ # 체크리스트 생성
44
+ checklist_md = "\n".join(f"- [ ] {check}" for check in active_checks)
45
+
46
+ return [TextContent(type="text", text=f"""# 설계 훅 생성 완료
47
+
48
+ ## 훅 설정
49
+
50
+ | 항목 | 값 |
51
+ |------|-----|
52
+ | 트리거 | `{trigger}` |
53
+ | 실패 시 차단 | {'예' if block_on_fail else '아니오'} |
54
+ | 체크 항목 | {len(active_checks)}개 |
55
+
56
+ ## 체크리스트
57
+
58
+ {checklist_md}
59
+
60
+ ## 저장 위치
61
+
62
+ `{hook_file}`
63
+
64
+ ---
65
+
66
+ ## 사용법
67
+
68
+ 이 훅은 **{trigger}** 시점에 자동 트리거됩니다.
69
+
70
+ ```
71
+ # 훅 실행 예시
72
+ Claude가 코드 작성 전:
73
+ 1. can_code 확인
74
+ 2. {trigger} 훅 체크리스트 확인
75
+ 3. 모든 체크 통과 시 진행
76
+ ```
77
+
78
+ **{'체크 실패 시 코드 작성이 차단됩니다.' if block_on_fail else '체크 실패해도 경고만 출력합니다.'}**
79
+ """)]
80
+
81
+
82
+ async def hook_verify(path: str, trigger: str, steps: list, parallel: bool, continue_on_error: bool) -> list[TextContent]:
83
+ """검증 훅 - 코드 완료 후 자동 검증 체크포인트"""
84
+ project_path = Path(path)
85
+
86
+ if not project_path.exists():
87
+ return [TextContent(type="text", text=f"❌ 경로가 존재하지 않습니다: {path}")]
88
+
89
+ # 트리거별 기본 단계
90
+ default_steps = {
91
+ "post_code": ["lint", "type_check"],
92
+ "post_feature": ["lint", "test", "build"],
93
+ "pre_commit": ["lint", "test", "security_scan"],
94
+ "pre_push": ["lint", "test", "build", "integration_test"]
95
+ }
96
+
97
+ # 단계 설정 (사용자 지정 또는 기본값)
98
+ active_steps = steps if steps else default_steps.get(trigger, ["lint", "test", "build"])
99
+
100
+ # 단계별 명령어 매핑
101
+ step_commands = {
102
+ "lint": {"cmd": "npm run lint || pylint . || ruff check .", "desc": "코드 스타일 검사"},
103
+ "type_check": {"cmd": "tsc --noEmit || mypy .", "desc": "타입 검사"},
104
+ "test": {"cmd": "npm test || pytest || go test ./...", "desc": "테스트 실행"},
105
+ "build": {"cmd": "npm run build || python -m build || go build", "desc": "빌드"},
106
+ "security_scan": {"cmd": "npm audit || safety check || gosec ./...", "desc": "보안 스캔"},
107
+ "integration_test": {"cmd": "npm run test:e2e || pytest tests/e2e", "desc": "통합 테스트"}
108
+ }
109
+
110
+ # 훅 설정 파일 생성
111
+ hooks_dir = project_path / ".claude" / "hooks"
112
+ hooks_dir.mkdir(parents=True, exist_ok=True)
113
+
114
+ hook_config = {
115
+ "name": f"verify_{trigger}",
116
+ "trigger": trigger,
117
+ "steps": active_steps,
118
+ "parallel": parallel,
119
+ "continue_on_error": continue_on_error,
120
+ "created": datetime.now().isoformat()
121
+ }
122
+
123
+ hook_file = hooks_dir / f"{trigger}.json"
124
+ hook_file.write_text(json.dumps(hook_config, indent=2, ensure_ascii=False), encoding='utf-8')
125
+
126
+ # 단계 목록 생성
127
+ steps_md = ""
128
+ for i, step in enumerate(active_steps, 1):
129
+ info = step_commands.get(step, {"cmd": step, "desc": "사용자 정의"})
130
+ steps_md += f"{i}. **{step}**: {info['desc']}\n ```\n {info['cmd']}\n ```\n\n"
131
+
132
+ execution_mode = "병렬 (동시 실행)" if parallel else "순차 (하나씩 실행)"
133
+ error_handling = "계속 진행" if continue_on_error else "즉시 중단"
134
+
135
+ return [TextContent(type="text", text=f"""# 검증 훅 생성 완료
136
+
137
+ ## 훅 설정
138
+
139
+ | 항목 | 값 |
140
+ |------|-----|
141
+ | 트리거 | `{trigger}` |
142
+ | 실행 모드 | {execution_mode} |
143
+ | 에러 처리 | {error_handling} |
144
+ | 단계 수 | {len(active_steps)}개 |
145
+
146
+ ## 검증 단계
147
+
148
+ {steps_md}
149
+
150
+ ## 저장 위치
151
+
152
+ `{hook_file}`
153
+
154
+ ---
155
+
156
+ ## 실행 흐름
157
+
158
+ ```
159
+ {trigger} 트리거 발생
160
+ |
161
+ v
162
+ {'병렬 실행' if parallel else '순차 실행'}
163
+ {' -> '.join(active_steps)}
164
+ |
165
+ v
166
+ 결과 리포트 생성
167
+ ```
168
+
169
+ **트리거 시점: {trigger}**
170
+ """)]