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.
Files changed (79) hide show
  1. skill_seekers/__init__.py +22 -0
  2. skill_seekers/cli/__init__.py +39 -0
  3. skill_seekers/cli/adaptors/__init__.py +120 -0
  4. skill_seekers/cli/adaptors/base.py +221 -0
  5. skill_seekers/cli/adaptors/claude.py +485 -0
  6. skill_seekers/cli/adaptors/gemini.py +453 -0
  7. skill_seekers/cli/adaptors/markdown.py +269 -0
  8. skill_seekers/cli/adaptors/openai.py +503 -0
  9. skill_seekers/cli/ai_enhancer.py +310 -0
  10. skill_seekers/cli/api_reference_builder.py +373 -0
  11. skill_seekers/cli/architectural_pattern_detector.py +525 -0
  12. skill_seekers/cli/code_analyzer.py +1462 -0
  13. skill_seekers/cli/codebase_scraper.py +1225 -0
  14. skill_seekers/cli/config_command.py +563 -0
  15. skill_seekers/cli/config_enhancer.py +431 -0
  16. skill_seekers/cli/config_extractor.py +871 -0
  17. skill_seekers/cli/config_manager.py +452 -0
  18. skill_seekers/cli/config_validator.py +394 -0
  19. skill_seekers/cli/conflict_detector.py +528 -0
  20. skill_seekers/cli/constants.py +72 -0
  21. skill_seekers/cli/dependency_analyzer.py +757 -0
  22. skill_seekers/cli/doc_scraper.py +2332 -0
  23. skill_seekers/cli/enhance_skill.py +488 -0
  24. skill_seekers/cli/enhance_skill_local.py +1096 -0
  25. skill_seekers/cli/enhance_status.py +194 -0
  26. skill_seekers/cli/estimate_pages.py +433 -0
  27. skill_seekers/cli/generate_router.py +1209 -0
  28. skill_seekers/cli/github_fetcher.py +534 -0
  29. skill_seekers/cli/github_scraper.py +1466 -0
  30. skill_seekers/cli/guide_enhancer.py +723 -0
  31. skill_seekers/cli/how_to_guide_builder.py +1267 -0
  32. skill_seekers/cli/install_agent.py +461 -0
  33. skill_seekers/cli/install_skill.py +178 -0
  34. skill_seekers/cli/language_detector.py +614 -0
  35. skill_seekers/cli/llms_txt_detector.py +60 -0
  36. skill_seekers/cli/llms_txt_downloader.py +104 -0
  37. skill_seekers/cli/llms_txt_parser.py +150 -0
  38. skill_seekers/cli/main.py +558 -0
  39. skill_seekers/cli/markdown_cleaner.py +132 -0
  40. skill_seekers/cli/merge_sources.py +806 -0
  41. skill_seekers/cli/package_multi.py +77 -0
  42. skill_seekers/cli/package_skill.py +241 -0
  43. skill_seekers/cli/pattern_recognizer.py +1825 -0
  44. skill_seekers/cli/pdf_extractor_poc.py +1166 -0
  45. skill_seekers/cli/pdf_scraper.py +617 -0
  46. skill_seekers/cli/quality_checker.py +519 -0
  47. skill_seekers/cli/rate_limit_handler.py +438 -0
  48. skill_seekers/cli/resume_command.py +160 -0
  49. skill_seekers/cli/run_tests.py +230 -0
  50. skill_seekers/cli/setup_wizard.py +93 -0
  51. skill_seekers/cli/split_config.py +390 -0
  52. skill_seekers/cli/swift_patterns.py +560 -0
  53. skill_seekers/cli/test_example_extractor.py +1081 -0
  54. skill_seekers/cli/test_unified_simple.py +179 -0
  55. skill_seekers/cli/unified_codebase_analyzer.py +572 -0
  56. skill_seekers/cli/unified_scraper.py +932 -0
  57. skill_seekers/cli/unified_skill_builder.py +1605 -0
  58. skill_seekers/cli/upload_skill.py +162 -0
  59. skill_seekers/cli/utils.py +432 -0
  60. skill_seekers/mcp/__init__.py +33 -0
  61. skill_seekers/mcp/agent_detector.py +316 -0
  62. skill_seekers/mcp/git_repo.py +273 -0
  63. skill_seekers/mcp/server.py +231 -0
  64. skill_seekers/mcp/server_fastmcp.py +1249 -0
  65. skill_seekers/mcp/server_legacy.py +2302 -0
  66. skill_seekers/mcp/source_manager.py +285 -0
  67. skill_seekers/mcp/tools/__init__.py +115 -0
  68. skill_seekers/mcp/tools/config_tools.py +251 -0
  69. skill_seekers/mcp/tools/packaging_tools.py +826 -0
  70. skill_seekers/mcp/tools/scraping_tools.py +842 -0
  71. skill_seekers/mcp/tools/source_tools.py +828 -0
  72. skill_seekers/mcp/tools/splitting_tools.py +212 -0
  73. skill_seekers/py.typed +0 -0
  74. skill_seekers-2.7.3.dist-info/METADATA +2027 -0
  75. skill_seekers-2.7.3.dist-info/RECORD +79 -0
  76. skill_seekers-2.7.3.dist-info/WHEEL +5 -0
  77. skill_seekers-2.7.3.dist-info/entry_points.txt +19 -0
  78. skill_seekers-2.7.3.dist-info/licenses/LICENSE +21 -0
  79. skill_seekers-2.7.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,485 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Claude AI Adaptor
4
+
5
+ Implements platform-specific handling for Claude AI (Anthropic) skills.
6
+ Refactored from upload_skill.py and enhance_skill.py.
7
+ """
8
+
9
+ import zipfile
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ from .base import SkillAdaptor, SkillMetadata
14
+
15
+
16
+ class ClaudeAdaptor(SkillAdaptor):
17
+ """
18
+ Claude AI platform adaptor.
19
+
20
+ Handles:
21
+ - YAML frontmatter format for SKILL.md
22
+ - ZIP packaging with standard Claude skill structure
23
+ - Upload to Anthropic Skills API
24
+ - AI enhancement using Claude API
25
+ """
26
+
27
+ PLATFORM = "claude"
28
+ PLATFORM_NAME = "Claude AI (Anthropic)"
29
+ DEFAULT_API_ENDPOINT = "https://api.anthropic.com/v1/skills"
30
+
31
+ def format_skill_md(self, skill_dir: Path, metadata: SkillMetadata) -> str:
32
+ """
33
+ Format SKILL.md with Claude's YAML frontmatter.
34
+
35
+ Args:
36
+ skill_dir: Path to skill directory
37
+ metadata: Skill metadata
38
+
39
+ Returns:
40
+ Formatted SKILL.md content with YAML frontmatter
41
+ """
42
+ # Read existing content (if any)
43
+ existing_content = self._read_existing_content(skill_dir)
44
+
45
+ # If existing content already has proper structure, use it
46
+ if existing_content and len(existing_content) > 100:
47
+ content_body = existing_content
48
+ else:
49
+ # Generate default content
50
+ content_body = f"""# {metadata.name.title()} Documentation Skill
51
+
52
+ {metadata.description}
53
+
54
+ ## When to use this skill
55
+
56
+ Use this skill when the user asks about {metadata.name} documentation, including API references, tutorials, examples, and best practices.
57
+
58
+ ## What's included
59
+
60
+ This skill contains comprehensive documentation organized into categorized reference files.
61
+
62
+ {self._generate_toc(skill_dir)}
63
+
64
+ ## Quick Reference
65
+
66
+ {self._extract_quick_reference(skill_dir)}
67
+
68
+ ## Navigation
69
+
70
+ See `references/index.md` for complete documentation structure.
71
+ """
72
+
73
+ # Format with YAML frontmatter
74
+ return f"""---
75
+ name: {metadata.name}
76
+ description: {metadata.description}
77
+ version: {metadata.version}
78
+ ---
79
+
80
+ {content_body}
81
+ """
82
+
83
+ def package(self, skill_dir: Path, output_path: Path) -> Path:
84
+ """
85
+ Package skill into ZIP file for Claude.
86
+
87
+ Creates standard Claude skill structure:
88
+ - SKILL.md
89
+ - references/*.md
90
+ - scripts/ (optional)
91
+ - assets/ (optional)
92
+
93
+ Args:
94
+ skill_dir: Path to skill directory
95
+ output_path: Output path/filename for ZIP
96
+
97
+ Returns:
98
+ Path to created ZIP file
99
+ """
100
+ skill_dir = Path(skill_dir)
101
+
102
+ # Determine output filename
103
+ if output_path.is_dir() or str(output_path).endswith("/"):
104
+ output_path = Path(output_path) / f"{skill_dir.name}.zip"
105
+ elif not str(output_path).endswith(".zip"):
106
+ output_path = Path(str(output_path) + ".zip")
107
+
108
+ output_path = Path(output_path)
109
+ output_path.parent.mkdir(parents=True, exist_ok=True)
110
+
111
+ # Create ZIP file
112
+ with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as zf:
113
+ # Add SKILL.md (required)
114
+ skill_md = skill_dir / "SKILL.md"
115
+ if skill_md.exists():
116
+ zf.write(skill_md, "SKILL.md")
117
+
118
+ # Add references directory (if exists)
119
+ refs_dir = skill_dir / "references"
120
+ if refs_dir.exists():
121
+ for ref_file in refs_dir.rglob("*"):
122
+ if ref_file.is_file() and not ref_file.name.startswith("."):
123
+ arcname = ref_file.relative_to(skill_dir)
124
+ zf.write(ref_file, str(arcname))
125
+
126
+ # Add scripts directory (if exists)
127
+ scripts_dir = skill_dir / "scripts"
128
+ if scripts_dir.exists():
129
+ for script_file in scripts_dir.rglob("*"):
130
+ if script_file.is_file() and not script_file.name.startswith("."):
131
+ arcname = script_file.relative_to(skill_dir)
132
+ zf.write(script_file, str(arcname))
133
+
134
+ # Add assets directory (if exists)
135
+ assets_dir = skill_dir / "assets"
136
+ if assets_dir.exists():
137
+ for asset_file in assets_dir.rglob("*"):
138
+ if asset_file.is_file() and not asset_file.name.startswith("."):
139
+ arcname = asset_file.relative_to(skill_dir)
140
+ zf.write(asset_file, str(arcname))
141
+
142
+ return output_path
143
+
144
+ def upload(self, package_path: Path, api_key: str, **kwargs) -> dict[str, Any]:
145
+ """
146
+ Upload skill ZIP to Anthropic Skills API.
147
+
148
+ Args:
149
+ package_path: Path to skill ZIP file
150
+ api_key: Anthropic API key
151
+ **kwargs: Additional arguments (timeout, etc.)
152
+
153
+ Returns:
154
+ Dictionary with upload result
155
+ """
156
+ # Check for requests library
157
+ try:
158
+ import requests
159
+ except ImportError:
160
+ return {
161
+ "success": False,
162
+ "skill_id": None,
163
+ "url": None,
164
+ "message": "requests library not installed. Run: pip install requests",
165
+ }
166
+
167
+ # Validate ZIP file
168
+ package_path = Path(package_path)
169
+ if not package_path.exists():
170
+ return {
171
+ "success": False,
172
+ "skill_id": None,
173
+ "url": None,
174
+ "message": f"File not found: {package_path}",
175
+ }
176
+
177
+ if package_path.suffix != ".zip":
178
+ return {
179
+ "success": False,
180
+ "skill_id": None,
181
+ "url": None,
182
+ "message": f"Not a ZIP file: {package_path}",
183
+ }
184
+
185
+ # Prepare API request
186
+ api_url = self.DEFAULT_API_ENDPOINT
187
+ headers = {
188
+ "x-api-key": api_key,
189
+ "anthropic-version": "2023-06-01",
190
+ "anthropic-beta": "skills-2025-10-02",
191
+ }
192
+
193
+ timeout = kwargs.get("timeout", 60)
194
+
195
+ try:
196
+ # Read ZIP file
197
+ with open(package_path, "rb") as f:
198
+ zip_data = f.read()
199
+
200
+ # Upload skill
201
+ files = {"files[]": (package_path.name, zip_data, "application/zip")}
202
+
203
+ response = requests.post(api_url, headers=headers, files=files, timeout=timeout)
204
+
205
+ # Check response
206
+ if response.status_code == 200:
207
+ # Extract skill ID if available
208
+ try:
209
+ response_data = response.json()
210
+ skill_id = response_data.get("id")
211
+ except Exception:
212
+ skill_id = None
213
+
214
+ return {
215
+ "success": True,
216
+ "skill_id": skill_id,
217
+ "url": "https://claude.ai/skills",
218
+ "message": "Skill uploaded successfully to Claude AI",
219
+ }
220
+
221
+ elif response.status_code == 401:
222
+ return {
223
+ "success": False,
224
+ "skill_id": None,
225
+ "url": None,
226
+ "message": "Authentication failed. Check your ANTHROPIC_API_KEY",
227
+ }
228
+
229
+ elif response.status_code == 400:
230
+ try:
231
+ error_msg = response.json().get("error", {}).get("message", "Unknown error")
232
+ except Exception:
233
+ error_msg = "Invalid skill format"
234
+
235
+ return {
236
+ "success": False,
237
+ "skill_id": None,
238
+ "url": None,
239
+ "message": f"Invalid skill format: {error_msg}",
240
+ }
241
+
242
+ else:
243
+ try:
244
+ error_msg = response.json().get("error", {}).get("message", "Unknown error")
245
+ except Exception:
246
+ error_msg = f"HTTP {response.status_code}"
247
+
248
+ return {
249
+ "success": False,
250
+ "skill_id": None,
251
+ "url": None,
252
+ "message": f"Upload failed: {error_msg}",
253
+ }
254
+
255
+ except requests.exceptions.Timeout:
256
+ return {
257
+ "success": False,
258
+ "skill_id": None,
259
+ "url": None,
260
+ "message": "Upload timed out. Try again or use manual upload",
261
+ }
262
+
263
+ except requests.exceptions.ConnectionError:
264
+ return {
265
+ "success": False,
266
+ "skill_id": None,
267
+ "url": None,
268
+ "message": "Connection error. Check your internet connection",
269
+ }
270
+
271
+ except Exception as e:
272
+ return {
273
+ "success": False,
274
+ "skill_id": None,
275
+ "url": None,
276
+ "message": f"Unexpected error: {str(e)}",
277
+ }
278
+
279
+ def validate_api_key(self, api_key: str) -> bool:
280
+ """
281
+ Validate Anthropic API key format.
282
+
283
+ Args:
284
+ api_key: API key to validate
285
+
286
+ Returns:
287
+ True if key starts with 'sk-ant-'
288
+ """
289
+ return api_key.strip().startswith("sk-ant-")
290
+
291
+ def get_env_var_name(self) -> str:
292
+ """
293
+ Get environment variable name for Anthropic API key.
294
+
295
+ Returns:
296
+ 'ANTHROPIC_API_KEY'
297
+ """
298
+ return "ANTHROPIC_API_KEY"
299
+
300
+ def supports_enhancement(self) -> bool:
301
+ """
302
+ Claude supports AI enhancement via Anthropic API.
303
+
304
+ Returns:
305
+ True
306
+ """
307
+ return True
308
+
309
+ def enhance(self, skill_dir: Path, api_key: str) -> bool:
310
+ """
311
+ Enhance SKILL.md using Claude API.
312
+
313
+ Reads reference files, sends them to Claude, and generates
314
+ an improved SKILL.md with real examples and better organization.
315
+
316
+ Args:
317
+ skill_dir: Path to skill directory
318
+ api_key: Anthropic API key
319
+
320
+ Returns:
321
+ True if enhancement succeeded
322
+ """
323
+ # Check for anthropic library
324
+ try:
325
+ import anthropic
326
+ except ImportError:
327
+ print("āŒ Error: anthropic package not installed")
328
+ print("Install with: pip install anthropic")
329
+ return False
330
+
331
+ skill_dir = Path(skill_dir)
332
+ references_dir = skill_dir / "references"
333
+ skill_md_path = skill_dir / "SKILL.md"
334
+
335
+ # Read reference files
336
+ print("šŸ“– Reading reference documentation...")
337
+ references = self._read_reference_files(references_dir)
338
+
339
+ if not references:
340
+ print("āŒ No reference files found to analyze")
341
+ return False
342
+
343
+ print(f" āœ“ Read {len(references)} reference files")
344
+ total_size = sum(len(c) for c in references.values())
345
+ print(f" āœ“ Total size: {total_size:,} characters\n")
346
+
347
+ # Read current SKILL.md
348
+ current_skill_md = None
349
+ if skill_md_path.exists():
350
+ current_skill_md = skill_md_path.read_text(encoding="utf-8")
351
+ print(f" ℹ Found existing SKILL.md ({len(current_skill_md)} chars)")
352
+ else:
353
+ print(" ℹ No existing SKILL.md, will create new one")
354
+
355
+ # Build enhancement prompt
356
+ prompt = self._build_enhancement_prompt(skill_dir.name, references, current_skill_md)
357
+
358
+ print("\nšŸ¤– Asking Claude to enhance SKILL.md...")
359
+ print(f" Input: {len(prompt):,} characters")
360
+
361
+ try:
362
+ client = anthropic.Anthropic(api_key=api_key)
363
+
364
+ message = client.messages.create(
365
+ model="claude-sonnet-4-20250514",
366
+ max_tokens=4096,
367
+ temperature=0.3,
368
+ messages=[{"role": "user", "content": prompt}],
369
+ )
370
+
371
+ enhanced_content = message.content[0].text
372
+ print(f" āœ“ Generated enhanced SKILL.md ({len(enhanced_content)} chars)\n")
373
+
374
+ # Backup original
375
+ if skill_md_path.exists():
376
+ backup_path = skill_md_path.with_suffix(".md.backup")
377
+ skill_md_path.rename(backup_path)
378
+ print(f" šŸ’¾ Backed up original to: {backup_path.name}")
379
+
380
+ # Save enhanced version
381
+ skill_md_path.write_text(enhanced_content, encoding="utf-8")
382
+ print(" āœ… Saved enhanced SKILL.md")
383
+
384
+ return True
385
+
386
+ except Exception as e:
387
+ print(f"āŒ Error calling Claude API: {e}")
388
+ return False
389
+
390
+ def _read_reference_files(
391
+ self, references_dir: Path, max_chars: int = 200000
392
+ ) -> dict[str, str]:
393
+ """
394
+ Read reference markdown files from skill directory.
395
+
396
+ Args:
397
+ references_dir: Path to references directory
398
+ max_chars: Maximum total characters to read
399
+
400
+ Returns:
401
+ Dictionary mapping filename to content
402
+ """
403
+ if not references_dir.exists():
404
+ return {}
405
+
406
+ references = {}
407
+ total_chars = 0
408
+
409
+ # Read all .md files
410
+ for ref_file in sorted(references_dir.glob("*.md")):
411
+ if total_chars >= max_chars:
412
+ break
413
+
414
+ try:
415
+ content = ref_file.read_text(encoding="utf-8")
416
+ # Limit individual file size
417
+ if len(content) > 30000:
418
+ content = content[:30000] + "\n\n...(truncated)"
419
+
420
+ references[ref_file.name] = content
421
+ total_chars += len(content)
422
+
423
+ except Exception as e:
424
+ print(f" āš ļø Could not read {ref_file.name}: {e}")
425
+
426
+ return references
427
+
428
+ def _build_enhancement_prompt(
429
+ self, skill_name: str, references: dict[str, str], current_skill_md: str = None
430
+ ) -> str:
431
+ """
432
+ Build Claude API prompt for enhancement.
433
+
434
+ Args:
435
+ skill_name: Name of the skill
436
+ references: Dictionary of reference content
437
+ current_skill_md: Existing SKILL.md content (optional)
438
+
439
+ Returns:
440
+ Enhancement prompt for Claude
441
+ """
442
+ prompt = f"""You are enhancing a Claude skill's SKILL.md file. This skill is about: {skill_name}
443
+
444
+ I've scraped documentation and organized it into reference files. Your job is to create an EXCELLENT SKILL.md that will help Claude use this documentation effectively.
445
+
446
+ CURRENT SKILL.MD:
447
+ {"```markdown" if current_skill_md else "(none - create from scratch)"}
448
+ {current_skill_md or "No existing SKILL.md"}
449
+ {"```" if current_skill_md else ""}
450
+
451
+ REFERENCE DOCUMENTATION:
452
+ """
453
+
454
+ for filename, content in references.items():
455
+ prompt += f"\n\n## {filename}\n```markdown\n{content[:30000]}\n```\n"
456
+
457
+ prompt += """
458
+
459
+ YOUR TASK:
460
+ Create an enhanced SKILL.md that includes:
461
+
462
+ 1. **Clear "When to Use This Skill" section** - Be specific about trigger conditions
463
+ 2. **Excellent Quick Reference section** - Extract 5-10 of the BEST, most practical code examples from the reference docs
464
+ - Choose SHORT, clear examples that demonstrate common tasks
465
+ - Include both simple and intermediate examples
466
+ - Annotate examples with clear descriptions
467
+ - Use proper language tags (cpp, python, javascript, json, etc.)
468
+ 3. **Detailed Reference Files description** - Explain what's in each reference file
469
+ 4. **Practical "Working with This Skill" section** - Give users clear guidance on how to navigate the skill
470
+ 5. **Key Concepts section** (if applicable) - Explain core concepts
471
+ 6. **Keep the frontmatter** (---\nname: ...\n---) intact
472
+
473
+ IMPORTANT:
474
+ - Extract REAL examples from the reference docs, don't make them up
475
+ - Prioritize SHORT, clear examples (5-20 lines max)
476
+ - Make it actionable and practical
477
+ - Don't be too verbose - be concise but useful
478
+ - Maintain the markdown structure for Claude skills
479
+ - Keep code examples properly formatted with language tags
480
+
481
+ OUTPUT:
482
+ Return ONLY the complete SKILL.md content, starting with the frontmatter (---).
483
+ """
484
+
485
+ return prompt