Initial commit
This commit is contained in:
151
skills/writing-openrewrite-recipes/scripts/categorize_recipes.py
Normal file
151
skills/writing-openrewrite-recipes/scripts/categorize_recipes.py
Normal file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to categorize recipes from recipes-all.csv into separate category files.
|
||||
"""
|
||||
|
||||
import csv
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Define categories and their matching patterns (keywords in id, name, or description)
|
||||
CATEGORIES = {
|
||||
'java-basic': {
|
||||
'keywords': [
|
||||
'ChangeType', 'ChangeMethodName', 'ChangePackage', 'AddAnnotation', 'RemoveAnnotation',
|
||||
'ChangeMethodAccessLevel', 'AddImport', 'RemoveImport', 'AddMethodParameter',
|
||||
'DeleteMethodArgument', 'ReplaceConstant', 'ReplaceStringLiteral',
|
||||
'org.openrewrite.java.ChangeType', 'org.openrewrite.java.ChangeMethod',
|
||||
'org.openrewrite.java.ChangePackage', 'org.openrewrite.java.AddComment',
|
||||
'org.openrewrite.java.ReplaceAnnotation', 'org.openrewrite.java.ShortenFullyQualifiedTypeReferences'
|
||||
],
|
||||
'description': 'Basic Java refactoring operations for types, methods, packages, and annotations'
|
||||
},
|
||||
'spring-boot': {
|
||||
'keywords': [
|
||||
'spring.boot', 'SpringBoot', 'spring-boot', 'UpgradeSpringBoot'
|
||||
],
|
||||
'description': 'Spring Boot migrations, upgrades, and best practices'
|
||||
},
|
||||
'security': {
|
||||
'keywords': [
|
||||
'security', 'vulnerability', 'injection', 'XSS', 'XXE', 'SQL injection',
|
||||
'command injection', 'path traversal', 'LDAP injection', 'unencrypted',
|
||||
'FindSqlInjection', 'FindXss', 'FindCommandInjection', 'OWASP'
|
||||
],
|
||||
'description': 'Security vulnerability detection and fixes'
|
||||
},
|
||||
'testing': {
|
||||
'keywords': [
|
||||
'junit', 'JUnit', 'test', 'Test', 'mockito', 'Mockito', 'assertj', 'AssertJ',
|
||||
'@Test', 'TestNG'
|
||||
],
|
||||
'description': 'Testing framework migrations and best practices'
|
||||
},
|
||||
'dependencies': {
|
||||
'keywords': [
|
||||
'maven', 'Maven', 'gradle', 'Gradle', 'dependency', 'Dependency',
|
||||
'UpgradeDependency', 'ChangeDependency', 'AddDependency', 'RemoveDependency',
|
||||
'pom.xml', 'build.gradle'
|
||||
],
|
||||
'description': 'Maven and Gradle dependency management'
|
||||
},
|
||||
'logging': {
|
||||
'keywords': [
|
||||
'log4j', 'Log4j', 'logback', 'Logback', 'slf4j', 'SLF4J', 'logging',
|
||||
'Logging', 'logger', 'Logger'
|
||||
],
|
||||
'description': 'Logging framework configuration and migrations'
|
||||
},
|
||||
'file-operations': {
|
||||
'keywords': [
|
||||
'CreateFile', 'DeleteFile', 'MoveFile', 'RenameFile', 'FindAndReplace',
|
||||
'ChangeText', 'AppendToTextFile', 'CreateTextFile', 'org.openrewrite.text',
|
||||
'org.openrewrite.DeleteSourceFiles', 'org.openrewrite.MoveFile'
|
||||
],
|
||||
'description': 'File and text manipulation operations'
|
||||
},
|
||||
'framework-migrations': {
|
||||
'keywords': [
|
||||
'MigrateTo', 'UpgradeTo', 'Kafka', 'Hibernate', 'Elasticsearch', 'Quarkus',
|
||||
'Jakarta', 'javax', 'migration'
|
||||
],
|
||||
'description': 'Major framework and library migrations'
|
||||
},
|
||||
'xml-yaml-json': {
|
||||
'keywords': [
|
||||
'xml', 'XML', 'yaml', 'YAML', 'json', 'JSON', 'properties', 'Properties',
|
||||
'ChangePropertyValue', 'MergeYaml', 'CreateXmlFile'
|
||||
],
|
||||
'description': 'Configuration file operations for XML, YAML, JSON, and properties files'
|
||||
},
|
||||
'static-analysis': {
|
||||
'keywords': [
|
||||
'Find', 'Search', 'Analysis', 'complexity', 'unused', 'unreachable',
|
||||
'dead code', 'null pointer', 'FindNullPointer', 'FindUnused'
|
||||
],
|
||||
'description': 'Code analysis and search recipes for finding patterns and issues'
|
||||
}
|
||||
}
|
||||
|
||||
def matches_category(recipe_id, recipe_name, recipe_desc, keywords):
|
||||
"""Check if a recipe matches any of the keywords for a category."""
|
||||
text_to_search = f"{recipe_id} {recipe_name} {recipe_desc}".lower()
|
||||
return any(keyword.lower() in text_to_search for keyword in keywords)
|
||||
|
||||
def main():
|
||||
script_dir = Path(__file__).parent
|
||||
references_dir = script_dir.parent / 'references'
|
||||
input_file = references_dir / 'recipes-all.csv'
|
||||
|
||||
# Dictionary to store recipes for each category
|
||||
categorized_recipes = {cat: [] for cat in CATEGORIES.keys()}
|
||||
|
||||
print(f"Reading recipes from {input_file}...")
|
||||
|
||||
# Read and categorize recipes
|
||||
with open(input_file, 'r', encoding='utf-8') as f:
|
||||
reader = csv.DictReader(f)
|
||||
for row in reader:
|
||||
recipe_id = row['id']
|
||||
recipe_name = row['name']
|
||||
recipe_desc = row['description']
|
||||
|
||||
# Skip C#, DevCenter, COBOL, and AWS SDK migration recipes globally
|
||||
if 'csharp' in recipe_id.lower() or '.csharp.' in recipe_id.lower():
|
||||
continue
|
||||
if 'io.moderne.devcenter' in recipe_id.lower():
|
||||
continue
|
||||
if 'cobol' in recipe_id.lower():
|
||||
continue
|
||||
if 'software.amazon.awssdk' in recipe_id.lower():
|
||||
continue
|
||||
|
||||
# Check each category
|
||||
for category, config in CATEGORIES.items():
|
||||
if matches_category(recipe_id, recipe_name, recipe_desc, config['keywords']):
|
||||
categorized_recipes[category].append({
|
||||
'id': recipe_id,
|
||||
'name': recipe_name,
|
||||
'description': recipe_desc
|
||||
})
|
||||
|
||||
# Write categorized CSV files
|
||||
for category, recipes in categorized_recipes.items():
|
||||
if recipes:
|
||||
output_file = references_dir / f'recipes-{category}.csv'
|
||||
print(f"Writing {len(recipes)} recipes to {output_file.name}...")
|
||||
|
||||
with open(output_file, 'w', encoding='utf-8', newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(['Fully Qualified Recipe Name', 'Recipe Name', 'Description'])
|
||||
|
||||
for recipe in recipes:
|
||||
writer.writerow([recipe['id'], recipe['name'], recipe['description']])
|
||||
|
||||
# Print summary
|
||||
print("\n=== Summary ===")
|
||||
for category, recipes in sorted(categorized_recipes.items(), key=lambda x: len(x[1]), reverse=True):
|
||||
print(f"{category:25} {len(recipes):4} recipes - {CATEGORIES[category]['description']}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,413 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to create curated "common" versions of large recipe categories.
|
||||
Selects the most practical and commonly used recipes (30-75 each).
|
||||
"""
|
||||
|
||||
import csv
|
||||
from pathlib import Path
|
||||
|
||||
def read_recipes(file_path):
|
||||
"""Read recipes from a CSV file."""
|
||||
recipes = []
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
reader = csv.DictReader(f)
|
||||
for row in reader:
|
||||
recipes.append(row)
|
||||
return recipes
|
||||
|
||||
def write_recipes(file_path, recipes):
|
||||
"""Write recipes to a CSV file."""
|
||||
with open(file_path, 'w', encoding='utf-8', newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(['Fully Qualified Recipe Name', 'Recipe Name', 'Description'])
|
||||
for recipe in recipes:
|
||||
writer.writerow([
|
||||
recipe['Fully Qualified Recipe Name'],
|
||||
recipe['Recipe Name'],
|
||||
recipe['Description']
|
||||
])
|
||||
|
||||
def curate_testing(recipes):
|
||||
"""Select most useful testing recipes - prioritize broad recipes over specific variations."""
|
||||
curated = []
|
||||
seen = set()
|
||||
|
||||
for recipe in recipes:
|
||||
recipe_id = recipe['Fully Qualified Recipe Name']
|
||||
recipe_name = recipe['Recipe Name']
|
||||
|
||||
# Skip overly specific sub-recipes (inner classes with $)
|
||||
if '$' in recipe_id:
|
||||
continue
|
||||
|
||||
# Exclude overly specific assertion patterns for individual numeric types
|
||||
if any(pattern in recipe_name for pattern in [
|
||||
'Byte Assert', 'Integer Assert', 'Long Assert', 'Float Assert',
|
||||
'Double Assert', 'Short Assert', 'BigInteger Assert', 'BigDecimal Assert'
|
||||
]):
|
||||
continue
|
||||
|
||||
# Prioritize broad testing migrations and frameworks
|
||||
is_useful = (
|
||||
# JUnit migrations
|
||||
'junit' in recipe_id.lower() and any(keyword in recipe_name.lower() for keyword in [
|
||||
'junit 5', 'junit5', 'migration', 'upgrade', 'best practices'
|
||||
]) or
|
||||
# Mockito
|
||||
'mockito' in recipe_id.lower() or
|
||||
# AssertJ (but only high-level)
|
||||
('assertj' in recipe_id.lower() and 'adopt' in recipe_name.lower() and
|
||||
not any(num in recipe_name for num in ['Byte', 'Integer', 'Long', 'Float', 'Double'])) or
|
||||
# TestNG
|
||||
'testng' in recipe_id.lower() or
|
||||
# General testing best practices
|
||||
any(keyword in recipe_name.lower() for keyword in [
|
||||
'test', 'assert', 'mock', 'verify', 'cleanup'
|
||||
]) and 'org.openrewrite.java.testing' in recipe_id
|
||||
)
|
||||
|
||||
if is_useful and recipe_name not in seen:
|
||||
curated.append(recipe)
|
||||
seen.add(recipe_name)
|
||||
|
||||
if len(curated) >= 60:
|
||||
break
|
||||
|
||||
return curated[:60]
|
||||
|
||||
def curate_framework_migrations(recipes):
|
||||
"""Select most useful framework migration recipes - prioritize framework diversity over multiple versions."""
|
||||
from collections import defaultdict
|
||||
|
||||
# Group recipes by framework
|
||||
framework_recipes = defaultdict(list)
|
||||
|
||||
for recipe in recipes:
|
||||
recipe_id = recipe['Fully Qualified Recipe Name']
|
||||
recipe_name = recipe['Recipe Name']
|
||||
|
||||
# Identify the framework
|
||||
framework = None
|
||||
if 'kafka' in recipe_id.lower() or 'kafka' in recipe_name.lower():
|
||||
framework = 'kafka'
|
||||
elif 'hibernate' in recipe_id.lower() or 'hibernate' in recipe_name.lower():
|
||||
framework = 'hibernate'
|
||||
elif 'quarkus' in recipe_id.lower() or 'quarkus' in recipe_name.lower():
|
||||
framework = 'quarkus'
|
||||
elif 'elasticsearch' in recipe_id.lower() or 'elasticsearch' in recipe_name.lower():
|
||||
framework = 'elasticsearch'
|
||||
elif 'spring.boot' in recipe_id.lower() or ('spring boot' in recipe_name.lower() and 'upgrade' in recipe_name.lower()):
|
||||
framework = 'spring-boot'
|
||||
elif 'spring.security' in recipe_id.lower() or 'spring security' in recipe_name.lower():
|
||||
framework = 'spring-security'
|
||||
elif 'spring.cloud' in recipe_id.lower() or 'spring cloud' in recipe_name.lower():
|
||||
framework = 'spring-cloud'
|
||||
elif 'spring.data' in recipe_id.lower() or 'spring data' in recipe_name.lower():
|
||||
framework = 'spring-data'
|
||||
elif 'jakarta' in recipe_id.lower() or 'javax' in recipe_id.lower():
|
||||
framework = 'jakarta-ee'
|
||||
elif 'micronaut' in recipe_id.lower() or 'micronaut' in recipe_name.lower():
|
||||
framework = 'micronaut'
|
||||
elif 'camel' in recipe_id.lower():
|
||||
framework = 'camel'
|
||||
elif 'vertx' in recipe_id.lower() or 'vert.x' in recipe_name.lower():
|
||||
framework = 'vertx'
|
||||
elif 'dropwizard' in recipe_id.lower():
|
||||
framework = 'dropwizard'
|
||||
elif 'guava' in recipe_id.lower():
|
||||
framework = 'guava'
|
||||
elif 'junit' in recipe_id.lower() or 'junit' in recipe_name.lower():
|
||||
framework = 'junit'
|
||||
|
||||
# Only include migration/upgrade recipes
|
||||
if framework and ('MigrateTo' in recipe_id or 'Upgrade' in recipe_id or
|
||||
'Migrate' in recipe_name or 'Upgrade' in recipe_name):
|
||||
framework_recipes[framework].append(recipe)
|
||||
|
||||
# Select top 1-2 versions per framework, prioritizing latest versions
|
||||
curated = []
|
||||
seen_names = set()
|
||||
|
||||
# Sort frameworks by recipe count to ensure we get diverse coverage
|
||||
for framework in sorted(framework_recipes.keys()):
|
||||
framework_list = framework_recipes[framework]
|
||||
|
||||
# For each framework, prefer recipes with higher version numbers (assumed to be more recent)
|
||||
# and avoid duplicate recipe names
|
||||
added_for_framework = 0
|
||||
max_per_framework = 2 # Keep at most 2 versions per framework
|
||||
|
||||
for recipe in framework_list:
|
||||
recipe_name = recipe['Recipe Name']
|
||||
if recipe_name not in seen_names and added_for_framework < max_per_framework:
|
||||
curated.append(recipe)
|
||||
seen_names.add(recipe_name)
|
||||
added_for_framework += 1
|
||||
|
||||
return curated[:50]
|
||||
|
||||
def curate_dependencies(recipes):
|
||||
"""Select most useful dependency management recipes - prefer Maven+Gradle over build-tool-specific."""
|
||||
curated = []
|
||||
seen = set()
|
||||
|
||||
# First pass: Prioritize org.openrewrite.java.dependencies (works for both Maven and Gradle)
|
||||
for recipe in recipes:
|
||||
recipe_id = recipe['Fully Qualified Recipe Name']
|
||||
recipe_name = recipe['Recipe Name']
|
||||
|
||||
# Exclude C# and DevCenter recipes
|
||||
if 'csharp' in recipe_id.lower() or 'devcenter' in recipe_id.lower():
|
||||
continue
|
||||
|
||||
# Prioritize org.openrewrite.java.dependencies (works for both Maven and Gradle)
|
||||
if 'org.openrewrite.java.dependencies' in recipe_id and recipe_name not in seen:
|
||||
curated.append(recipe)
|
||||
seen.add(recipe_name)
|
||||
|
||||
# Second pass: Add common Maven operations
|
||||
for recipe in recipes:
|
||||
recipe_id = recipe['Fully Qualified Recipe Name']
|
||||
recipe_name = recipe['Recipe Name']
|
||||
|
||||
if 'csharp' in recipe_id.lower() or 'devcenter' in recipe_id.lower():
|
||||
continue
|
||||
|
||||
# Include Maven recipes for common operations
|
||||
if 'org.openrewrite.maven' in recipe_id and recipe_name not in seen:
|
||||
is_common_maven = any(keyword in recipe_name for keyword in [
|
||||
'Change dependency', 'Add dependency', 'Remove dependency',
|
||||
'Upgrade dependency', 'parent', 'BOM', 'managed', 'plugin'
|
||||
])
|
||||
|
||||
if is_common_maven:
|
||||
curated.append(recipe)
|
||||
seen.add(recipe_name)
|
||||
|
||||
if len(curated) >= 35:
|
||||
break
|
||||
|
||||
# Third pass: Add common Gradle operations (but fewer since Maven is covered)
|
||||
for recipe in recipes:
|
||||
recipe_id = recipe['Fully Qualified Recipe Name']
|
||||
recipe_name = recipe['Recipe Name']
|
||||
|
||||
if 'csharp' in recipe_id.lower() or 'devcenter' in recipe_id.lower():
|
||||
continue
|
||||
|
||||
# Include Gradle recipes for operations not covered by Maven
|
||||
if 'org.openrewrite.gradle' in recipe_id and recipe_name not in seen:
|
||||
is_essential_gradle = any(keyword in recipe_name for keyword in [
|
||||
'Change dependency', 'Add dependency', 'Remove dependency',
|
||||
'Upgrade version', 'wrapper', 'plugin'
|
||||
])
|
||||
|
||||
if is_essential_gradle:
|
||||
curated.append(recipe)
|
||||
seen.add(recipe_name)
|
||||
|
||||
if len(curated) >= 50:
|
||||
break
|
||||
|
||||
return curated[:50]
|
||||
|
||||
def curate_xml_yaml_json(recipes):
|
||||
"""Select most useful configuration file recipes."""
|
||||
curated = []
|
||||
seen = set()
|
||||
|
||||
for recipe in recipes:
|
||||
recipe_id = recipe['Fully Qualified Recipe Name']
|
||||
recipe_name = recipe['Recipe Name']
|
||||
|
||||
# Include recipes from core packages for YAML, XML, JSON, Properties, TOML
|
||||
is_config_operation = (
|
||||
'org.openrewrite.yaml' in recipe_id or
|
||||
'org.openrewrite.xml' in recipe_id or
|
||||
'org.openrewrite.json' in recipe_id or
|
||||
'org.openrewrite.properties' in recipe_id or
|
||||
'org.openrewrite.toml' in recipe_id
|
||||
)
|
||||
|
||||
# Also include common operations by name
|
||||
has_config_keywords = any(keyword in recipe_name for keyword in [
|
||||
'YAML', 'XML', 'JSON', 'Properties', 'TOML', 'property', 'tag',
|
||||
'attribute', 'value', 'file'
|
||||
])
|
||||
|
||||
if (is_config_operation or has_config_keywords) and recipe_name not in seen:
|
||||
curated.append(recipe)
|
||||
seen.add(recipe_name)
|
||||
|
||||
if len(curated) >= 50:
|
||||
break
|
||||
|
||||
return curated[:50]
|
||||
|
||||
def curate_static_analysis(recipes):
|
||||
"""Select most useful static analysis recipes."""
|
||||
curated = []
|
||||
seen = set()
|
||||
|
||||
for recipe in recipes:
|
||||
recipe_id = recipe['Fully Qualified Recipe Name']
|
||||
recipe_name = recipe['Recipe Name']
|
||||
|
||||
# Include Find* and Search* recipes from core packages
|
||||
is_search_recipe = (
|
||||
recipe_name.startswith('Find') or
|
||||
recipe_name.startswith('Search') or
|
||||
'org.openrewrite.search' in recipe_id or
|
||||
'org.openrewrite.analysis' in recipe_id
|
||||
)
|
||||
|
||||
# Prioritize security-related searches
|
||||
is_security = any(keyword in recipe_name.lower() for keyword in [
|
||||
'security', 'vulnerability', 'injection', 'xss', 'xxe'
|
||||
])
|
||||
|
||||
if (is_search_recipe or is_security) and recipe_name not in seen:
|
||||
curated.append(recipe)
|
||||
seen.add(recipe_name)
|
||||
|
||||
if len(curated) >= 50:
|
||||
break
|
||||
|
||||
return curated[:50]
|
||||
|
||||
def curate_spring_boot(recipes):
|
||||
"""Select most useful Spring Boot recipes."""
|
||||
curated = []
|
||||
seen = set()
|
||||
|
||||
for recipe in recipes:
|
||||
recipe_id = recipe['Fully Qualified Recipe Name']
|
||||
recipe_name = recipe['Recipe Name']
|
||||
|
||||
# Prioritize major upgrade recipes and common operations
|
||||
is_major_upgrade = any(version in recipe_name for version in [
|
||||
'3.5', '3.4', '3.3', '3.2', '3.1', '3.0', '2.7'
|
||||
])
|
||||
|
||||
is_common_operation = any(keyword in recipe_name for keyword in [
|
||||
'Upgrade', 'Migrate', 'Best Practices', 'Properties',
|
||||
'Actuator', 'Configuration', 'Deprecat'
|
||||
])
|
||||
|
||||
# Avoid overly specific internal changes
|
||||
is_specific = any(keyword in recipe_name.lower() for keyword in [
|
||||
'replace', 'remove', 'use', 'comment', 'add @valid'
|
||||
])
|
||||
|
||||
if (is_major_upgrade or is_common_operation) and not is_specific and recipe_name not in seen:
|
||||
curated.append(recipe)
|
||||
seen.add(recipe_name)
|
||||
|
||||
if len(curated) >= 60:
|
||||
break
|
||||
|
||||
return curated[:60]
|
||||
|
||||
def curate_security(recipes):
|
||||
"""Select most useful security recipes."""
|
||||
curated = []
|
||||
seen = set()
|
||||
|
||||
for recipe in recipes:
|
||||
recipe_id = recipe['Fully Qualified Recipe Name']
|
||||
recipe_name = recipe['Recipe Name']
|
||||
|
||||
# Prioritize high-impact security issues
|
||||
is_high_impact = any(keyword in recipe_name.lower() for keyword in [
|
||||
'sql injection', 'xss', 'xxe', 'command injection',
|
||||
'path traversal', 'ldap injection', 'vulnerability',
|
||||
'unencrypted', 'insecure', 'csrf', 'authentication'
|
||||
])
|
||||
|
||||
# Include general security finding recipes
|
||||
is_finding_recipe = recipe_name.startswith('Find') and any(keyword in recipe_name.lower() for keyword in [
|
||||
'security', 'vulnerability', 'injection', 'exposure'
|
||||
])
|
||||
|
||||
# Include OWASP related
|
||||
is_owasp = 'owasp' in recipe_id.lower() or 'owasp' in recipe_name.lower()
|
||||
|
||||
if (is_high_impact or is_finding_recipe or is_owasp) and recipe_name not in seen:
|
||||
curated.append(recipe)
|
||||
seen.add(recipe_name)
|
||||
|
||||
if len(curated) >= 60:
|
||||
break
|
||||
|
||||
return curated[:60]
|
||||
|
||||
def curate_logging(recipes):
|
||||
"""Select most useful logging recipes."""
|
||||
curated = []
|
||||
seen = set()
|
||||
|
||||
for recipe in recipes:
|
||||
recipe_id = recipe['Fully Qualified Recipe Name']
|
||||
recipe_name = recipe['Recipe Name']
|
||||
|
||||
# Prioritize common logging operations
|
||||
is_common_logging = any(keyword in recipe_name for keyword in [
|
||||
'Parameterize', 'SLF4J', 'Log4j', 'Logback', 'System.out',
|
||||
'System.err', 'printStackTrace', 'Containerize', 'Migration',
|
||||
'logger', 'Logger'
|
||||
])
|
||||
|
||||
# Exclude overly specific internal operations
|
||||
is_specific = 'internal' in recipe_name.lower()
|
||||
|
||||
if is_common_logging and not is_specific and recipe_name not in seen:
|
||||
curated.append(recipe)
|
||||
seen.add(recipe_name)
|
||||
|
||||
if len(curated) >= 50:
|
||||
break
|
||||
|
||||
return curated[:50]
|
||||
|
||||
def main():
|
||||
script_dir = Path(__file__).parent
|
||||
references_dir = script_dir.parent / 'references'
|
||||
|
||||
# Categories to curate (those with >150 recipes or too large to be useful)
|
||||
categories = {
|
||||
'testing': curate_testing,
|
||||
'framework-migrations': curate_framework_migrations,
|
||||
'dependencies': curate_dependencies,
|
||||
'xml-yaml-json': curate_xml_yaml_json,
|
||||
'static-analysis': curate_static_analysis,
|
||||
'spring-boot': curate_spring_boot,
|
||||
'security': curate_security,
|
||||
'logging': curate_logging,
|
||||
}
|
||||
|
||||
print("Creating curated 'common' versions of large categories...\n")
|
||||
|
||||
for category, curate_func in categories.items():
|
||||
input_file = references_dir / f'recipes-{category}.csv'
|
||||
output_file = references_dir / f'recipes-{category}-common.csv'
|
||||
|
||||
if not input_file.exists():
|
||||
print(f"Warning: {input_file.name} not found, skipping...")
|
||||
continue
|
||||
|
||||
# Read all recipes
|
||||
all_recipes = read_recipes(input_file)
|
||||
|
||||
# Curate selection
|
||||
curated_recipes = curate_func(all_recipes)
|
||||
|
||||
# Write curated file
|
||||
write_recipes(output_file, curated_recipes)
|
||||
|
||||
print(f"{category:25} {len(curated_recipes):3} curated from {len(all_recipes):4} total -> {output_file.name}")
|
||||
|
||||
print("\nCurated files created successfully!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
136
skills/writing-openrewrite-recipes/scripts/upload-skill.sh
Executable file
136
skills/writing-openrewrite-recipes/scripts/upload-skill.sh
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/bin/bash
|
||||
|
||||
# OpenRewrite Recipe Writing Skill - Upload Script
|
||||
# This script creates or updates the skill in Claude's Skills API
|
||||
#
|
||||
# Usage:
|
||||
# Option 1: Use anthropic.key file (recommended)
|
||||
# echo "your-api-key" > anthropic.key
|
||||
# ./upload-skill.sh
|
||||
#
|
||||
# Option 2: Export environment variable
|
||||
# export ANTHROPIC_API_KEY="your-api-key-here"
|
||||
# ./upload-skill.sh
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
# Change to script directory
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Try to load API key from anthropic.key file if it exists and ANTHROPIC_API_KEY is not set
|
||||
if [ -z "$ANTHROPIC_API_KEY" ] && [ -f "anthropic.key" ]; then
|
||||
echo "Loading API key from anthropic.key file..."
|
||||
ANTHROPIC_API_KEY=$(cat anthropic.key | tr -d '[:space:]')
|
||||
fi
|
||||
|
||||
# Check if API key is set
|
||||
if [ -z "$ANTHROPIC_API_KEY" ]; then
|
||||
echo "Error: ANTHROPIC_API_KEY not found"
|
||||
echo ""
|
||||
echo "Usage (option 1 - recommended):"
|
||||
echo " echo 'your-api-key' > anthropic.key"
|
||||
echo " ./upload-skill.sh"
|
||||
echo ""
|
||||
echo "Usage (option 2):"
|
||||
echo " export ANTHROPIC_API_KEY='your-api-key-here'"
|
||||
echo " ./upload-skill.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if skill ID file exists
|
||||
SKILL_ID_FILE="skill-id.txt"
|
||||
if [ -f "$SKILL_ID_FILE" ]; then
|
||||
SKILL_ID=$(cat "$SKILL_ID_FILE" | tr -d '[:space:]')
|
||||
ACTION="update"
|
||||
HTTP_METHOD="PATCH"
|
||||
echo "Found existing skill ID: $SKILL_ID"
|
||||
echo "Updating OpenRewrite Recipe Writing skill..."
|
||||
else
|
||||
ACTION="create"
|
||||
HTTP_METHOD="POST"
|
||||
echo "No existing skill ID found."
|
||||
echo "Creating new OpenRewrite Recipe Writing skill..."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Skill directory name (must match 'name' field in SKILL.md)
|
||||
SKILL_DIR="openrewrite-recipe-writer"
|
||||
|
||||
# Build curl command based on action
|
||||
if [ "$ACTION" = "create" ]; then
|
||||
# Create new skill
|
||||
response=$(curl -s -w "\n%{http_code}" https://api.anthropic.com/v1/skills \
|
||||
-X POST \
|
||||
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
||||
-H "anthropic-version: 2023-06-01" \
|
||||
-H "anthropic-beta: skills-2025-10-02" \
|
||||
-F "display_title=OpenRewrite Recipe Writing" \
|
||||
-F "files[]=@SKILL.md;filename=$SKILL_DIR/SKILL.md" \
|
||||
-F "files[]=@template-imperative-recipe.java;filename=$SKILL_DIR/template-imperative-recipe.java" \
|
||||
-F "files[]=@template-declarative-recipe.yml;filename=$SKILL_DIR/template-declarative-recipe.yml" \
|
||||
-F "files[]=@template-recipe-test.java;filename=$SKILL_DIR/template-recipe-test.java" \
|
||||
-F "files[]=@example-say-hello-recipe.java;filename=$SKILL_DIR/example-say-hello-recipe.java" \
|
||||
-F "files[]=@example-scanning-recipe.java;filename=$SKILL_DIR/example-scanning-recipe.java" \
|
||||
-F "files[]=@example-declarative-migration.yml;filename=$SKILL_DIR/example-declarative-migration.yml" \
|
||||
-F "files[]=@checklist-recipe-development.md;filename=$SKILL_DIR/checklist-recipe-development.md")
|
||||
else
|
||||
# Update existing skill
|
||||
response=$(curl -s -w "\n%{http_code}" https://api.anthropic.com/v1/skills/$SKILL_ID \
|
||||
-X PATCH \
|
||||
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
||||
-H "anthropic-version: 2023-06-01" \
|
||||
-H "anthropic-beta: skills-2025-10-02" \
|
||||
-F "files[]=@SKILL.md;filename=$SKILL_DIR/SKILL.md" \
|
||||
-F "files[]=@template-imperative-recipe.java;filename=$SKILL_DIR/template-imperative-recipe.java" \
|
||||
-F "files[]=@template-declarative-recipe.yml;filename=$SKILL_DIR/template-declarative-recipe.yml" \
|
||||
-F "files[]=@template-recipe-test.java;filename=$SKILL_DIR/template-recipe-test.java" \
|
||||
-F "files[]=@example-say-hello-recipe.java;filename=$SKILL_DIR/example-say-hello-recipe.java" \
|
||||
-F "files[]=@example-scanning-recipe.java;filename=$SKILL_DIR/example-scanning-recipe.java" \
|
||||
-F "files[]=@example-declarative-migration.yml;filename=$SKILL_DIR/example-declarative-migration.yml" \
|
||||
-F "files[]=@checklist-recipe-development.md;filename=$SKILL_DIR/checklist-recipe-development.md")
|
||||
fi
|
||||
|
||||
# Extract HTTP status code (last line)
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
|
||||
# Extract response body (everything except last line)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
# Check status code
|
||||
if [ "$http_code" -eq 200 ]; then
|
||||
if [ "$ACTION" = "create" ]; then
|
||||
echo "✓ Success! Skill created successfully."
|
||||
echo ""
|
||||
echo "Response:"
|
||||
echo "$body" | jq '.' 2>/dev/null || echo "$body"
|
||||
echo ""
|
||||
|
||||
# Extract and save skill ID
|
||||
new_skill_id=$(echo "$body" | jq -r '.id' 2>/dev/null)
|
||||
if [ -n "$new_skill_id" ] && [ "$new_skill_id" != "null" ]; then
|
||||
echo "$new_skill_id" > "$SKILL_ID_FILE"
|
||||
echo "Skill ID saved to $SKILL_ID_FILE for future updates."
|
||||
echo ""
|
||||
echo "Your skill is now available! Use it with:"
|
||||
echo " - Claude API (automatically activates for OpenRewrite recipe questions)"
|
||||
echo " - Claude.ai web interface"
|
||||
else
|
||||
echo "Warning: Could not extract skill ID from response."
|
||||
echo "You may need to save it manually from the response above."
|
||||
fi
|
||||
else
|
||||
echo "✓ Success! Skill updated successfully."
|
||||
echo ""
|
||||
echo "Response:"
|
||||
echo "$body" | jq '.' 2>/dev/null || echo "$body"
|
||||
echo ""
|
||||
echo "The skill has been updated to a new version."
|
||||
fi
|
||||
else
|
||||
echo "✗ Error: $ACTION failed with status code $http_code"
|
||||
echo ""
|
||||
echo "Response:"
|
||||
echo "$body"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user