Skills › Knowledge Work Plugins › data-context-extractor › package_data_skill.py
package_data_skill.py
126 lines · data/skills/data-context-extractor/scripts/package_data_skill.py at 2574e29
1#!/usr/bin/env python3
2"""
3Package a generated data analysis skill into a distributable .skill file (zip format).
5Usage:
6 python package_data_skill.py <path/to/skill-folder> [output-directory]
8Example:
9 python package_data_skill.py /home/claude/acme-data-analyst
10 python package_data_skill.py /home/claude/acme-data-analyst /tmp/outputs
11"""
13import sys
14import zipfile
15from pathlib import Path
18def validate_skill(skill_path: Path) -> tuple[bool, str]:
19 """Basic validation of skill structure."""
21 # Check SKILL.md exists
22 skill_md = skill_path / "SKILL.md"
23 if not skill_md.exists():
24 return False, "Missing SKILL.md"
26 # Check SKILL.md has frontmatter
27 content = skill_md.read_text()
28 if not content.startswith("---"):
29 return False, "SKILL.md missing YAML frontmatter"
31 # Check for required frontmatter fields
32 if "name:" not in content[:500]:
33 return False, "SKILL.md missing 'name' in frontmatter"
34 if "description:" not in content[:1000]:
35 return False, "SKILL.md missing 'description' in frontmatter"
37 # Check for placeholder text that wasn't filled in
38 if "[PLACEHOLDER]" in content or "[COMPANY]" in content:
39 return False, "SKILL.md contains unfilled placeholder text"
41 return True, "Validation passed"
44def package_skill(skill_path: str, output_dir: str = None) -> Path | None:
45 """
46 Package a skill folder into a .skill file.
48 Args:
49 skill_path: Path to the skill folder
50 output_dir: Optional output directory
52 Returns:
53 Path to the created .skill file, or None if error
54 """
55 skill_path = Path(skill_path).resolve()
57 # Validate folder exists
58 if not skill_path.exists():
59 print(f"Error: Skill folder not found: {skill_path}")
60 return None
62 if not skill_path.is_dir():
63 print(f"Error: Path is not a directory: {skill_path}")
64 return None
66 # Run validation
67 print("Validating skill...")
68 valid, message = validate_skill(skill_path)
69 if not valid:
70 print(f"Validation failed: {message}")
71 return None
72 print(f"{message}\n")
74 # Determine output location
75 skill_name = skill_path.name
76 if output_dir:
77 output_path = Path(output_dir).resolve()
78 else:
79 output_path = Path.cwd()
81 output_path.mkdir(parents=True, exist_ok=True)
82 skill_filename = output_path / f"{skill_name}.zip"
84 # Create the zip file
85 try:
86 with zipfile.ZipFile(skill_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
87 for file_path in skill_path.rglob('*'):
88 if file_path.is_file():
89 # Skip hidden files and common junk
90 if any(part.startswith('.') for part in file_path.parts):
91 continue
92 if file_path.name in ['__pycache__', '.DS_Store', 'Thumbs.db']:
93 continue
95 # Calculate relative path within the zip
96 arcname = file_path.relative_to(skill_path.parent)
97 zipf.write(file_path, arcname)
98 print(f" Added: {arcname}")
100 print(f"\nSuccessfully packaged skill to: {skill_filename}")
101 return skill_filename
103 except Exception as e:
104 print(f"Error creating zip file: {e}")
105 return None
108def main():
109 if len(sys.argv) < 2:
110 print(__doc__)
111 sys.exit(1)
113 skill_path = sys.argv[1]
114 output_dir = sys.argv[2] if len(sys.argv) > 2 else None
116 print(f"Packaging skill: {skill_path}")
117 if output_dir:
118 print(f" Output directory: {output_dir}")
119 print()
121 result = package_skill(skill_path, output_dir)
122 sys.exit(0 if result else 1)
125if __name__ == "__main__":
126 main()