@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,174 @@
|
|
|
1
|
+
# GitHub CLI Reference
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# macOS
|
|
7
|
+
brew install gh
|
|
8
|
+
|
|
9
|
+
# Ubuntu/Debian
|
|
10
|
+
sudo apt install gh
|
|
11
|
+
|
|
12
|
+
# Windows
|
|
13
|
+
winget install GitHub.cli
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Authentication
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Login interactively
|
|
20
|
+
gh auth login
|
|
21
|
+
|
|
22
|
+
# Check status
|
|
23
|
+
gh auth status
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Repository Search
|
|
27
|
+
|
|
28
|
+
### Basic Search
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Search repositories
|
|
32
|
+
gh search repos "keyword"
|
|
33
|
+
|
|
34
|
+
# With options
|
|
35
|
+
gh search repos "keyword" --limit 20 --sort stars
|
|
36
|
+
|
|
37
|
+
# Filter by language
|
|
38
|
+
gh search repos "keyword" --language python
|
|
39
|
+
|
|
40
|
+
# Filter by owner
|
|
41
|
+
gh search repos "keyword" --owner username
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Sort Options
|
|
45
|
+
|
|
46
|
+
- `stars` - Sort by star count (default)
|
|
47
|
+
- `forks` - Sort by fork count
|
|
48
|
+
- `updated` - Sort by last update
|
|
49
|
+
- `help-wanted-issues` - Sort by help-wanted issues
|
|
50
|
+
- `best-match` - Sort by relevance
|
|
51
|
+
|
|
52
|
+
### JSON Output
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Get specific fields as JSON
|
|
56
|
+
gh search repos "keyword" --json fullName,description,stargazersCount,url
|
|
57
|
+
|
|
58
|
+
# Available JSON fields
|
|
59
|
+
# fullName, name, owner, description, url, createdAt, updatedAt
|
|
60
|
+
# stargazersCount, forksCount, watchersCount, language, isArchived
|
|
61
|
+
# isFork, isPrivate, visibility, license, topics
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Repository Operations
|
|
65
|
+
|
|
66
|
+
### View Repository
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# View in terminal
|
|
70
|
+
gh repo view owner/repo
|
|
71
|
+
|
|
72
|
+
# View specific fields as JSON
|
|
73
|
+
gh repo view owner/repo --json name,description,stargazersCount,readme
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Clone Repository
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Clone
|
|
80
|
+
gh repo clone owner/repo
|
|
81
|
+
|
|
82
|
+
# Clone to specific directory
|
|
83
|
+
gh repo clone owner/repo ./my-directory
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### List Repository Contents
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# List files (requires being in repo or using gh api)
|
|
90
|
+
gh api repos/owner/repo/contents/path
|
|
91
|
+
|
|
92
|
+
# Get file content
|
|
93
|
+
gh api repos/owner/repo/contents/README.md --jq '.content' | base64 -d
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Code Search
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Search code
|
|
100
|
+
gh search code "function_name"
|
|
101
|
+
|
|
102
|
+
# Filter by file extension
|
|
103
|
+
gh search code "import torch" --extension py
|
|
104
|
+
|
|
105
|
+
# Filter by repository
|
|
106
|
+
gh search code "keyword" --repo owner/repo
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## API Usage
|
|
110
|
+
|
|
111
|
+
### Direct API Calls
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# GET request
|
|
115
|
+
gh api repos/owner/repo
|
|
116
|
+
|
|
117
|
+
# With pagination
|
|
118
|
+
gh api repos/owner/repo/commits --paginate
|
|
119
|
+
|
|
120
|
+
# With query parameters
|
|
121
|
+
gh api search/repositories -f q="keyword" -f sort=stars
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Common Endpoints
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Repository info
|
|
128
|
+
gh api repos/owner/repo
|
|
129
|
+
|
|
130
|
+
# Repository contents
|
|
131
|
+
gh api repos/owner/repo/contents/path
|
|
132
|
+
|
|
133
|
+
# Releases
|
|
134
|
+
gh api repos/owner/repo/releases
|
|
135
|
+
|
|
136
|
+
# Contributors
|
|
137
|
+
gh api repos/owner/repo/contributors
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Useful Examples
|
|
141
|
+
|
|
142
|
+
### Find Popular Python ML Repositories
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
gh search repos "machine learning" --language python --sort stars --limit 20
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Get Repository Structure
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
gh api repos/owner/repo/git/trees/main?recursive=1 --jq '.tree[].path'
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Download Specific File
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
curl -sL "https://raw.githubusercontent.com/owner/repo/main/path/to/file"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Check Repository Activity
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
gh api repos/owner/repo/commits --jq '.[0:5] | .[] | "\(.commit.author.date) - \(.commit.message | split("\n")[0])"'
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Rate Limits
|
|
167
|
+
|
|
168
|
+
- Authenticated: 5,000 requests/hour
|
|
169
|
+
- Search API: 30 requests/minute
|
|
170
|
+
|
|
171
|
+
Check rate limit:
|
|
172
|
+
```bash
|
|
173
|
+
gh api rate_limit
|
|
174
|
+
```
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Hugging Face Hub Reference
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# For uvx hf (no install needed)
|
|
7
|
+
uvx hf --help
|
|
8
|
+
|
|
9
|
+
# Or install huggingface_hub globally
|
|
10
|
+
pip install -U huggingface_hub
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Authentication
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Login interactively
|
|
17
|
+
uvx hf auth login
|
|
18
|
+
|
|
19
|
+
# Or set token
|
|
20
|
+
export HF_TOKEN="your_token_here"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# uvx hf CLI Commands
|
|
26
|
+
|
|
27
|
+
Note: `hf` CLI does NOT have search commands. Use Python API for searching.
|
|
28
|
+
|
|
29
|
+
## Download Commands
|
|
30
|
+
|
|
31
|
+
### Download Repository
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Download entire model
|
|
35
|
+
uvx hf download <repo_id>
|
|
36
|
+
|
|
37
|
+
# Download specific files
|
|
38
|
+
uvx hf download <repo_id> --include "*.py"
|
|
39
|
+
uvx hf download <repo_id> --include "config.json" --include "*.safetensors"
|
|
40
|
+
|
|
41
|
+
# Download to specific directory
|
|
42
|
+
uvx hf download <repo_id> --local-dir ./my-model
|
|
43
|
+
|
|
44
|
+
# Download from specific repo type
|
|
45
|
+
uvx hf download <repo_id> --repo-type space
|
|
46
|
+
uvx hf download <repo_id> --repo-type dataset
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Download Examples
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Download model config (temporary analysis)
|
|
53
|
+
uvx hf download microsoft/resnet-50 --include "*.json" --local-dir /tmp/resnet-50
|
|
54
|
+
|
|
55
|
+
# Download space source code (temporary analysis)
|
|
56
|
+
uvx hf download Qwen/Qwen3-VL-Demo --repo-type space --include "*.py" --local-dir /tmp/qwen3-vl-demo
|
|
57
|
+
|
|
58
|
+
# Download specific file
|
|
59
|
+
uvx hf download Qwen/Qwen2.5-VL-7B-Instruct config.json --local-dir /tmp/qwen-config
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Note**: Always use `--local-dir /tmp/` for temporary code analysis to avoid cluttering the project.
|
|
63
|
+
|
|
64
|
+
## Repository Commands
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# View repo info
|
|
68
|
+
uvx hf repo info <repo_id>
|
|
69
|
+
|
|
70
|
+
# List files in repo
|
|
71
|
+
uvx hf repo-files list <repo_id>
|
|
72
|
+
|
|
73
|
+
# Create new repo
|
|
74
|
+
uvx hf repo create <repo_name>
|
|
75
|
+
uvx hf repo create <repo_name> --type space
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Upload Commands
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Upload file
|
|
82
|
+
uvx hf upload <repo_id> <local_path> <path_in_repo>
|
|
83
|
+
|
|
84
|
+
# Upload folder
|
|
85
|
+
uvx hf upload <repo_id> ./local_folder
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
# Python API (huggingface_hub) - For Searching
|
|
91
|
+
|
|
92
|
+
## Basic Setup
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from huggingface_hub import HfApi
|
|
96
|
+
|
|
97
|
+
api = HfApi()
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Search Models
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
# Basic search
|
|
104
|
+
models = list(api.list_models(search="object detection", limit=10))
|
|
105
|
+
|
|
106
|
+
# With filters
|
|
107
|
+
models = list(api.list_models(
|
|
108
|
+
search="qwen vl",
|
|
109
|
+
task="image-text-to-text",
|
|
110
|
+
sort="downloads",
|
|
111
|
+
direction=-1,
|
|
112
|
+
limit=20
|
|
113
|
+
))
|
|
114
|
+
|
|
115
|
+
# Print results
|
|
116
|
+
for m in models:
|
|
117
|
+
print(f"{m.id}: {m.downloads:,} downloads")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Model Tasks
|
|
121
|
+
|
|
122
|
+
Common task filters:
|
|
123
|
+
- `object-detection`
|
|
124
|
+
- `image-segmentation`
|
|
125
|
+
- `image-classification`
|
|
126
|
+
- `image-text-to-text`
|
|
127
|
+
- `text-generation`
|
|
128
|
+
|
|
129
|
+
## Search Datasets
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
datasets = list(api.list_datasets(search="coco", limit=10))
|
|
133
|
+
|
|
134
|
+
for d in datasets:
|
|
135
|
+
print(f"{d.id}: {d.downloads:,} downloads")
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Search Spaces
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
spaces = list(api.list_spaces(search="gradio demo", limit=10))
|
|
142
|
+
|
|
143
|
+
# Filter by SDK
|
|
144
|
+
spaces = list(api.list_spaces(
|
|
145
|
+
search="object detection",
|
|
146
|
+
sdk="gradio",
|
|
147
|
+
sort="likes",
|
|
148
|
+
direction=-1,
|
|
149
|
+
limit=20
|
|
150
|
+
))
|
|
151
|
+
|
|
152
|
+
for s in spaces:
|
|
153
|
+
print(f"{s.id}: {s.likes} likes, SDK: {s.sdk}")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Download Files (Python)
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from huggingface_hub import hf_hub_download
|
|
160
|
+
|
|
161
|
+
# Download model file
|
|
162
|
+
file_path = hf_hub_download(
|
|
163
|
+
repo_id="microsoft/resnet-50",
|
|
164
|
+
filename="config.json"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Download from space
|
|
168
|
+
file_path = hf_hub_download(
|
|
169
|
+
repo_id="Qwen/Qwen3-VL-Demo",
|
|
170
|
+
filename="app.py",
|
|
171
|
+
repo_type="space"
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## List Repository Files
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
# List model files
|
|
179
|
+
files = api.list_repo_files("microsoft/resnet-50")
|
|
180
|
+
|
|
181
|
+
# List space files
|
|
182
|
+
files = api.list_repo_files("Qwen/Qwen3-VL-Demo", repo_type="space")
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
# Web URLs
|
|
188
|
+
|
|
189
|
+
## Direct Access
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
Models: https://huggingface.co/{model_id}
|
|
193
|
+
Datasets: https://huggingface.co/datasets/{dataset_id}
|
|
194
|
+
Spaces: https://huggingface.co/spaces/{space_id}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## View Files
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
https://huggingface.co/{model_id}/tree/main
|
|
201
|
+
https://huggingface.co/spaces/{space_id}/tree/main
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Raw File Access
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
https://huggingface.co/{model_id}/raw/main/{filename}
|
|
208
|
+
https://huggingface.co/spaces/{space_id}/raw/main/app.py
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
# Common Patterns
|
|
214
|
+
|
|
215
|
+
## Search + Download Workflow
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
# 1. Search for spaces (using script)
|
|
219
|
+
python scripts/search_huggingface.py "qwen vl" --type spaces
|
|
220
|
+
|
|
221
|
+
# 2. Download the source code (to /tmp/ for temporary analysis)
|
|
222
|
+
uvx hf download Qwen/Qwen3-VL-Demo --repo-type space --include "*.py" --local-dir /tmp/qwen-demo
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Analyze Space Implementation
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# Download app.py and requirements (to /tmp/)
|
|
229
|
+
uvx hf download username/space-name --repo-type space --include "app.py" --include "requirements.txt" --local-dir /tmp/space-code
|
|
230
|
+
|
|
231
|
+
# Read the code
|
|
232
|
+
cat /tmp/space-code/app.py
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
# Rate Limits
|
|
238
|
+
|
|
239
|
+
- Anonymous: 1,000 requests/hour
|
|
240
|
+
- Authenticated: 10,000 requests/hour
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
GitHub Repository Search Script
|
|
4
|
+
|
|
5
|
+
Search GitHub repositories using the gh CLI.
|
|
6
|
+
Requires: gh CLI installed and authenticated (https://cli.github.com/)
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python search_github.py "keyword" [options]
|
|
10
|
+
|
|
11
|
+
Examples:
|
|
12
|
+
python search_github.py "object detection" --limit 10
|
|
13
|
+
python search_github.py "gradio app" --language python
|
|
14
|
+
python search_github.py "yolo world" --detailed --sort stars
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def check_gh_cli() -> bool:
|
|
25
|
+
"""Check if gh CLI is installed and authenticated."""
|
|
26
|
+
try:
|
|
27
|
+
result = subprocess.run(
|
|
28
|
+
["gh", "auth", "status"],
|
|
29
|
+
capture_output=True,
|
|
30
|
+
text=True
|
|
31
|
+
)
|
|
32
|
+
return result.returncode == 0
|
|
33
|
+
except FileNotFoundError:
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def search_repos(
|
|
38
|
+
query: str,
|
|
39
|
+
limit: int = 10,
|
|
40
|
+
language: Optional[str] = None,
|
|
41
|
+
sort: str = "stars",
|
|
42
|
+
detailed: bool = False
|
|
43
|
+
) -> list[dict]:
|
|
44
|
+
"""
|
|
45
|
+
Search GitHub repositories.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
query: Search keyword
|
|
49
|
+
limit: Maximum number of results
|
|
50
|
+
language: Filter by programming language
|
|
51
|
+
sort: Sort by 'stars', 'forks', or 'updated'
|
|
52
|
+
detailed: Include detailed info (description, topics)
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
List of repository information dictionaries
|
|
56
|
+
"""
|
|
57
|
+
cmd = [
|
|
58
|
+
"gh", "search", "repos", query,
|
|
59
|
+
"--limit", str(limit),
|
|
60
|
+
"--sort", sort,
|
|
61
|
+
"--json", "fullName,description,stargazersCount,forksCount,updatedAt,language,url,isArchived"
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
if language:
|
|
65
|
+
cmd.extend(["--language", language])
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
69
|
+
repos = json.loads(result.stdout)
|
|
70
|
+
return repos
|
|
71
|
+
except subprocess.CalledProcessError as e:
|
|
72
|
+
print(f"Error searching repositories: {e.stderr}", file=sys.stderr)
|
|
73
|
+
return []
|
|
74
|
+
except json.JSONDecodeError as e:
|
|
75
|
+
print(f"Error parsing response: {e}", file=sys.stderr)
|
|
76
|
+
return []
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_repo_details(repo_name: str) -> Optional[dict]:
|
|
80
|
+
"""
|
|
81
|
+
Get detailed information about a repository.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
repo_name: Repository full name (owner/repo)
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Repository details dictionary or None
|
|
88
|
+
"""
|
|
89
|
+
cmd = [
|
|
90
|
+
"gh", "repo", "view", repo_name,
|
|
91
|
+
"--json", "name,owner,description,stargazersCount,forksCount,watchers,issues,pullRequests,url,homepageUrl,createdAt,updatedAt,pushedAt,isArchived,isFork,languages,repositoryTopics,licenseInfo,readme"
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
96
|
+
return json.loads(result.stdout)
|
|
97
|
+
except subprocess.CalledProcessError as e:
|
|
98
|
+
print(f"Error fetching repo details: {e.stderr}", file=sys.stderr)
|
|
99
|
+
return None
|
|
100
|
+
except json.JSONDecodeError as e:
|
|
101
|
+
print(f"Error parsing response: {e}", file=sys.stderr)
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def format_repo_summary(repo: dict) -> str:
|
|
106
|
+
"""Format repository info as a summary string."""
|
|
107
|
+
archived = " [ARCHIVED]" if repo.get("isArchived") else ""
|
|
108
|
+
lang = repo.get("language") or "N/A"
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
f"\n{'='*60}\n"
|
|
112
|
+
f"{repo['fullName']}{archived}\n"
|
|
113
|
+
f"{'='*60}\n"
|
|
114
|
+
f"Stars: {repo['stargazersCount']:,} | Forks: {repo['forksCount']:,} | Language: {lang}\n"
|
|
115
|
+
f"Updated: {repo['updatedAt'][:10]}\n"
|
|
116
|
+
f"URL: {repo['url']}\n"
|
|
117
|
+
f"\nDescription:\n{repo.get('description') or 'No description'}\n"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def format_repo_detailed(details: dict) -> str:
|
|
122
|
+
"""Format detailed repository info."""
|
|
123
|
+
topics = details.get("repositoryTopics", [])
|
|
124
|
+
topic_names = [t.get("name", "") for t in topics] if topics else []
|
|
125
|
+
|
|
126
|
+
languages = details.get("languages", [])
|
|
127
|
+
lang_names = [l.get("node", {}).get("name", "") for l in languages] if languages else []
|
|
128
|
+
|
|
129
|
+
license_info = details.get("licenseInfo")
|
|
130
|
+
license_name = license_info.get("name", "N/A") if license_info else "N/A"
|
|
131
|
+
|
|
132
|
+
readme = details.get("readme") or "No README available"
|
|
133
|
+
# Truncate README if too long
|
|
134
|
+
if len(readme) > 2000:
|
|
135
|
+
readme = readme[:2000] + "\n... [truncated]"
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
f"\n--- Detailed Info ---\n"
|
|
139
|
+
f"Topics: {', '.join(topic_names) if topic_names else 'None'}\n"
|
|
140
|
+
f"Languages: {', '.join(lang_names) if lang_names else 'N/A'}\n"
|
|
141
|
+
f"License: {license_name}\n"
|
|
142
|
+
f"Created: {details.get('createdAt', 'N/A')[:10]}\n"
|
|
143
|
+
f"Last Push: {details.get('pushedAt', 'N/A')[:10]}\n"
|
|
144
|
+
f"Open Issues: {details.get('issues', {}).get('totalCount', 'N/A')}\n"
|
|
145
|
+
f"Open PRs: {details.get('pullRequests', {}).get('totalCount', 'N/A')}\n"
|
|
146
|
+
f"\n--- README ---\n{readme}\n"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def main():
|
|
151
|
+
parser = argparse.ArgumentParser(
|
|
152
|
+
description="Search GitHub repositories",
|
|
153
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
154
|
+
epilog="""
|
|
155
|
+
Examples:
|
|
156
|
+
%(prog)s "object detection" --limit 10
|
|
157
|
+
%(prog)s "gradio app" --language python
|
|
158
|
+
%(prog)s "yolo world" --detailed --sort stars
|
|
159
|
+
"""
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
parser.add_argument("query", help="Search keyword")
|
|
163
|
+
parser.add_argument("--limit", "-n", type=int, default=10, help="Number of results (default: 10)")
|
|
164
|
+
parser.add_argument("--language", "-l", help="Filter by programming language")
|
|
165
|
+
parser.add_argument("--sort", "-s", choices=["stars", "forks", "updated"], default="stars", help="Sort criteria (default: stars)")
|
|
166
|
+
parser.add_argument("--detailed", "-d", action="store_true", help="Show detailed info including README")
|
|
167
|
+
parser.add_argument("--json", "-j", action="store_true", help="Output as JSON")
|
|
168
|
+
|
|
169
|
+
args = parser.parse_args()
|
|
170
|
+
|
|
171
|
+
# Check gh CLI
|
|
172
|
+
if not check_gh_cli():
|
|
173
|
+
print("Error: gh CLI is not installed or not authenticated.", file=sys.stderr)
|
|
174
|
+
print("Install: https://cli.github.com/", file=sys.stderr)
|
|
175
|
+
print("Then run: gh auth login", file=sys.stderr)
|
|
176
|
+
sys.exit(1)
|
|
177
|
+
|
|
178
|
+
# Search repositories
|
|
179
|
+
print(f"Searching GitHub for: '{args.query}'...\n", file=sys.stderr)
|
|
180
|
+
repos = search_repos(
|
|
181
|
+
query=args.query,
|
|
182
|
+
limit=args.limit,
|
|
183
|
+
language=args.language,
|
|
184
|
+
sort=args.sort,
|
|
185
|
+
detailed=args.detailed
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if not repos:
|
|
189
|
+
print("No repositories found.", file=sys.stderr)
|
|
190
|
+
sys.exit(0)
|
|
191
|
+
|
|
192
|
+
# Output results
|
|
193
|
+
if args.json:
|
|
194
|
+
print(json.dumps(repos, indent=2))
|
|
195
|
+
else:
|
|
196
|
+
print(f"Found {len(repos)} repositories:\n")
|
|
197
|
+
|
|
198
|
+
for i, repo in enumerate(repos, 1):
|
|
199
|
+
print(f"[{i}] {format_repo_summary(repo)}")
|
|
200
|
+
|
|
201
|
+
if args.detailed:
|
|
202
|
+
details = get_repo_details(repo["fullName"])
|
|
203
|
+
if details:
|
|
204
|
+
print(format_repo_detailed(details))
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
if __name__ == "__main__":
|
|
208
|
+
main()
|