convert_to_cursor.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #!/usr/bin/env python3
  2. """
  3. Convert Agent Skills to Cursor format
  4. This script converts Agent Skills to Cursor custom instruction format.
  5. Cursor supports custom instructions via .cursorrules files or project context.
  6. Usage:
  7. python convert_to_cursor.py <skill_path> [output_path]
  8. Example:
  9. python convert_to_cursor.py ../../skills/course-designer .cursor/rules/
  10. """
  11. import sys
  12. import re
  13. from pathlib import Path
  14. try:
  15. import yaml
  16. HAS_YAML = True
  17. except ImportError:
  18. HAS_YAML = False
  19. def parse_yaml_simple(text):
  20. """Simple YAML parser for frontmatter (handles basic key: value pairs)"""
  21. result = {}
  22. for line in text.split('\n'):
  23. line = line.strip()
  24. if ':' in line and not line.startswith('#'):
  25. key, value = line.split(':', 1)
  26. key = key.strip()
  27. value = value.strip().strip('"').strip("'")
  28. result[key] = value
  29. return result
  30. def extract_frontmatter(content):
  31. """Extract YAML frontmatter from SKILL.md"""
  32. frontmatter_pattern = r'^---\n(.*?)\n---\n'
  33. match = re.match(frontmatter_pattern, content, re.DOTALL)
  34. if match:
  35. frontmatter_text = match.group(1)
  36. if HAS_YAML:
  37. frontmatter = yaml.safe_load(frontmatter_text)
  38. else:
  39. frontmatter = parse_yaml_simple(frontmatter_text)
  40. body_start = match.end()
  41. body = content[body_start:].strip()
  42. return frontmatter, body
  43. else:
  44. return {}, content.strip()
  45. def convert_to_cursor_rule(skill_path, output_dir=None):
  46. """
  47. Convert Agent Skill to Cursor custom instruction format
  48. Args:
  49. skill_path: Path to skill directory containing SKILL.md
  50. output_dir: Output directory for .cursorrules file (optional)
  51. """
  52. skill_path = Path(skill_path)
  53. skill_file = skill_path / "SKILL.md"
  54. if not skill_file.exists():
  55. print(f"Error: SKILL.md not found in {skill_path}")
  56. return None
  57. # Read skill file
  58. with open(skill_file, "r", encoding="utf-8") as f:
  59. content = f.read()
  60. # Extract frontmatter and body
  61. frontmatter, body = extract_frontmatter(content)
  62. if not frontmatter:
  63. print(f"Warning: No frontmatter found in {skill_file}")
  64. frontmatter = {"name": skill_path.name, "description": ""}
  65. # Create Cursor rule format
  66. cursor_rule = f"""# {frontmatter.get('name', skill_path.name).replace('-', ' ').title()}
  67. ## Description
  68. {frontmatter.get('description', 'No description available')}
  69. ## When to Use
  70. Use this skill when the user asks about: {frontmatter.get('description', 'related tasks')}
  71. ## Instructions
  72. {body}
  73. """
  74. # Write output
  75. if output_dir:
  76. output_dir = Path(output_dir)
  77. output_dir.mkdir(parents=True, exist_ok=True)
  78. output_file = output_dir / f"{frontmatter.get('name', skill_path.name)}.md"
  79. with open(output_file, "w", encoding="utf-8") as f:
  80. f.write(cursor_rule)
  81. print(f"✅ Converted to Cursor format: {output_file}")
  82. return output_file
  83. else:
  84. return cursor_rule
  85. def convert_all_skills(skills_dir, output_dir):
  86. """Convert all skills in a directory"""
  87. skills_dir = Path(skills_dir)
  88. output_dir = Path(output_dir)
  89. output_dir.mkdir(parents=True, exist_ok=True)
  90. converted = []
  91. for skill_dir in skills_dir.iterdir():
  92. if skill_dir.is_dir() and (skill_dir / "SKILL.md").exists():
  93. try:
  94. result = convert_to_cursor_rule(skill_dir, output_dir)
  95. if result:
  96. converted.append(skill_dir.name)
  97. except Exception as e:
  98. print(f"Error converting {skill_dir.name}: {e}")
  99. print(f"\n✅ Converted {len(converted)} skills: {', '.join(converted)}")
  100. return converted
  101. if __name__ == "__main__":
  102. if len(sys.argv) < 2:
  103. print("Usage: python convert_to_cursor.py <skill_path> [output_path]")
  104. print(" or: python convert_to_cursor.py --all <skills_dir> <output_dir>")
  105. sys.exit(1)
  106. if sys.argv[1] == "--all":
  107. if len(sys.argv) < 4:
  108. print("Usage: python convert_to_cursor.py --all <skills_dir> <output_dir>")
  109. sys.exit(1)
  110. convert_all_skills(sys.argv[2], sys.argv[3])
  111. else:
  112. skill_path = sys.argv[1]
  113. output_path = sys.argv[2] if len(sys.argv) > 2 else None
  114. convert_to_cursor_rule(skill_path, output_path)