skill-seekers 2.7.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.
- skill_seekers/__init__.py +22 -0
- skill_seekers/cli/__init__.py +39 -0
- skill_seekers/cli/adaptors/__init__.py +120 -0
- skill_seekers/cli/adaptors/base.py +221 -0
- skill_seekers/cli/adaptors/claude.py +485 -0
- skill_seekers/cli/adaptors/gemini.py +453 -0
- skill_seekers/cli/adaptors/markdown.py +269 -0
- skill_seekers/cli/adaptors/openai.py +503 -0
- skill_seekers/cli/ai_enhancer.py +310 -0
- skill_seekers/cli/api_reference_builder.py +373 -0
- skill_seekers/cli/architectural_pattern_detector.py +525 -0
- skill_seekers/cli/code_analyzer.py +1462 -0
- skill_seekers/cli/codebase_scraper.py +1225 -0
- skill_seekers/cli/config_command.py +563 -0
- skill_seekers/cli/config_enhancer.py +431 -0
- skill_seekers/cli/config_extractor.py +871 -0
- skill_seekers/cli/config_manager.py +452 -0
- skill_seekers/cli/config_validator.py +394 -0
- skill_seekers/cli/conflict_detector.py +528 -0
- skill_seekers/cli/constants.py +72 -0
- skill_seekers/cli/dependency_analyzer.py +757 -0
- skill_seekers/cli/doc_scraper.py +2332 -0
- skill_seekers/cli/enhance_skill.py +488 -0
- skill_seekers/cli/enhance_skill_local.py +1096 -0
- skill_seekers/cli/enhance_status.py +194 -0
- skill_seekers/cli/estimate_pages.py +433 -0
- skill_seekers/cli/generate_router.py +1209 -0
- skill_seekers/cli/github_fetcher.py +534 -0
- skill_seekers/cli/github_scraper.py +1466 -0
- skill_seekers/cli/guide_enhancer.py +723 -0
- skill_seekers/cli/how_to_guide_builder.py +1267 -0
- skill_seekers/cli/install_agent.py +461 -0
- skill_seekers/cli/install_skill.py +178 -0
- skill_seekers/cli/language_detector.py +614 -0
- skill_seekers/cli/llms_txt_detector.py +60 -0
- skill_seekers/cli/llms_txt_downloader.py +104 -0
- skill_seekers/cli/llms_txt_parser.py +150 -0
- skill_seekers/cli/main.py +558 -0
- skill_seekers/cli/markdown_cleaner.py +132 -0
- skill_seekers/cli/merge_sources.py +806 -0
- skill_seekers/cli/package_multi.py +77 -0
- skill_seekers/cli/package_skill.py +241 -0
- skill_seekers/cli/pattern_recognizer.py +1825 -0
- skill_seekers/cli/pdf_extractor_poc.py +1166 -0
- skill_seekers/cli/pdf_scraper.py +617 -0
- skill_seekers/cli/quality_checker.py +519 -0
- skill_seekers/cli/rate_limit_handler.py +438 -0
- skill_seekers/cli/resume_command.py +160 -0
- skill_seekers/cli/run_tests.py +230 -0
- skill_seekers/cli/setup_wizard.py +93 -0
- skill_seekers/cli/split_config.py +390 -0
- skill_seekers/cli/swift_patterns.py +560 -0
- skill_seekers/cli/test_example_extractor.py +1081 -0
- skill_seekers/cli/test_unified_simple.py +179 -0
- skill_seekers/cli/unified_codebase_analyzer.py +572 -0
- skill_seekers/cli/unified_scraper.py +932 -0
- skill_seekers/cli/unified_skill_builder.py +1605 -0
- skill_seekers/cli/upload_skill.py +162 -0
- skill_seekers/cli/utils.py +432 -0
- skill_seekers/mcp/__init__.py +33 -0
- skill_seekers/mcp/agent_detector.py +316 -0
- skill_seekers/mcp/git_repo.py +273 -0
- skill_seekers/mcp/server.py +231 -0
- skill_seekers/mcp/server_fastmcp.py +1249 -0
- skill_seekers/mcp/server_legacy.py +2302 -0
- skill_seekers/mcp/source_manager.py +285 -0
- skill_seekers/mcp/tools/__init__.py +115 -0
- skill_seekers/mcp/tools/config_tools.py +251 -0
- skill_seekers/mcp/tools/packaging_tools.py +826 -0
- skill_seekers/mcp/tools/scraping_tools.py +842 -0
- skill_seekers/mcp/tools/source_tools.py +828 -0
- skill_seekers/mcp/tools/splitting_tools.py +212 -0
- skill_seekers/py.typed +0 -0
- skill_seekers-2.7.3.dist-info/METADATA +2027 -0
- skill_seekers-2.7.3.dist-info/RECORD +79 -0
- skill_seekers-2.7.3.dist-info/WHEEL +5 -0
- skill_seekers-2.7.3.dist-info/entry_points.txt +19 -0
- skill_seekers-2.7.3.dist-info/licenses/LICENSE +21 -0
- skill_seekers-2.7.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
SKILL.md Enhancement Script
|
|
4
|
+
Uses platform AI APIs to improve SKILL.md by analyzing reference documentation.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
# Claude (default)
|
|
8
|
+
skill-seekers enhance output/react/
|
|
9
|
+
skill-seekers enhance output/react/ --api-key sk-ant-...
|
|
10
|
+
|
|
11
|
+
# Gemini
|
|
12
|
+
skill-seekers enhance output/react/ --target gemini --api-key AIzaSy...
|
|
13
|
+
|
|
14
|
+
# OpenAI
|
|
15
|
+
skill-seekers enhance output/react/ --target openai --api-key sk-proj-...
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
# Add parent directory to path for imports when run as script
|
|
24
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
25
|
+
|
|
26
|
+
from skill_seekers.cli.constants import API_CONTENT_LIMIT, API_PREVIEW_LIMIT
|
|
27
|
+
from skill_seekers.cli.utils import read_reference_files
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
import anthropic
|
|
31
|
+
except ImportError:
|
|
32
|
+
print("ā Error: anthropic package not installed")
|
|
33
|
+
print("Install with: pip3 install anthropic")
|
|
34
|
+
sys.exit(1)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SkillEnhancer:
|
|
38
|
+
def __init__(self, skill_dir, api_key=None):
|
|
39
|
+
self.skill_dir = Path(skill_dir)
|
|
40
|
+
self.references_dir = self.skill_dir / "references"
|
|
41
|
+
self.skill_md_path = self.skill_dir / "SKILL.md"
|
|
42
|
+
|
|
43
|
+
# Get API key - support both ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN
|
|
44
|
+
self.api_key = (
|
|
45
|
+
api_key or os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("ANTHROPIC_AUTH_TOKEN")
|
|
46
|
+
)
|
|
47
|
+
if not self.api_key:
|
|
48
|
+
raise ValueError(
|
|
49
|
+
"No API key provided. Set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN "
|
|
50
|
+
"environment variable or use --api-key argument"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Support custom base URL for alternative API endpoints
|
|
54
|
+
base_url = os.environ.get("ANTHROPIC_BASE_URL")
|
|
55
|
+
client_kwargs = {"api_key": self.api_key}
|
|
56
|
+
if base_url:
|
|
57
|
+
client_kwargs["base_url"] = base_url
|
|
58
|
+
print(f"ā¹ļø Using custom API base URL: {base_url}")
|
|
59
|
+
|
|
60
|
+
self.client = anthropic.Anthropic(**client_kwargs)
|
|
61
|
+
|
|
62
|
+
def read_current_skill_md(self):
|
|
63
|
+
"""Read existing SKILL.md"""
|
|
64
|
+
if not self.skill_md_path.exists():
|
|
65
|
+
return None
|
|
66
|
+
return self.skill_md_path.read_text(encoding="utf-8")
|
|
67
|
+
|
|
68
|
+
def enhance_skill_md(self, references, current_skill_md):
|
|
69
|
+
"""Use Claude to enhance SKILL.md"""
|
|
70
|
+
|
|
71
|
+
# Build prompt
|
|
72
|
+
prompt = self._build_enhancement_prompt(references, current_skill_md)
|
|
73
|
+
|
|
74
|
+
print("\nš¤ Asking Claude to enhance SKILL.md...")
|
|
75
|
+
print(f" Input: {len(prompt):,} characters")
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
message = self.client.messages.create(
|
|
79
|
+
model="claude-sonnet-4-20250514",
|
|
80
|
+
max_tokens=4096,
|
|
81
|
+
temperature=0.3,
|
|
82
|
+
messages=[{"role": "user", "content": prompt}],
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Handle response content - newer SDK versions may include ThinkingBlock
|
|
86
|
+
# Find the TextBlock containing the actual response
|
|
87
|
+
enhanced_content = None
|
|
88
|
+
for block in message.content:
|
|
89
|
+
if hasattr(block, "text"):
|
|
90
|
+
enhanced_content = block.text
|
|
91
|
+
break
|
|
92
|
+
|
|
93
|
+
if not enhanced_content:
|
|
94
|
+
print("ā Error: No text content found in API response")
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
return enhanced_content
|
|
98
|
+
|
|
99
|
+
except Exception as e:
|
|
100
|
+
print(f"ā Error calling Claude API: {e}")
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
def _build_enhancement_prompt(self, references, current_skill_md):
|
|
104
|
+
"""Build the prompt for Claude with multi-source awareness"""
|
|
105
|
+
|
|
106
|
+
# Extract skill name and description
|
|
107
|
+
skill_name = self.skill_dir.name
|
|
108
|
+
|
|
109
|
+
# Analyze sources
|
|
110
|
+
sources_found = set()
|
|
111
|
+
for metadata in references.values():
|
|
112
|
+
sources_found.add(metadata["source"])
|
|
113
|
+
|
|
114
|
+
# Analyze conflicts if present
|
|
115
|
+
has_conflicts = any("conflicts" in meta["path"] for meta in references.values())
|
|
116
|
+
|
|
117
|
+
prompt = f"""You are enhancing a Claude skill's SKILL.md file. This skill is about: {skill_name}
|
|
118
|
+
|
|
119
|
+
I've scraped documentation from multiple sources and organized it into reference files. Your job is to create an EXCELLENT SKILL.md that synthesizes knowledge from these sources.
|
|
120
|
+
|
|
121
|
+
SKILL OVERVIEW:
|
|
122
|
+
- Name: {skill_name}
|
|
123
|
+
- Source Types: {", ".join(sorted(sources_found))}
|
|
124
|
+
- Multi-Source: {"Yes" if len(sources_found) > 1 else "No"}
|
|
125
|
+
- Conflicts Detected: {"Yes - see conflicts.md in references" if has_conflicts else "No"}
|
|
126
|
+
|
|
127
|
+
CURRENT SKILL.MD:
|
|
128
|
+
{"```markdown" if current_skill_md else "(none - create from scratch)"}
|
|
129
|
+
{current_skill_md or "No existing SKILL.md"}
|
|
130
|
+
{"```" if current_skill_md else ""}
|
|
131
|
+
|
|
132
|
+
SOURCE ANALYSIS:
|
|
133
|
+
This skill combines knowledge from {len(sources_found)} source type(s):
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
# Group references by (source_type, repo_id) for multi-source support
|
|
138
|
+
by_source = {}
|
|
139
|
+
for filename, metadata in references.items():
|
|
140
|
+
source = metadata["source"]
|
|
141
|
+
repo_id = metadata.get("repo_id") # None for single-source
|
|
142
|
+
key = (source, repo_id) if repo_id else (source, None)
|
|
143
|
+
|
|
144
|
+
if key not in by_source:
|
|
145
|
+
by_source[key] = []
|
|
146
|
+
by_source[key].append((filename, metadata))
|
|
147
|
+
|
|
148
|
+
# Add source breakdown with repo identity
|
|
149
|
+
for source, repo_id in sorted(by_source.keys()):
|
|
150
|
+
files = by_source[(source, repo_id)]
|
|
151
|
+
if repo_id:
|
|
152
|
+
prompt += f"\n**{source.upper()} - {repo_id} ({len(files)} file(s))**\n"
|
|
153
|
+
else:
|
|
154
|
+
prompt += f"\n**{source.upper()} ({len(files)} file(s))**\n"
|
|
155
|
+
for filename, metadata in files[:5]: # Top 5 per source
|
|
156
|
+
prompt += f"- {filename} (confidence: {metadata['confidence']}, {metadata['size']:,} chars)\n"
|
|
157
|
+
if len(files) > 5:
|
|
158
|
+
prompt += f"- ... and {len(files) - 5} more\n"
|
|
159
|
+
|
|
160
|
+
prompt += "\n\nREFERENCE DOCUMENTATION:\n"
|
|
161
|
+
|
|
162
|
+
# Add references grouped by (source, repo_id) with metadata
|
|
163
|
+
for source, repo_id in sorted(by_source.keys()):
|
|
164
|
+
if repo_id:
|
|
165
|
+
prompt += f"\n### {source.upper()} SOURCES - {repo_id}\n\n"
|
|
166
|
+
else:
|
|
167
|
+
prompt += f"\n### {source.upper()} SOURCES\n\n"
|
|
168
|
+
|
|
169
|
+
for filename, metadata in by_source[(source, repo_id)]:
|
|
170
|
+
content = metadata["content"]
|
|
171
|
+
# Limit per-file to 30K
|
|
172
|
+
if len(content) > 30000:
|
|
173
|
+
content = content[:30000] + "\n\n[Content truncated for size...]"
|
|
174
|
+
|
|
175
|
+
prompt += f"\n#### {filename}\n"
|
|
176
|
+
if repo_id:
|
|
177
|
+
prompt += f"*Source: {metadata['source']} ({repo_id}), Confidence: {metadata['confidence']}*\n\n"
|
|
178
|
+
else:
|
|
179
|
+
prompt += (
|
|
180
|
+
f"*Source: {metadata['source']}, Confidence: {metadata['confidence']}*\n\n"
|
|
181
|
+
)
|
|
182
|
+
prompt += f"```markdown\n{content}\n```\n"
|
|
183
|
+
|
|
184
|
+
prompt += """
|
|
185
|
+
|
|
186
|
+
REFERENCE PRIORITY (when sources differ):
|
|
187
|
+
1. **Code patterns (codebase_analysis)**: Ground truth - what the code actually does
|
|
188
|
+
2. **Official documentation**: Intended API and usage patterns
|
|
189
|
+
3. **GitHub issues**: Real-world usage and known problems
|
|
190
|
+
4. **PDF documentation**: Additional context and tutorials
|
|
191
|
+
|
|
192
|
+
MULTI-REPOSITORY HANDLING:
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
# Detect multiple repos from same source type
|
|
196
|
+
repo_ids = set()
|
|
197
|
+
for metadata in references.values():
|
|
198
|
+
if metadata.get("repo_id"):
|
|
199
|
+
repo_ids.add(metadata["repo_id"])
|
|
200
|
+
|
|
201
|
+
if len(repo_ids) > 1:
|
|
202
|
+
prompt += f"""
|
|
203
|
+
ā ļø MULTIPLE REPOSITORIES DETECTED: {", ".join(sorted(repo_ids))}
|
|
204
|
+
|
|
205
|
+
This skill combines codebase analysis from {len(repo_ids)} different repositories.
|
|
206
|
+
Each repo has its own ARCHITECTURE.md, patterns, examples, and configuration.
|
|
207
|
+
|
|
208
|
+
When synthesizing:
|
|
209
|
+
- Clearly identify which content comes from which repo
|
|
210
|
+
- Compare and contrast patterns across repos (e.g., "httpx uses Strategy pattern 50 times, httpcore uses it 32 times")
|
|
211
|
+
- Highlight relationships (e.g., "httpx is a client library built on top of httpcore")
|
|
212
|
+
- Present examples from BOTH repos to show different use cases
|
|
213
|
+
- If repos serve different purposes, explain when to use each
|
|
214
|
+
"""
|
|
215
|
+
else:
|
|
216
|
+
prompt += "\nSingle repository - standard synthesis applies.\n"
|
|
217
|
+
|
|
218
|
+
prompt += """
|
|
219
|
+
|
|
220
|
+
YOUR TASK:
|
|
221
|
+
Create an enhanced SKILL.md that synthesizes knowledge from multiple sources:
|
|
222
|
+
|
|
223
|
+
1. **Multi-Source Synthesis**
|
|
224
|
+
- Acknowledge that this skill combines multiple sources
|
|
225
|
+
- Highlight agreements between sources (builds confidence)
|
|
226
|
+
- Note discrepancies transparently (if present)
|
|
227
|
+
- Use source priority when synthesizing conflicting information
|
|
228
|
+
|
|
229
|
+
2. **Clear "When to Use This Skill" section**
|
|
230
|
+
- Be SPECIFIC about trigger conditions
|
|
231
|
+
- List concrete use cases
|
|
232
|
+
- Include perspective from both docs AND real-world usage (if GitHub/codebase data available)
|
|
233
|
+
|
|
234
|
+
3. **Excellent Quick Reference section**
|
|
235
|
+
- Extract 5-10 of the BEST, most practical code examples
|
|
236
|
+
- Prefer examples from HIGH CONFIDENCE sources first
|
|
237
|
+
- If code examples exist from codebase analysis, prioritize those (real usage)
|
|
238
|
+
- If docs examples exist, include those too (official patterns)
|
|
239
|
+
- Choose SHORT, clear examples (5-20 lines max)
|
|
240
|
+
- Use proper language tags (cpp, python, javascript, json, etc.)
|
|
241
|
+
- Add clear descriptions noting the source (e.g., "From official docs" or "From codebase")
|
|
242
|
+
|
|
243
|
+
4. **Detailed Reference Files description**
|
|
244
|
+
- Explain what's in each reference file
|
|
245
|
+
- Note the source type and confidence level
|
|
246
|
+
- Help users navigate multi-source documentation
|
|
247
|
+
|
|
248
|
+
5. **Practical "Working with This Skill" section**
|
|
249
|
+
- Clear guidance for beginners, intermediate, and advanced users
|
|
250
|
+
- Navigation tips for multi-source references
|
|
251
|
+
- How to resolve conflicts if present
|
|
252
|
+
|
|
253
|
+
6. **Key Concepts section** (if applicable)
|
|
254
|
+
- Explain core concepts
|
|
255
|
+
- Define important terminology
|
|
256
|
+
- Reconcile differences between sources if needed
|
|
257
|
+
|
|
258
|
+
7. **Conflict Handling** (if conflicts detected)
|
|
259
|
+
- Add a "Known Discrepancies" section
|
|
260
|
+
- Explain major conflicts transparently
|
|
261
|
+
- Provide guidance on which source to trust in each case
|
|
262
|
+
|
|
263
|
+
8. **Keep the frontmatter** (---\nname: ...\n---) intact
|
|
264
|
+
|
|
265
|
+
IMPORTANT:
|
|
266
|
+
- Extract REAL examples from the reference docs, don't make them up
|
|
267
|
+
- Prioritize HIGH CONFIDENCE sources when synthesizing
|
|
268
|
+
- Note source attribution when helpful (e.g., "Official docs say X, but codebase shows Y")
|
|
269
|
+
- Make discrepancies transparent, not hidden
|
|
270
|
+
- Prioritize SHORT, clear examples (5-20 lines max)
|
|
271
|
+
- Make it actionable and practical
|
|
272
|
+
- Don't be too verbose - be concise but useful
|
|
273
|
+
- Maintain the markdown structure for Claude skills
|
|
274
|
+
- Keep code examples properly formatted with language tags
|
|
275
|
+
|
|
276
|
+
OUTPUT:
|
|
277
|
+
Return ONLY the complete SKILL.md content, starting with the frontmatter (---).
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
return prompt
|
|
281
|
+
|
|
282
|
+
def save_enhanced_skill_md(self, content):
|
|
283
|
+
"""Save the enhanced SKILL.md"""
|
|
284
|
+
# Backup original
|
|
285
|
+
if self.skill_md_path.exists():
|
|
286
|
+
backup_path = self.skill_md_path.with_suffix(".md.backup")
|
|
287
|
+
self.skill_md_path.rename(backup_path)
|
|
288
|
+
print(f" š¾ Backed up original to: {backup_path.name}")
|
|
289
|
+
|
|
290
|
+
# Save enhanced version
|
|
291
|
+
self.skill_md_path.write_text(content, encoding="utf-8")
|
|
292
|
+
print(" ā
Saved enhanced SKILL.md")
|
|
293
|
+
|
|
294
|
+
def run(self):
|
|
295
|
+
"""Main enhancement workflow"""
|
|
296
|
+
print(f"\n{'=' * 60}")
|
|
297
|
+
print(f"ENHANCING SKILL: {self.skill_dir.name}")
|
|
298
|
+
print(f"{'=' * 60}\n")
|
|
299
|
+
|
|
300
|
+
# Read reference files
|
|
301
|
+
print("š Reading reference documentation...")
|
|
302
|
+
references = read_reference_files(
|
|
303
|
+
self.skill_dir, max_chars=API_CONTENT_LIMIT, preview_limit=API_PREVIEW_LIMIT
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
if not references:
|
|
307
|
+
print("ā No reference files found to analyze")
|
|
308
|
+
return False
|
|
309
|
+
|
|
310
|
+
# Analyze sources
|
|
311
|
+
sources_found = set()
|
|
312
|
+
for metadata in references.values():
|
|
313
|
+
sources_found.add(metadata["source"])
|
|
314
|
+
|
|
315
|
+
print(f" ā Read {len(references)} reference files")
|
|
316
|
+
print(f" ā Sources: {', '.join(sorted(sources_found))}")
|
|
317
|
+
total_size = sum(meta["size"] for meta in references.values())
|
|
318
|
+
print(f" ā Total size: {total_size:,} characters\n")
|
|
319
|
+
|
|
320
|
+
# Read current SKILL.md
|
|
321
|
+
current_skill_md = self.read_current_skill_md()
|
|
322
|
+
if current_skill_md:
|
|
323
|
+
print(f" ā¹ Found existing SKILL.md ({len(current_skill_md)} chars)")
|
|
324
|
+
else:
|
|
325
|
+
print(" ā¹ No existing SKILL.md, will create new one")
|
|
326
|
+
|
|
327
|
+
# Enhance with Claude
|
|
328
|
+
enhanced = self.enhance_skill_md(references, current_skill_md)
|
|
329
|
+
|
|
330
|
+
if not enhanced:
|
|
331
|
+
print("ā Enhancement failed")
|
|
332
|
+
return False
|
|
333
|
+
|
|
334
|
+
print(f" ā Generated enhanced SKILL.md ({len(enhanced)} chars)\n")
|
|
335
|
+
|
|
336
|
+
# Save
|
|
337
|
+
print("š¾ Saving enhanced SKILL.md...")
|
|
338
|
+
self.save_enhanced_skill_md(enhanced)
|
|
339
|
+
|
|
340
|
+
print("\nā
Enhancement complete!")
|
|
341
|
+
print("\nNext steps:")
|
|
342
|
+
print(f" 1. Review: {self.skill_md_path}")
|
|
343
|
+
print(
|
|
344
|
+
f" 2. If you don't like it, restore backup: {self.skill_md_path.with_suffix('.md.backup')}"
|
|
345
|
+
)
|
|
346
|
+
print(" 3. Package your skill:")
|
|
347
|
+
print(f" skill-seekers package {self.skill_dir}/")
|
|
348
|
+
|
|
349
|
+
return True
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def main():
|
|
353
|
+
parser = argparse.ArgumentParser(
|
|
354
|
+
description="Enhance SKILL.md using platform AI APIs",
|
|
355
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
356
|
+
epilog="""
|
|
357
|
+
Examples:
|
|
358
|
+
# Claude (default)
|
|
359
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
360
|
+
skill-seekers enhance output/react/
|
|
361
|
+
|
|
362
|
+
# Gemini
|
|
363
|
+
export GOOGLE_API_KEY=AIzaSy...
|
|
364
|
+
skill-seekers enhance output/react/ --target gemini
|
|
365
|
+
|
|
366
|
+
# OpenAI
|
|
367
|
+
export OPENAI_API_KEY=sk-proj-...
|
|
368
|
+
skill-seekers enhance output/react/ --target openai
|
|
369
|
+
|
|
370
|
+
# With explicit API key
|
|
371
|
+
skill-seekers enhance output/react/ --api-key sk-ant-...
|
|
372
|
+
|
|
373
|
+
# Dry run
|
|
374
|
+
skill-seekers enhance output/godot/ --dry-run
|
|
375
|
+
""",
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
parser.add_argument(
|
|
379
|
+
"skill_dir", type=str, help="Path to skill directory (e.g., output/steam-inventory/)"
|
|
380
|
+
)
|
|
381
|
+
parser.add_argument(
|
|
382
|
+
"--api-key", type=str, help="Platform API key (or set environment variable)"
|
|
383
|
+
)
|
|
384
|
+
parser.add_argument(
|
|
385
|
+
"--target",
|
|
386
|
+
choices=["claude", "gemini", "openai"],
|
|
387
|
+
default="claude",
|
|
388
|
+
help="Target LLM platform (default: claude)",
|
|
389
|
+
)
|
|
390
|
+
parser.add_argument(
|
|
391
|
+
"--dry-run", action="store_true", help="Show what would be done without calling API"
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
args = parser.parse_args()
|
|
395
|
+
|
|
396
|
+
# Validate skill directory
|
|
397
|
+
skill_dir = Path(args.skill_dir)
|
|
398
|
+
if not skill_dir.exists():
|
|
399
|
+
print(f"ā Error: Directory not found: {skill_dir}")
|
|
400
|
+
sys.exit(1)
|
|
401
|
+
|
|
402
|
+
if not skill_dir.is_dir():
|
|
403
|
+
print(f"ā Error: Not a directory: {skill_dir}")
|
|
404
|
+
sys.exit(1)
|
|
405
|
+
|
|
406
|
+
# Dry run mode
|
|
407
|
+
if args.dry_run:
|
|
408
|
+
print("š DRY RUN MODE")
|
|
409
|
+
print(f" Would enhance: {skill_dir}")
|
|
410
|
+
print(f" References: {skill_dir / 'references'}")
|
|
411
|
+
print(f" SKILL.md: {skill_dir / 'SKILL.md'}")
|
|
412
|
+
|
|
413
|
+
refs_dir = skill_dir / "references"
|
|
414
|
+
if refs_dir.exists():
|
|
415
|
+
ref_files = list(refs_dir.glob("*.md"))
|
|
416
|
+
print(f" Found {len(ref_files)} reference files:")
|
|
417
|
+
for rf in ref_files:
|
|
418
|
+
size = rf.stat().st_size
|
|
419
|
+
print(f" - {rf.name} ({size:,} bytes)")
|
|
420
|
+
|
|
421
|
+
print("\nTo actually run enhancement:")
|
|
422
|
+
print(f" skill-seekers enhance {skill_dir}")
|
|
423
|
+
return
|
|
424
|
+
|
|
425
|
+
# Check if platform supports enhancement
|
|
426
|
+
try:
|
|
427
|
+
from skill_seekers.cli.adaptors import get_adaptor
|
|
428
|
+
|
|
429
|
+
adaptor = get_adaptor(args.target)
|
|
430
|
+
|
|
431
|
+
if not adaptor.supports_enhancement():
|
|
432
|
+
print(f"ā Error: {adaptor.PLATFORM_NAME} does not support AI enhancement")
|
|
433
|
+
print("\nSupported platforms for enhancement:")
|
|
434
|
+
print(" - Claude AI (Anthropic)")
|
|
435
|
+
print(" - Google Gemini")
|
|
436
|
+
print(" - OpenAI ChatGPT")
|
|
437
|
+
sys.exit(1)
|
|
438
|
+
|
|
439
|
+
# Get API key
|
|
440
|
+
api_key = args.api_key
|
|
441
|
+
if not api_key:
|
|
442
|
+
api_key = os.environ.get(adaptor.get_env_var_name(), "").strip()
|
|
443
|
+
|
|
444
|
+
if not api_key:
|
|
445
|
+
print(f"ā Error: {adaptor.get_env_var_name()} not set")
|
|
446
|
+
print(f"\nSet your API key for {adaptor.PLATFORM_NAME}:")
|
|
447
|
+
print(f" export {adaptor.get_env_var_name()}=...")
|
|
448
|
+
print("Or provide it directly:")
|
|
449
|
+
print(f" skill-seekers enhance {skill_dir} --target {args.target} --api-key ...")
|
|
450
|
+
sys.exit(1)
|
|
451
|
+
|
|
452
|
+
# Run enhancement using adaptor
|
|
453
|
+
print(f"\n{'=' * 60}")
|
|
454
|
+
print(f"ENHANCING SKILL: {skill_dir}")
|
|
455
|
+
print(f"Platform: {adaptor.PLATFORM_NAME}")
|
|
456
|
+
print(f"{'=' * 60}\n")
|
|
457
|
+
|
|
458
|
+
success = adaptor.enhance(Path(skill_dir), api_key)
|
|
459
|
+
|
|
460
|
+
if success:
|
|
461
|
+
print("\nā
Enhancement complete!")
|
|
462
|
+
print("\nNext steps:")
|
|
463
|
+
print(f" 1. Review: {Path(skill_dir) / 'SKILL.md'}")
|
|
464
|
+
print(
|
|
465
|
+
f" 2. If you don't like it, restore backup: {Path(skill_dir) / 'SKILL.md.backup'}"
|
|
466
|
+
)
|
|
467
|
+
print(" 3. Package your skill:")
|
|
468
|
+
print(f" skill-seekers package {skill_dir}/ --target {args.target}")
|
|
469
|
+
|
|
470
|
+
sys.exit(0 if success else 1)
|
|
471
|
+
|
|
472
|
+
except ImportError as e:
|
|
473
|
+
print(f"ā Error: {e}")
|
|
474
|
+
print("\nAdaptor system not available. Reinstall skill-seekers.")
|
|
475
|
+
sys.exit(1)
|
|
476
|
+
except ValueError as e:
|
|
477
|
+
print(f"ā Error: {e}")
|
|
478
|
+
sys.exit(1)
|
|
479
|
+
except Exception as e:
|
|
480
|
+
print(f"ā Unexpected error: {e}")
|
|
481
|
+
import traceback
|
|
482
|
+
|
|
483
|
+
traceback.print_exc()
|
|
484
|
+
sys.exit(1)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
if __name__ == "__main__":
|
|
488
|
+
main()
|