feat: referentiel projets dynamique + validation nommage by design
Durcit la convention de nommage des projets (dérive constatée : 'Sliding Automation', 'code_versioning'... au lieu des formes canoniques). - trilium_api.py : projets_canoniques() lit le référentiel = valeurs du label projet sur les notes de type=projet (source unique, pas de constante en dur). Note-projet CodeVersioning créée (manquait). - mcp_server.py : _valider_projet() branché dans les 6 tools de création (add_decision/history/backlog, new_conversation, create_entite, add_skill). Refuse un projet non canonique (suggestion si faute) ou inconnu (renvoi au processus de création de projet). Ne verrouille pas si référentiel illisible. - lint_audit.py : VAL-nommage aligné sur le référentiel (attrape casse, espace ET snake_case ; l'ancien 'contient un espace' ratait code_versioning). - Données : 79 notes ré-étiquetées vers les 3 formes canoniques. Quality by design : l'erreur de nommage devient impossible à l'écriture, le Lint n'est plus que le filet de sécurité.
This commit is contained in:
+41
-1
@@ -24,7 +24,7 @@ from trilium_api import (
|
||||
create_note, get_note_id, get_note, get_note_content, get_children,
|
||||
update_note_content, search_by_label, set_label, get_label_value,
|
||||
find_note_by_title, delete_note_safe, move_note_safe, add_relation_safe,
|
||||
remove_relation_safe, move_relation_safe,
|
||||
remove_relation_safe, move_relation_safe, projets_canoniques,
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
@@ -397,7 +397,37 @@ def tool_get_contexte(projet, llm_cible="LLM"):
|
||||
lines += ["---", "Confirme en 3 lignes : (a) objectif (b) prochaine action (c) incertitude"]
|
||||
return "\n".join(lines)
|
||||
|
||||
def _valider_projet(projet):
|
||||
"""Valide un nom de projet contre le referentiel (notes de type=projet).
|
||||
Retourne None si OK, sinon un dict d'erreur explicite.
|
||||
- projet None/vide : OK (cas skill universel).
|
||||
- referentiel vide (bug lecture) : OK (ne pas verrouiller le systeme).
|
||||
- proche d'un canonique (casse/accent/espace) : erreur avec suggestion.
|
||||
- inconnu : erreur renvoyant au processus de creation de projet."""
|
||||
if not projet:
|
||||
return None
|
||||
canon = projets_canoniques()
|
||||
if not canon:
|
||||
return None # garde-fou : ne jamais bloquer si le referentiel est illisible
|
||||
if projet in canon:
|
||||
return None
|
||||
import unicodedata
|
||||
def norm(s):
|
||||
s = "".join(ch for ch in unicodedata.normalize("NFD", s) if unicodedata.category(ch) != "Mn")
|
||||
return s.lower().replace(" ", "").replace("_", "").replace("-", "")
|
||||
cible = None
|
||||
for cand in canon:
|
||||
if norm(cand) == norm(projet):
|
||||
cible = cand
|
||||
break
|
||||
if cible:
|
||||
return {"error": "Nom de projet non canonique: %r. Utiliser %r (referentiel: notes de type projet)." % (projet, cible)}
|
||||
return {"error": "Projet inconnu: %r. Projets valides: %s. Pour un nouveau projet, creer d'abord une note de type=projet (processus dedie)." % (projet, sorted(canon))}
|
||||
|
||||
|
||||
def tool_add_decision(projet, enonce, justification=""):
|
||||
err = _valider_projet(projet)
|
||||
if err: return err
|
||||
ids = load_ids()
|
||||
res = create_note(ids["Decisions"], enonce,
|
||||
content=f"<p><b>Justification</b> : {justification or '-'}</p>")
|
||||
@@ -407,6 +437,8 @@ def tool_add_decision(projet, enonce, justification=""):
|
||||
return {"id": nid, "enonce": enonce, "status": "cree"}
|
||||
|
||||
def tool_add_history(projet, enonce, type_historique, detail=""):
|
||||
err = _valider_projet(projet)
|
||||
if err: return err
|
||||
valides = ["Fait etabli", "Test effectue", "Hypothese invalidee", "Contrainte decouverte"]
|
||||
if type_historique not in valides:
|
||||
return {"error": f"type_historique invalide. Valeurs : {valides}"}
|
||||
@@ -419,6 +451,8 @@ def tool_add_history(projet, enonce, type_historique, detail=""):
|
||||
return {"id": nid, "enonce": enonce, "status": "cree"}
|
||||
|
||||
def tool_add_backlog(projet, titre, priorite="moyenne"):
|
||||
err = _valider_projet(projet)
|
||||
if err: return err
|
||||
if priorite not in ("haute", "moyenne", "basse"):
|
||||
return {"error": "priorite doit etre haute, moyenne ou basse"}
|
||||
ids = load_ids()
|
||||
@@ -440,6 +474,8 @@ def tool_update_backlog(note_id, statut):
|
||||
return {"id": note_id, "statut": statut, "status": "mis a jour"}
|
||||
|
||||
def tool_new_conversation(projet, titre, llm):
|
||||
err = _valider_projet(projet)
|
||||
if err: return err
|
||||
ids = load_ids()
|
||||
date = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
full = f"[{llm}] {date} — {titre}"
|
||||
@@ -527,6 +563,8 @@ def tool_update_note(note_id, contenu, format="markdown"):
|
||||
return {"id": note_id, "titre": note.get("title", ""), "format": format, "status": "mis a jour"}
|
||||
|
||||
def tool_create_entite(type, projet, titre, contenu=None, definition=None):
|
||||
err = _valider_projet(projet)
|
||||
if err: return err
|
||||
"""Cree une entite de la couche connaissance/technique (concept, composantLogiciel,
|
||||
service). Le type est valide contre une liste blanche ; le dossier cible est resolu
|
||||
via trilium_ids.json. Garde-fou anti-doublon sur le titre dans le dossier cible.
|
||||
@@ -589,6 +627,8 @@ def tool_move_note(note_id, ancien_parent_id, nouveau_parent_id):
|
||||
return {"success": ok, "message": message}
|
||||
|
||||
def tool_add_skill(titre, contenu, portee="projet", projet=None):
|
||||
err = _valider_projet(projet)
|
||||
if err: return err
|
||||
if portee != "universel" and not projet:
|
||||
return {"error": "projet requis sauf si portee='universel'"}
|
||||
ids = load_ids()
|
||||
|
||||
Reference in New Issue
Block a user