303 lines
9.6 KiB
Python
303 lines
9.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Agent Reuse Manager
|
|
Avoids regenerating automation for similar projects
|
|
Reuses successful configurations
|
|
"""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from typing import Dict, List, Optional
|
|
from difflib import SequenceMatcher
|
|
|
|
class AgentReuseManager:
|
|
"""Manages reuse of automation configurations"""
|
|
|
|
def __init__(self, storage_path: str = ".claude/meta-automation/configurations"):
|
|
self.storage_dir = Path(storage_path)
|
|
self.storage_dir.mkdir(parents=True, exist_ok=True)
|
|
self.index_path = self.storage_dir / "index.json"
|
|
self.index = self._load_index()
|
|
|
|
def _load_index(self) -> Dict:
|
|
"""Load configuration index"""
|
|
if self.index_path.exists():
|
|
try:
|
|
with open(self.index_path, 'r') as f:
|
|
return json.load(f)
|
|
except:
|
|
return {'configurations': []}
|
|
return {'configurations': []}
|
|
|
|
def _save_index(self):
|
|
"""Save configuration index"""
|
|
with open(self.index_path, 'w') as f:
|
|
json.dump(self.index, f, indent=2)
|
|
|
|
def save_configuration(self, config: Dict) -> str:
|
|
"""
|
|
Save a successful automation configuration
|
|
|
|
Args:
|
|
config: {
|
|
'project_type': str,
|
|
'project_name': str,
|
|
'tech_stack': List[str],
|
|
'agents_used': List[str],
|
|
'skills_generated': List[str],
|
|
'commands_generated': List[str],
|
|
'hooks_generated': List[str],
|
|
'success_metrics': Dict,
|
|
'user_satisfaction': int (1-5)
|
|
}
|
|
|
|
Returns:
|
|
Configuration ID
|
|
"""
|
|
config_id = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
|
|
# Add metadata
|
|
config_with_meta = {
|
|
**config,
|
|
'config_id': config_id,
|
|
'created_at': datetime.now().isoformat(),
|
|
'reuse_count': 0
|
|
}
|
|
|
|
# Save full configuration
|
|
config_path = self.storage_dir / f"{config_id}.json"
|
|
with open(config_path, 'w') as f:
|
|
json.dump(config_with_meta, f, indent=2)
|
|
|
|
# Update index
|
|
self.index['configurations'].append({
|
|
'config_id': config_id,
|
|
'project_type': config['project_type'],
|
|
'project_name': config.get('project_name', 'unknown'),
|
|
'tech_stack': config.get('tech_stack', []),
|
|
'created_at': config_with_meta['created_at'],
|
|
'reuse_count': 0
|
|
})
|
|
self._save_index()
|
|
|
|
return config_id
|
|
|
|
def find_similar_configurations(self, project_info: Dict, min_similarity: float = 0.7) -> List[Dict]:
|
|
"""
|
|
Find similar configurations that could be reused
|
|
|
|
Args:
|
|
project_info: {
|
|
'project_type': str,
|
|
'tech_stack': List[str],
|
|
'existing_tools': List[str]
|
|
}
|
|
min_similarity: Minimum similarity score (0-1)
|
|
|
|
Returns:
|
|
List of similar configurations sorted by similarity
|
|
"""
|
|
similar = []
|
|
|
|
for config_ref in self.index['configurations']:
|
|
config = self._load_configuration(config_ref['config_id'])
|
|
if not config:
|
|
continue
|
|
|
|
similarity = self._calculate_similarity(project_info, config)
|
|
|
|
if similarity >= min_similarity:
|
|
similar.append({
|
|
**config_ref,
|
|
'similarity': round(similarity, 2),
|
|
'full_config': config
|
|
})
|
|
|
|
# Sort by similarity (descending)
|
|
similar.sort(key=lambda x: x['similarity'], reverse=True)
|
|
|
|
return similar
|
|
|
|
def _calculate_similarity(self, project_info: Dict, config: Dict) -> float:
|
|
"""
|
|
Calculate similarity between project and configuration
|
|
|
|
Returns:
|
|
Similarity score 0-1
|
|
"""
|
|
score = 0.0
|
|
weights = {
|
|
'project_type': 0.4,
|
|
'tech_stack': 0.4,
|
|
'size': 0.2
|
|
}
|
|
|
|
# Project type match
|
|
if project_info.get('project_type') == config.get('project_type'):
|
|
score += weights['project_type']
|
|
|
|
# Tech stack similarity
|
|
project_stack = set(project_info.get('tech_stack', []))
|
|
config_stack = set(config.get('tech_stack', []))
|
|
|
|
if project_stack and config_stack:
|
|
intersection = len(project_stack & config_stack)
|
|
union = len(project_stack | config_stack)
|
|
tech_similarity = intersection / union if union > 0 else 0
|
|
score += weights['tech_stack'] * tech_similarity
|
|
|
|
return min(score, 1.0)
|
|
|
|
def reuse_configuration(self, config_id: str) -> Dict:
|
|
"""
|
|
Reuse a configuration
|
|
|
|
Args:
|
|
config_id: ID of configuration to reuse
|
|
|
|
Returns:
|
|
Configuration to apply
|
|
"""
|
|
config = self._load_configuration(config_id)
|
|
if not config:
|
|
return None
|
|
|
|
# Increment reuse count
|
|
config['reuse_count'] += 1
|
|
self._save_configuration(config_id, config)
|
|
|
|
# Update index
|
|
for cfg in self.index['configurations']:
|
|
if cfg['config_id'] == config_id:
|
|
cfg['reuse_count'] += 1
|
|
self._save_index()
|
|
|
|
return config
|
|
|
|
def get_reuse_recommendation(self, project_info: Dict) -> Optional[Dict]:
|
|
"""
|
|
Get recommendation for reusing a configuration
|
|
|
|
Args:
|
|
project_info: Information about current project
|
|
|
|
Returns:
|
|
Recommendation or None if no good match
|
|
"""
|
|
similar = self.find_similar_configurations(project_info, min_similarity=0.75)
|
|
|
|
if not similar:
|
|
return None
|
|
|
|
best_match = similar[0]
|
|
|
|
return {
|
|
'recommended': True,
|
|
'config_id': best_match['config_id'],
|
|
'similarity': best_match['similarity'],
|
|
'project_name': best_match['project_name'],
|
|
'created_at': best_match['created_at'],
|
|
'reuse_count': best_match['reuse_count'],
|
|
'time_saved': '5-10 minutes (no need to regenerate)',
|
|
'agents': best_match['full_config']['agents_used'],
|
|
'skills': best_match['full_config']['skills_generated'],
|
|
'reason': f"This configuration was successful for a similar {best_match['project_type']} project"
|
|
}
|
|
|
|
def _load_configuration(self, config_id: str) -> Optional[Dict]:
|
|
"""Load a configuration file"""
|
|
config_path = self.storage_dir / f"{config_id}.json"
|
|
if not config_path.exists():
|
|
return None
|
|
|
|
try:
|
|
with open(config_path, 'r') as f:
|
|
return json.load(f)
|
|
except:
|
|
return None
|
|
|
|
def _save_configuration(self, config_id: str, config: Dict):
|
|
"""Save a configuration file"""
|
|
config_path = self.storage_dir / f"{config_id}.json"
|
|
with open(config_path, 'w') as f:
|
|
json.dump(config, f, indent=2)
|
|
|
|
def get_statistics(self) -> Dict:
|
|
"""Get reuse statistics"""
|
|
total_configs = len(self.index['configurations'])
|
|
total_reuses = sum(cfg['reuse_count'] for cfg in self.index['configurations'])
|
|
|
|
project_types = {}
|
|
for cfg in self.index['configurations']:
|
|
ptype = cfg['project_type']
|
|
if ptype not in project_types:
|
|
project_types[ptype] = 0
|
|
project_types[ptype] += 1
|
|
|
|
return {
|
|
'total_configurations': total_configs,
|
|
'total_reuses': total_reuses,
|
|
'average_reuses': round(total_reuses / total_configs, 1) if total_configs > 0 else 0,
|
|
'project_types': project_types,
|
|
'most_reused': sorted(
|
|
self.index['configurations'],
|
|
key=lambda x: x['reuse_count'],
|
|
reverse=True
|
|
)[:3]
|
|
}
|
|
|
|
# Example usage
|
|
if __name__ == '__main__':
|
|
manager = AgentReuseManager()
|
|
|
|
# Save a configuration
|
|
print("Saving successful configuration...")
|
|
config_id = manager.save_configuration({
|
|
'project_type': 'programming',
|
|
'project_name': 'my-web-app',
|
|
'tech_stack': ['TypeScript', 'React', 'Next.js'],
|
|
'agents_used': ['project-analyzer', 'security-analyzer', 'test-coverage-analyzer'],
|
|
'skills_generated': ['security-scanner', 'test-generator'],
|
|
'commands_generated': ['/security-check', '/generate-tests'],
|
|
'hooks_generated': ['pre-commit-security'],
|
|
'success_metrics': {
|
|
'time_saved': 50,
|
|
'issues_prevented': 3
|
|
},
|
|
'user_satisfaction': 5
|
|
})
|
|
|
|
print(f"Saved configuration: {config_id}\n")
|
|
|
|
# Find similar
|
|
print("Finding similar configurations for new project...")
|
|
similar = manager.find_similar_configurations({
|
|
'project_type': 'programming',
|
|
'tech_stack': ['TypeScript', 'React', 'Vite'], # Similar but not exact
|
|
'existing_tools': ['ESLint']
|
|
})
|
|
|
|
print(f"Found {len(similar)} similar configurations\n")
|
|
|
|
if similar:
|
|
print("Best match:")
|
|
print(json.dumps({
|
|
'config_id': similar[0]['config_id'],
|
|
'similarity': similar[0]['similarity'],
|
|
'project_name': similar[0]['project_name']
|
|
}, indent=2))
|
|
|
|
# Get recommendation
|
|
print("\nRecommendation:")
|
|
rec = manager.get_reuse_recommendation({
|
|
'project_type': 'programming',
|
|
'tech_stack': ['TypeScript', 'React', 'Vite']
|
|
})
|
|
print(json.dumps(rec, indent=2))
|
|
|
|
# Statistics
|
|
print("\nReuse Statistics:")
|
|
stats = manager.get_statistics()
|
|
print(json.dumps(stats, indent=2))
|