301 lines
9.6 KiB
Python
301 lines
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()
|