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,461 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Install skills to AI coding agent directories.
4
+
5
+ This module provides functionality to install Skill Seekers-generated skills
6
+ to various AI coding agents (Claude Code, Cursor, VS Code, Amp, Goose, etc.)
7
+ by copying skill directories to agent-specific installation paths.
8
+
9
+ Usage:
10
+ skill-seekers install-agent <skill_directory> --agent <agent_name> [--force] [--dry-run]
11
+
12
+ Examples:
13
+ # Install to specific agent
14
+ skill-seekers install-agent output/react/ --agent cursor
15
+
16
+ # Install to all agents at once
17
+ skill-seekers install-agent output/react/ --agent all
18
+
19
+ # Force overwrite existing installation
20
+ skill-seekers install-agent output/react/ --agent claude --force
21
+
22
+ # Preview installation without making changes
23
+ skill-seekers install-agent output/react/ --agent cursor --dry-run
24
+ """
25
+
26
+ import argparse
27
+ import shutil
28
+ import sys
29
+ from difflib import get_close_matches
30
+ from pathlib import Path
31
+
32
+ # Agent installation paths
33
+ # Global paths (install to home directory): Use ~/.{agent}/skills/
34
+ # Project paths (install to current directory): Use .{agent}/skills/
35
+ AGENT_PATHS = {
36
+ "claude": "~/.claude/skills/", # Global (home)
37
+ "cursor": ".cursor/skills/", # Project-relative
38
+ "vscode": ".github/skills/", # Project-relative
39
+ "copilot": ".github/skills/", # Same as VSCode
40
+ "amp": "~/.amp/skills/", # Global
41
+ "goose": "~/.config/goose/skills/", # Global
42
+ "opencode": "~/.opencode/skills/", # Global
43
+ "letta": "~/.letta/skills/", # Global
44
+ "aide": "~/.aide/skills/", # Global
45
+ "windsurf": "~/.windsurf/skills/", # Global
46
+ "neovate": "~/.neovate/skills/", # Global
47
+ }
48
+
49
+
50
+ def get_agent_path(agent_name: str, project_root: Path | None = None) -> Path:
51
+ """
52
+ Resolve the installation path for a given agent.
53
+
54
+ Handles both global paths (~/.<agent>/skills/) and project-relative paths
55
+ (.cursor/skills/, .github/skills/).
56
+
57
+ Args:
58
+ agent_name: Name of the agent (e.g., 'claude', 'cursor')
59
+ project_root: Optional project root directory for project-relative paths
60
+ (defaults to current working directory)
61
+
62
+ Returns:
63
+ Absolute path to the agent's skill installation directory
64
+
65
+ Raises:
66
+ ValueError: If agent_name is not recognized
67
+ """
68
+ agent_name = agent_name.lower()
69
+
70
+ if agent_name not in AGENT_PATHS:
71
+ raise ValueError(f"Unknown agent: {agent_name}")
72
+
73
+ path_template = AGENT_PATHS[agent_name]
74
+
75
+ # Handle home directory expansion (~)
76
+ if path_template.startswith("~"):
77
+ return Path(path_template).expanduser()
78
+
79
+ # Handle project-relative paths
80
+ if project_root is None:
81
+ project_root = Path.cwd()
82
+
83
+ return project_root / path_template
84
+
85
+
86
+ def get_available_agents() -> list:
87
+ """
88
+ Get list of all supported agent names.
89
+
90
+ Returns:
91
+ List of agent names (lowercase)
92
+ """
93
+ return sorted(AGENT_PATHS.keys())
94
+
95
+
96
+ def validate_agent_name(agent_name: str) -> tuple[bool, str | None]:
97
+ """
98
+ Validate an agent name and provide suggestions if invalid.
99
+
100
+ Performs case-insensitive matching and fuzzy matching to suggest
101
+ similar agent names if the provided name is invalid.
102
+
103
+ Args:
104
+ agent_name: Agent name to validate
105
+
106
+ Returns:
107
+ Tuple of (is_valid, error_message)
108
+ - is_valid: True if agent name is valid, False otherwise
109
+ - error_message: None if valid, error message with suggestions if invalid
110
+ """
111
+ # Special case: 'all' is valid for installing to all agents
112
+ if agent_name.lower() == "all":
113
+ return True, None
114
+
115
+ # Case-insensitive check
116
+ if agent_name.lower() in AGENT_PATHS:
117
+ return True, None
118
+
119
+ # Agent not found - provide suggestions
120
+ available = get_available_agents()
121
+
122
+ # Try fuzzy matching (find similar names)
123
+ suggestions = get_close_matches(agent_name.lower(), available, n=1, cutoff=0.6)
124
+
125
+ error_msg = f"Unknown agent '{agent_name}'\n\n"
126
+
127
+ if suggestions:
128
+ error_msg += f"Did you mean: {suggestions[0]}?\n\n"
129
+
130
+ error_msg += "Available agents:\n "
131
+ error_msg += ", ".join(available + ["all"])
132
+ error_msg += f"\n\nUsage:\n skill-seekers install-agent <skill_directory> --agent {suggestions[0] if suggestions else 'claude'}"
133
+
134
+ return False, error_msg
135
+
136
+
137
+ def validate_skill_directory(skill_dir: Path) -> tuple[bool, str | None]:
138
+ """
139
+ Validate that a directory is a valid skill directory.
140
+
141
+ A valid skill directory must:
142
+ - Exist
143
+ - Be a directory
144
+ - Contain a SKILL.md file
145
+
146
+ Args:
147
+ skill_dir: Path to skill directory
148
+
149
+ Returns:
150
+ Tuple of (is_valid, error_message)
151
+ """
152
+ if not skill_dir.exists():
153
+ return False, f"Skill directory does not exist: {skill_dir}"
154
+
155
+ if not skill_dir.is_dir():
156
+ return False, f"Path is not a directory: {skill_dir}"
157
+
158
+ skill_md = skill_dir / "SKILL.md"
159
+ if not skill_md.exists():
160
+ return False, f"SKILL.md not found in {skill_dir}"
161
+
162
+ return True, None
163
+
164
+
165
+ def install_to_agent(
166
+ skill_dir: str | Path, agent_name: str, force: bool = False, dry_run: bool = False
167
+ ) -> tuple[bool, str]:
168
+ """
169
+ Install a skill to a specific agent's directory.
170
+
171
+ Copies the skill directory to the agent's installation path, excluding
172
+ backup files and temporary files.
173
+
174
+ Args:
175
+ skill_dir: Path to skill directory
176
+ agent_name: Name of agent to install to
177
+ force: If True, overwrite existing installation without asking
178
+ dry_run: If True, preview installation without making changes
179
+
180
+ Returns:
181
+ Tuple of (success, message)
182
+ - success: True if installation succeeded, False otherwise
183
+ - message: Success message or error description
184
+ """
185
+ # Convert to Path
186
+ skill_dir = Path(skill_dir).resolve()
187
+ skill_name = skill_dir.name
188
+
189
+ # Validate skill directory
190
+ is_valid, error_msg = validate_skill_directory(skill_dir)
191
+ if not is_valid:
192
+ return False, f"❌ {error_msg}"
193
+
194
+ # Validate agent name
195
+ is_valid, error_msg = validate_agent_name(agent_name)
196
+ if not is_valid:
197
+ return False, f"❌ {error_msg}"
198
+
199
+ # Get agent installation path
200
+ try:
201
+ agent_base_path = get_agent_path(agent_name.lower())
202
+ except ValueError as e:
203
+ return False, f"❌ {str(e)}"
204
+
205
+ # Target path: {agent_base_path}/{skill_name}/
206
+ target_path = agent_base_path / skill_name
207
+
208
+ # Check if already exists
209
+ if target_path.exists() and not force:
210
+ error_msg = "❌ Skill already installed\n\n"
211
+ error_msg += f"Location: {target_path}\n\n"
212
+ error_msg += "Options:\n"
213
+ error_msg += f" 1. Overwrite: skill-seekers install-agent {skill_dir} --agent {agent_name} --force\n"
214
+ error_msg += f" 2. Remove: rm -rf {target_path}\n"
215
+ error_msg += f" 3. Rename: mv {skill_dir} {skill_dir.parent / (skill_name + '-v2')}"
216
+ return False, error_msg
217
+
218
+ # Dry run mode - just preview
219
+ if dry_run:
220
+ msg = "🔍 DRY RUN - No changes will be made\n\n"
221
+ msg += f"Would install skill: {skill_name}\n"
222
+ msg += f" Source: {skill_dir}\n"
223
+ msg += f" Target: {target_path}\n\n"
224
+
225
+ # Calculate total size
226
+ total_size = sum(f.stat().st_size for f in skill_dir.rglob("*") if f.is_file())
227
+
228
+ msg += "Files to copy:\n"
229
+ msg += f" SKILL.md ({(skill_dir / 'SKILL.md').stat().st_size / 1024:.1f} KB)\n"
230
+
231
+ references_dir = skill_dir / "references"
232
+ if references_dir.exists():
233
+ ref_files = list(references_dir.rglob("*.md"))
234
+ ref_size = sum(f.stat().st_size for f in ref_files)
235
+ msg += f" references/ ({len(ref_files)} files, {ref_size / 1024:.1f} KB)\n"
236
+
237
+ for subdir in ["scripts", "assets"]:
238
+ subdir_path = skill_dir / subdir
239
+ if subdir_path.exists():
240
+ files = list(subdir_path.rglob("*"))
241
+ if files:
242
+ msg += f" {subdir}/ ({len(files)} files)\n"
243
+ else:
244
+ msg += f" {subdir}/ (empty)\n"
245
+
246
+ msg += f"\nTotal size: {total_size / 1024:.1f} KB\n\n"
247
+ msg += "To actually install, run:\n"
248
+ msg += f" skill-seekers install-agent {skill_dir} --agent {agent_name}"
249
+
250
+ return True, msg
251
+
252
+ # Create parent directories if needed
253
+ try:
254
+ agent_base_path.mkdir(parents=True, exist_ok=True)
255
+ except PermissionError:
256
+ return (
257
+ False,
258
+ f"❌ Permission denied: {agent_base_path}\n\nTry: sudo mkdir -p {agent_base_path} && sudo chown -R $USER {agent_base_path}",
259
+ )
260
+
261
+ # Copy skill directory
262
+ def ignore_files(_directory, files):
263
+ """Filter function for shutil.copytree to exclude unwanted files."""
264
+ ignored = []
265
+ for f in files:
266
+ # Exclude backup files
267
+ if (
268
+ f.endswith(".backup")
269
+ or f == "__pycache__"
270
+ or f == ".DS_Store"
271
+ or f.startswith(".")
272
+ and f not in [".github", ".cursor"]
273
+ ):
274
+ ignored.append(f)
275
+ return ignored
276
+
277
+ try:
278
+ # Remove existing if force mode
279
+ if target_path.exists() and force:
280
+ shutil.rmtree(target_path)
281
+
282
+ # Copy directory
283
+ shutil.copytree(skill_dir, target_path, ignore=ignore_files)
284
+
285
+ # Success message
286
+ msg = "✅ Installation complete!\n\n"
287
+ msg += f"Skill '{skill_name}' installed to {agent_name}\n"
288
+ msg += f"Location: {target_path}\n\n"
289
+
290
+ # Agent-specific restart instructions
291
+ if agent_name.lower() == "claude":
292
+ msg += "Restart Claude Code to load the new skill."
293
+ elif agent_name.lower() == "cursor":
294
+ msg += "Restart Cursor to load the new skill."
295
+ elif agent_name.lower() in ["vscode", "copilot"]:
296
+ msg += "Restart VS Code to load the new skill."
297
+ else:
298
+ msg += f"Restart {agent_name.capitalize()} to load the new skill."
299
+
300
+ return True, msg
301
+
302
+ except PermissionError as e:
303
+ return (
304
+ False,
305
+ f"❌ Permission denied: {e}\n\nTry: sudo mkdir -p {agent_base_path} && sudo chown -R $USER {agent_base_path}",
306
+ )
307
+ except Exception as e:
308
+ return False, f"❌ Installation failed: {e}"
309
+
310
+
311
+ def install_to_all_agents(
312
+ skill_dir: str | Path, force: bool = False, dry_run: bool = False
313
+ ) -> dict[str, tuple[bool, str]]:
314
+ """
315
+ Install a skill to all available agents.
316
+
317
+ Attempts to install the skill to all agents in AGENT_PATHS,
318
+ collecting results for each agent.
319
+
320
+ Args:
321
+ skill_dir: Path to skill directory
322
+ force: If True, overwrite existing installations
323
+ dry_run: If True, preview installations without making changes
324
+
325
+ Returns:
326
+ Dictionary mapping agent names to (success, message) tuples
327
+ """
328
+ results = {}
329
+
330
+ for agent_name in get_available_agents():
331
+ success, message = install_to_agent(skill_dir, agent_name, force=force, dry_run=dry_run)
332
+ results[agent_name] = (success, message)
333
+
334
+ return results
335
+
336
+
337
+ def main() -> int:
338
+ """
339
+ Main entry point for install-agent CLI.
340
+
341
+ Returns:
342
+ Exit code (0 for success, 1 for error)
343
+ """
344
+ parser = argparse.ArgumentParser(
345
+ prog="skill-seekers-install-agent",
346
+ description="Install skills to AI coding agent directories",
347
+ formatter_class=argparse.RawDescriptionHelpFormatter,
348
+ epilog="""
349
+ Examples:
350
+ # Install to specific agent
351
+ skill-seekers install-agent output/react/ --agent cursor
352
+
353
+ # Install to all agents
354
+ skill-seekers install-agent output/react/ --agent all
355
+
356
+ # Force overwrite
357
+ skill-seekers install-agent output/react/ --agent claude --force
358
+
359
+ # Preview installation
360
+ skill-seekers install-agent output/react/ --agent cursor --dry-run
361
+
362
+ Supported agents:
363
+ claude, cursor, vscode, copilot, amp, goose, opencode, letta, aide, windsurf, neovate, all
364
+ """,
365
+ )
366
+
367
+ parser.add_argument("skill_directory", help="Path to skill directory (e.g., output/react/)")
368
+
369
+ parser.add_argument(
370
+ "--agent", required=True, help="Agent name (use 'all' to install to all agents)"
371
+ )
372
+
373
+ parser.add_argument(
374
+ "--force", action="store_true", help="Overwrite existing installation without asking"
375
+ )
376
+
377
+ parser.add_argument(
378
+ "--dry-run", action="store_true", help="Preview installation without making changes"
379
+ )
380
+
381
+ args = parser.parse_args()
382
+
383
+ # Convert skill directory to Path
384
+ skill_dir = Path(args.skill_directory)
385
+ skill_name = skill_dir.name
386
+
387
+ # Handle 'all' agent
388
+ if args.agent.lower() == "all":
389
+ print(f"\n📋 Installing skill to all agents: {skill_name}\n")
390
+
391
+ if args.dry_run:
392
+ print("🔍 DRY RUN MODE - No changes will be made\n")
393
+
394
+ results = install_to_all_agents(skill_dir, force=args.force, dry_run=args.dry_run)
395
+
396
+ # Print results
397
+ installed_count = 0
398
+ failed_count = 0
399
+ skipped_count = 0
400
+
401
+ for agent_name, (success, message) in results.items():
402
+ if success:
403
+ if args.dry_run:
404
+ print(f"⏳ Would install to {agent_name}...")
405
+ else:
406
+ agent_path = get_agent_path(agent_name)
407
+ print(f"⏳ Installing to {agent_name}... ✅ {agent_path / skill_name}")
408
+ installed_count += 1
409
+ else:
410
+ # Check if it's a permission error or skip
411
+ if "Permission denied" in message:
412
+ print(f"⏳ Installing to {agent_name}... ❌ Permission denied")
413
+ failed_count += 1
414
+ elif "does not exist" in message or "SKILL.md not found" in message:
415
+ # Validation error - only show once
416
+ print(message)
417
+ return 1
418
+ else:
419
+ print(f"⏳ Installing to {agent_name}... ⚠️ Skipped (not installed)")
420
+ skipped_count += 1
421
+
422
+ # Summary
423
+ print("\n📊 Summary:")
424
+ if args.dry_run:
425
+ print(f" Would install: {installed_count} agents")
426
+ else:
427
+ print(f" ✅ Installed: {installed_count} agents")
428
+ if failed_count > 0:
429
+ print(f" ❌ Failed: {failed_count} agent(s) (permission denied)")
430
+ if skipped_count > 0:
431
+ print(f" ⚠️ Skipped: {skipped_count} agent(s) (not installed)")
432
+
433
+ if not args.dry_run:
434
+ print("\nRestart your agents to load the skill.")
435
+
436
+ if failed_count > 0:
437
+ print("\nFix permission errors:")
438
+ print(" sudo mkdir -p ~/.amp && sudo chown -R $USER ~/.amp")
439
+
440
+ return 0 if installed_count > 0 else 1
441
+
442
+ # Single agent installation
443
+ agent_name = args.agent
444
+
445
+ print(f"\n📋 Installing skill: {skill_name}")
446
+ print(f" Agent: {agent_name}")
447
+
448
+ if args.dry_run:
449
+ print("\n🔍 DRY RUN MODE - No changes will be made\n")
450
+
451
+ success, message = install_to_agent(
452
+ skill_dir, agent_name, force=args.force, dry_run=args.dry_run
453
+ )
454
+
455
+ print(message)
456
+
457
+ return 0 if success else 1
458
+
459
+
460
+ if __name__ == "__main__":
461
+ sys.exit(main())
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Complete Skill Installation Workflow
4
+ One-command installation: fetch → scrape → enhance → package → upload
5
+
6
+ This CLI tool orchestrates the complete skill installation workflow by calling
7
+ the install_skill MCP tool.
8
+
9
+ Usage:
10
+ skill-seekers install --config react
11
+ skill-seekers install --config configs/custom.json --no-upload
12
+ skill-seekers install --config django --unlimited
13
+ skill-seekers install --config react --dry-run
14
+
15
+ Examples:
16
+ # Install React skill from official configs
17
+ skill-seekers install --config react
18
+
19
+ # Install from local config file
20
+ skill-seekers install --config configs/custom.json
21
+
22
+ # Install without uploading
23
+ skill-seekers install --config django --no-upload
24
+
25
+ # Preview workflow without executing
26
+ skill-seekers install --config react --dry-run
27
+ """
28
+
29
+ import argparse
30
+ import asyncio
31
+ import sys
32
+ from pathlib import Path
33
+
34
+ # Add parent directory to path to import MCP server
35
+ sys.path.insert(0, str(Path(__file__).parent.parent))
36
+
37
+ # Import the MCP tool function (with lazy loading)
38
+ try:
39
+ from skill_seekers.mcp.server import install_skill_tool
40
+
41
+ MCP_AVAILABLE = True
42
+ except ImportError:
43
+ MCP_AVAILABLE = False
44
+ install_skill_tool = None
45
+
46
+
47
+ def main():
48
+ """Main entry point for CLI"""
49
+ # Check MCP availability first
50
+ if not MCP_AVAILABLE:
51
+ print("\n❌ Error: MCP package not installed")
52
+ print("\nThe 'install' command requires MCP support.")
53
+ print("Install with:")
54
+ print(" pip install skill-seekers[mcp]")
55
+ print("\nOr use these alternatives:")
56
+ print(" skill-seekers scrape --config react")
57
+ print(" skill-seekers package output/react/")
58
+ print()
59
+ sys.exit(1)
60
+
61
+ parser = argparse.ArgumentParser(
62
+ description="Complete skill installation workflow (fetch → scrape → enhance → package → upload)",
63
+ formatter_class=argparse.RawDescriptionHelpFormatter,
64
+ epilog="""
65
+ Examples:
66
+ # Install React skill from official API
67
+ skill-seekers install --config react
68
+
69
+ # Install from local config file
70
+ skill-seekers install --config configs/custom.json
71
+
72
+ # Install without uploading
73
+ skill-seekers install --config django --no-upload
74
+
75
+ # Unlimited scraping (no page limits)
76
+ skill-seekers install --config godot --unlimited
77
+
78
+ # Preview workflow (dry run)
79
+ skill-seekers install --config react --dry-run
80
+
81
+ # Install for Gemini instead of Claude
82
+ skill-seekers install --config react --target gemini
83
+
84
+ # Install for OpenAI ChatGPT
85
+ skill-seekers install --config fastapi --target openai
86
+
87
+ Important:
88
+ - Enhancement is MANDATORY (30-60 sec) for quality (3/10→9/10)
89
+ - Total time: 20-45 minutes (mostly scraping)
90
+ - Multi-platform support: claude (default), gemini, openai, markdown
91
+ - Auto-uploads if API key is set (ANTHROPIC_API_KEY, GOOGLE_API_KEY, or OPENAI_API_KEY)
92
+
93
+ Phases:
94
+ 1. Fetch config (if config name provided)
95
+ 2. Scrape documentation
96
+ 3. AI Enhancement (MANDATORY - no skip option)
97
+ 4. Package for target platform (ZIP or tar.gz)
98
+ 5. Upload to target platform (optional)
99
+ """,
100
+ )
101
+
102
+ parser.add_argument(
103
+ "--config",
104
+ required=True,
105
+ help="Config name (e.g., 'react') or path (e.g., 'configs/custom.json')",
106
+ )
107
+
108
+ parser.add_argument(
109
+ "--destination",
110
+ default="output",
111
+ help="Output directory for skill files (default: output/)",
112
+ )
113
+
114
+ parser.add_argument("--no-upload", action="store_true", help="Skip automatic upload to Claude")
115
+
116
+ parser.add_argument(
117
+ "--unlimited",
118
+ action="store_true",
119
+ help="Remove page limits during scraping (WARNING: Can take hours)",
120
+ )
121
+
122
+ parser.add_argument("--dry-run", action="store_true", help="Preview workflow without executing")
123
+
124
+ parser.add_argument(
125
+ "--target",
126
+ choices=["claude", "gemini", "openai", "markdown"],
127
+ default="claude",
128
+ help="Target LLM platform (default: claude)",
129
+ )
130
+
131
+ args = parser.parse_args()
132
+
133
+ # Determine if config is a name or path
134
+ config_arg = args.config
135
+ if config_arg.endswith(".json") or "/" in config_arg or "\\" in config_arg:
136
+ # It's a path
137
+ config_path = config_arg
138
+ config_name = None
139
+ else:
140
+ # It's a name
141
+ config_name = config_arg
142
+ config_path = None
143
+
144
+ # Build arguments for install_skill_tool
145
+ tool_args = {
146
+ "config_name": config_name,
147
+ "config_path": config_path,
148
+ "destination": args.destination,
149
+ "auto_upload": not args.no_upload,
150
+ "unlimited": args.unlimited,
151
+ "dry_run": args.dry_run,
152
+ "target": args.target,
153
+ }
154
+
155
+ # Run async tool
156
+ try:
157
+ result = asyncio.run(install_skill_tool(tool_args))
158
+
159
+ # Print output
160
+ for content in result:
161
+ print(content.text)
162
+
163
+ # Return success/failure based on output
164
+ output_text = result[0].text
165
+ if "❌" in output_text and "WORKFLOW COMPLETE" not in output_text:
166
+ return 1
167
+ return 0
168
+
169
+ except KeyboardInterrupt:
170
+ print("\n\n⚠️ Workflow interrupted by user")
171
+ return 130 # Standard exit code for SIGINT
172
+ except Exception as e:
173
+ print(f"\n\n❌ Unexpected error: {str(e)}")
174
+ return 1
175
+
176
+
177
+ if __name__ == "__main__":
178
+ sys.exit(main())