301 lines
No EOL
9.6 KiB
Python
301 lines
No EOL
9.6 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Script de synchronisation pour Cursor AI
|
||
Génère automatiquement le contexte et les prompts optimisés
|
||
"""
|
||
|
||
import json
|
||
import yaml
|
||
import os
|
||
from pathlib import Path
|
||
from datetime import datetime
|
||
import subprocess
|
||
import sys
|
||
|
||
class CursorSync:
|
||
def __init__(self):
|
||
self.project_root = Path.cwd()
|
||
self.cursor_config = self.project_root / ".cursor"
|
||
self.veza_config = self.project_root / ".veza"
|
||
|
||
def load_architecture(self):
|
||
"""Charge les informations d'architecture"""
|
||
try:
|
||
with open(self.project_root / "docs/arch/current.mmd", "r") as f:
|
||
return {
|
||
"type": "mermaid",
|
||
"content": f.read()
|
||
}
|
||
except FileNotFoundError:
|
||
return {
|
||
"type": "hexagonal",
|
||
"description": "Architecture hexagonale avec core backend et features modulaires"
|
||
}
|
||
|
||
def scan_features(self):
|
||
"""Scanne toutes les features du projet"""
|
||
features = {}
|
||
|
||
# Core backend
|
||
if (self.project_root / "veza-backend-api").exists():
|
||
features["core"] = {
|
||
"status": "stable",
|
||
"version": "1.0.0",
|
||
"location": "veza-backend-api/",
|
||
"language": "go"
|
||
}
|
||
|
||
# Chat server
|
||
if (self.project_root / "veza-chat-server").exists():
|
||
features["chat-server"] = {
|
||
"status": "stable",
|
||
"version": "1.0.0",
|
||
"location": "veza-chat-server/",
|
||
"language": "rust"
|
||
}
|
||
|
||
# Stream server
|
||
if (self.project_root / "veza-stream-server").exists():
|
||
features["stream-server"] = {
|
||
"status": "stable",
|
||
"version": "1.0.0",
|
||
"location": "veza-stream-server/",
|
||
"language": "rust"
|
||
}
|
||
|
||
# Documentation
|
||
if (self.project_root / "veza-docs").exists():
|
||
features["docs"] = {
|
||
"status": "stable",
|
||
"version": "1.0.0",
|
||
"location": "veza-docs/",
|
||
"language": "javascript"
|
||
}
|
||
|
||
return features
|
||
|
||
def load_contracts(self):
|
||
"""Charge les contrats d'interface"""
|
||
contracts = {}
|
||
contracts_dir = self.project_root / "veza-backend-api/internal/core/contracts"
|
||
|
||
if contracts_dir.exists():
|
||
for contract_file in contracts_dir.glob("*.go"):
|
||
contracts[contract_file.stem] = {
|
||
"file": str(contract_file.relative_to(self.project_root)),
|
||
"type": "interface"
|
||
}
|
||
|
||
return contracts
|
||
|
||
def analyze_dependencies(self):
|
||
"""Analyse les dépendances du projet"""
|
||
dependencies = {}
|
||
|
||
# Go dependencies
|
||
go_mod = self.project_root / "veza-backend-api/go.mod"
|
||
if go_mod.exists():
|
||
dependencies["go"] = {
|
||
"file": "veza-backend-api/go.mod",
|
||
"framework": "gin"
|
||
}
|
||
|
||
# Rust dependencies
|
||
for rust_project in ["veza-chat-server", "veza-stream-server"]:
|
||
cargo_toml = self.project_root / rust_project / "Cargo.toml"
|
||
if cargo_toml.exists():
|
||
dependencies[rust_project] = {
|
||
"file": f"{rust_project}/Cargo.toml",
|
||
"language": "rust"
|
||
}
|
||
|
||
return dependencies
|
||
|
||
def load_tasks(self):
|
||
"""Charge les tâches en cours"""
|
||
tasks = []
|
||
|
||
# Vérifier s'il y a des branches en cours
|
||
try:
|
||
result = subprocess.run(
|
||
["git", "branch", "--show-current"],
|
||
capture_output=True,
|
||
text=True,
|
||
cwd=self.project_root
|
||
)
|
||
if result.returncode == 0:
|
||
current_branch = result.stdout.strip()
|
||
if current_branch != "main" and current_branch != "develop":
|
||
tasks.append({
|
||
"type": "feature_development",
|
||
"name": f"Working on branch: {current_branch}",
|
||
"status": "in_progress"
|
||
})
|
||
except:
|
||
pass
|
||
|
||
return tasks
|
||
|
||
def load_validation_rules(self):
|
||
"""Charge les règles de validation"""
|
||
return [
|
||
"version_compatibility",
|
||
"interface_contracts",
|
||
"event_consistency",
|
||
"commit_format",
|
||
"test_coverage"
|
||
]
|
||
|
||
def generate_context_file(self):
|
||
"""Génère un fichier de contexte pour Cursor AI"""
|
||
context = {
|
||
"project": "Veza",
|
||
"architecture": self.load_architecture(),
|
||
"features": self.scan_features(),
|
||
"contracts": self.load_contracts(),
|
||
"dependencies": self.analyze_dependencies(),
|
||
"current_tasks": self.load_tasks(),
|
||
"validation_rules": self.load_validation_rules(),
|
||
"last_updated": datetime.now().isoformat()
|
||
}
|
||
|
||
self.cursor_config.mkdir(exist_ok=True)
|
||
|
||
with open(self.cursor_config / "context.json", "w") as f:
|
||
json.dump(context, f, indent=2)
|
||
|
||
print(f"✅ Context file generated: {self.cursor_config / 'context.json'}")
|
||
|
||
def create_implementation_prompt(self):
|
||
"""Crée un prompt optimisé pour l'implémentation"""
|
||
return """
|
||
En tant qu'assistant Cursor AI pour le projet Veza:
|
||
|
||
1. TOUJOURS vérifier la compatibilité avec le core avant d'implémenter
|
||
2. RESPECTER les contrats définis dans internal/core/contracts/
|
||
3. SUIVRE la structure modulaire dans features/
|
||
4. UTILISER les types et interfaces du core
|
||
5. GÉNÉRER les tests en parallèle de l'implémentation
|
||
6. DOCUMENTER chaque fonction publique
|
||
7. VALIDER avec le script check-compatibility.sh
|
||
8. SUIVRE le format de commit: [SCOPE][TYPE] Description
|
||
|
||
Contexte actuel: {context}
|
||
Feature en cours: {current_feature}
|
||
Dépendances: {dependencies}
|
||
|
||
Architecture: Hexagonale avec core immutable
|
||
Patterns: Repository, Service, Handler
|
||
Validation: Tests unitaires + intégration obligatoires
|
||
"""
|
||
|
||
def create_testing_prompt(self):
|
||
"""Crée un prompt optimisé pour les tests"""
|
||
return """
|
||
Pour les tests dans Veza:
|
||
|
||
1. TOUJOURS tester les contrats d'interface
|
||
2. COUVRIR les cas d'erreur et edge cases
|
||
3. UTILISER testify pour Go, built-in pour Rust
|
||
4. MOCKER les dépendances externes
|
||
5. TESTER l'intégration entre features
|
||
6. VALIDER la compatibilité des versions
|
||
|
||
Scope: {scope}
|
||
Feature: {feature}
|
||
Framework: {framework}
|
||
"""
|
||
|
||
def create_compatibility_prompt(self):
|
||
"""Crée un prompt pour la vérification de compatibilité"""
|
||
return """
|
||
Vérification de compatibilité Veza:
|
||
|
||
1. VÉRIFIER les versions des dépendances
|
||
2. CONTRÔLER les interfaces exposées
|
||
3. VALIDER les événements publiés/souscrits
|
||
4. TESTER l'intégration entre modules
|
||
5. DOCUMENTER les changements breaking
|
||
|
||
Features: {features}
|
||
Contracts: {contracts}
|
||
Events: {events}
|
||
"""
|
||
|
||
def create_documentation_prompt(self):
|
||
"""Crée un prompt pour la documentation"""
|
||
return """
|
||
Documentation Veza:
|
||
|
||
1. DOCUMENTER toutes les interfaces publiques
|
||
2. EXPLIQUER les patterns d'architecture
|
||
3. FOURNIR des exemples d'utilisation
|
||
4. MAINTENIR la cohérence avec l'architecture
|
||
5. METTRE À JOUR les diagrammes si nécessaire
|
||
|
||
Type: {doc_type}
|
||
Scope: {scope}
|
||
Format: Markdown
|
||
"""
|
||
|
||
def generate_prompts(self):
|
||
"""Génère des prompts optimisés pour Cursor"""
|
||
prompts = {
|
||
"implementation": self.create_implementation_prompt(),
|
||
"testing": self.create_testing_prompt(),
|
||
"compatibility": self.create_compatibility_prompt(),
|
||
"documentation": self.create_documentation_prompt()
|
||
}
|
||
|
||
self.cursor_config.mkdir(exist_ok=True)
|
||
|
||
with open(self.cursor_config / "prompts.yaml", "w") as f:
|
||
yaml.dump(prompts, f, default_flow_style=False)
|
||
|
||
print(f"✅ Prompts generated: {self.cursor_config / 'prompts.yaml'}")
|
||
|
||
def update_manifest(self):
|
||
"""Met à jour le manifeste des features"""
|
||
if not self.veza_config.exists():
|
||
self.veza_config.mkdir(exist_ok=True)
|
||
|
||
manifest_file = self.veza_config / "feature-manifest.yaml"
|
||
|
||
if not manifest_file.exists():
|
||
# Créer un manifeste de base
|
||
base_manifest = {
|
||
"version": "1.0.0",
|
||
"last_updated": datetime.now().isoformat(),
|
||
"features": self.scan_features(),
|
||
"compatibility_matrix": {},
|
||
"validation_rules": self.load_validation_rules()
|
||
}
|
||
|
||
with open(manifest_file, "w") as f:
|
||
yaml.dump(base_manifest, f, default_flow_style=False)
|
||
|
||
print(f"✅ Base manifest created: {manifest_file}")
|
||
else:
|
||
print(f"ℹ️ Manifest already exists: {manifest_file}")
|
||
|
||
def sync_all(self):
|
||
"""Synchronise tout le contexte Cursor"""
|
||
print("🔄 Synchronizing Cursor AI context...")
|
||
|
||
self.generate_context_file()
|
||
self.generate_prompts()
|
||
self.update_manifest()
|
||
|
||
print("✅ Cursor AI synchronization completed!")
|
||
|
||
def main():
|
||
"""Fonction principale"""
|
||
try:
|
||
sync = CursorSync()
|
||
sync.sync_all()
|
||
except Exception as e:
|
||
print(f"❌ Error during synchronization: {e}")
|
||
sys.exit(1)
|
||
|
||
if __name__ == "__main__":
|
||
main() |