@codihaus/claude-skills 1.0.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/README.md +167 -0
- package/bin/cli.js +58 -0
- package/package.json +46 -0
- package/skills/_quality-attributes.md +392 -0
- package/skills/_registry.md +189 -0
- package/skills/debrief/SKILL.md +647 -0
- package/skills/debrief/references/change-request-template.md +124 -0
- package/skills/debrief/references/file-patterns.md +173 -0
- package/skills/debrief/references/group-codes.md +72 -0
- package/skills/debrief/references/research-queries.md +106 -0
- package/skills/debrief/references/use-case-template.md +141 -0
- package/skills/debrief/scripts/generate_questionnaire.py +195 -0
- package/skills/dev-arch/SKILL.md +747 -0
- package/skills/dev-changelog/SKILL.md +378 -0
- package/skills/dev-coding/SKILL.md +470 -0
- package/skills/dev-coding-backend/SKILL.md +361 -0
- package/skills/dev-coding-frontend/SKILL.md +534 -0
- package/skills/dev-coding-frontend/references/nextjs.md +477 -0
- package/skills/dev-review/SKILL.md +548 -0
- package/skills/dev-scout/SKILL.md +723 -0
- package/skills/dev-scout/references/feature-patterns.md +210 -0
- package/skills/dev-scout/references/file-patterns.md +252 -0
- package/skills/dev-scout/references/tech-detection.md +211 -0
- package/skills/dev-scout/scripts/scout-analyze.sh +280 -0
- package/skills/dev-specs/SKILL.md +577 -0
- package/skills/dev-specs/references/checklist.md +176 -0
- package/skills/dev-specs/references/spec-templates.md +460 -0
- package/skills/dev-test/SKILL.md +364 -0
- package/skills/utils/diagram/SKILL.md +205 -0
- package/skills/utils/diagram/references/common-errors.md +305 -0
- package/skills/utils/diagram/references/diagram-types.md +636 -0
- package/skills/utils/docs-graph/SKILL.md +204 -0
- package/skills/utils/gemini/SKILL.md +292 -0
- package/skills/utils/gemini/scripts/gemini-scan.py +340 -0
- package/skills/utils/gemini/scripts/setup.sh +169 -0
- package/src/commands/add.js +64 -0
- package/src/commands/doctor.js +179 -0
- package/src/commands/init.js +251 -0
- package/src/commands/list.js +88 -0
- package/src/commands/remove.js +60 -0
- package/src/commands/update.js +72 -0
- package/src/index.js +26 -0
- package/src/utils/config.js +272 -0
- package/src/utils/deps.js +599 -0
- package/src/utils/skills.js +253 -0
- package/templates/CLAUDE.md.template +58 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Gemini Codebase Scanner
|
|
4
|
+
|
|
5
|
+
Scans a codebase using Gemini Flash's large context window
|
|
6
|
+
and produces a structured summary for Claude Code to use.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python gemini-scan.py --path /path/to/project --output summary.md
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import argparse
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import List, Set
|
|
17
|
+
|
|
18
|
+
# Check for google-generativeai
|
|
19
|
+
try:
|
|
20
|
+
import google.generativeai as genai
|
|
21
|
+
except ImportError:
|
|
22
|
+
print("Error: google-generativeai not installed")
|
|
23
|
+
print("Run: pip install google-generativeai")
|
|
24
|
+
sys.exit(1)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Default file extensions to include
|
|
28
|
+
DEFAULT_EXTENSIONS = {
|
|
29
|
+
# JavaScript/TypeScript
|
|
30
|
+
'.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
|
|
31
|
+
# Python
|
|
32
|
+
'.py',
|
|
33
|
+
# Web
|
|
34
|
+
'.html', '.css', '.scss', '.sass', '.less',
|
|
35
|
+
# Config
|
|
36
|
+
'.json', '.yaml', '.yml', '.toml', '.env.example',
|
|
37
|
+
# Docs
|
|
38
|
+
'.md', '.mdx',
|
|
39
|
+
# Other
|
|
40
|
+
'.sql', '.graphql', '.prisma',
|
|
41
|
+
# Config files (no extension)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Files to always include (by name)
|
|
45
|
+
INCLUDE_FILES = {
|
|
46
|
+
'package.json', 'tsconfig.json', 'next.config.js', 'next.config.mjs',
|
|
47
|
+
'vite.config.ts', 'vite.config.js', 'tailwind.config.js', 'tailwind.config.ts',
|
|
48
|
+
'prisma/schema.prisma', '.env.example', 'docker-compose.yml', 'Dockerfile',
|
|
49
|
+
'requirements.txt', 'pyproject.toml', 'Cargo.toml', 'go.mod',
|
|
50
|
+
'README.md', 'CLAUDE.md',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Default ignore patterns
|
|
54
|
+
DEFAULT_IGNORE = {
|
|
55
|
+
'node_modules', '.git', '.next', '.nuxt', 'dist', 'build', 'out',
|
|
56
|
+
'__pycache__', '.pytest_cache', '.mypy_cache', 'venv', '.venv',
|
|
57
|
+
'coverage', '.coverage', 'htmlcov', '.nyc_output',
|
|
58
|
+
'.idea', '.vscode', '*.log', '*.lock',
|
|
59
|
+
'vendor', 'target', 'bin', 'obj',
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
DEFAULT_PROMPT = """Analyze this codebase and provide a structured summary:
|
|
63
|
+
|
|
64
|
+
1. **Project Overview**
|
|
65
|
+
- What does this project do? (1-2 sentences)
|
|
66
|
+
- Main technologies/frameworks used
|
|
67
|
+
|
|
68
|
+
2. **File Organization**
|
|
69
|
+
- Key directories and their purposes
|
|
70
|
+
- Entry points (main files)
|
|
71
|
+
- Configuration files
|
|
72
|
+
|
|
73
|
+
3. **Architecture Patterns**
|
|
74
|
+
- Overall architecture (MVC, component-based, microservices, etc.)
|
|
75
|
+
- Coding conventions observed
|
|
76
|
+
- State management approach (if frontend)
|
|
77
|
+
|
|
78
|
+
4. **Key Files** (most important files to understand the project)
|
|
79
|
+
- Entry points
|
|
80
|
+
- Core business logic
|
|
81
|
+
- API/route definitions
|
|
82
|
+
- Database models/schema
|
|
83
|
+
|
|
84
|
+
5. **Dependencies**
|
|
85
|
+
- Key external packages and their purpose
|
|
86
|
+
- Internal module structure
|
|
87
|
+
|
|
88
|
+
6. **For Scout** (recommendations for deeper analysis)
|
|
89
|
+
- Which files/folders need closer inspection
|
|
90
|
+
- Potential complexity areas
|
|
91
|
+
- Suggested focus areas
|
|
92
|
+
|
|
93
|
+
Be concise but comprehensive. Use markdown formatting with headers and tables where appropriate.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def load_gitignore(path: Path) -> Set[str]:
|
|
98
|
+
"""Load patterns from .gitignore file."""
|
|
99
|
+
gitignore_path = path / '.gitignore'
|
|
100
|
+
patterns = set()
|
|
101
|
+
|
|
102
|
+
if gitignore_path.exists():
|
|
103
|
+
with open(gitignore_path, 'r') as f:
|
|
104
|
+
for line in f:
|
|
105
|
+
line = line.strip()
|
|
106
|
+
if line and not line.startswith('#'):
|
|
107
|
+
# Simple pattern handling
|
|
108
|
+
patterns.add(line.rstrip('/'))
|
|
109
|
+
|
|
110
|
+
return patterns
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def should_ignore(path: Path, ignore_patterns: Set[str]) -> bool:
|
|
114
|
+
"""Check if path should be ignored."""
|
|
115
|
+
name = path.name
|
|
116
|
+
|
|
117
|
+
# Check against ignore patterns
|
|
118
|
+
for pattern in ignore_patterns:
|
|
119
|
+
if pattern.startswith('*'):
|
|
120
|
+
if name.endswith(pattern[1:]):
|
|
121
|
+
return True
|
|
122
|
+
elif name == pattern or pattern in str(path):
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def collect_files(
|
|
129
|
+
root_path: Path,
|
|
130
|
+
extensions: Set[str],
|
|
131
|
+
ignore_patterns: Set[str],
|
|
132
|
+
max_files: int
|
|
133
|
+
) -> List[Path]:
|
|
134
|
+
"""Collect files from the directory."""
|
|
135
|
+
files = []
|
|
136
|
+
|
|
137
|
+
for path in root_path.rglob('*'):
|
|
138
|
+
if len(files) >= max_files:
|
|
139
|
+
break
|
|
140
|
+
|
|
141
|
+
if not path.is_file():
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
if should_ignore(path, ignore_patterns):
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
# Check if it's in INCLUDE_FILES or has valid extension
|
|
148
|
+
rel_path = str(path.relative_to(root_path))
|
|
149
|
+
if rel_path in INCLUDE_FILES or path.name in INCLUDE_FILES:
|
|
150
|
+
files.append(path)
|
|
151
|
+
elif path.suffix.lower() in extensions:
|
|
152
|
+
files.append(path)
|
|
153
|
+
|
|
154
|
+
return sorted(files)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def read_file_content(path: Path, max_lines: int = 500) -> str:
|
|
158
|
+
"""Read file content with line limit."""
|
|
159
|
+
try:
|
|
160
|
+
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
161
|
+
lines = f.readlines()
|
|
162
|
+
|
|
163
|
+
if len(lines) > max_lines:
|
|
164
|
+
content = ''.join(lines[:max_lines])
|
|
165
|
+
content += f"\n... (truncated, {len(lines) - max_lines} more lines)"
|
|
166
|
+
else:
|
|
167
|
+
content = ''.join(lines)
|
|
168
|
+
|
|
169
|
+
return content
|
|
170
|
+
except Exception as e:
|
|
171
|
+
return f"Error reading file: {e}"
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def build_codebase_content(root_path: Path, files: List[Path]) -> str:
|
|
175
|
+
"""Build the codebase content string."""
|
|
176
|
+
parts = []
|
|
177
|
+
|
|
178
|
+
# Add file tree first
|
|
179
|
+
parts.append("# File Structure\n")
|
|
180
|
+
parts.append("```")
|
|
181
|
+
|
|
182
|
+
# Build simple tree
|
|
183
|
+
for f in files[:100]: # Limit tree display
|
|
184
|
+
rel_path = f.relative_to(root_path)
|
|
185
|
+
parts.append(str(rel_path))
|
|
186
|
+
|
|
187
|
+
if len(files) > 100:
|
|
188
|
+
parts.append(f"... and {len(files) - 100} more files")
|
|
189
|
+
|
|
190
|
+
parts.append("```\n")
|
|
191
|
+
|
|
192
|
+
# Add file contents
|
|
193
|
+
parts.append("# File Contents\n")
|
|
194
|
+
|
|
195
|
+
for f in files:
|
|
196
|
+
rel_path = f.relative_to(root_path)
|
|
197
|
+
content = read_file_content(f)
|
|
198
|
+
|
|
199
|
+
parts.append(f"\n## {rel_path}\n")
|
|
200
|
+
parts.append(f"```{f.suffix[1:] if f.suffix else ''}")
|
|
201
|
+
parts.append(content)
|
|
202
|
+
parts.append("```\n")
|
|
203
|
+
|
|
204
|
+
return '\n'.join(parts)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def scan_with_gemini(content: str, prompt: str, api_key: str) -> str:
|
|
208
|
+
"""Send content to Gemini and get summary."""
|
|
209
|
+
genai.configure(api_key=api_key)
|
|
210
|
+
|
|
211
|
+
model = genai.GenerativeModel(
|
|
212
|
+
model_name="gemini-1.5-flash",
|
|
213
|
+
generation_config={
|
|
214
|
+
"temperature": 0.3,
|
|
215
|
+
"max_output_tokens": 8192,
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
full_prompt = f"{prompt}\n\n---\n\n{content}"
|
|
220
|
+
|
|
221
|
+
print(f"Sending to Gemini ({len(full_prompt):,} characters)...")
|
|
222
|
+
|
|
223
|
+
response = model.generate_content(full_prompt)
|
|
224
|
+
|
|
225
|
+
return response.text
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def main():
|
|
229
|
+
parser = argparse.ArgumentParser(
|
|
230
|
+
description='Scan codebase with Gemini Flash'
|
|
231
|
+
)
|
|
232
|
+
parser.add_argument(
|
|
233
|
+
'--path', '-p',
|
|
234
|
+
type=str,
|
|
235
|
+
default='.',
|
|
236
|
+
help='Path to scan (default: current directory)'
|
|
237
|
+
)
|
|
238
|
+
parser.add_argument(
|
|
239
|
+
'--output', '-o',
|
|
240
|
+
type=str,
|
|
241
|
+
help='Output file path (default: stdout)'
|
|
242
|
+
)
|
|
243
|
+
parser.add_argument(
|
|
244
|
+
'--prompt',
|
|
245
|
+
type=str,
|
|
246
|
+
default=DEFAULT_PROMPT,
|
|
247
|
+
help='Custom prompt for Gemini'
|
|
248
|
+
)
|
|
249
|
+
parser.add_argument(
|
|
250
|
+
'--extensions', '-e',
|
|
251
|
+
type=str,
|
|
252
|
+
nargs='+',
|
|
253
|
+
help='File extensions to include (e.g., .py .js)'
|
|
254
|
+
)
|
|
255
|
+
parser.add_argument(
|
|
256
|
+
'--max-files', '-m',
|
|
257
|
+
type=int,
|
|
258
|
+
default=500,
|
|
259
|
+
help='Maximum files to process (default: 500)'
|
|
260
|
+
)
|
|
261
|
+
parser.add_argument(
|
|
262
|
+
'--ignore', '-i',
|
|
263
|
+
type=str,
|
|
264
|
+
nargs='+',
|
|
265
|
+
help='Additional patterns to ignore'
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
args = parser.parse_args()
|
|
269
|
+
|
|
270
|
+
# Check API key
|
|
271
|
+
api_key = os.environ.get('GEMINI_API_KEY')
|
|
272
|
+
if not api_key:
|
|
273
|
+
print("Error: GEMINI_API_KEY environment variable not set")
|
|
274
|
+
print("Run the setup script: ./scripts/setup.sh")
|
|
275
|
+
sys.exit(1)
|
|
276
|
+
|
|
277
|
+
# Setup paths
|
|
278
|
+
root_path = Path(args.path).resolve()
|
|
279
|
+
if not root_path.exists():
|
|
280
|
+
print(f"Error: Path does not exist: {root_path}")
|
|
281
|
+
sys.exit(1)
|
|
282
|
+
|
|
283
|
+
print(f"Scanning: {root_path}")
|
|
284
|
+
|
|
285
|
+
# Setup extensions
|
|
286
|
+
extensions = set(args.extensions) if args.extensions else DEFAULT_EXTENSIONS
|
|
287
|
+
|
|
288
|
+
# Setup ignore patterns
|
|
289
|
+
ignore_patterns = DEFAULT_IGNORE.copy()
|
|
290
|
+
ignore_patterns.update(load_gitignore(root_path))
|
|
291
|
+
if args.ignore:
|
|
292
|
+
ignore_patterns.update(args.ignore)
|
|
293
|
+
|
|
294
|
+
# Collect files
|
|
295
|
+
print("Collecting files...")
|
|
296
|
+
files = collect_files(root_path, extensions, ignore_patterns, args.max_files)
|
|
297
|
+
print(f"Found {len(files)} files")
|
|
298
|
+
|
|
299
|
+
if not files:
|
|
300
|
+
print("No files found to scan")
|
|
301
|
+
sys.exit(1)
|
|
302
|
+
|
|
303
|
+
# Build content
|
|
304
|
+
print("Building content...")
|
|
305
|
+
content = build_codebase_content(root_path, files)
|
|
306
|
+
|
|
307
|
+
# Scan with Gemini
|
|
308
|
+
try:
|
|
309
|
+
result = scan_with_gemini(content, args.prompt, api_key)
|
|
310
|
+
except Exception as e:
|
|
311
|
+
print(f"Error calling Gemini API: {e}")
|
|
312
|
+
sys.exit(1)
|
|
313
|
+
|
|
314
|
+
# Add metadata header
|
|
315
|
+
output = f"""# Gemini Codebase Summary
|
|
316
|
+
|
|
317
|
+
> Generated by: gemini-scan.py
|
|
318
|
+
> Path: {root_path}
|
|
319
|
+
> Files scanned: {len(files)}
|
|
320
|
+
> Model: gemini-1.5-flash
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
{result}
|
|
325
|
+
"""
|
|
326
|
+
|
|
327
|
+
# Output
|
|
328
|
+
if args.output:
|
|
329
|
+
output_path = Path(args.output)
|
|
330
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
331
|
+
with open(output_path, 'w') as f:
|
|
332
|
+
f.write(output)
|
|
333
|
+
print(f"Output written to: {output_path}")
|
|
334
|
+
else:
|
|
335
|
+
print("\n" + "="*60 + "\n")
|
|
336
|
+
print(output)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
if __name__ == '__main__':
|
|
340
|
+
main()
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Gemini Setup Script
|
|
4
|
+
# Sets up API key and verifies connection
|
|
5
|
+
|
|
6
|
+
echo "========================================"
|
|
7
|
+
echo " Gemini Flash Setup for Claude Code"
|
|
8
|
+
echo "========================================"
|
|
9
|
+
echo ""
|
|
10
|
+
|
|
11
|
+
# Colors
|
|
12
|
+
RED='\033[0;31m'
|
|
13
|
+
GREEN='\033[0;32m'
|
|
14
|
+
YELLOW='\033[1;33m'
|
|
15
|
+
NC='\033[0m' # No Color
|
|
16
|
+
|
|
17
|
+
# Check if API key already exists
|
|
18
|
+
if [ -n "$GEMINI_API_KEY" ]; then
|
|
19
|
+
echo -e "${GREEN}[OK]${NC} GEMINI_API_KEY found in environment"
|
|
20
|
+
EXISTING_KEY=true
|
|
21
|
+
else
|
|
22
|
+
echo -e "${YELLOW}[!]${NC} GEMINI_API_KEY not found"
|
|
23
|
+
EXISTING_KEY=false
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Check .env file
|
|
27
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
28
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
29
|
+
ENV_FILE="$PROJECT_ROOT/.env"
|
|
30
|
+
|
|
31
|
+
if [ -f "$ENV_FILE" ]; then
|
|
32
|
+
if grep -q "GEMINI_API_KEY" "$ENV_FILE"; then
|
|
33
|
+
echo -e "${GREEN}[OK]${NC} GEMINI_API_KEY found in .env file"
|
|
34
|
+
# Source it
|
|
35
|
+
export $(grep "GEMINI_API_KEY" "$ENV_FILE" | xargs)
|
|
36
|
+
EXISTING_KEY=true
|
|
37
|
+
fi
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
echo ""
|
|
41
|
+
|
|
42
|
+
if [ "$EXISTING_KEY" = false ]; then
|
|
43
|
+
echo "To get a Gemini API key:"
|
|
44
|
+
echo ""
|
|
45
|
+
echo "1. Go to: https://aistudio.google.com/app/apikey"
|
|
46
|
+
echo "2. Click 'Create API key'"
|
|
47
|
+
echo "3. Copy the key"
|
|
48
|
+
echo ""
|
|
49
|
+
read -p "Enter your Gemini API key: " API_KEY
|
|
50
|
+
|
|
51
|
+
if [ -z "$API_KEY" ]; then
|
|
52
|
+
echo -e "${RED}[ERROR]${NC} No API key provided"
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Save to .env file
|
|
57
|
+
echo ""
|
|
58
|
+
echo "Where to save the API key?"
|
|
59
|
+
echo "1. Project .env file (recommended)"
|
|
60
|
+
echo "2. Shell profile (~/.bashrc or ~/.zshrc)"
|
|
61
|
+
echo "3. Both"
|
|
62
|
+
read -p "Choice [1]: " SAVE_CHOICE
|
|
63
|
+
SAVE_CHOICE=${SAVE_CHOICE:-1}
|
|
64
|
+
|
|
65
|
+
case $SAVE_CHOICE in
|
|
66
|
+
1)
|
|
67
|
+
echo "GEMINI_API_KEY=$API_KEY" >> "$ENV_FILE"
|
|
68
|
+
echo -e "${GREEN}[OK]${NC} Saved to $ENV_FILE"
|
|
69
|
+
;;
|
|
70
|
+
2)
|
|
71
|
+
SHELL_PROFILE="$HOME/.bashrc"
|
|
72
|
+
if [ -f "$HOME/.zshrc" ]; then
|
|
73
|
+
SHELL_PROFILE="$HOME/.zshrc"
|
|
74
|
+
fi
|
|
75
|
+
echo "export GEMINI_API_KEY=$API_KEY" >> "$SHELL_PROFILE"
|
|
76
|
+
echo -e "${GREEN}[OK]${NC} Saved to $SHELL_PROFILE"
|
|
77
|
+
echo -e "${YELLOW}[!]${NC} Run: source $SHELL_PROFILE"
|
|
78
|
+
;;
|
|
79
|
+
3)
|
|
80
|
+
echo "GEMINI_API_KEY=$API_KEY" >> "$ENV_FILE"
|
|
81
|
+
SHELL_PROFILE="$HOME/.bashrc"
|
|
82
|
+
if [ -f "$HOME/.zshrc" ]; then
|
|
83
|
+
SHELL_PROFILE="$HOME/.zshrc"
|
|
84
|
+
fi
|
|
85
|
+
echo "export GEMINI_API_KEY=$API_KEY" >> "$SHELL_PROFILE"
|
|
86
|
+
echo -e "${GREEN}[OK]${NC} Saved to both"
|
|
87
|
+
;;
|
|
88
|
+
esac
|
|
89
|
+
|
|
90
|
+
export GEMINI_API_KEY="$API_KEY"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
echo ""
|
|
94
|
+
echo "Checking Python dependencies..."
|
|
95
|
+
|
|
96
|
+
# Check Python
|
|
97
|
+
if ! command -v python3 &> /dev/null; then
|
|
98
|
+
echo -e "${RED}[ERROR]${NC} Python 3 not found"
|
|
99
|
+
echo "Install Python 3 first: https://www.python.org/downloads/"
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
echo -e "${GREEN}[OK]${NC} Python 3 found"
|
|
103
|
+
|
|
104
|
+
# Check google-generativeai
|
|
105
|
+
if python3 -c "import google.generativeai" 2>/dev/null; then
|
|
106
|
+
echo -e "${GREEN}[OK]${NC} google-generativeai installed"
|
|
107
|
+
else
|
|
108
|
+
echo -e "${YELLOW}[!]${NC} google-generativeai not installed"
|
|
109
|
+
read -p "Install now? [Y/n]: " INSTALL
|
|
110
|
+
INSTALL=${INSTALL:-Y}
|
|
111
|
+
if [[ $INSTALL =~ ^[Yy]$ ]]; then
|
|
112
|
+
pip install google-generativeai
|
|
113
|
+
if [ $? -eq 0 ]; then
|
|
114
|
+
echo -e "${GREEN}[OK]${NC} Installed successfully"
|
|
115
|
+
else
|
|
116
|
+
echo -e "${RED}[ERROR]${NC} Installation failed"
|
|
117
|
+
exit 1
|
|
118
|
+
fi
|
|
119
|
+
else
|
|
120
|
+
echo "Please run: pip install google-generativeai"
|
|
121
|
+
exit 1
|
|
122
|
+
fi
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
echo ""
|
|
126
|
+
echo "Testing connection..."
|
|
127
|
+
|
|
128
|
+
# Test API
|
|
129
|
+
python3 << 'EOF'
|
|
130
|
+
import os
|
|
131
|
+
import sys
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
import google.generativeai as genai
|
|
135
|
+
except ImportError:
|
|
136
|
+
print("ERROR: google-generativeai not installed")
|
|
137
|
+
sys.exit(1)
|
|
138
|
+
|
|
139
|
+
api_key = os.environ.get("GEMINI_API_KEY")
|
|
140
|
+
if not api_key:
|
|
141
|
+
print("ERROR: GEMINI_API_KEY not set")
|
|
142
|
+
sys.exit(1)
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
genai.configure(api_key=api_key)
|
|
146
|
+
model = genai.GenerativeModel("gemini-1.5-flash")
|
|
147
|
+
response = model.generate_content("Say 'Hello from Gemini!' in exactly those words.")
|
|
148
|
+
print(f"Response: {response.text.strip()}")
|
|
149
|
+
print("SUCCESS: Gemini API working!")
|
|
150
|
+
except Exception as e:
|
|
151
|
+
print(f"ERROR: {e}")
|
|
152
|
+
sys.exit(1)
|
|
153
|
+
EOF
|
|
154
|
+
|
|
155
|
+
if [ $? -eq 0 ]; then
|
|
156
|
+
echo ""
|
|
157
|
+
echo -e "${GREEN}========================================"
|
|
158
|
+
echo " Setup Complete!"
|
|
159
|
+
echo "========================================${NC}"
|
|
160
|
+
echo ""
|
|
161
|
+
echo "You can now use Gemini for codebase scanning:"
|
|
162
|
+
echo ""
|
|
163
|
+
echo " python scripts/gemini-scan.py --path /your/project"
|
|
164
|
+
echo ""
|
|
165
|
+
else
|
|
166
|
+
echo ""
|
|
167
|
+
echo -e "${RED}Setup failed. Check errors above.${NC}"
|
|
168
|
+
exit 1
|
|
169
|
+
fi
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* add command
|
|
3
|
+
*
|
|
4
|
+
* Add a specific skill to the project.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import ora from 'ora';
|
|
9
|
+
|
|
10
|
+
import { getAvailableSkills, getInstalledSkills, copySkillsToProject } from '../utils/skills.js';
|
|
11
|
+
|
|
12
|
+
export async function add(skillName) {
|
|
13
|
+
const projectPath = process.cwd();
|
|
14
|
+
|
|
15
|
+
console.log(chalk.bold(`\n➕ Adding skill: ${skillName}\n`));
|
|
16
|
+
|
|
17
|
+
// Check if skill exists
|
|
18
|
+
const available = await getAvailableSkills();
|
|
19
|
+
const skill = available.find(s => s.name === skillName);
|
|
20
|
+
|
|
21
|
+
if (!skill) {
|
|
22
|
+
console.log(chalk.red(`❌ Skill not found: ${skillName}\n`));
|
|
23
|
+
console.log('Available skills:');
|
|
24
|
+
|
|
25
|
+
for (const s of available.filter(s => s.type !== 'config')) {
|
|
26
|
+
console.log(` - ${s.name}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log('');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check if already installed
|
|
34
|
+
const installed = await getInstalledSkills(projectPath);
|
|
35
|
+
const alreadyInstalled = installed.find(s => s.name === skillName);
|
|
36
|
+
|
|
37
|
+
if (alreadyInstalled) {
|
|
38
|
+
console.log(chalk.yellow(`⚠️ Skill already installed: ${skillName}\n`));
|
|
39
|
+
console.log(chalk.gray('Use `claude-skills update` to update to latest version.\n'));
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Install the skill
|
|
44
|
+
const spinner = ora(`Installing ${skillName}...`).start();
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const { copied, errors } = await copySkillsToProject(projectPath, [skillName]);
|
|
48
|
+
|
|
49
|
+
if (errors.length > 0) {
|
|
50
|
+
spinner.fail(`Failed to install ${skillName}`);
|
|
51
|
+
console.log(chalk.red(errors[0].error));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
spinner.succeed(`Installed ${skillName}`);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
spinner.fail('Installation failed');
|
|
58
|
+
console.error(chalk.red(e.message));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log(chalk.green(`\n✅ ${skillName} added successfully!\n`));
|
|
63
|
+
console.log(`Try it: ${chalk.cyan(`/${skillName}`)}\n`);
|
|
64
|
+
}
|