@yeongjaeyou/claude-code-config 0.1.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.
- package/.claude/agents/generate-llmstxt.md +165 -0
- package/.claude/agents/langconnect-rag-expert.md +98 -0
- package/.claude/agents/python-pro.md +33 -0
- package/.claude/agents/web-researcher.md +292 -0
- package/.claude/commands/ask-deepwiki.md +26 -0
- package/.claude/commands/code-review.md +75 -0
- package/.claude/commands/commit-and-push.md +52 -0
- package/.claude/commands/edit-notebook.md +42 -0
- package/.claude/commands/gh/create-issue-label.md +29 -0
- package/.claude/commands/gh/decompose-issue.md +60 -0
- package/.claude/commands/gh/post-merge.md +71 -0
- package/.claude/commands/gh/resolve-issue.md +54 -0
- package/.claude/commands/plan.md +25 -0
- package/.claude/skills/code-explorer/SKILL.md +186 -0
- package/.claude/skills/code-explorer/references/github_api.md +174 -0
- package/.claude/skills/code-explorer/references/huggingface_api.md +240 -0
- package/.claude/skills/code-explorer/scripts/search_github.py +208 -0
- package/.claude/skills/code-explorer/scripts/search_huggingface.py +306 -0
- package/LICENSE +21 -0
- package/README.md +159 -0
- package/bin/cli.js +133 -0
- package/package.json +29 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Hugging Face Search Script
|
|
4
|
+
|
|
5
|
+
Search Models, Datasets, and Spaces on Hugging Face Hub.
|
|
6
|
+
Requires: huggingface_hub library (pip install huggingface_hub)
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python search_huggingface.py "keyword" --type {models,datasets,spaces,all}
|
|
10
|
+
|
|
11
|
+
Examples:
|
|
12
|
+
python search_huggingface.py "object detection" --type models --limit 10
|
|
13
|
+
python search_huggingface.py "coco" --type datasets --limit 5
|
|
14
|
+
python search_huggingface.py "gradio demo" --type spaces --limit 10
|
|
15
|
+
python search_huggingface.py "segment anything" --type all
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import json
|
|
20
|
+
import sys
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from huggingface_hub import HfApi, list_models, list_datasets, list_spaces
|
|
25
|
+
HF_AVAILABLE = True
|
|
26
|
+
except ImportError:
|
|
27
|
+
HF_AVAILABLE = False
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def search_models(
|
|
31
|
+
query: str,
|
|
32
|
+
limit: int = 10,
|
|
33
|
+
task: Optional[str] = None,
|
|
34
|
+
library: Optional[str] = None
|
|
35
|
+
) -> list[dict]:
|
|
36
|
+
"""
|
|
37
|
+
Search Hugging Face models.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
query: Search keyword
|
|
41
|
+
limit: Maximum number of results
|
|
42
|
+
task: Filter by task (e.g., 'object-detection', 'image-segmentation')
|
|
43
|
+
library: Filter by library (e.g., 'transformers', 'diffusers')
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
List of model information dictionaries
|
|
47
|
+
"""
|
|
48
|
+
api = HfApi()
|
|
49
|
+
|
|
50
|
+
kwargs = {
|
|
51
|
+
"search": query,
|
|
52
|
+
"limit": limit,
|
|
53
|
+
"sort": "downloads",
|
|
54
|
+
"direction": -1, # Descending
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if task:
|
|
58
|
+
kwargs["task"] = task
|
|
59
|
+
if library:
|
|
60
|
+
kwargs["library"] = library
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
models = list(api.list_models(**kwargs))
|
|
64
|
+
return [
|
|
65
|
+
{
|
|
66
|
+
"id": m.id,
|
|
67
|
+
"author": m.author,
|
|
68
|
+
"downloads": m.downloads,
|
|
69
|
+
"likes": m.likes,
|
|
70
|
+
"task": m.pipeline_tag,
|
|
71
|
+
"library": m.library_name,
|
|
72
|
+
"tags": m.tags[:10] if m.tags else [],
|
|
73
|
+
"url": f"https://huggingface.co/{m.id}",
|
|
74
|
+
"last_modified": str(m.last_modified) if m.last_modified else None,
|
|
75
|
+
}
|
|
76
|
+
for m in models
|
|
77
|
+
]
|
|
78
|
+
except Exception as e:
|
|
79
|
+
print(f"Error searching models: {e}", file=sys.stderr)
|
|
80
|
+
return []
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def search_datasets(
|
|
84
|
+
query: str,
|
|
85
|
+
limit: int = 10,
|
|
86
|
+
task: Optional[str] = None
|
|
87
|
+
) -> list[dict]:
|
|
88
|
+
"""
|
|
89
|
+
Search Hugging Face datasets.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
query: Search keyword
|
|
93
|
+
limit: Maximum number of results
|
|
94
|
+
task: Filter by task category
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
List of dataset information dictionaries
|
|
98
|
+
"""
|
|
99
|
+
api = HfApi()
|
|
100
|
+
|
|
101
|
+
kwargs = {
|
|
102
|
+
"search": query,
|
|
103
|
+
"limit": limit,
|
|
104
|
+
"sort": "downloads",
|
|
105
|
+
"direction": -1,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if task:
|
|
109
|
+
kwargs["task_categories"] = task
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
datasets = list(api.list_datasets(**kwargs))
|
|
113
|
+
return [
|
|
114
|
+
{
|
|
115
|
+
"id": d.id,
|
|
116
|
+
"author": d.author,
|
|
117
|
+
"downloads": d.downloads,
|
|
118
|
+
"likes": d.likes,
|
|
119
|
+
"tags": d.tags[:10] if d.tags else [],
|
|
120
|
+
"url": f"https://huggingface.co/datasets/{d.id}",
|
|
121
|
+
"last_modified": str(d.last_modified) if d.last_modified else None,
|
|
122
|
+
}
|
|
123
|
+
for d in datasets
|
|
124
|
+
]
|
|
125
|
+
except Exception as e:
|
|
126
|
+
print(f"Error searching datasets: {e}", file=sys.stderr)
|
|
127
|
+
return []
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def search_spaces(
|
|
131
|
+
query: str,
|
|
132
|
+
limit: int = 10,
|
|
133
|
+
sdk: Optional[str] = None
|
|
134
|
+
) -> list[dict]:
|
|
135
|
+
"""
|
|
136
|
+
Search Hugging Face Spaces.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
query: Search keyword
|
|
140
|
+
limit: Maximum number of results
|
|
141
|
+
sdk: Filter by SDK ('gradio', 'streamlit', 'docker', 'static')
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
List of space information dictionaries
|
|
145
|
+
"""
|
|
146
|
+
api = HfApi()
|
|
147
|
+
|
|
148
|
+
kwargs = {
|
|
149
|
+
"search": query,
|
|
150
|
+
"limit": limit,
|
|
151
|
+
"sort": "likes",
|
|
152
|
+
"direction": -1,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if sdk:
|
|
156
|
+
kwargs["sdk"] = sdk
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
spaces = list(api.list_spaces(**kwargs))
|
|
160
|
+
return [
|
|
161
|
+
{
|
|
162
|
+
"id": s.id,
|
|
163
|
+
"author": s.author,
|
|
164
|
+
"likes": s.likes,
|
|
165
|
+
"sdk": s.sdk,
|
|
166
|
+
"tags": s.tags[:10] if s.tags else [],
|
|
167
|
+
"url": f"https://huggingface.co/spaces/{s.id}",
|
|
168
|
+
"last_modified": str(s.last_modified) if s.last_modified else None,
|
|
169
|
+
}
|
|
170
|
+
for s in spaces
|
|
171
|
+
]
|
|
172
|
+
except Exception as e:
|
|
173
|
+
print(f"Error searching spaces: {e}", file=sys.stderr)
|
|
174
|
+
return []
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def format_model(model: dict, index: int) -> str:
|
|
178
|
+
"""Format model info as a summary string."""
|
|
179
|
+
return (
|
|
180
|
+
f"\n[{index}] MODEL: {model['id']}\n"
|
|
181
|
+
f" Downloads: {model['downloads']:,} | Likes: {model['likes']:,}\n"
|
|
182
|
+
f" Task: {model['task'] or 'N/A'} | Library: {model['library'] or 'N/A'}\n"
|
|
183
|
+
f" Tags: {', '.join(model['tags'][:5]) if model['tags'] else 'None'}\n"
|
|
184
|
+
f" URL: {model['url']}\n"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def format_dataset(dataset: dict, index: int) -> str:
|
|
189
|
+
"""Format dataset info as a summary string."""
|
|
190
|
+
return (
|
|
191
|
+
f"\n[{index}] DATASET: {dataset['id']}\n"
|
|
192
|
+
f" Downloads: {dataset['downloads']:,} | Likes: {dataset['likes']:,}\n"
|
|
193
|
+
f" Tags: {', '.join(dataset['tags'][:5]) if dataset['tags'] else 'None'}\n"
|
|
194
|
+
f" URL: {dataset['url']}\n"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def format_space(space: dict, index: int) -> str:
|
|
199
|
+
"""Format space info as a summary string."""
|
|
200
|
+
return (
|
|
201
|
+
f"\n[{index}] SPACE: {space['id']}\n"
|
|
202
|
+
f" Likes: {space['likes']:,} | SDK: {space['sdk'] or 'N/A'}\n"
|
|
203
|
+
f" Tags: {', '.join(space['tags'][:5]) if space['tags'] else 'None'}\n"
|
|
204
|
+
f" URL: {space['url']}\n"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def main():
|
|
209
|
+
parser = argparse.ArgumentParser(
|
|
210
|
+
description="Search Hugging Face Hub (Models, Datasets, Spaces)",
|
|
211
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
212
|
+
epilog="""
|
|
213
|
+
Examples:
|
|
214
|
+
%(prog)s "object detection" --type models --limit 10
|
|
215
|
+
%(prog)s "coco" --type datasets --limit 5
|
|
216
|
+
%(prog)s "gradio demo" --type spaces --limit 10
|
|
217
|
+
%(prog)s "segment anything" --type all
|
|
218
|
+
|
|
219
|
+
Tasks (for --task filter):
|
|
220
|
+
Models: object-detection, image-segmentation, image-classification, etc.
|
|
221
|
+
Datasets: object-detection, image-classification, etc.
|
|
222
|
+
|
|
223
|
+
SDKs (for --sdk filter):
|
|
224
|
+
gradio, streamlit, docker, static
|
|
225
|
+
"""
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
parser.add_argument("query", help="Search keyword")
|
|
229
|
+
parser.add_argument("--type", "-t", choices=["models", "datasets", "spaces", "all"], default="all", help="Resource type to search (default: all)")
|
|
230
|
+
parser.add_argument("--limit", "-n", type=int, default=10, help="Number of results per type (default: 10)")
|
|
231
|
+
parser.add_argument("--task", help="Filter by task (models/datasets only)")
|
|
232
|
+
parser.add_argument("--library", help="Filter by library (models only)")
|
|
233
|
+
parser.add_argument("--sdk", help="Filter by SDK (spaces only): gradio, streamlit, docker, static")
|
|
234
|
+
parser.add_argument("--json", "-j", action="store_true", help="Output as JSON")
|
|
235
|
+
|
|
236
|
+
args = parser.parse_args()
|
|
237
|
+
|
|
238
|
+
# Check huggingface_hub
|
|
239
|
+
if not HF_AVAILABLE:
|
|
240
|
+
print("Error: huggingface_hub is not installed.", file=sys.stderr)
|
|
241
|
+
print("Install: pip install huggingface_hub", file=sys.stderr)
|
|
242
|
+
sys.exit(1)
|
|
243
|
+
|
|
244
|
+
results = {}
|
|
245
|
+
|
|
246
|
+
# Search based on type
|
|
247
|
+
if args.type in ["models", "all"]:
|
|
248
|
+
print(f"Searching Models for: '{args.query}'...", file=sys.stderr)
|
|
249
|
+
results["models"] = search_models(
|
|
250
|
+
query=args.query,
|
|
251
|
+
limit=args.limit,
|
|
252
|
+
task=args.task,
|
|
253
|
+
library=args.library
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
if args.type in ["datasets", "all"]:
|
|
257
|
+
print(f"Searching Datasets for: '{args.query}'...", file=sys.stderr)
|
|
258
|
+
results["datasets"] = search_datasets(
|
|
259
|
+
query=args.query,
|
|
260
|
+
limit=args.limit,
|
|
261
|
+
task=args.task
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
if args.type in ["spaces", "all"]:
|
|
265
|
+
print(f"Searching Spaces for: '{args.query}'...", file=sys.stderr)
|
|
266
|
+
results["spaces"] = search_spaces(
|
|
267
|
+
query=args.query,
|
|
268
|
+
limit=args.limit,
|
|
269
|
+
sdk=args.sdk
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Output results
|
|
273
|
+
if args.json:
|
|
274
|
+
print(json.dumps(results, indent=2, default=str))
|
|
275
|
+
else:
|
|
276
|
+
print(f"\n{'='*60}")
|
|
277
|
+
print(f"Hugging Face Search Results for: '{args.query}'")
|
|
278
|
+
print(f"{'='*60}")
|
|
279
|
+
|
|
280
|
+
if "models" in results:
|
|
281
|
+
print(f"\n--- MODELS ({len(results['models'])} found) ---")
|
|
282
|
+
if results["models"]:
|
|
283
|
+
for i, model in enumerate(results["models"], 1):
|
|
284
|
+
print(format_model(model, i))
|
|
285
|
+
else:
|
|
286
|
+
print(" No models found.")
|
|
287
|
+
|
|
288
|
+
if "datasets" in results:
|
|
289
|
+
print(f"\n--- DATASETS ({len(results['datasets'])} found) ---")
|
|
290
|
+
if results["datasets"]:
|
|
291
|
+
for i, dataset in enumerate(results["datasets"], 1):
|
|
292
|
+
print(format_dataset(dataset, i))
|
|
293
|
+
else:
|
|
294
|
+
print(" No datasets found.")
|
|
295
|
+
|
|
296
|
+
if "spaces" in results:
|
|
297
|
+
print(f"\n--- SPACES ({len(results['spaces'])} found) ---")
|
|
298
|
+
if results["spaces"]:
|
|
299
|
+
for i, space in enumerate(results["spaces"], 1):
|
|
300
|
+
print(format_space(space, i))
|
|
301
|
+
else:
|
|
302
|
+
print(" No spaces found.")
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
if __name__ == "__main__":
|
|
306
|
+
main()
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 YoungjaeDev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Claude Code Config
|
|
2
|
+
|
|
3
|
+
Claude Code CLI를 위한 커스텀 슬래시 커맨드, 에이전트, 스킬 모음입니다.
|
|
4
|
+
|
|
5
|
+
## 구조
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
.claude/
|
|
9
|
+
├── commands/ # 슬래시 커맨드
|
|
10
|
+
│ ├── ask-deepwiki.md # DeepWiki로 GitHub 리포 심층 질의
|
|
11
|
+
│ ├── code-review.md # CodeRabbit 등 외부 리뷰 처리
|
|
12
|
+
│ ├── commit-and-push.md # Git 커밋 및 푸시 자동화
|
|
13
|
+
│ ├── edit-notebook.md # Jupyter Notebook 안전 편집
|
|
14
|
+
│ ├── plan.md # 구현 계획 수립 (코드 작성 전)
|
|
15
|
+
│ └── gh/
|
|
16
|
+
│ ├── create-issue-label.md # GitHub 이슈 라벨 생성
|
|
17
|
+
│ ├── decompose-issue.md # 큰 작업을 이슈로 분해
|
|
18
|
+
│ ├── post-merge.md # PR 머지 후 정리 작업
|
|
19
|
+
│ └── resolve-issue.md # GitHub 이슈 해결 워크플로우
|
|
20
|
+
├── agents/ # 커스텀 에이전트
|
|
21
|
+
│ ├── web-researcher.md # 다중 플랫폼 웹 리서치
|
|
22
|
+
│ ├── python-pro.md # Python 전문가
|
|
23
|
+
│ ├── generate-llmstxt.md # llms.txt 생성
|
|
24
|
+
│ └── langconnect-rag-expert.md # RAG 기반 문서 검색
|
|
25
|
+
└── skills/ # 스킬 (재사용 가능한 도구 모음)
|
|
26
|
+
└── code-explorer/ # GitHub/HuggingFace 코드 탐색
|
|
27
|
+
├── SKILL.md
|
|
28
|
+
├── scripts/
|
|
29
|
+
│ ├── search_github.py
|
|
30
|
+
│ └── search_huggingface.py
|
|
31
|
+
└── references/
|
|
32
|
+
├── github_api.md
|
|
33
|
+
└── huggingface_api.md
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 슬래시 커맨드
|
|
37
|
+
|
|
38
|
+
### 일반 커맨드
|
|
39
|
+
|
|
40
|
+
| 커맨드 | 설명 |
|
|
41
|
+
|--------|------|
|
|
42
|
+
| `/plan` | 요구사항을 분석하고 코드 작성 전 구현 계획만 수립 |
|
|
43
|
+
| `/commit-and-push` | 변경된 파일 분석 후 Conventional Commits 형식으로 커밋 및 푸시 |
|
|
44
|
+
| `/code-review` | CodeRabbit 등 외부 코드 리뷰 결과를 분석하고 자동 수정 적용 |
|
|
45
|
+
| `/edit-notebook` | Jupyter Notebook 파일을 NotebookEdit 도구로 안전하게 편집 |
|
|
46
|
+
| `/ask-deepwiki` | DeepWiki MCP를 활용하여 GitHub 리포지토리에 심층 질의 |
|
|
47
|
+
|
|
48
|
+
### GitHub 워크플로우 커맨드 (`/gh/`)
|
|
49
|
+
|
|
50
|
+
| 커맨드 | 설명 |
|
|
51
|
+
|--------|------|
|
|
52
|
+
| `/gh/create-issue-label` | 프로젝트 구조 분석 후 적절한 GitHub 이슈 라벨 생성 |
|
|
53
|
+
| `/gh/decompose-issue` | 큰 작업을 관리 가능한 독립적인 이슈들로 분해 |
|
|
54
|
+
| `/gh/post-merge` | PR 머지 후 브랜치 정리 및 CLAUDE.md 업데이트 |
|
|
55
|
+
| `/gh/resolve-issue` | GitHub 이슈 번호를 받아 체계적으로 분석하고 해결 |
|
|
56
|
+
|
|
57
|
+
## 에이전트
|
|
58
|
+
|
|
59
|
+
| 에이전트 | 설명 |
|
|
60
|
+
|----------|------|
|
|
61
|
+
| `web-researcher` | 다중 플랫폼(Reddit, GitHub, SO, HF, arXiv 등)에서 기술 리서치 수행 |
|
|
62
|
+
| `python-pro` | Python 고급 기능(데코레이터, 제너레이터, async/await) 전문가 |
|
|
63
|
+
| `generate-llmstxt` | 웹사이트나 로컬 디렉토리에서 llms.txt 문서 생성 |
|
|
64
|
+
| `langconnect-rag-expert` | RAG 기반 문서 컬렉션에서 정보 검색 및 종합 |
|
|
65
|
+
|
|
66
|
+
## 스킬
|
|
67
|
+
|
|
68
|
+
### code-explorer
|
|
69
|
+
|
|
70
|
+
GitHub와 Hugging Face에서 코드/모델/데이터셋을 검색하고 분석하는 스킬입니다.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# GitHub 리포지토리 검색
|
|
74
|
+
python scripts/search_github.py "object detection" --limit 10
|
|
75
|
+
|
|
76
|
+
# Hugging Face 모델/데이터셋/Spaces 검색
|
|
77
|
+
python scripts/search_huggingface.py "qwen vl" --type models
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 설치
|
|
81
|
+
|
|
82
|
+
### npx로 설치 (권장)
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# 현재 프로젝트에 설치
|
|
86
|
+
npx @yeongjaeyou/claude-code-config
|
|
87
|
+
|
|
88
|
+
# 전역 설치 (모든 프로젝트에서 사용)
|
|
89
|
+
npx @yeongjaeyou/claude-code-config --global
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 수동 설치
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# 방법 1: 직접 복사
|
|
96
|
+
git clone https://github.com/YoungjaeDev/claude-code-config.git
|
|
97
|
+
cp -r claude-code-config/.claude /path/to/your/project/
|
|
98
|
+
|
|
99
|
+
# 방법 2: 심볼릭 링크
|
|
100
|
+
ln -s /path/to/claude-code-config/.claude /path/to/your/project/.claude
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### CLI 옵션
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npx @yeongjaeyou/claude-code-config [옵션]
|
|
107
|
+
|
|
108
|
+
옵션:
|
|
109
|
+
-g, --global 전역 설치 (~/.claude/)
|
|
110
|
+
-h, --help 도움말 출력
|
|
111
|
+
-v, --version 버전 출력
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 사용법
|
|
115
|
+
|
|
116
|
+
### 실행 예시
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# 구현 계획 수립
|
|
120
|
+
/plan 새로운 인증 시스템 구현
|
|
121
|
+
|
|
122
|
+
# 커밋 및 푸시
|
|
123
|
+
/commit-and-push src/auth.ts src/utils.ts
|
|
124
|
+
|
|
125
|
+
# GitHub 이슈 해결
|
|
126
|
+
/gh/resolve-issue 42
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 주요 기능
|
|
130
|
+
|
|
131
|
+
### `/plan` - 구현 계획 수립
|
|
132
|
+
- 요구사항의 의도를 파악하고 불명확하면 질문
|
|
133
|
+
- 관련 코드베이스를 조사하고 이해
|
|
134
|
+
- 단계별 실행 계획 작성
|
|
135
|
+
- **바로 코드를 작성하지 않고 계획만 수립**
|
|
136
|
+
|
|
137
|
+
### `/commit-and-push` - Git 자동화
|
|
138
|
+
- Conventional Commits 형식 준수 (feat, fix, refactor, docs 등)
|
|
139
|
+
- 변경 내용 분석 후 적절한 커밋 메시지 생성
|
|
140
|
+
- 지정된 파일만 선택적으로 커밋
|
|
141
|
+
|
|
142
|
+
### `/edit-notebook` - Jupyter Notebook 편집
|
|
143
|
+
- `NotebookEdit` 도구만 사용 (JSON 구조 보호)
|
|
144
|
+
- 셀 삽입 순서 보장을 위한 cell_id 추적
|
|
145
|
+
- source 형식 문제 해결 가이드 포함
|
|
146
|
+
|
|
147
|
+
### `web-researcher` 에이전트
|
|
148
|
+
- GitHub(`gh` CLI), Hugging Face(`huggingface_hub`), Reddit, SO, arXiv 등 다중 플랫폼 검색
|
|
149
|
+
- Context7/DeepWiki를 통한 공식 문서 수집
|
|
150
|
+
- 한글 리서치 리포트 자동 생성
|
|
151
|
+
|
|
152
|
+
### `code-explorer` 스킬
|
|
153
|
+
- `gh` CLI를 활용한 GitHub 리포지토리/코드 검색
|
|
154
|
+
- `huggingface_hub` API를 활용한 모델/데이터셋/Spaces 검색
|
|
155
|
+
- 임시 디렉토리(`/tmp/`)에 소스 코드 다운로드 및 분석
|
|
156
|
+
|
|
157
|
+
## 라이선스
|
|
158
|
+
|
|
159
|
+
MIT License
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const readline = require('readline');
|
|
7
|
+
|
|
8
|
+
const pkg = require('../package.json');
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
|
|
11
|
+
// 옵션 파싱
|
|
12
|
+
const isGlobal = args.includes('--global') || args.includes('-g');
|
|
13
|
+
const showHelp = args.includes('--help') || args.includes('-h');
|
|
14
|
+
const showVersion = args.includes('--version') || args.includes('-v');
|
|
15
|
+
|
|
16
|
+
// 버전 출력
|
|
17
|
+
if (showVersion) {
|
|
18
|
+
console.log(pkg.version);
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 도움말 출력
|
|
23
|
+
if (showHelp) {
|
|
24
|
+
console.log(`
|
|
25
|
+
Claude Code Config v${pkg.version}
|
|
26
|
+
|
|
27
|
+
사용법:
|
|
28
|
+
npx @yeongjaeyou/claude-code-config [옵션]
|
|
29
|
+
|
|
30
|
+
옵션:
|
|
31
|
+
-g, --global 전역 설치 (~/.claude/)
|
|
32
|
+
-h, --help 도움말 출력
|
|
33
|
+
-v, --version 버전 출력
|
|
34
|
+
|
|
35
|
+
예시:
|
|
36
|
+
npx @yeongjaeyou/claude-code-config # 현재 프로젝트에 설치
|
|
37
|
+
npx @yeongjaeyou/claude-code-config --global # 전역 설치
|
|
38
|
+
`);
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const source = path.join(__dirname, '..', '.claude');
|
|
43
|
+
const dest = isGlobal
|
|
44
|
+
? path.join(os.homedir(), '.claude')
|
|
45
|
+
: path.join(process.cwd(), '.claude');
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 사용자에게 질문하고 답변을 받는다
|
|
49
|
+
* @param {string} question - 질문 내용
|
|
50
|
+
* @returns {Promise<string>} - 사용자 답변
|
|
51
|
+
*/
|
|
52
|
+
function askQuestion(question) {
|
|
53
|
+
// 비대화형 환경 감지 (CI/CD, 파이프라인 등)
|
|
54
|
+
if (!process.stdin.isTTY) {
|
|
55
|
+
console.log('비대화형 환경이 감지되었습니다. 기본값(Y)을 사용합니다.');
|
|
56
|
+
return Promise.resolve('');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const rl = readline.createInterface({
|
|
60
|
+
input: process.stdin,
|
|
61
|
+
output: process.stdout
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return new Promise((resolve) => {
|
|
65
|
+
rl.question(question, (answer) => {
|
|
66
|
+
rl.close();
|
|
67
|
+
resolve(answer.trim().toLowerCase());
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 메인 함수
|
|
74
|
+
*/
|
|
75
|
+
async function main() {
|
|
76
|
+
console.log('');
|
|
77
|
+
console.log('Claude Code Config');
|
|
78
|
+
console.log('==================');
|
|
79
|
+
console.log('');
|
|
80
|
+
|
|
81
|
+
// 소스 폴더 존재 확인
|
|
82
|
+
if (!fs.existsSync(source)) {
|
|
83
|
+
console.error('오류: 소스 .claude/ 폴더를 찾을 수 없습니다.');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 설치 위치 안내
|
|
88
|
+
if (isGlobal) {
|
|
89
|
+
console.log('전역 설치 모드: ~/.claude/에 설치합니다.');
|
|
90
|
+
} else {
|
|
91
|
+
console.log('로컬 설치 모드: 현재 디렉토리에 설치합니다.');
|
|
92
|
+
}
|
|
93
|
+
console.log(`설치 경로: ${dest}`);
|
|
94
|
+
console.log('');
|
|
95
|
+
|
|
96
|
+
// 대상 폴더가 이미 존재하는 경우
|
|
97
|
+
if (fs.existsSync(dest)) {
|
|
98
|
+
console.log('기존 .claude/ 폴더가 발견되었습니다.');
|
|
99
|
+
|
|
100
|
+
const answer = await askQuestion('덮어쓰시겠습니까? (Y/n): ');
|
|
101
|
+
|
|
102
|
+
// 빈 값(Enter)이거나 y/Y면 덮어쓰기
|
|
103
|
+
if (answer === '' || answer === 'y') {
|
|
104
|
+
fs.rmSync(dest, { recursive: true });
|
|
105
|
+
console.log('기존 폴더를 삭제했습니다.');
|
|
106
|
+
} else {
|
|
107
|
+
console.log('설치가 취소되었습니다.');
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 폴더 복사
|
|
113
|
+
fs.cpSync(source, dest, { recursive: true });
|
|
114
|
+
|
|
115
|
+
console.log('');
|
|
116
|
+
console.log('.claude/ 폴더가 성공적으로 설치되었습니다!');
|
|
117
|
+
console.log('');
|
|
118
|
+
console.log('설치된 내용:');
|
|
119
|
+
console.log(' - commands/ : 슬래시 커맨드');
|
|
120
|
+
console.log(' - agents/ : 커스텀 에이전트');
|
|
121
|
+
console.log(' - skills/ : 스킬 (재사용 가능한 도구 모음)');
|
|
122
|
+
console.log('');
|
|
123
|
+
|
|
124
|
+
if (isGlobal) {
|
|
125
|
+
console.log('전역 설치가 완료되었습니다.');
|
|
126
|
+
console.log('모든 프로젝트에서 Claude Code 커맨드를 사용할 수 있습니다.');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
main().catch((error) => {
|
|
131
|
+
console.error('오류:', error.message);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yeongjaeyou/claude-code-config",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Claude Code CLI custom commands, agents, and skills",
|
|
5
|
+
"bin": {
|
|
6
|
+
"claude-code-config": "./bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
".claude",
|
|
10
|
+
"bin"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"claude-code",
|
|
14
|
+
"cli",
|
|
15
|
+
"commands",
|
|
16
|
+
"agents",
|
|
17
|
+
"skills"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/YoungjaeDev/claude-code-config.git"
|
|
22
|
+
},
|
|
23
|
+
"author": "YoungjaeDev",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/YoungjaeDev/claude-code-config/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/YoungjaeDev/claude-code-config#readme"
|
|
29
|
+
}
|