Initial commit
This commit is contained in:
113
skills/skill-customizer/scripts/finalize_skill.py
Executable file
113
skills/skill-customizer/scripts/finalize_skill.py
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Skill Packager for Customized Skills - Creates a timestamped zip file
|
||||
|
||||
Usage:
|
||||
python scripts/finalize_skill.py <path/to/skill-folder> [output-directory]
|
||||
|
||||
Example:
|
||||
python scripts/finalize_skill.py my-custom-skill
|
||||
python scripts/finalize_skill.py my-custom-skill ./dist
|
||||
"""
|
||||
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from quick_validate import validate_skill
|
||||
|
||||
|
||||
def package_skill(skill_path, output_dir=None):
|
||||
"""
|
||||
Package a skill folder into a timestamped zip file.
|
||||
|
||||
Args:
|
||||
skill_path: Path to the skill folder
|
||||
output_dir: Optional output directory for the zip file (defaults to current directory)
|
||||
|
||||
Returns:
|
||||
Path to the created zip file, or None if error
|
||||
"""
|
||||
skill_path = Path(skill_path).resolve()
|
||||
|
||||
# Validate skill folder exists
|
||||
if not skill_path.exists():
|
||||
print(f"❌ Error: Skill folder not found: {skill_path}")
|
||||
return None
|
||||
|
||||
if not skill_path.is_dir():
|
||||
print(f"❌ Error: Path is not a directory: {skill_path}")
|
||||
return None
|
||||
|
||||
# Validate SKILL.md exists
|
||||
skill_md = skill_path / "SKILL.md"
|
||||
if not skill_md.exists():
|
||||
print(f"❌ Error: SKILL.md not found in {skill_path}")
|
||||
return None
|
||||
|
||||
# Run validation before packaging
|
||||
print("🔍 Validating skill...")
|
||||
valid, message = validate_skill(skill_path)
|
||||
if not valid:
|
||||
print(f"❌ Validation failed: {message}")
|
||||
print(" Please fix the validation errors before packaging.")
|
||||
return None
|
||||
print(f"✅ {message}\n")
|
||||
|
||||
# Determine output location
|
||||
skill_name = skill_path.name
|
||||
if output_dir:
|
||||
output_path = Path(output_dir).resolve()
|
||||
output_path.mkdir(parents=True, exist_ok=True)
|
||||
else:
|
||||
output_path = Path.cwd()
|
||||
|
||||
# Generate timestamp for version tracking
|
||||
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
zip_filename = output_path / f"{skill_name}-{timestamp}.zip"
|
||||
|
||||
# Create the zip file
|
||||
try:
|
||||
with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
||||
# Walk through the skill directory
|
||||
for file_path in skill_path.rglob('*'):
|
||||
if file_path.is_file():
|
||||
# Calculate the relative path within the zip
|
||||
arcname = file_path.relative_to(skill_path.parent)
|
||||
zipf.write(file_path, arcname)
|
||||
print(f" Added: {arcname}")
|
||||
|
||||
print(f"\n✅ Successfully packaged skill to: {zip_filename}")
|
||||
return zip_filename
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error creating zip file: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python scripts/finalize_skill.py <path/to/skill-folder> [output-directory]")
|
||||
print("\nExample:")
|
||||
print(" python scripts/finalize_skill.py my-custom-skill")
|
||||
print(" python scripts/finalize_skill.py my-custom-skill ./dist")
|
||||
sys.exit(1)
|
||||
|
||||
skill_path = sys.argv[1]
|
||||
output_dir = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
|
||||
print(f"📦 Packaging skill: {skill_path}")
|
||||
if output_dir:
|
||||
print(f" Output directory: {output_dir}")
|
||||
print()
|
||||
|
||||
result = package_skill(skill_path, output_dir)
|
||||
|
||||
if result:
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
260
skills/skill-customizer/scripts/fork_skill.py
Executable file
260
skills/skill-customizer/scripts/fork_skill.py
Executable file
@@ -0,0 +1,260 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Skill Forker - Creates a customized copy of an existing skill
|
||||
|
||||
This script copies an existing skill to a new location with a new name,
|
||||
preserving all structure and resources while updating metadata to reflect
|
||||
the customization.
|
||||
|
||||
Usage:
|
||||
fork_skill.py <source-skill-path> <new-skill-name> --path <output-directory>
|
||||
|
||||
Examples:
|
||||
fork_skill.py ./pdf my-pdf-workflow --path ./custom-skills
|
||||
fork_skill.py ../skills/canvas-design my-design-style --path .
|
||||
fork_skill.py ./internal-comms company-comms --path ~/my-skills
|
||||
|
||||
The script will:
|
||||
- Copy all files and directories from the source skill
|
||||
- Update the skill name in SKILL.md frontmatter
|
||||
- Append customization metadata
|
||||
- Preserve all scripts, references, and assets
|
||||
- Create a customization log for tracking changes
|
||||
"""
|
||||
|
||||
import sys
|
||||
import shutil
|
||||
import re
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def update_skill_metadata(skill_md_path, new_name, source_skill_name):
|
||||
"""
|
||||
Update the SKILL.md file with new name and customization metadata.
|
||||
|
||||
Args:
|
||||
skill_md_path: Path to the SKILL.md file
|
||||
new_name: New skill name
|
||||
source_skill_name: Original skill name for reference
|
||||
"""
|
||||
content = skill_md_path.read_text()
|
||||
|
||||
# Update the name in frontmatter
|
||||
content = re.sub(
|
||||
r'(name:\s*)([^\n]+)',
|
||||
f'\\1{new_name}',
|
||||
content,
|
||||
count=1
|
||||
)
|
||||
|
||||
# Add or update metadata section in frontmatter
|
||||
frontmatter_match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
|
||||
if frontmatter_match:
|
||||
frontmatter = frontmatter_match.group(1)
|
||||
|
||||
# Check if metadata already exists
|
||||
if 'metadata:' not in frontmatter:
|
||||
# Add metadata before the closing ---
|
||||
new_frontmatter = frontmatter + f'\nmetadata:\n customized-from: {source_skill_name}\n customization-date: {datetime.now().strftime("%Y-%m-%d")}'
|
||||
content = content.replace(
|
||||
f'---\n{frontmatter}\n---',
|
||||
f'---\n{new_frontmatter}\n---'
|
||||
)
|
||||
else:
|
||||
# Update existing metadata
|
||||
if 'customized-from:' not in frontmatter:
|
||||
# Add to existing metadata section
|
||||
content = re.sub(
|
||||
r'(metadata:)',
|
||||
f'\\1\n customized-from: {source_skill_name}\n customization-date: {datetime.now().strftime("%Y-%m-%d")}',
|
||||
content,
|
||||
count=1
|
||||
)
|
||||
|
||||
skill_md_path.write_text(content)
|
||||
|
||||
|
||||
def create_customization_log(target_dir, source_skill_name, new_name):
|
||||
"""
|
||||
Create a customization log to track changes.
|
||||
|
||||
Args:
|
||||
target_dir: Target skill directory
|
||||
source_skill_name: Original skill name
|
||||
new_name: New skill name
|
||||
"""
|
||||
log_content = f"""# Customization Log: {new_name}
|
||||
|
||||
## Base Skill
|
||||
- **Source**: {source_skill_name}
|
||||
- **Forked on**: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
|
||||
|
||||
## Customization History
|
||||
|
||||
### Version 1.0 - Initial Fork
|
||||
- Created customized version from `{source_skill_name}`
|
||||
- Ready for iterative improvements based on user feedback
|
||||
|
||||
---
|
||||
|
||||
## How to Track Changes
|
||||
|
||||
Document each customization iteration below with:
|
||||
1. Date and version number
|
||||
2. What was changed (SKILL.md, scripts, references, assets)
|
||||
3. Why it was changed (user feedback, preference, workflow improvement)
|
||||
4. How to test the change
|
||||
|
||||
### Example Entry:
|
||||
|
||||
### Version 1.1 - [Date]
|
||||
**Changes:**
|
||||
- Modified SKILL.md: Updated default output format from JSON to Markdown
|
||||
- Added script: custom_formatter.py for company-specific formatting
|
||||
|
||||
**Reason:**
|
||||
- User prefers Markdown output for easier sharing with team
|
||||
- Company style guide requires specific heading formats
|
||||
|
||||
**Testing:**
|
||||
- Run the skill on sample document
|
||||
- Verify output matches company style guide
|
||||
|
||||
---
|
||||
|
||||
## Modification Notes
|
||||
|
||||
Add your customization notes here as you iterate...
|
||||
"""
|
||||
|
||||
log_path = target_dir / 'CUSTOMIZATION_LOG.md'
|
||||
log_path.write_text(log_content)
|
||||
return log_path
|
||||
|
||||
|
||||
def fork_skill(source_path, new_name, output_path):
|
||||
"""
|
||||
Fork an existing skill to create a customized version.
|
||||
|
||||
Args:
|
||||
source_path: Path to the source skill directory
|
||||
new_name: Name for the new customized skill
|
||||
output_path: Directory where the new skill should be created
|
||||
|
||||
Returns:
|
||||
Path to the created skill directory, or None if error
|
||||
"""
|
||||
source_path = Path(source_path).resolve()
|
||||
output_path = Path(output_path).resolve()
|
||||
|
||||
# Validate source skill exists
|
||||
if not source_path.exists():
|
||||
print(f"❌ Error: Source skill not found: {source_path}")
|
||||
return None
|
||||
|
||||
if not source_path.is_dir():
|
||||
print(f"❌ Error: Source path is not a directory: {source_path}")
|
||||
return None
|
||||
|
||||
# Validate SKILL.md exists in source
|
||||
source_skill_md = source_path / 'SKILL.md'
|
||||
if not source_skill_md.exists():
|
||||
print(f"❌ Error: SKILL.md not found in source skill: {source_path}")
|
||||
return None
|
||||
|
||||
# Extract source skill name from directory or SKILL.md
|
||||
source_skill_name = source_path.name
|
||||
|
||||
# Create target directory
|
||||
target_dir = output_path / new_name
|
||||
|
||||
if target_dir.exists():
|
||||
print(f"❌ Error: Target directory already exists: {target_dir}")
|
||||
return None
|
||||
|
||||
# Copy the entire skill directory
|
||||
try:
|
||||
print(f"📋 Copying skill from {source_path} to {target_dir}...")
|
||||
shutil.copytree(source_path, target_dir)
|
||||
print(f"✅ Copied all files and directories")
|
||||
except Exception as e:
|
||||
print(f"❌ Error copying skill directory: {e}")
|
||||
return None
|
||||
|
||||
# Update SKILL.md metadata
|
||||
try:
|
||||
target_skill_md = target_dir / 'SKILL.md'
|
||||
update_skill_metadata(target_skill_md, new_name, source_skill_name)
|
||||
print(f"✅ Updated SKILL.md metadata")
|
||||
except Exception as e:
|
||||
print(f"❌ Error updating SKILL.md: {e}")
|
||||
return None
|
||||
|
||||
# Create customization log
|
||||
try:
|
||||
log_path = create_customization_log(target_dir, source_skill_name, new_name)
|
||||
print(f"✅ Created customization log: {log_path.name}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Warning: Could not create customization log: {e}")
|
||||
|
||||
print(f"\n✅ Successfully forked '{source_skill_name}' to '{new_name}'")
|
||||
print(f" Location: {target_dir}")
|
||||
print(f"\n📝 Next steps:")
|
||||
print(f" 1. Review SKILL.md and identify customization needs")
|
||||
print(f" 2. Use the skill on real tasks to gather feedback")
|
||||
print(f" 3. Make iterative improvements based on user preferences")
|
||||
print(f" 4. Document changes in CUSTOMIZATION_LOG.md")
|
||||
|
||||
return target_dir
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 4 or '--path' not in sys.argv:
|
||||
print("Usage: fork_skill.py <source-skill-path> <new-skill-name> --path <output-directory>")
|
||||
print("\nExamples:")
|
||||
print(" fork_skill.py ./pdf my-pdf-workflow --path ./custom-skills")
|
||||
print(" fork_skill.py ../skills/canvas-design my-design-style --path .")
|
||||
print(" fork_skill.py ./internal-comms company-comms --path ~/my-skills")
|
||||
print("\nSkill name requirements:")
|
||||
print(" - Hyphen-case (lowercase with hyphens)")
|
||||
print(" - Alphanumeric characters and hyphens only")
|
||||
print(" - Must match directory name")
|
||||
sys.exit(1)
|
||||
|
||||
# Parse arguments
|
||||
source_path = sys.argv[1]
|
||||
new_name = sys.argv[2]
|
||||
|
||||
try:
|
||||
path_index = sys.argv.index('--path')
|
||||
output_path = sys.argv[path_index + 1]
|
||||
except (ValueError, IndexError):
|
||||
print("❌ Error: --path flag requires an output directory")
|
||||
sys.exit(1)
|
||||
|
||||
# Validate new skill name format
|
||||
if not re.match(r'^[a-z0-9-]+$', new_name):
|
||||
print(f"❌ Error: Skill name '{new_name}' must be hyphen-case (lowercase, hyphens only)")
|
||||
sys.exit(1)
|
||||
|
||||
if new_name.startswith('-') or new_name.endswith('-') or '--' in new_name:
|
||||
print(f"❌ Error: Skill name '{new_name}' cannot start/end with hyphen or contain consecutive hyphens")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"🔀 Forking skill...")
|
||||
print(f" Source: {source_path}")
|
||||
print(f" New name: {new_name}")
|
||||
print(f" Output: {output_path}")
|
||||
print()
|
||||
|
||||
result = fork_skill(source_path, new_name, output_path)
|
||||
|
||||
if result:
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
64
skills/skill-customizer/scripts/quick_validate.py
Normal file
64
skills/skill-customizer/scripts/quick_validate.py
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Quick validation script for skills - minimal version
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
def validate_skill(skill_path):
|
||||
"""Basic validation of a skill"""
|
||||
skill_path = Path(skill_path)
|
||||
|
||||
# Check SKILL.md exists
|
||||
skill_md = skill_path / 'SKILL.md'
|
||||
if not skill_md.exists():
|
||||
return False, "SKILL.md not found"
|
||||
|
||||
# Read and validate frontmatter
|
||||
content = skill_md.read_text()
|
||||
if not content.startswith('---'):
|
||||
return False, "No YAML frontmatter found"
|
||||
|
||||
# Extract frontmatter
|
||||
match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
|
||||
if not match:
|
||||
return False, "Invalid frontmatter format"
|
||||
|
||||
frontmatter = match.group(1)
|
||||
|
||||
# Check required fields
|
||||
if 'name:' not in frontmatter:
|
||||
return False, "Missing 'name' in frontmatter"
|
||||
if 'description:' not in frontmatter:
|
||||
return False, "Missing 'description' in frontmatter"
|
||||
|
||||
# Extract name for validation
|
||||
name_match = re.search(r'name:\s*(.+)', frontmatter)
|
||||
if name_match:
|
||||
name = name_match.group(1).strip()
|
||||
# Check naming convention (hyphen-case: lowercase with hyphens)
|
||||
if not re.match(r'^[a-z0-9-]+$', name):
|
||||
return False, f"Name '{name}' should be hyphen-case (lowercase letters, digits, and hyphens only)"
|
||||
if name.startswith('-') or name.endswith('-') or '--' in name:
|
||||
return False, f"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens"
|
||||
|
||||
# Extract and validate description
|
||||
desc_match = re.search(r'description:\s*(.+)', frontmatter)
|
||||
if desc_match:
|
||||
description = desc_match.group(1).strip()
|
||||
# Check for angle brackets
|
||||
if '<' in description or '>' in description:
|
||||
return False, "Description cannot contain angle brackets (< or >)"
|
||||
|
||||
return True, "Skill is valid!"
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python quick_validate.py <skill_directory>")
|
||||
sys.exit(1)
|
||||
|
||||
valid, message = validate_skill(sys.argv[1])
|
||||
print(message)
|
||||
sys.exit(0 if valid else 1)
|
||||
257
skills/skill-customizer/scripts/track_feedback.py
Executable file
257
skills/skill-customizer/scripts/track_feedback.py
Executable file
@@ -0,0 +1,257 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Feedback Tracker - Captures and organizes user feedback for skill customization
|
||||
|
||||
This script helps structure user feedback about skill performance, making it
|
||||
easier to identify specific areas for improvement and track customization needs.
|
||||
|
||||
Usage:
|
||||
track_feedback.py <skill-directory>
|
||||
|
||||
Interactive mode:
|
||||
The script will guide you through collecting structured feedback about:
|
||||
- What task was attempted
|
||||
- What worked well
|
||||
- What didn't match expectations
|
||||
- Specific preferences or requirements
|
||||
- Suggested improvements
|
||||
|
||||
Output:
|
||||
Creates/appends to FEEDBACK.md in the skill directory with structured feedback entries
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
FEEDBACK_TEMPLATE = """# Skill Feedback Log
|
||||
|
||||
This file tracks user feedback and customization needs for iterative skill improvement.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
|
||||
FEEDBACK_ENTRY_TEMPLATE = """
|
||||
## Feedback Entry #{entry_num} - {date}
|
||||
|
||||
### Task Context
|
||||
**What were you trying to accomplish?**
|
||||
{task_description}
|
||||
|
||||
### What Worked Well
|
||||
{what_worked}
|
||||
|
||||
### What Didn't Match Expectations
|
||||
{what_didnt_work}
|
||||
|
||||
### Specific Preferences/Requirements
|
||||
{preferences}
|
||||
|
||||
### Suggested Improvements
|
||||
{improvements}
|
||||
|
||||
### Priority
|
||||
{priority}
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
|
||||
def collect_feedback_interactive():
|
||||
"""
|
||||
Interactively collect structured feedback from user.
|
||||
|
||||
Returns:
|
||||
Dictionary with feedback components
|
||||
"""
|
||||
print("\n📝 Skill Feedback Collection")
|
||||
print("=" * 60)
|
||||
print("Please provide detailed feedback to help customize this skill.\n")
|
||||
|
||||
feedback = {}
|
||||
|
||||
# Task context
|
||||
print("1️⃣ What task were you trying to accomplish?")
|
||||
print(" (Be specific: e.g., 'Extract tables from a 20-page PDF report')")
|
||||
feedback['task_description'] = input(" > ").strip()
|
||||
print()
|
||||
|
||||
# What worked
|
||||
print("2️⃣ What aspects of the skill worked well?")
|
||||
print(" (e.g., 'Text extraction was accurate', 'Fast processing')")
|
||||
feedback['what_worked'] = input(" > ").strip()
|
||||
if not feedback['what_worked']:
|
||||
feedback['what_worked'] = "N/A"
|
||||
print()
|
||||
|
||||
# What didn't work
|
||||
print("3️⃣ What didn't match your expectations?")
|
||||
print(" (e.g., 'Output format was JSON, I needed Markdown', 'Too verbose')")
|
||||
feedback['what_didnt_work'] = input(" > ").strip()
|
||||
if not feedback['what_didnt_work']:
|
||||
feedback['what_didnt_work'] = "N/A"
|
||||
print()
|
||||
|
||||
# Preferences
|
||||
print("4️⃣ What are your specific preferences or requirements?")
|
||||
print(" (e.g., 'Always output in Markdown', 'Include source page numbers')")
|
||||
feedback['preferences'] = input(" > ").strip()
|
||||
if not feedback['preferences']:
|
||||
feedback['preferences'] = "N/A"
|
||||
print()
|
||||
|
||||
# Improvements
|
||||
print("5️⃣ What specific improvements would help?")
|
||||
print(" (e.g., 'Add option to filter by date range', 'Default to concise output')")
|
||||
feedback['improvements'] = input(" > ").strip()
|
||||
if not feedback['improvements']:
|
||||
feedback['improvements'] = "N/A"
|
||||
print()
|
||||
|
||||
# Priority
|
||||
print("6️⃣ How important is this customization?")
|
||||
print(" Options: critical, high, medium, low")
|
||||
priority = input(" > ").strip().lower()
|
||||
if priority not in ['critical', 'high', 'medium', 'low']:
|
||||
priority = 'medium'
|
||||
feedback['priority'] = priority.capitalize()
|
||||
print()
|
||||
|
||||
return feedback
|
||||
|
||||
|
||||
def save_feedback(skill_dir, feedback):
|
||||
"""
|
||||
Save feedback to FEEDBACK.md file.
|
||||
|
||||
Args:
|
||||
skill_dir: Path to skill directory
|
||||
feedback: Dictionary with feedback components
|
||||
|
||||
Returns:
|
||||
Path to feedback file
|
||||
"""
|
||||
feedback_path = skill_dir / 'FEEDBACK.md'
|
||||
|
||||
# Create feedback file if it doesn't exist
|
||||
if not feedback_path.exists():
|
||||
feedback_path.write_text(FEEDBACK_TEMPLATE)
|
||||
entry_num = 1
|
||||
else:
|
||||
# Count existing entries
|
||||
content = feedback_path.read_text()
|
||||
entry_num = content.count('## Feedback Entry #') + 1
|
||||
|
||||
# Format feedback entry
|
||||
entry = FEEDBACK_ENTRY_TEMPLATE.format(
|
||||
entry_num=entry_num,
|
||||
date=datetime.now().strftime("%Y-%m-%d %H:%M"),
|
||||
task_description=feedback['task_description'],
|
||||
what_worked=feedback['what_worked'],
|
||||
what_didnt_work=feedback['what_didnt_work'],
|
||||
preferences=feedback['preferences'],
|
||||
improvements=feedback['improvements'],
|
||||
priority=feedback['priority']
|
||||
)
|
||||
|
||||
# Append to file
|
||||
with open(feedback_path, 'a') as f:
|
||||
f.write(entry)
|
||||
|
||||
return feedback_path
|
||||
|
||||
|
||||
def analyze_feedback_patterns(feedback_path):
|
||||
"""
|
||||
Analyze feedback file for common patterns.
|
||||
|
||||
Args:
|
||||
feedback_path: Path to feedback file
|
||||
|
||||
Returns:
|
||||
Dictionary with analysis results
|
||||
"""
|
||||
if not feedback_path.exists():
|
||||
return None
|
||||
|
||||
content = feedback_path.read_text()
|
||||
|
||||
analysis = {
|
||||
'total_entries': content.count('## Feedback Entry #'),
|
||||
'critical_priority': content.count('Priority\nCritical'),
|
||||
'high_priority': content.count('Priority\nHigh'),
|
||||
'common_themes': []
|
||||
}
|
||||
|
||||
# Simple keyword analysis for common themes
|
||||
keywords = {
|
||||
'output format': ['format', 'markdown', 'json', 'output'],
|
||||
'verbosity': ['verbose', 'concise', 'too much', 'too little'],
|
||||
'performance': ['slow', 'fast', 'speed', 'performance'],
|
||||
'accuracy': ['accurate', 'wrong', 'incorrect', 'missing'],
|
||||
}
|
||||
|
||||
for theme, words in keywords.items():
|
||||
if any(word.lower() in content.lower() for word in words):
|
||||
analysis['common_themes'].append(theme)
|
||||
|
||||
return analysis
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: track_feedback.py <skill-directory>")
|
||||
print("\nThis script helps collect structured feedback for skill customization.")
|
||||
print("Run it after using a skill to capture what needs to be improved.")
|
||||
sys.exit(1)
|
||||
|
||||
skill_path = Path(sys.argv[1]).resolve()
|
||||
|
||||
# Validate skill directory
|
||||
if not skill_path.exists():
|
||||
print(f"❌ Error: Skill directory not found: {skill_path}")
|
||||
sys.exit(1)
|
||||
|
||||
if not skill_path.is_dir():
|
||||
print(f"❌ Error: Path is not a directory: {skill_path}")
|
||||
sys.exit(1)
|
||||
|
||||
skill_md = skill_path / 'SKILL.md'
|
||||
if not skill_md.exists():
|
||||
print(f"❌ Error: Not a valid skill directory (SKILL.md not found): {skill_path}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"📂 Skill: {skill_path.name}")
|
||||
|
||||
# Collect feedback
|
||||
feedback = collect_feedback_interactive()
|
||||
|
||||
# Save feedback
|
||||
try:
|
||||
feedback_path = save_feedback(skill_path, feedback)
|
||||
print(f"\n✅ Feedback saved to: {feedback_path.name}")
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error saving feedback: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Analyze patterns
|
||||
analysis = analyze_feedback_patterns(feedback_path)
|
||||
if analysis and analysis['total_entries'] > 1:
|
||||
print(f"\n📊 Feedback Summary:")
|
||||
print(f" Total entries: {analysis['total_entries']}")
|
||||
print(f" Critical priority: {analysis['critical_priority']}")
|
||||
print(f" High priority: {analysis['high_priority']}")
|
||||
if analysis['common_themes']:
|
||||
print(f" Common themes: {', '.join(analysis['common_themes'])}")
|
||||
|
||||
print(f"\n💡 Next steps:")
|
||||
print(f" 1. Review feedback in {feedback_path.name}")
|
||||
print(f" 2. Identify specific changes to make in SKILL.md or scripts")
|
||||
print(f" 3. Apply improvements and test")
|
||||
print(f" 4. Document changes in CUSTOMIZATION_LOG.md")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user