Files
skill-seekers-reference/src/skill_seekers/cli/package_skill.py
yusyus b636a0a292 fix: resolve issue #299 and Phase 1 cleanup
- Fix #299: rename --chunk-size/--chunk-overlap to --streaming-chunk-size/
  --streaming-overlap in arguments/package.py to avoid collision with the
  RAG --chunk-size flag from arguments/common.py
- Phase 1a: make package_skill.py import args via add_package_arguments()
  instead of a 105-line inline duplicate argparse block; fixes the root
  cause of _reconstruct_argv() passing unrecognised flag names
- Phase 1b: centralise setup_logging() into utils.py and remove 4
  duplicate module-level logging.basicConfig() calls from doc_scraper.py,
  github_scraper.py, codebase_scraper.py, and unified_scraper.py
- Fix test_package_structure.py / test_cli_paths.py version strings
  (3.1.1 → 3.1.2)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 21:22:05 +03:00

296 lines
9.2 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Simple Skill Packager
Packages a skill directory into a .zip file for Claude.
Usage:
skill-seekers package output/steam-inventory/
skill-seekers package output/react/
skill-seekers package output/react/ --no-open # Don't open folder
"""
import argparse
import os
import sys
from pathlib import Path
# Import utilities
try:
from quality_checker import SkillQualityChecker, print_report
from utils import (
format_file_size,
open_folder,
print_upload_instructions,
validate_skill_directory,
)
except ImportError:
# If running from different directory, add cli to path
sys.path.insert(0, str(Path(__file__).parent))
from quality_checker import SkillQualityChecker, print_report
from utils import (
format_file_size,
open_folder,
print_upload_instructions,
validate_skill_directory,
)
def package_skill(
skill_dir,
open_folder_after=True,
skip_quality_check=False,
target="claude",
streaming=False,
chunk_size=4000,
chunk_overlap=200,
batch_size=100,
enable_chunking=False,
chunk_max_tokens=512,
preserve_code_blocks=True,
):
"""
Package a skill directory into platform-specific format
Args:
skill_dir: Path to skill directory
open_folder_after: Whether to open the output folder after packaging
skip_quality_check: Skip quality checks before packaging
target: Target LLM platform ('claude', 'gemini', 'openai', 'markdown')
streaming: Use streaming ingestion for large docs
chunk_size: Maximum characters per chunk (streaming mode)
chunk_overlap: Overlap between chunks (streaming mode)
batch_size: Number of chunks per batch (streaming mode)
enable_chunking: Enable intelligent chunking for RAG platforms
chunk_max_tokens: Maximum tokens per chunk (default: 512)
preserve_code_blocks: Preserve code blocks during chunking
Returns:
tuple: (success, package_path) where success is bool and package_path is Path or None
"""
skill_path = Path(skill_dir)
# Validate skill directory
is_valid, error_msg = validate_skill_directory(skill_path)
if not is_valid:
print(f"❌ Error: {error_msg}")
return False, None
# Run quality checks (unless skipped)
if not skip_quality_check:
print("\n" + "=" * 60)
print("QUALITY CHECK")
print("=" * 60)
checker = SkillQualityChecker(skill_path)
report = checker.check_all()
# Print report
print_report(report, verbose=False)
# If there are errors or warnings, ask user to confirm
if report.has_errors or report.has_warnings:
print("=" * 60)
response = input("\nContinue with packaging? (y/n): ").strip().lower()
if response != "y":
print("\n❌ Packaging cancelled by user")
return False, None
print()
else:
print("=" * 60)
print()
# Get platform-specific adaptor
try:
from skill_seekers.cli.adaptors import get_adaptor
adaptor = get_adaptor(target)
except (ImportError, ValueError) as e:
print(f"❌ Error: {e}")
return False, None
# Create package using adaptor
skill_name = skill_path.name
output_dir = skill_path.parent
# Auto-enable chunking for RAG platforms
RAG_PLATFORMS = [
"langchain",
"llama-index",
"haystack",
"weaviate",
"chroma",
"faiss",
"qdrant",
]
if target in RAG_PLATFORMS and not enable_chunking:
print(f" Auto-enabling chunking for {target} platform")
enable_chunking = True
print(f"📦 Packaging skill: {skill_name}")
print(f" Target: {adaptor.PLATFORM_NAME}")
print(f" Source: {skill_path}")
if streaming:
print(f" Mode: Streaming (chunk_size={chunk_size}, overlap={chunk_overlap})")
elif enable_chunking:
print(
f" Chunking: Enabled (max_tokens={chunk_max_tokens}, preserve_code={preserve_code_blocks})"
)
try:
# Use streaming if requested and supported
if streaming and hasattr(adaptor, "package_streaming"):
package_path = adaptor.package_streaming(
skill_path,
output_dir,
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
batch_size=batch_size,
)
elif streaming:
print("⚠️ Streaming not supported for this platform, using standard packaging")
package_path = adaptor.package(
skill_path,
output_dir,
enable_chunking=enable_chunking,
chunk_max_tokens=chunk_max_tokens,
preserve_code_blocks=preserve_code_blocks,
)
else:
package_path = adaptor.package(
skill_path,
output_dir,
enable_chunking=enable_chunking,
chunk_max_tokens=chunk_max_tokens,
preserve_code_blocks=preserve_code_blocks,
)
print(f" Output: {package_path}")
except Exception as e:
print(f"❌ Error creating package: {e}")
return False, None
# Get package size
package_size = package_path.stat().st_size
print(f"\n✅ Package created: {package_path}")
print(f" Size: {package_size:,} bytes ({format_file_size(package_size)})")
# Open folder in file browser
if open_folder_after:
print(f"\n📂 Opening folder: {package_path.parent}")
open_folder(package_path.parent)
# Print upload instructions
print_upload_instructions(package_path)
return True, package_path
def main():
from skill_seekers.cli.arguments.package import add_package_arguments
parser = argparse.ArgumentParser(
description="Package a skill directory into a .zip file for Claude",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Package skill with quality checks (recommended)
skill-seekers package output/react/
# Package skill without opening folder
skill-seekers package output/react/ --no-open
# Skip quality checks (faster, but not recommended)
skill-seekers package output/react/ --skip-quality-check
# Package and auto-upload to Claude
skill-seekers package output/react/ --upload
# Get help
skill-seekers package --help
""",
)
add_package_arguments(parser)
args = parser.parse_args()
success, package_path = package_skill(
args.skill_directory,
open_folder_after=not args.no_open,
skip_quality_check=args.skip_quality_check,
target=args.target,
streaming=args.streaming,
chunk_size=args.streaming_chunk_size,
chunk_overlap=args.streaming_overlap,
batch_size=args.batch_size,
enable_chunking=args.chunk,
chunk_max_tokens=args.chunk_tokens,
preserve_code_blocks=not args.no_preserve_code,
)
if not success:
sys.exit(1)
# Auto-upload if requested
if args.upload:
try:
from skill_seekers.cli.adaptors import get_adaptor
# Get adaptor for target platform
adaptor = get_adaptor(args.target)
# Get API key from environment
api_key = os.environ.get(adaptor.get_env_var_name(), "").strip()
if not api_key:
# No API key - show helpful message but DON'T fail
print("\n" + "=" * 60)
print("💡 Automatic Upload")
print("=" * 60)
print()
print(f"To enable automatic upload to {adaptor.PLATFORM_NAME}:")
print(" 1. Get API key from the platform")
print(f" 2. Set: export {adaptor.get_env_var_name()}=...")
print(" 3. Run package command with --upload flag")
print()
print("For now, use manual upload (instructions above) ☝️")
print("=" * 60)
# Exit successfully - packaging worked!
sys.exit(0)
# API key exists - try upload
print("\n" + "=" * 60)
print(f"📤 Uploading to {adaptor.PLATFORM_NAME}...")
print("=" * 60)
result = adaptor.upload(package_path, api_key)
if result["success"]:
print(f"\n{result['message']}")
if result["url"]:
print(f" View at: {result['url']}")
print("=" * 60)
sys.exit(0)
else:
print(f"\n❌ Upload failed: {result['message']}")
print()
print("💡 Try manual upload instead (instructions above) ☝️")
print("=" * 60)
# Exit successfully - packaging worked even if upload failed
sys.exit(0)
except ImportError as e:
print(f"\n❌ Error: {e}")
print("Install required dependencies for this platform")
sys.exit(1)
except Exception as e:
print(f"\n❌ Upload error: {e}")
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
main()