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,503 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ OpenAI ChatGPT Adaptor
4
+
5
+ Implements platform-specific handling for OpenAI ChatGPT Assistants.
6
+ Uses Assistants API with Vector Store for file search.
7
+ """
8
+
9
+ import json
10
+ import zipfile
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from .base import SkillAdaptor, SkillMetadata
15
+
16
+
17
+ class OpenAIAdaptor(SkillAdaptor):
18
+ """
19
+ OpenAI ChatGPT platform adaptor.
20
+
21
+ Handles:
22
+ - Assistant instructions format (not YAML frontmatter)
23
+ - ZIP packaging for Assistants API
24
+ - Upload creates Assistant + Vector Store
25
+ - AI enhancement using GPT-4o
26
+ """
27
+
28
+ PLATFORM = "openai"
29
+ PLATFORM_NAME = "OpenAI ChatGPT"
30
+ DEFAULT_API_ENDPOINT = "https://api.openai.com/v1/assistants"
31
+
32
+ def format_skill_md(self, skill_dir: Path, metadata: SkillMetadata) -> str:
33
+ """
34
+ Format SKILL.md as Assistant instructions.
35
+
36
+ OpenAI Assistants use instructions rather than markdown docs.
37
+
38
+ Args:
39
+ skill_dir: Path to skill directory
40
+ metadata: Skill metadata
41
+
42
+ Returns:
43
+ Formatted instructions for OpenAI Assistant
44
+ """
45
+ # Read existing content (if any)
46
+ existing_content = self._read_existing_content(skill_dir)
47
+
48
+ # If existing content is substantial, adapt it to instructions format
49
+ if existing_content and len(existing_content) > 100:
50
+ content_body = f"""You are an expert assistant for {metadata.name}.
51
+
52
+ {metadata.description}
53
+
54
+ Use the attached knowledge files to provide accurate, detailed answers about {metadata.name}.
55
+
56
+ {existing_content}
57
+
58
+ ## How to Assist Users
59
+
60
+ When users ask questions:
61
+ 1. Search the knowledge files for relevant information
62
+ 2. Provide clear, practical answers with code examples
63
+ 3. Reference specific documentation sections when helpful
64
+ 4. Be concise but thorough
65
+
66
+ Always prioritize accuracy by consulting the knowledge base before responding."""
67
+ else:
68
+ # Generate default instructions
69
+ content_body = f"""You are an expert assistant for {metadata.name}.
70
+
71
+ {metadata.description}
72
+
73
+ ## Your Knowledge Base
74
+
75
+ You have access to comprehensive documentation files about {metadata.name}. Use these files to provide accurate answers to user questions.
76
+
77
+ {self._generate_toc(skill_dir)}
78
+
79
+ ## Quick Reference
80
+
81
+ {self._extract_quick_reference(skill_dir)}
82
+
83
+ ## How to Assist Users
84
+
85
+ When users ask questions about {metadata.name}:
86
+
87
+ 1. **Search the knowledge files** - Use file_search to find relevant information
88
+ 2. **Provide code examples** - Include practical, working code snippets
89
+ 3. **Reference documentation** - Cite specific sections when helpful
90
+ 4. **Be practical** - Focus on real-world usage and best practices
91
+ 5. **Stay accurate** - Always verify information against the knowledge base
92
+
93
+ ## Response Guidelines
94
+
95
+ - Keep answers clear and concise
96
+ - Use proper code formatting with language tags
97
+ - Provide both simple and detailed explanations as needed
98
+ - Suggest related topics when relevant
99
+ - Admit when information isn't in the knowledge base
100
+
101
+ Always prioritize accuracy by consulting the attached documentation files before responding."""
102
+
103
+ # Return plain text instructions (NO frontmatter)
104
+ return content_body
105
+
106
+ def package(self, skill_dir: Path, output_path: Path) -> Path:
107
+ """
108
+ Package skill into ZIP file for OpenAI Assistants.
109
+
110
+ Creates OpenAI-compatible structure:
111
+ - assistant_instructions.txt (main instructions)
112
+ - vector_store_files/*.md (reference files for vector store)
113
+ - openai_metadata.json (skill metadata)
114
+
115
+ Args:
116
+ skill_dir: Path to skill directory
117
+ output_path: Output path/filename for ZIP
118
+
119
+ Returns:
120
+ Path to created ZIP file
121
+ """
122
+ skill_dir = Path(skill_dir)
123
+
124
+ # Determine output filename
125
+ if output_path.is_dir() or str(output_path).endswith("/"):
126
+ output_path = Path(output_path) / f"{skill_dir.name}-openai.zip"
127
+ elif not str(output_path).endswith(".zip") and not str(output_path).endswith("-openai.zip"):
128
+ # Keep .zip extension
129
+ output_str = str(output_path).replace(".zip", "-openai.zip")
130
+ if not output_str.endswith(".zip"):
131
+ output_str += ".zip"
132
+ output_path = Path(output_str)
133
+
134
+ output_path = Path(output_path)
135
+ output_path.parent.mkdir(parents=True, exist_ok=True)
136
+
137
+ # Create ZIP file
138
+ with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as zf:
139
+ # Add SKILL.md as assistant_instructions.txt
140
+ skill_md = skill_dir / "SKILL.md"
141
+ if skill_md.exists():
142
+ instructions = skill_md.read_text(encoding="utf-8")
143
+ zf.writestr("assistant_instructions.txt", instructions)
144
+
145
+ # Add references directory as vector_store_files/
146
+ refs_dir = skill_dir / "references"
147
+ if refs_dir.exists():
148
+ for ref_file in refs_dir.rglob("*.md"):
149
+ if ref_file.is_file() and not ref_file.name.startswith("."):
150
+ # Place all reference files in vector_store_files/
151
+ arcname = f"vector_store_files/{ref_file.name}"
152
+ zf.write(ref_file, arcname)
153
+
154
+ # Create and add metadata file
155
+ metadata = {
156
+ "platform": "openai",
157
+ "name": skill_dir.name,
158
+ "version": "1.0.0",
159
+ "created_with": "skill-seekers",
160
+ "model": "gpt-4o",
161
+ "tools": ["file_search"],
162
+ }
163
+
164
+ zf.writestr("openai_metadata.json", json.dumps(metadata, indent=2))
165
+
166
+ return output_path
167
+
168
+ def upload(self, package_path: Path, api_key: str, **kwargs) -> dict[str, Any]:
169
+ """
170
+ Upload skill ZIP to OpenAI Assistants API.
171
+
172
+ Creates:
173
+ 1. Vector Store with reference files
174
+ 2. Assistant with file_search tool
175
+
176
+ Args:
177
+ package_path: Path to skill ZIP file
178
+ api_key: OpenAI API key
179
+ **kwargs: Additional arguments (model, etc.)
180
+
181
+ Returns:
182
+ Dictionary with upload result
183
+ """
184
+ # Validate package file FIRST
185
+ package_path = Path(package_path)
186
+ if not package_path.exists():
187
+ return {
188
+ "success": False,
189
+ "skill_id": None,
190
+ "url": None,
191
+ "message": f"File not found: {package_path}",
192
+ }
193
+
194
+ if package_path.suffix != ".zip":
195
+ return {
196
+ "success": False,
197
+ "skill_id": None,
198
+ "url": None,
199
+ "message": f"Not a ZIP file: {package_path}",
200
+ }
201
+
202
+ # Check for openai library
203
+ try:
204
+ from openai import OpenAI
205
+ except ImportError:
206
+ return {
207
+ "success": False,
208
+ "skill_id": None,
209
+ "url": None,
210
+ "message": "openai library not installed. Run: pip install openai",
211
+ }
212
+
213
+ # Configure OpenAI client
214
+ try:
215
+ client = OpenAI(api_key=api_key)
216
+
217
+ # Extract package to temp directory
218
+ import tempfile
219
+
220
+ with tempfile.TemporaryDirectory() as temp_dir:
221
+ # Extract ZIP
222
+ with zipfile.ZipFile(package_path, "r") as zf:
223
+ zf.extractall(temp_dir)
224
+
225
+ temp_path = Path(temp_dir)
226
+
227
+ # Read instructions
228
+ instructions_file = temp_path / "assistant_instructions.txt"
229
+ if not instructions_file.exists():
230
+ return {
231
+ "success": False,
232
+ "skill_id": None,
233
+ "url": None,
234
+ "message": "Invalid package: assistant_instructions.txt not found",
235
+ }
236
+
237
+ instructions = instructions_file.read_text(encoding="utf-8")
238
+
239
+ # Read metadata
240
+ metadata_file = temp_path / "openai_metadata.json"
241
+ skill_name = package_path.stem
242
+ model = kwargs.get("model", "gpt-4o")
243
+
244
+ if metadata_file.exists():
245
+ with open(metadata_file) as f:
246
+ metadata = json.load(f)
247
+ skill_name = metadata.get("name", skill_name)
248
+ model = metadata.get("model", model)
249
+
250
+ # Create vector store
251
+ vector_store = client.beta.vector_stores.create(name=f"{skill_name} Documentation")
252
+
253
+ # Upload reference files to vector store
254
+ vector_files_dir = temp_path / "vector_store_files"
255
+ file_ids = []
256
+
257
+ if vector_files_dir.exists():
258
+ for ref_file in vector_files_dir.glob("*.md"):
259
+ # Upload file
260
+ with open(ref_file, "rb") as f:
261
+ uploaded_file = client.files.create(file=f, purpose="assistants")
262
+ file_ids.append(uploaded_file.id)
263
+
264
+ # Attach files to vector store
265
+ if file_ids:
266
+ client.beta.vector_stores.files.create_batch(
267
+ vector_store_id=vector_store.id, file_ids=file_ids
268
+ )
269
+
270
+ # Create assistant
271
+ assistant = client.beta.assistants.create(
272
+ name=skill_name,
273
+ instructions=instructions,
274
+ model=model,
275
+ tools=[{"type": "file_search"}],
276
+ tool_resources={"file_search": {"vector_store_ids": [vector_store.id]}},
277
+ )
278
+
279
+ return {
280
+ "success": True,
281
+ "skill_id": assistant.id,
282
+ "url": f"https://platform.openai.com/assistants/{assistant.id}",
283
+ "message": f"Assistant created with {len(file_ids)} knowledge files",
284
+ }
285
+
286
+ except Exception as e:
287
+ return {
288
+ "success": False,
289
+ "skill_id": None,
290
+ "url": None,
291
+ "message": f"Upload failed: {str(e)}",
292
+ }
293
+
294
+ def validate_api_key(self, api_key: str) -> bool:
295
+ """
296
+ Validate OpenAI API key format.
297
+
298
+ Args:
299
+ api_key: API key to validate
300
+
301
+ Returns:
302
+ True if key starts with 'sk-'
303
+ """
304
+ return api_key.strip().startswith("sk-")
305
+
306
+ def get_env_var_name(self) -> str:
307
+ """
308
+ Get environment variable name for OpenAI API key.
309
+
310
+ Returns:
311
+ 'OPENAI_API_KEY'
312
+ """
313
+ return "OPENAI_API_KEY"
314
+
315
+ def supports_enhancement(self) -> bool:
316
+ """
317
+ OpenAI supports AI enhancement via GPT-4o.
318
+
319
+ Returns:
320
+ True
321
+ """
322
+ return True
323
+
324
+ def enhance(self, skill_dir: Path, api_key: str) -> bool:
325
+ """
326
+ Enhance SKILL.md using GPT-4o API.
327
+
328
+ Args:
329
+ skill_dir: Path to skill directory
330
+ api_key: OpenAI API key
331
+
332
+ Returns:
333
+ True if enhancement succeeded
334
+ """
335
+ # Check for openai library
336
+ try:
337
+ from openai import OpenAI
338
+ except ImportError:
339
+ print("āŒ Error: openai package not installed")
340
+ print("Install with: pip install openai")
341
+ return False
342
+
343
+ skill_dir = Path(skill_dir)
344
+ references_dir = skill_dir / "references"
345
+ skill_md_path = skill_dir / "SKILL.md"
346
+
347
+ # Read reference files
348
+ print("šŸ“– Reading reference documentation...")
349
+ references = self._read_reference_files(references_dir)
350
+
351
+ if not references:
352
+ print("āŒ No reference files found to analyze")
353
+ return False
354
+
355
+ print(f" āœ“ Read {len(references)} reference files")
356
+ total_size = sum(len(c) for c in references.values())
357
+ print(f" āœ“ Total size: {total_size:,} characters\n")
358
+
359
+ # Read current SKILL.md
360
+ current_skill_md = None
361
+ if skill_md_path.exists():
362
+ current_skill_md = skill_md_path.read_text(encoding="utf-8")
363
+ print(f" ℹ Found existing SKILL.md ({len(current_skill_md)} chars)")
364
+ else:
365
+ print(" ℹ No existing SKILL.md, will create new one")
366
+
367
+ # Build enhancement prompt
368
+ prompt = self._build_enhancement_prompt(skill_dir.name, references, current_skill_md)
369
+
370
+ print("\nšŸ¤– Asking GPT-4o to enhance SKILL.md...")
371
+ print(f" Input: {len(prompt):,} characters")
372
+
373
+ try:
374
+ client = OpenAI(api_key=api_key)
375
+
376
+ response = client.chat.completions.create(
377
+ model="gpt-4o",
378
+ messages=[
379
+ {
380
+ "role": "system",
381
+ "content": "You are an expert technical writer creating Assistant instructions for OpenAI ChatGPT.",
382
+ },
383
+ {"role": "user", "content": prompt},
384
+ ],
385
+ temperature=0.3,
386
+ max_tokens=4096,
387
+ )
388
+
389
+ enhanced_content = response.choices[0].message.content
390
+ print(f" āœ“ Generated enhanced SKILL.md ({len(enhanced_content)} chars)\n")
391
+
392
+ # Backup original
393
+ if skill_md_path.exists():
394
+ backup_path = skill_md_path.with_suffix(".md.backup")
395
+ skill_md_path.rename(backup_path)
396
+ print(f" šŸ’¾ Backed up original to: {backup_path.name}")
397
+
398
+ # Save enhanced version
399
+ skill_md_path.write_text(enhanced_content, encoding="utf-8")
400
+ print(" āœ… Saved enhanced SKILL.md")
401
+
402
+ return True
403
+
404
+ except Exception as e:
405
+ print(f"āŒ Error calling OpenAI API: {e}")
406
+ return False
407
+
408
+ def _read_reference_files(
409
+ self, references_dir: Path, max_chars: int = 200000
410
+ ) -> dict[str, str]:
411
+ """
412
+ Read reference markdown files from skill directory.
413
+
414
+ Args:
415
+ references_dir: Path to references directory
416
+ max_chars: Maximum total characters to read
417
+
418
+ Returns:
419
+ Dictionary mapping filename to content
420
+ """
421
+ if not references_dir.exists():
422
+ return {}
423
+
424
+ references = {}
425
+ total_chars = 0
426
+
427
+ # Read all .md files
428
+ for ref_file in sorted(references_dir.glob("*.md")):
429
+ if total_chars >= max_chars:
430
+ break
431
+
432
+ try:
433
+ content = ref_file.read_text(encoding="utf-8")
434
+ # Limit individual file size
435
+ if len(content) > 30000:
436
+ content = content[:30000] + "\n\n...(truncated)"
437
+
438
+ references[ref_file.name] = content
439
+ total_chars += len(content)
440
+
441
+ except Exception as e:
442
+ print(f" āš ļø Could not read {ref_file.name}: {e}")
443
+
444
+ return references
445
+
446
+ def _build_enhancement_prompt(
447
+ self, skill_name: str, references: dict[str, str], current_skill_md: str = None
448
+ ) -> str:
449
+ """
450
+ Build OpenAI API prompt for enhancement.
451
+
452
+ Args:
453
+ skill_name: Name of the skill
454
+ references: Dictionary of reference content
455
+ current_skill_md: Existing SKILL.md content (optional)
456
+
457
+ Returns:
458
+ Enhancement prompt for GPT-4o
459
+ """
460
+ prompt = f"""You are creating Assistant instructions for an OpenAI ChatGPT Assistant about: {skill_name}
461
+
462
+ I've scraped documentation and organized it into reference files. Your job is to create EXCELLENT Assistant instructions that will help the Assistant use this documentation effectively.
463
+
464
+ CURRENT INSTRUCTIONS:
465
+ {"```" if current_skill_md else "(none - create from scratch)"}
466
+ {current_skill_md or "No existing instructions"}
467
+ {"```" if current_skill_md else ""}
468
+
469
+ REFERENCE DOCUMENTATION:
470
+ """
471
+
472
+ for filename, content in references.items():
473
+ prompt += f"\n\n## {filename}\n```markdown\n{content[:30000]}\n```\n"
474
+
475
+ prompt += """
476
+
477
+ YOUR TASK:
478
+ Create enhanced Assistant instructions that include:
479
+
480
+ 1. **Clear role definition** - "You are an expert assistant for [topic]"
481
+ 2. **Knowledge base description** - What documentation is attached
482
+ 3. **Excellent Quick Reference** - Extract 5-10 of the BEST, most practical code examples from the reference docs
483
+ - Choose SHORT, clear examples that demonstrate common tasks
484
+ - Include both simple and intermediate examples
485
+ - Annotate examples with clear descriptions
486
+ - Use proper language tags (cpp, python, javascript, json, etc.)
487
+ 4. **Response guidelines** - How the Assistant should help users
488
+ 5. **Search strategy** - When to use file_search, how to find information
489
+ 6. **DO NOT use YAML frontmatter** - This is plain text instructions for OpenAI
490
+
491
+ IMPORTANT:
492
+ - Extract REAL examples from the reference docs, don't make them up
493
+ - Prioritize SHORT, clear examples (5-20 lines max)
494
+ - Make it actionable and practical for the Assistant
495
+ - Write clear, direct instructions
496
+ - Focus on how the Assistant should behave and respond
497
+ - NO YAML frontmatter (no --- blocks)
498
+
499
+ OUTPUT:
500
+ Return ONLY the complete Assistant instructions as plain text.
501
+ """
502
+
503
+ return prompt