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,453 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Google Gemini Adaptor
4
+
5
+ Implements platform-specific handling for Google Gemini skills.
6
+ Uses Gemini Files API for grounding and Gemini 2.0 Flash for enhancement.
7
+ """
8
+
9
+ import json
10
+ import os
11
+ import tarfile
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ from .base import SkillAdaptor, SkillMetadata
16
+
17
+
18
+ class GeminiAdaptor(SkillAdaptor):
19
+ """
20
+ Google Gemini platform adaptor.
21
+
22
+ Handles:
23
+ - Plain markdown format (no YAML frontmatter)
24
+ - tar.gz packaging for Gemini Files API
25
+ - Upload to Google AI Studio / Files API
26
+ - AI enhancement using Gemini 2.0 Flash
27
+ """
28
+
29
+ PLATFORM = "gemini"
30
+ PLATFORM_NAME = "Google Gemini"
31
+ DEFAULT_API_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/files"
32
+
33
+ def format_skill_md(self, skill_dir: Path, metadata: SkillMetadata) -> str:
34
+ """
35
+ Format SKILL.md with plain markdown (no frontmatter).
36
+
37
+ Gemini doesn't use YAML frontmatter - just clean markdown.
38
+
39
+ Args:
40
+ skill_dir: Path to skill directory
41
+ metadata: Skill metadata
42
+
43
+ Returns:
44
+ Formatted SKILL.md content (plain markdown)
45
+ """
46
+ # Read existing content (if any)
47
+ existing_content = self._read_existing_content(skill_dir)
48
+
49
+ # If existing content is substantial, use it
50
+ if existing_content and len(existing_content) > 100:
51
+ content_body = existing_content
52
+ else:
53
+ # Generate default content
54
+ content_body = f"""# {metadata.name.title()} Documentation
55
+
56
+ **Description:** {metadata.description}
57
+
58
+ ## Quick Reference
59
+
60
+ {self._extract_quick_reference(skill_dir)}
61
+
62
+ ## Table of Contents
63
+
64
+ {self._generate_toc(skill_dir)}
65
+
66
+ ## Documentation Structure
67
+
68
+ This skill contains comprehensive documentation organized into categorized reference files.
69
+
70
+ ### Available References
71
+
72
+ {self._generate_toc(skill_dir)}
73
+
74
+ ## How to Use This Skill
75
+
76
+ When asking questions about {metadata.name}:
77
+ 1. Mention specific topics or features you need help with
78
+ 2. Reference documentation sections will be automatically consulted
79
+ 3. You'll receive detailed answers with code examples
80
+
81
+ ## Navigation
82
+
83
+ See the references directory for complete documentation with examples and best practices.
84
+ """
85
+
86
+ # Return plain markdown (NO frontmatter)
87
+ return content_body
88
+
89
+ def package(self, skill_dir: Path, output_path: Path) -> Path:
90
+ """
91
+ Package skill into tar.gz file for Gemini.
92
+
93
+ Creates Gemini-compatible structure:
94
+ - system_instructions.md (main SKILL.md)
95
+ - references/*.md
96
+ - gemini_metadata.json (skill metadata)
97
+
98
+ Args:
99
+ skill_dir: Path to skill directory
100
+ output_path: Output path/filename for tar.gz
101
+
102
+ Returns:
103
+ Path to created tar.gz file
104
+ """
105
+ skill_dir = Path(skill_dir)
106
+
107
+ # Determine output filename
108
+ if output_path.is_dir() or str(output_path).endswith("/"):
109
+ output_path = Path(output_path) / f"{skill_dir.name}-gemini.tar.gz"
110
+ elif not str(output_path).endswith(".tar.gz"):
111
+ # Replace .zip with .tar.gz if needed
112
+ output_str = str(output_path).replace(".zip", ".tar.gz")
113
+ if not output_str.endswith(".tar.gz"):
114
+ output_str += ".tar.gz"
115
+ output_path = Path(output_str)
116
+
117
+ output_path = Path(output_path)
118
+ output_path.parent.mkdir(parents=True, exist_ok=True)
119
+
120
+ # Create tar.gz file
121
+ with tarfile.open(output_path, "w:gz") as tar:
122
+ # Add SKILL.md as system_instructions.md
123
+ skill_md = skill_dir / "SKILL.md"
124
+ if skill_md.exists():
125
+ tar.add(skill_md, arcname="system_instructions.md")
126
+
127
+ # Add references directory (if exists)
128
+ refs_dir = skill_dir / "references"
129
+ if refs_dir.exists():
130
+ for ref_file in refs_dir.rglob("*"):
131
+ if ref_file.is_file() and not ref_file.name.startswith("."):
132
+ arcname = ref_file.relative_to(skill_dir)
133
+ tar.add(ref_file, arcname=str(arcname))
134
+
135
+ # Create and add metadata file
136
+ metadata = {
137
+ "platform": "gemini",
138
+ "name": skill_dir.name,
139
+ "version": "1.0.0",
140
+ "created_with": "skill-seekers",
141
+ }
142
+
143
+ # Write metadata to temp file and add to archive
144
+ import tempfile
145
+
146
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp:
147
+ json.dump(metadata, tmp, indent=2)
148
+ tmp_path = tmp.name
149
+
150
+ try:
151
+ tar.add(tmp_path, arcname="gemini_metadata.json")
152
+ finally:
153
+ os.unlink(tmp_path)
154
+
155
+ return output_path
156
+
157
+ def upload(self, package_path: Path, api_key: str, **_kwargs) -> dict[str, Any]:
158
+ """
159
+ Upload skill tar.gz to Gemini Files API.
160
+
161
+ Args:
162
+ package_path: Path to skill tar.gz file
163
+ api_key: Google API key
164
+ **kwargs: Additional arguments
165
+
166
+ Returns:
167
+ Dictionary with upload result
168
+ """
169
+ # Validate package file FIRST
170
+ package_path = Path(package_path)
171
+ if not package_path.exists():
172
+ return {
173
+ "success": False,
174
+ "skill_id": None,
175
+ "url": None,
176
+ "message": f"File not found: {package_path}",
177
+ }
178
+
179
+ if package_path.suffix != ".gz":
180
+ return {
181
+ "success": False,
182
+ "skill_id": None,
183
+ "url": None,
184
+ "message": f"Not a tar.gz file: {package_path}",
185
+ }
186
+
187
+ # Check for google-generativeai library
188
+ try:
189
+ import google.generativeai as genai
190
+ except ImportError:
191
+ return {
192
+ "success": False,
193
+ "skill_id": None,
194
+ "url": None,
195
+ "message": "google-generativeai library not installed. Run: pip install google-generativeai",
196
+ }
197
+
198
+ # Configure Gemini
199
+ try:
200
+ genai.configure(api_key=api_key)
201
+
202
+ # Extract tar.gz to temp directory
203
+ import tempfile
204
+
205
+ with tempfile.TemporaryDirectory() as temp_dir:
206
+ # Extract archive
207
+ with tarfile.open(package_path, "r:gz") as tar:
208
+ tar.extractall(temp_dir)
209
+
210
+ temp_path = Path(temp_dir)
211
+
212
+ # Upload main file (system_instructions.md)
213
+ main_file = temp_path / "system_instructions.md"
214
+ if not main_file.exists():
215
+ return {
216
+ "success": False,
217
+ "skill_id": None,
218
+ "url": None,
219
+ "message": "Invalid package: system_instructions.md not found",
220
+ }
221
+
222
+ # Upload to Files API
223
+ uploaded_file = genai.upload_file(
224
+ path=str(main_file), display_name=f"{package_path.stem}_instructions"
225
+ )
226
+
227
+ # Upload reference files (if any)
228
+ refs_dir = temp_path / "references"
229
+ uploaded_refs = []
230
+ if refs_dir.exists():
231
+ for ref_file in refs_dir.glob("*.md"):
232
+ ref_uploaded = genai.upload_file(
233
+ path=str(ref_file), display_name=f"{package_path.stem}_{ref_file.stem}"
234
+ )
235
+ uploaded_refs.append(ref_uploaded.name)
236
+
237
+ return {
238
+ "success": True,
239
+ "skill_id": uploaded_file.name,
240
+ "url": f"https://aistudio.google.com/app/files/{uploaded_file.name}",
241
+ "message": f"Skill uploaded to Google AI Studio ({len(uploaded_refs) + 1} files)",
242
+ }
243
+
244
+ except Exception as e:
245
+ return {
246
+ "success": False,
247
+ "skill_id": None,
248
+ "url": None,
249
+ "message": f"Upload failed: {str(e)}",
250
+ }
251
+
252
+ def validate_api_key(self, api_key: str) -> bool:
253
+ """
254
+ Validate Google API key format.
255
+
256
+ Args:
257
+ api_key: API key to validate
258
+
259
+ Returns:
260
+ True if key starts with 'AIza'
261
+ """
262
+ return api_key.strip().startswith("AIza")
263
+
264
+ def get_env_var_name(self) -> str:
265
+ """
266
+ Get environment variable name for Google API key.
267
+
268
+ Returns:
269
+ 'GOOGLE_API_KEY'
270
+ """
271
+ return "GOOGLE_API_KEY"
272
+
273
+ def supports_enhancement(self) -> bool:
274
+ """
275
+ Gemini supports AI enhancement via Gemini 2.0 Flash.
276
+
277
+ Returns:
278
+ True
279
+ """
280
+ return True
281
+
282
+ def enhance(self, skill_dir: Path, api_key: str) -> bool:
283
+ """
284
+ Enhance SKILL.md using Gemini 2.0 Flash API.
285
+
286
+ Args:
287
+ skill_dir: Path to skill directory
288
+ api_key: Google API key
289
+
290
+ Returns:
291
+ True if enhancement succeeded
292
+ """
293
+ # Check for google-generativeai library
294
+ try:
295
+ import google.generativeai as genai
296
+ except ImportError:
297
+ print("āŒ Error: google-generativeai package not installed")
298
+ print("Install with: pip install google-generativeai")
299
+ return False
300
+
301
+ skill_dir = Path(skill_dir)
302
+ references_dir = skill_dir / "references"
303
+ skill_md_path = skill_dir / "SKILL.md"
304
+
305
+ # Read reference files
306
+ print("šŸ“– Reading reference documentation...")
307
+ references = self._read_reference_files(references_dir)
308
+
309
+ if not references:
310
+ print("āŒ No reference files found to analyze")
311
+ return False
312
+
313
+ print(f" āœ“ Read {len(references)} reference files")
314
+ total_size = sum(len(c) for c in references.values())
315
+ print(f" āœ“ Total size: {total_size:,} characters\n")
316
+
317
+ # Read current SKILL.md
318
+ current_skill_md = None
319
+ if skill_md_path.exists():
320
+ current_skill_md = skill_md_path.read_text(encoding="utf-8")
321
+ print(f" ℹ Found existing SKILL.md ({len(current_skill_md)} chars)")
322
+ else:
323
+ print(" ℹ No existing SKILL.md, will create new one")
324
+
325
+ # Build enhancement prompt
326
+ prompt = self._build_enhancement_prompt(skill_dir.name, references, current_skill_md)
327
+
328
+ print("\nšŸ¤– Asking Gemini to enhance SKILL.md...")
329
+ print(f" Input: {len(prompt):,} characters")
330
+
331
+ try:
332
+ genai.configure(api_key=api_key)
333
+
334
+ model = genai.GenerativeModel("gemini-2.0-flash-exp")
335
+
336
+ response = model.generate_content(prompt)
337
+
338
+ enhanced_content = response.text
339
+ print(f" āœ“ Generated enhanced SKILL.md ({len(enhanced_content)} chars)\n")
340
+
341
+ # Backup original
342
+ if skill_md_path.exists():
343
+ backup_path = skill_md_path.with_suffix(".md.backup")
344
+ skill_md_path.rename(backup_path)
345
+ print(f" šŸ’¾ Backed up original to: {backup_path.name}")
346
+
347
+ # Save enhanced version
348
+ skill_md_path.write_text(enhanced_content, encoding="utf-8")
349
+ print(" āœ… Saved enhanced SKILL.md")
350
+
351
+ return True
352
+
353
+ except Exception as e:
354
+ print(f"āŒ Error calling Gemini API: {e}")
355
+ return False
356
+
357
+ def _read_reference_files(
358
+ self, references_dir: Path, max_chars: int = 200000
359
+ ) -> dict[str, str]:
360
+ """
361
+ Read reference markdown files from skill directory.
362
+
363
+ Args:
364
+ references_dir: Path to references directory
365
+ max_chars: Maximum total characters to read
366
+
367
+ Returns:
368
+ Dictionary mapping filename to content
369
+ """
370
+ if not references_dir.exists():
371
+ return {}
372
+
373
+ references = {}
374
+ total_chars = 0
375
+
376
+ # Read all .md files
377
+ for ref_file in sorted(references_dir.glob("*.md")):
378
+ if total_chars >= max_chars:
379
+ break
380
+
381
+ try:
382
+ content = ref_file.read_text(encoding="utf-8")
383
+ # Limit individual file size
384
+ if len(content) > 30000:
385
+ content = content[:30000] + "\n\n...(truncated)"
386
+
387
+ references[ref_file.name] = content
388
+ total_chars += len(content)
389
+
390
+ except Exception as e:
391
+ print(f" āš ļø Could not read {ref_file.name}: {e}")
392
+
393
+ return references
394
+
395
+ def _build_enhancement_prompt(
396
+ self, skill_name: str, references: dict[str, str], current_skill_md: str = None
397
+ ) -> str:
398
+ """
399
+ Build Gemini API prompt for enhancement.
400
+
401
+ Args:
402
+ skill_name: Name of the skill
403
+ references: Dictionary of reference content
404
+ current_skill_md: Existing SKILL.md content (optional)
405
+
406
+ Returns:
407
+ Enhancement prompt for Gemini
408
+ """
409
+ prompt = f"""You are enhancing a skill's documentation file for use with Google Gemini. This skill is about: {skill_name}
410
+
411
+ I've scraped documentation and organized it into reference files. Your job is to create an EXCELLENT markdown documentation file that will help Gemini use this documentation effectively.
412
+
413
+ CURRENT DOCUMENTATION:
414
+ {"```markdown" if current_skill_md else "(none - create from scratch)"}
415
+ {current_skill_md or "No existing documentation"}
416
+ {"```" if current_skill_md else ""}
417
+
418
+ REFERENCE DOCUMENTATION:
419
+ """
420
+
421
+ for filename, content in references.items():
422
+ prompt += f"\n\n## {filename}\n```markdown\n{content[:30000]}\n```\n"
423
+
424
+ prompt += """
425
+
426
+ YOUR TASK:
427
+ Create enhanced documentation that includes:
428
+
429
+ 1. **Clear description** - What this skill covers and when to use it
430
+ 2. **Excellent Quick Reference section** - Extract 5-10 of the BEST, most practical code examples from the reference docs
431
+ - Choose SHORT, clear examples that demonstrate common tasks
432
+ - Include both simple and intermediate examples
433
+ - Annotate examples with clear descriptions
434
+ - Use proper language tags (cpp, python, javascript, json, etc.)
435
+ 3. **Table of Contents** - List all reference sections
436
+ 4. **Practical usage guidance** - Help users navigate the documentation
437
+ 5. **Key Concepts section** (if applicable) - Explain core concepts
438
+ 6. **DO NOT use YAML frontmatter** - This is for Gemini, which uses plain markdown
439
+
440
+ IMPORTANT:
441
+ - Extract REAL examples from the reference docs, don't make them up
442
+ - Prioritize SHORT, clear examples (5-20 lines max)
443
+ - Make it actionable and practical
444
+ - Don't be too verbose - be concise but useful
445
+ - Use clean markdown formatting
446
+ - Keep code examples properly formatted with language tags
447
+ - NO YAML frontmatter (no --- blocks)
448
+
449
+ OUTPUT:
450
+ Return ONLY the complete markdown content, starting with the main title (#).
451
+ """
452
+
453
+ return prompt