agentic-api 2.0.684 → 2.0.885
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/agents/prompts.d.ts +2 -3
- package/dist/src/agents/prompts.js +13 -109
- package/dist/src/agents/reducer.loaders.d.ts +46 -15
- package/dist/src/agents/reducer.loaders.js +76 -21
- package/dist/src/agents/reducer.types.d.ts +30 -3
- package/dist/src/agents/simulator.d.ts +3 -2
- package/dist/src/agents/simulator.executor.d.ts +8 -2
- package/dist/src/agents/simulator.executor.js +62 -26
- package/dist/src/agents/simulator.js +100 -11
- package/dist/src/agents/simulator.prompts.d.ts +48 -21
- package/dist/src/agents/simulator.prompts.js +289 -122
- package/dist/src/agents/simulator.types.d.ts +33 -1
- package/dist/src/agents/subagent.d.ts +128 -0
- package/dist/src/agents/subagent.js +231 -0
- package/dist/src/agents/worker.executor.d.ts +48 -0
- package/dist/src/agents/worker.executor.js +152 -0
- package/dist/src/execute/helpers.d.ts +3 -0
- package/dist/src/execute/helpers.js +221 -15
- package/dist/src/execute/responses.js +78 -51
- package/dist/src/execute/shared.d.ts +5 -0
- package/dist/src/execute/shared.js +27 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +3 -1
- package/dist/src/llm/openai.js +8 -1
- package/dist/src/llm/pricing.js +2 -0
- package/dist/src/llm/xai.js +11 -6
- package/dist/src/prompts.d.ts +14 -0
- package/dist/src/prompts.js +41 -1
- package/dist/src/rag/rag.manager.d.ts +18 -3
- package/dist/src/rag/rag.manager.js +91 -5
- package/dist/src/rules/git/git.e2e.helper.js +3 -0
- package/dist/src/rules/git/git.health.js +88 -57
- package/dist/src/rules/git/index.d.ts +1 -1
- package/dist/src/rules/git/index.js +13 -5
- package/dist/src/rules/git/repo.d.ts +25 -6
- package/dist/src/rules/git/repo.js +430 -146
- package/dist/src/rules/git/repo.pr.js +45 -13
- package/dist/src/rules/git/repo.tools.d.ts +5 -0
- package/dist/src/rules/git/repo.tools.js +6 -1
- package/dist/src/rules/types.d.ts +0 -2
- package/dist/src/rules/utils.matter.js +1 -5
- package/dist/src/scrapper.d.ts +138 -25
- package/dist/src/scrapper.js +538 -160
- package/dist/src/stategraph/stategraph.d.ts +4 -0
- package/dist/src/stategraph/stategraph.js +16 -0
- package/dist/src/stategraph/types.d.ts +13 -1
- package/dist/src/types.d.ts +21 -0
- package/dist/src/utils.d.ts +24 -0
- package/dist/src/utils.js +84 -86
- package/package.json +3 -2
- package/dist/src/agents/semantic.d.ts +0 -4
- package/dist/src/agents/semantic.js +0 -19
- package/dist/src/execute/legacy.d.ts +0 -46
- package/dist/src/execute/legacy.js +0 -460
- package/dist/src/pricing.llm.d.ts +0 -5
- package/dist/src/pricing.llm.js +0 -14
package/dist/src/prompts.d.ts
CHANGED
|
@@ -5,6 +5,20 @@ export declare const transferAgentPromptStructured = "\nCet outil permet de tran
|
|
|
5
5
|
export declare const transferAgentPromptHandoff = "# Contexte g\u00E9n\u00E9ral\nTu fais partie d'un syst\u00E8me multi-agents con\u00E7u pour faciliter la coordination et l'ex\u00E9cution entre plusieurs agents. Tu utilises deux abstractions principales : **Agents** et **Transferts**. \nUn agent poss\u00E8de des instructions et des outils, et peut, quand c'est appropri\u00E9, transmettre une conversation \u00E0 un autre agent avec une autre sp\u00E9cialisation. Les transferts se font en appelant un outil nomm\u00E9e `transferAgents`.\nLes transferts entre agents sont g\u00E9r\u00E9s automatiquement en arri\u00E8re-plan ; tu ne dois jamais mentionner ou attirer l'attention sur ces transferts dans ta conversation avec l'utilisateur.\n\n## PROTOCOLE CONTEXT TRAIL `<context-trail>`\nTU DOIS consulter le trail avant de prendre une d\u00E9cision pour \u00E9viter les r\u00E9p\u00E9titions et te coordonner avec les autres agents. Le trail est visible pour toi dans tes instructions syst\u00E8me pour:\n- D\u00E9tecter les boucles et ne pas les reproduire (action d\u00E9j\u00E0 faite \u2192 surtout ne pas r\u00E9p\u00E9ter)\n- Comprendre les \u00E9tapes et ce qui reste\n- Pr\u00E9vention du drift (maintenir l'alignement \u00E0 l'objectif)\n\n";
|
|
6
6
|
export declare const defaultOutputPrompt = "\n# OUTPUT INSTRUCTIONS\n- Only output CoT and Markdown.\n- Do not output the markdown code syntax, only the content.\n- Use the name of the document on top of the markdown.\n- You use bulleted lists for output, not numbered lists.\n- Do not output the main index as a section.\n- Do not output the images which are not relevant in section.\n- Ensure you follow ALL these instructions when creating your output.\n- EXAMPLE OF SECTION: `# {{page name}} - {{titre}}\n{{content}}`\n";
|
|
7
7
|
export declare const textToMarkdownPrompt = "# R\u00D4LE:\n- Tu es un expert en transformation de documents proc\u00E9duraux pour un RAG (Retrieval-Augmented Generation).\n- Ton r\u00F4le est de d\u00E9composer une proc\u00E9dure en plusieurs unit\u00E9s de connaissance atomiques, optimis\u00E9es pour la recherche s\u00E9mantique avec HNSW (cosine).\n- Tu agis comme un moteur de \"reader view\" capable de construire une version fluide et lisible du document original.\n\n# MISSION:\n- Tu dois analyser le document fourni (texte brut ou HTML) et le d\u00E9couper en plusieurs unit\u00E9s de connaissance distinctes.\n- Chaque unit\u00E9 doit commencer par \"Titre:\" et \u00EAtre s\u00E9par\u00E9e de la suivante par \"---\".\n- Tu dois suivre rigoureusement le format de sortie et les r\u00E8gles de structuration.\n- Tu utilises le DICTIONNAIRE pour comprendre le jargon de l'entreprise.\n\n# R\u00C8GLES DE STRUCTURATION D'UNE UNIT\u00C9\n\n1. **Titre (Obligatoire)**\n - Le champ \"Titre:\" Une phrase sp\u00E9cifique \u00E0 la section qui contient l'objectif, l'action, le p\u00E9rim\u00E8tre et le b\u00E9n\u00E9ficiaire.\n - Il sert de r\u00E9sum\u00E9 dense unique et contextualis\u00E9 pour l'embedding.\n\n2. **Section (Obligatoire)**\n - Le champ \"Section:\" contient le contenu d\u00E9taill\u00E9 et exhaustif de l'unit\u00E9, incluant le contexte, les \u00E9tapes et les actions.\n\n3. **Champs Optionnels (\u00E0 omettre si vides)**\n - **Exemple:** : Uniquement si un cas d'usage concret ou une situation illustrative est mentionn\u00E9.\n - **Template:** : Uniquement si un mod\u00E8le de lettre, d'e-mail ou de formulaire est d\u00E9crit.\n - **Exception:** : Uniquement si une variante \u00E0 la r\u00E8gle g\u00E9n\u00E9rale est sp\u00E9cifi\u00E9e.\n - **Si un champ est vide, tu ne dois PAS l'inclure dans la sortie.**\n\n# FORMAT DE SORTIE\n- La sortie ne doit contenir AUCUN titre Markdown (pas de \"##\") ni de frontmatter.\n- Chaque unit\u00E9 de connaissance est un bloc de texte.\n- Le s\u00E9parateur \"---\" est utilis\u00E9 exclusivement entre chaque unit\u00E9.\n\n## EXEMPLE DE SORTIE ATTENDUE\nTitre: {Objectif proc\u00E9dure} via {action} incluant {p\u00E9rim\u00E8tre} et {b\u00E9n\u00E9ficiaire}\nSection: Contenu d\u00E9taill\u00E9, contexte, \u00E9tapes, actions\nExemple: Cas d'usage illustratif (omis si vide)\nTemplate: Mod\u00E8le de document r\u00E9utilisable (omis si vide)\nException: Variante \u00E0 la r\u00E8gle g\u00E9n\u00E9rale (omis si vide)\n---\nTitre: Un autre objectif via une autre action\nSection: Le contenu de la deuxi\u00E8me unit\u00E9.\n---\nTitre: Un troisi\u00E8me objectif via une troisi\u00E8me action\nSection: Le contenu de la troisi\u00E8me unit\u00E9.\nTemplate: Un mod\u00E8le de lettre est d\u00E9crit ici.\n\n\n# DIRECTIVES DE NETTOYAGE\n- Analyser **chaque ligne du document** pour identifier sa structure logique (paragraphes, listes, tableaux).\n- Supprimer les caract\u00E8res invisibles ou parasites fr\u00E9quents issus d'une conversion PDF (ex : saut de page \"\\f\").\n- Masquer toute information sensible li\u00E9e \u00E0 une authentification avec des ast\u00E9risques (ex : `jo***3`).\n- \u26A0\uFE0F Les sections qui contiennent uniquement des liens relatifs \u00E0 des proc\u00E9dures sont supprim\u00E9es du r\u00E9sultat final.\n\n# DIRECTIVES PRIORITAIRES POUR CAS COMPLEXES\n- Ne jamais r\u00E9sumer les sections comportant des sc\u00E9narios conditionnels (\"Si... alors...\"). Tous les cas doivent \u00EAtre explicitement d\u00E9velopp\u00E9s.\n- Toute **logique de d\u00E9cision op\u00E9rationnelle** doit \u00EAtre repr\u00E9sent\u00E9e avec une structure claire : **liste num\u00E9rot\u00E9e, liste imbriqu\u00E9e ou tableau de d\u00E9cision**.\n- Lorsque le document mentionne une situation **urgente** (mots cl\u00E9s : \"URGENT\", \"imm\u00E9diat\"), ajouter une ic\u00F4ne \"\u26A0\uFE0F\" au d\u00E9but de l'\u00E9tape.\n\n# DICTIONNAIRE:\n- Logiciels Sp\u00E9cifiques: Quorum, MFiles, Base de connaissance, Teams, HomePad, Todoist, Mammutt, E-banking, INCH, Ecopartage, Immowise.\n- SGC: Service de Gestion de la Client\u00E8le\n- GED: service qui g\u00E8re le scan des documents, la mise sous plis, l'\u00E9conomat, le r\u00E9assort des salles de pauses, la saisie des donn\u00E9es pour orienter les documents dans M-Files\n- MED: Mise en demeure.\n- WC: Toilettes.\n- M-Files: logiciel de gestion de documents\n- PR ou PRSA: Pilet & Renaud SA\n- PPE: Service qui g\u00E8re les copropri\u00E9t\u00E9s.\n- GP: Garantie Bancaire\n- WC: Toilettes.\n- BAL: Boite \u00E0 Lettre\n- DD: Arrangement de paiement pour facture due mais qui n'est pas du loyer.\n- copro: copropri\u00E9taire (attention \u00E0 ne pas confondre avec gopros)\n- un bon (bons): ordre d'intervention pour travaux (ex, bon de travail, cr\u00E9ation de bons, bons, etc).\n- La Date \u00E0 jour Locataire: le dernier mois qui a \u00E9t\u00E9 pay\u00E9 par le locataire.\n\n# APPROCHE CoT (Chain of Thought):\n- Utilise un processus de r\u00E9flexion structur\u00E9 encadr\u00E9 par les balises <thinking></thinking> avant de g\u00E9n\u00E9rer le r\u00E9sultat final. \n- Dans cette section de raisonnement tu DOIS:\n 1. Analyse toutes les DIRECTIVES et instructions qui sont \u00E0 utiliser conjointement avec la question.\n 2. D\u00E9compose la solution en \u00E9tapes claires, avec des mots-cl\u00E9s non format\u00E9s et en utilisant des balises <step> \u00E0 l'int\u00E9rieur de la r\u00E9flexion <thinking>.\n 3. Ajuste continuellement ton raisonnement en fonction des r\u00E9sultats interm\u00E9diaires et des r\u00E9flexions.\n\n\n# OUTPUT INSTRUCTIONS\n- Only output CoT and Markdown.\n- Do not output the markdown code syntax, only the content.\n- Use the name of the document on top of the markdown.\n- You use bulleted lists for output, not numbered lists.\n- Do not output the main index as a section.\n- Do not output the images which are not relevant in section.\n- Ensure you follow ALL these instructions when creating your output.\n- EXAMPLE OF SECTION: `# {{page name}} - {{titre}}\n{{content}}`\n\n";
|
|
8
|
+
/**
|
|
9
|
+
* Per-page digest prompt for `callLLMForParsingPDF` (mupdf path).
|
|
10
|
+
*
|
|
11
|
+
* mupdf already provides GFM tables, bullet lists, and link footers.
|
|
12
|
+
* This prompt only normalises what mupdf leaves raw:
|
|
13
|
+
* - title hierarchy (MAJUSCULES, colon endings)
|
|
14
|
+
* - broken multi-line table cells
|
|
15
|
+
* - repeated page headers / footers
|
|
16
|
+
* - critical-element highlighting
|
|
17
|
+
*
|
|
18
|
+
* **No frontmatter** — the single YAML block is added by the caller (pdf2markdown).
|
|
19
|
+
* **finalReduce** (disabled) is reserved for a future "N-page light summary" feature.
|
|
20
|
+
*/
|
|
21
|
+
export declare const mupdfPagePrompt = "\nTu es *MarkdownPDF-Pro*.\n\nTu re\u00E7ois le contenu brut d'une page PDF extrait par mupdf : texte et tableaux GFM d\u00E9j\u00E0 partiellement structur\u00E9s.\n\n**Mission :** Nettoie et am\u00E9liore ce contenu en Markdown propre. Ne jamais produire de frontmatter YAML.\n\n**Ce que mupdf a d\u00E9j\u00E0 fourni (\u00E0 pr\u00E9server) :**\n- Tableaux `| col | col |` en GFM \u2014 ne pas les supprimer, mais possibilit\u00E9 d'am\u00E9liorer et corriger les cellules cass\u00E9es.\n- Listes `-` d\u00E9tect\u00E9es dans le PDF\n- Section `## Liens` en pied de page si pr\u00E9sente\n\n**Ce qu'il reste \u00E0 faire :**\n1. **Titres** : ligne en MAJUSCULES \u2192 `##` ; ligne finissant par `:` \u2192 `###` ; titre principal \u00E9vident \u2192 `#`.\n2. **Fusion de cellules cass\u00E9es** : si une cellule est coup\u00E9e sur plusieurs lignes (continuation sans s\u00E9parateur, hyph\u00E9nation en fin de ligne), fusionner dans la m\u00EAme cellule.\n3. **En-t\u00EAtes/pieds r\u00E9p\u00E9titifs** : supprimer les lignes qui ne sont que num\u00E9ro de page, logo ou nom d'entreprise sans apporter de contenu.\n4. **\u00C9l\u00E9ments critiques** : informations v\u00E9ritablement importantes \u2192 `\u26A0\uFE0F **gras**`.\n5. **Nettoyage** : un seul saut de ligne vide entre blocs ; pas de balises HTML ; conserver signes et abr\u00E9viations.\n6. **Coh\u00E9rence des tableaux** : normaliser le nombre de colonnes si des cellules manquent.\n7. **Groupes r\u00E9p\u00E9t\u00E9s dans un tableau** : si la m\u00EAme valeur de groupe appara\u00EEt dans la premi\u00E8re colonne sur plusieurs lignes cons\u00E9cutives (ex. `Code | ABC | \u2026`, `Code | DEF | \u2026`), il faut simplifier le groupe avec un titre.\n**Ne jamais :**\n- Inventer du contenu ou reformuler le fond\n- Ajouter un frontmatter YAML (`---`)\n- Ajouter des commentaires ou explications\n\nR\u00E9ponds uniquement avec le Markdown nettoy\u00E9 de la page.\n";
|
|
8
22
|
export declare const htmlToMarkdownPrompt = "\nVous \u00EAtes *MarkdownPDF-Pro*, un assistant expert charg\u00E9 de transformer un texte brut issu d\u2019un PDF en un fichier Markdown clair et minimaliste, sans mise en page complexe.\nTu travailles dans le contexte de l'entreprise Pilet & Renaud SA. Tu sais que les proc\u00E9dures proviennent des services suivants: SGC (Gestion de la Client\u00E8le), RH, PPE (copropri\u00E9t\u00E9), g\u00E9rance locataire, direction, comptabilit\u00E9, contentieux, IT.\n\n**But :**\nStructurer le contenu pour qu\u2019il soit imm\u00E9diatement lisible dans n\u2019importe quel \u00E9diteur Markdown, tout en pr\u00E9servant la hi\u00E9rarchie logique (titres, listes, tableaux simples).\n\n**Contraintes globales (IMP\u00C9RATIF) :**\n1. Aucune syntaxe HTML, pas de balises <div>, <span>, etc. \n2. Pas de styles CSS ni de code LaTeX. \n3. Utiliser exclusivement les \u00E9l\u00E9ments Markdown suivants : \n * `#` \u2026 `######` pour les titres \n * listes \u00E0 puces `-` ou num\u00E9rot\u00E9es `1.` \n * blocs de citation `>` si n\u00E9cessaire \n * tableaux au format pipe `| Col 1 | Col 2 |` \n * emphase `*italique*` et `**gras**` \n * Front-matter YAML `---\n ...\n---\n` pour les m\u00E9tadonn\u00E9es.\n4. Ne pas ajouter d\u2019analyse ou de commentaires personnels.\n5. Si le service n\u2019est pas d\u00E9duit du texte, laisser `service: unknown`.\n6. **Les tableaux doivent \u00EAtre d\u00E9tect\u00E9s et normalis\u00E9s selon les \u201CR\u00E8gles de TABLE Markdown\u201D ci-dessous (pas de copie brute ligne par ligne).**\n\nRemplir automatiquement le champ `title` sous la forme `{service} - {action} {object} {b\u00E9n\u00E9ficiaire}` en minuscules **uniquement si ces \u00E9l\u00E9ments sont clairement inf\u00E9r\u00E9s** ; sinon, utiliser le premier `# Titre` du document. Ne jamais \u00E9crire \u201Cunknown\u201D dans `title`.\n\n---\n\n### Heuristique de structuration\n1. **D\u00E9tection des titres** \n * Ligne en MAJUSCULES \u2192 `##` \n * Ligne qui finit par \u00AB : \u00BB \u2192 `###` \n * Saut de ligne double + mot initial capitalis\u00E9 \u2192 `##` si > 3 mots sinon `###`.\n2. **Paragraphes** : regrouper les lignes cons\u00E9cutives jusqu\u2019au prochain saut de ligne vide.\n3. **Listes** \n * Convertir les puces d\u2019origine (`\u2022`, `-`, `*`, chiffres suivis de `)` ou `.`). \n * Conserver l\u2019imbrication (quatre espaces par niveau).\n\n---\n\n### R\u00E8gles de TABLE Markdown (g\u00E9n\u00E9riques, robustes)\n1. **D\u00E9tection d\u2019un bloc tableau** : s\u00E9quence de \u22653 lignes pr\u00E9sentant un alignement r\u00E9gulier (espaces/tabs), des s\u00E9parateurs r\u00E9currents, OU une premi\u00E8re colonne contenant des *identifiants plausibles* (ex. codes, IDs, sigles). \n2. **Colonnes** : inf\u00E9rer le nombre de colonnes en analysant la majorit\u00E9 des lignes. Normaliser toutes les lignes \u00E0 ce nombre (remplir les cellules manquantes par vide). \n3. **En-t\u00EAte** : \n - Si une premi\u00E8re ligne contient des libell\u00E9s de colonnes \u2192 utiliser comme `header`. \n - Sinon, g\u00E9n\u00E9rer un en-t\u00EAte minimal et neutre (`Col 1`, `Col 2`, \u2026) ou, si identifi\u00E9, des noms g\u00E9n\u00E9riques (`Identifiant`, `Libell\u00E9`, `Commentaire`). \n4. **Fusion de lignes cass\u00E9es** : joindre aux cellules la/les lignes qui suivent quand elles sont des continuations (indentation, absence de s\u00E9parateur, hyph\u00E9nation en fin de ligne) pour \u00E9viter de cr\u00E9er de fausses lignes. \n5. **En-t\u00EAtes visuels (cat\u00E9gories)** : lignes isol\u00E9es sans s\u00E9parateurs ni motif tabulaire, servant d\u2019intertitre (ex. cat\u00E9gories, sections) \u2192 les rendre en `###` au-dessus du tableau ou entre deux tableaux, **pas** comme des lignes de donn\u00E9es. \n6. **Identifiants / codes** : d\u00E9tecter les *tokens* de type ID (lettres/chiffres/points/traits). Si pr\u00E9sents en premi\u00E8re colonne, **ne pas trier** ; **pr\u00E9server l\u2019ordre d\u2019origine du PDF**. \n7. **S\u00E9paration multi-tableaux** : d\u00E9marrer un nouveau tableau lorsque le sch\u00E9ma de colonnes change, lorsqu\u2019un intertitre visuel survient, ou apr\u00E8s un blanc significatif. \n8. **Nettoyage des cellules** : trim, consolidation des espaces multiples, \u00E9chapper `|` par `|`, conserver les signes et abr\u00E9viations. \n9. **R\u00E9f\u00E9rences crois\u00E9es non list\u00E9es** : si des identifiants sont mentionn\u00E9s dans une cellule (ex. \u201Cutiliser le 123456\u201D) mais n\u2019apparaissent pas comme ligne d\u00E9di\u00E9e, **ne pas les inventer** dans le tableau ; \u00E0 la place, ajouter apr\u00E8s le tableau une section `#### R\u00E9f\u00E9rences crois\u00E9es non list\u00E9es` avec une liste `- 123456 : contexte (extrait bref)` si utile. \n10. **Validation post-g\u00E9n\u00E9ration** : \n - Chaque tableau doit avoir \u22652 colonnes et \u22652 lignes de donn\u00E9es. \n - V\u00E9rifier la coh\u00E9rence (pas de d\u00E9calage de colonnes, pas de titre de cat\u00E9gorie dans les lignes). \n - Si la structure reste ambig\u00FCe, pr\u00E9f\u00E9rer une **liste structur\u00E9e** plut\u00F4t qu\u2019un tableau incorrect.\n\n---\n\n### Sp\u00E9cifique \u00E0 l'entreprise\n- Les informations v\u00E9ritablement critiques peuvent \u00EAtre signal\u00E9es avec `\u26A0\uFE0F` et `**gras**`, sans r\u00E9\u00E9crire le fond. \n- Mentionner par inf\u00E9rence le `service` **uniquement dans le front-matter** ; le corps doit rester fid\u00E8le \u00E0 la source. \n- Ne pas promouvoir des intertitres visuels en alertes `\u26A0\uFE0F`.\n\n---\n\n### \u00C9l\u00E9ments hors-texte\n- **Images** : `` (une ligne par image, \u00E0 l\u2019emplacement d\u2019origine). \n- **Notes de bas de page** : convertir en liens `[^n]` et regrouper \u00E0 la fin sous `## Notes`.\n\n---\n\n### Nettoyage final\n- Supprimer en-t\u00EAtes/pieds de page r\u00E9p\u00E9t\u00E9s (m\u00EAme s\u00E9quence sur \u2265 3 pages). \n- R\u00E9duire les blancs multiples \u00E0 un seul. \n- Conserver un seul saut de ligne vide entre blocs. \n- Supprimer les sections de revue automatique (ex. \u201Crapport de r\u00E9vision\u2026\u201D) quand d\u00E9tectables.\n\n---\n\n### Format d\u2019entr\u00E9e attendu\n**Proc\u00E9dure :**\n1. Lire attentivement le bloc `<<PDF_TEXT>>`. \n2. Appliquer les heuristiques ci-dessus, \u00E9tape par \u00E9tape (raisonnement interne, ne pas afficher). \n3. Produire un **front-matter** puis le **Markdown final brut**, sans d\u00E9limiteurs et sans commentaires.\n\n**Front-matter (YAML) \u00E0 produire :**\n---\ntitle: {service} - {action} {object} {b\u00E9n\u00E9ficiaire} # en minuscules si inf\u00E9r\u00E9s, sinon premier H1\nservice: {service} # \"unknown\" si non d\u00E9duit\nrole: rule\nowner: {owner}\n---\n\n\n";
|
|
9
23
|
export interface UsecaseExtractionOptions {
|
|
10
24
|
model?: string;
|
package/dist/src/prompts.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.usecaseExtractionPrompt = exports.htmlToMarkdownPrompt = exports.textToMarkdownPrompt = exports.defaultOutputPrompt = exports.transferAgentPromptHandoff = exports.transferAgentPromptStructured = exports.transferAgentPrompt = exports.transferAgentPrompt__ = exports.transferAgentPrompt_ = void 0;
|
|
3
|
+
exports.usecaseExtractionPrompt = exports.htmlToMarkdownPrompt = exports.mupdfPagePrompt = exports.textToMarkdownPrompt = exports.defaultOutputPrompt = exports.transferAgentPromptHandoff = exports.transferAgentPromptStructured = exports.transferAgentPrompt = exports.transferAgentPrompt__ = exports.transferAgentPrompt_ = void 0;
|
|
4
4
|
exports.transferAgentPrompt_ = `Triggers a transfer of the user to a more specialized agent.
|
|
5
5
|
Calls escalate to a more specialized LLM agent or to a human agent, with additional context.
|
|
6
6
|
Only call this function if one of the available agents is appropriate. Don't transfer to your own agent type.
|
|
@@ -149,6 +149,46 @@ Template: Un modèle de lettre est décrit ici.
|
|
|
149
149
|
|
|
150
150
|
${exports.defaultOutputPrompt}
|
|
151
151
|
`;
|
|
152
|
+
/**
|
|
153
|
+
* Per-page digest prompt for `callLLMForParsingPDF` (mupdf path).
|
|
154
|
+
*
|
|
155
|
+
* mupdf already provides GFM tables, bullet lists, and link footers.
|
|
156
|
+
* This prompt only normalises what mupdf leaves raw:
|
|
157
|
+
* - title hierarchy (MAJUSCULES, colon endings)
|
|
158
|
+
* - broken multi-line table cells
|
|
159
|
+
* - repeated page headers / footers
|
|
160
|
+
* - critical-element highlighting
|
|
161
|
+
*
|
|
162
|
+
* **No frontmatter** — the single YAML block is added by the caller (pdf2markdown).
|
|
163
|
+
* **finalReduce** (disabled) is reserved for a future "N-page light summary" feature.
|
|
164
|
+
*/
|
|
165
|
+
exports.mupdfPagePrompt = `
|
|
166
|
+
Tu es *MarkdownPDF-Pro*.
|
|
167
|
+
|
|
168
|
+
Tu reçois le contenu brut d'une page PDF extrait par mupdf : texte et tableaux GFM déjà partiellement structurés.
|
|
169
|
+
|
|
170
|
+
**Mission :** Nettoie et améliore ce contenu en Markdown propre. Ne jamais produire de frontmatter YAML.
|
|
171
|
+
|
|
172
|
+
**Ce que mupdf a déjà fourni (à préserver) :**
|
|
173
|
+
- Tableaux \`| col | col |\` en GFM — ne pas les supprimer, mais possibilité d'améliorer et corriger les cellules cassées.
|
|
174
|
+
- Listes \`-\` détectées dans le PDF
|
|
175
|
+
- Section \`## Liens\` en pied de page si présente
|
|
176
|
+
|
|
177
|
+
**Ce qu'il reste à faire :**
|
|
178
|
+
1. **Titres** : ligne en MAJUSCULES → \`##\` ; ligne finissant par \`:\` → \`###\` ; titre principal évident → \`#\`.
|
|
179
|
+
2. **Fusion de cellules cassées** : si une cellule est coupée sur plusieurs lignes (continuation sans séparateur, hyphénation en fin de ligne), fusionner dans la même cellule.
|
|
180
|
+
3. **En-têtes/pieds répétitifs** : supprimer les lignes qui ne sont que numéro de page, logo ou nom d'entreprise sans apporter de contenu.
|
|
181
|
+
4. **Éléments critiques** : informations véritablement importantes → \`⚠️ **gras**\`.
|
|
182
|
+
5. **Nettoyage** : un seul saut de ligne vide entre blocs ; pas de balises HTML ; conserver signes et abréviations.
|
|
183
|
+
6. **Cohérence des tableaux** : normaliser le nombre de colonnes si des cellules manquent.
|
|
184
|
+
7. **Groupes répétés dans un tableau** : si la même valeur de groupe apparaît dans la première colonne sur plusieurs lignes consécutives (ex. \`Code | ABC | …\`, \`Code | DEF | …\`), il faut simplifier le groupe avec un titre.
|
|
185
|
+
**Ne jamais :**
|
|
186
|
+
- Inventer du contenu ou reformuler le fond
|
|
187
|
+
- Ajouter un frontmatter YAML (\`---\`)
|
|
188
|
+
- Ajouter des commentaires ou explications
|
|
189
|
+
|
|
190
|
+
Réponds uniquement avec le Markdown nettoyé de la page.
|
|
191
|
+
`;
|
|
152
192
|
exports.htmlToMarkdownPrompt = `
|
|
153
193
|
Vous êtes *MarkdownPDF-Pro*, un assistant expert chargé de transformer un texte brut issu d’un PDF en un fichier Markdown clair et minimaliste, sans mise en page complexe.
|
|
154
194
|
Tu travailles dans le contexte de l'entreprise Pilet & Renaud SA. Tu sais que les procédures proviennent des services suivants: SGC (Gestion de la Clientèle), RH, PPE (copropriété), gérance locataire, direction, comptabilité, contentieux, IT.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RAGManagerConfig, RAGRegistryEntry, RAGConfig, ParsedDocument, DocumentQueries } from './types';
|
|
1
|
+
import { RAGManagerConfig, RAGRegistryEntry, RAGConfig, ParsedDocument, RAGMetadata, DocumentQueries } from './types';
|
|
2
2
|
import { Embeddings } from './embeddings';
|
|
3
3
|
/**
|
|
4
4
|
* Gestionnaire multi-RAG stateless pour gérer plusieurs index RAG
|
|
@@ -75,6 +75,7 @@ export declare class RAGManager {
|
|
|
75
75
|
*/
|
|
76
76
|
notifyUpdate(name: string, opts?: {
|
|
77
77
|
action?: 'rename';
|
|
78
|
+
id?: number;
|
|
78
79
|
oldFile?: string;
|
|
79
80
|
newFile?: string;
|
|
80
81
|
}): void;
|
|
@@ -86,6 +87,15 @@ export declare class RAGManager {
|
|
|
86
87
|
* Archive un RAG existant
|
|
87
88
|
*/
|
|
88
89
|
private archive;
|
|
90
|
+
/**
|
|
91
|
+
* Charge la configuration depuis le fichier metadata
|
|
92
|
+
*/
|
|
93
|
+
private readMetadataFile;
|
|
94
|
+
/**
|
|
95
|
+
* Charge les métadonnées d'un RAG sans instancier un nouvel Embeddings.
|
|
96
|
+
* Si le RAG est déjà chargé, réutilise les métadonnées en mémoire.
|
|
97
|
+
*/
|
|
98
|
+
loadMetadata(name: string): RAGMetadata;
|
|
89
99
|
/**
|
|
90
100
|
* Charge la configuration depuis le fichier metadata
|
|
91
101
|
*/
|
|
@@ -107,6 +117,10 @@ export declare class RAGManager {
|
|
|
107
117
|
* @private
|
|
108
118
|
*/
|
|
109
119
|
private saveMapping;
|
|
120
|
+
/**
|
|
121
|
+
* Supprime les fichiers orphelins d'un dossier RAG à partir des documents indexés.
|
|
122
|
+
*/
|
|
123
|
+
private cleanupOrphanFiles;
|
|
110
124
|
/**
|
|
111
125
|
* Nettoie les anciennes archives d'un RAG
|
|
112
126
|
*/
|
|
@@ -311,6 +325,7 @@ export declare class RAGManager {
|
|
|
311
325
|
* Supprime un RAG du registre
|
|
312
326
|
*/
|
|
313
327
|
delete(name: string, archiveFlag?: boolean): Promise<void | Error>;
|
|
328
|
+
private resolveDocumentFilenameById;
|
|
314
329
|
/**
|
|
315
330
|
* Renomme un document dans un RAG existant
|
|
316
331
|
*
|
|
@@ -323,7 +338,7 @@ export declare class RAGManager {
|
|
|
323
338
|
* Et met à jour les références dans les fichiers JSON (mapping, metadata, queries)
|
|
324
339
|
*
|
|
325
340
|
* @param ragName Nom du RAG concerné
|
|
326
|
-
* @param
|
|
341
|
+
* @param oldFileOrId Ancien nom du fichier (ex: 'old-name.md') ou ID documentaire
|
|
327
342
|
* @param newFile Nouveau nom du fichier (ex: 'new-name.md')
|
|
328
343
|
*
|
|
329
344
|
* @example
|
|
@@ -331,7 +346,7 @@ export declare class RAGManager {
|
|
|
331
346
|
* ragManager.renameDocument('rule-validation-1', 'old-procedure.md', 'new-procedure.md');
|
|
332
347
|
* ```
|
|
333
348
|
*/
|
|
334
|
-
renameDocument(ragName: string,
|
|
349
|
+
renameDocument(ragName: string, oldFileOrId: string | number, newFile: string): void;
|
|
335
350
|
/**
|
|
336
351
|
* Met à jour les références d'un fichier dans les JSON du RAG
|
|
337
352
|
*
|
|
@@ -318,13 +318,39 @@ class RAGManager {
|
|
|
318
318
|
/**
|
|
319
319
|
* Charge la configuration depuis le fichier metadata
|
|
320
320
|
*/
|
|
321
|
-
|
|
322
|
-
const metadataFile = path_1.default.join(configPath,
|
|
321
|
+
readMetadataFile(configPath) {
|
|
322
|
+
const metadataFile = path_1.default.join(configPath, types_1.RAG_FILES.METADATA);
|
|
323
323
|
if (!(0, fs_1.existsSync)(metadataFile)) {
|
|
324
324
|
throw new Error(`Fichier metadata manquant: ${metadataFile}`);
|
|
325
325
|
}
|
|
326
326
|
try {
|
|
327
|
-
|
|
327
|
+
return JSON.parse((0, fs_1.readFileSync)(metadataFile, 'utf8'));
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
throw new Error(`Erreur lors du chargement des métadonnées RAG: ${error}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Charge les métadonnées d'un RAG sans instancier un nouvel Embeddings.
|
|
335
|
+
* Si le RAG est déjà chargé, réutilise les métadonnées en mémoire.
|
|
336
|
+
*/
|
|
337
|
+
loadMetadata(name) {
|
|
338
|
+
if (!this.exists(name)) {
|
|
339
|
+
throw new Error(`RAG '${name}' n'existe pas`);
|
|
340
|
+
}
|
|
341
|
+
const embeddingData = this.loadedEmbeddings.get(name);
|
|
342
|
+
if (embeddingData) {
|
|
343
|
+
return embeddingData.embedding.getMetadata();
|
|
344
|
+
}
|
|
345
|
+
const entry = this.getEntry(name);
|
|
346
|
+
return this.readMetadataFile(entry.configPath);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Charge la configuration depuis le fichier metadata
|
|
350
|
+
*/
|
|
351
|
+
loadConfig(configPath) {
|
|
352
|
+
try {
|
|
353
|
+
const metadata = this.readMetadataFile(configPath);
|
|
328
354
|
// Extraire la configuration depuis les métadonnées
|
|
329
355
|
const ragConfig = {
|
|
330
356
|
baseDir: configPath,
|
|
@@ -386,6 +412,41 @@ class RAGManager {
|
|
|
386
412
|
throw new Error(`Échec de la sauvegarde du mapping: ${error}`);
|
|
387
413
|
}
|
|
388
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* Supprime les fichiers orphelins d'un dossier RAG à partir des documents indexés.
|
|
417
|
+
*/
|
|
418
|
+
cleanupOrphanFiles(configPath, metadata) {
|
|
419
|
+
const allowedFiles = new Set([
|
|
420
|
+
types_1.RAG_FILES.VECTORS,
|
|
421
|
+
types_1.RAG_FILES.METADATA,
|
|
422
|
+
types_1.RAG_FILES.MAPPING,
|
|
423
|
+
types_1.RAG_FILES.QUERIES
|
|
424
|
+
]);
|
|
425
|
+
const uniqueFilenames = new Set();
|
|
426
|
+
for (const doc of Object.values(metadata.documents || {})) {
|
|
427
|
+
if (doc.filename) {
|
|
428
|
+
uniqueFilenames.add(doc.filename);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
for (const filename of uniqueFilenames) {
|
|
432
|
+
const baseName = filename.replace(/\.md$/, '');
|
|
433
|
+
allowedFiles.add(filename);
|
|
434
|
+
allowedFiles.add(`${filename}.sha`);
|
|
435
|
+
allowedFiles.add(`${baseName}.enhanced.md`);
|
|
436
|
+
allowedFiles.add(`${baseName}.query.json`);
|
|
437
|
+
}
|
|
438
|
+
for (const fileName of (0, fs_1.readdirSync)(configPath)) {
|
|
439
|
+
const fullPath = path_1.default.join(configPath, fileName);
|
|
440
|
+
if (!allowedFiles.has(fileName)) {
|
|
441
|
+
try {
|
|
442
|
+
(0, fs_1.rmSync)(fullPath, { recursive: true, force: true });
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
console.warn(`⚠️ Erreur suppression fichier orphelin ${fileName}:`, error);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
389
450
|
/**
|
|
390
451
|
* Nettoie les anciennes archives d'un RAG
|
|
391
452
|
*/
|
|
@@ -871,6 +932,7 @@ class RAGManager {
|
|
|
871
932
|
// Sauvegarder les métadonnées et le mapping
|
|
872
933
|
this.saveMetadata(buildPath, metadata);
|
|
873
934
|
this.saveMapping(buildPath, buildResult.mapping, buildResult);
|
|
935
|
+
this.cleanupOrphanFiles(buildPath, metadata);
|
|
874
936
|
// ✅ En mode legacy, on construit directement dans configPath (pas de déplacement)
|
|
875
937
|
if (!isLegacyMode && destDir) {
|
|
876
938
|
// Archiver l'ancienne version si elle existe
|
|
@@ -1012,6 +1074,20 @@ class RAGManager {
|
|
|
1012
1074
|
return error;
|
|
1013
1075
|
}
|
|
1014
1076
|
}
|
|
1077
|
+
resolveDocumentFilenameById(ragName, configPath, documentId) {
|
|
1078
|
+
try {
|
|
1079
|
+
const metadata = this.loadMetadata(ragName);
|
|
1080
|
+
for (const doc of Object.values(metadata.documents || {})) {
|
|
1081
|
+
if (typeof doc.metadata?.id === 'number' && doc.metadata.id === documentId) {
|
|
1082
|
+
return doc.filename;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
catch (error) {
|
|
1087
|
+
console.warn(`⚠️ Erreur lecture metadata pour résolution par ID:`, error);
|
|
1088
|
+
}
|
|
1089
|
+
return undefined;
|
|
1090
|
+
}
|
|
1015
1091
|
/**
|
|
1016
1092
|
* Renomme un document dans un RAG existant
|
|
1017
1093
|
*
|
|
@@ -1024,7 +1100,7 @@ class RAGManager {
|
|
|
1024
1100
|
* Et met à jour les références dans les fichiers JSON (mapping, metadata, queries)
|
|
1025
1101
|
*
|
|
1026
1102
|
* @param ragName Nom du RAG concerné
|
|
1027
|
-
* @param
|
|
1103
|
+
* @param oldFileOrId Ancien nom du fichier (ex: 'old-name.md') ou ID documentaire
|
|
1028
1104
|
* @param newFile Nouveau nom du fichier (ex: 'new-name.md')
|
|
1029
1105
|
*
|
|
1030
1106
|
* @example
|
|
@@ -1032,7 +1108,7 @@ class RAGManager {
|
|
|
1032
1108
|
* ragManager.renameDocument('rule-validation-1', 'old-procedure.md', 'new-procedure.md');
|
|
1033
1109
|
* ```
|
|
1034
1110
|
*/
|
|
1035
|
-
renameDocument(ragName,
|
|
1111
|
+
renameDocument(ragName, oldFileOrId, newFile) {
|
|
1036
1112
|
const entry = this.getEntry(ragName);
|
|
1037
1113
|
if (!entry) {
|
|
1038
1114
|
if (this.config.verbose) {
|
|
@@ -1041,6 +1117,15 @@ class RAGManager {
|
|
|
1041
1117
|
return;
|
|
1042
1118
|
}
|
|
1043
1119
|
const configPath = entry.configPath;
|
|
1120
|
+
const oldFile = typeof oldFileOrId === 'number'
|
|
1121
|
+
? this.resolveDocumentFilenameById(ragName, configPath, oldFileOrId)
|
|
1122
|
+
: oldFileOrId;
|
|
1123
|
+
if (!oldFile) {
|
|
1124
|
+
if (this.config.verbose) {
|
|
1125
|
+
console.log(`⚠️ Aucun document trouvé pour ${typeof oldFileOrId === 'number' ? `id=${oldFileOrId}` : oldFileOrId} dans RAG '${ragName}'`);
|
|
1126
|
+
}
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1044
1129
|
//
|
|
1045
1130
|
// Liste des fichiers à renommer (sans extension pour le basename)
|
|
1046
1131
|
const oldBase = oldFile.replace(/\.md$/, '');
|
|
@@ -1083,6 +1168,7 @@ class RAGManager {
|
|
|
1083
1168
|
// Notifier les embeddings chargés de se mettre à jour avec l'action rename
|
|
1084
1169
|
this.notifyUpdate(ragName, {
|
|
1085
1170
|
action: 'rename',
|
|
1171
|
+
id: typeof oldFileOrId === 'number' ? oldFileOrId : undefined,
|
|
1086
1172
|
oldFile,
|
|
1087
1173
|
newFile
|
|
1088
1174
|
});
|
|
@@ -423,6 +423,9 @@ const defaultConfig = (tempDir) => {
|
|
|
423
423
|
instance: (0, simple_git_1.default)({ baseDir: tempDir, binary: 'git' }),
|
|
424
424
|
repoPath: tempDir,
|
|
425
425
|
uploadPath: (0, path_1.join)(tempDir, '.assets'), // Add uploadPath to default config
|
|
426
|
+
// FIXME(test-config): inject a dedicated fake remote when we need remote-aware E2E coverage.
|
|
427
|
+
// Empty string explicitly disables inheriting GIT_REMOTE_URL from the developer environment.
|
|
428
|
+
remoteUrl: '',
|
|
426
429
|
draftBranch: 'rule-editor',
|
|
427
430
|
mainBranch: 'main',
|
|
428
431
|
validationPrefix: 'rule-validation-',
|
|
@@ -27,6 +27,7 @@ class GitHealthManager {
|
|
|
27
27
|
async repairRepository(branch) {
|
|
28
28
|
// CRITICAL FIX: Checkout sur la branche cible avant forceRepair()
|
|
29
29
|
// Sinon ensureSafeBranch() vérifie la branche courante (qui peut être 'main')
|
|
30
|
+
const failedBranches = [];
|
|
30
31
|
try {
|
|
31
32
|
await this.git.checkout(branch);
|
|
32
33
|
}
|
|
@@ -197,6 +198,7 @@ class GitHealthManager {
|
|
|
197
198
|
*/
|
|
198
199
|
async syncValidationBranches() {
|
|
199
200
|
const { validationPrefix } = this.config;
|
|
201
|
+
const failedBranches = [];
|
|
200
202
|
if (!validationPrefix) {
|
|
201
203
|
console.error("Erreur: Le préfixe de validation n'est pas configuré.");
|
|
202
204
|
return;
|
|
@@ -218,6 +220,9 @@ class GitHealthManager {
|
|
|
218
220
|
for (const branch of validationBranches) {
|
|
219
221
|
try {
|
|
220
222
|
await this.repairRepository(branch);
|
|
223
|
+
// Synchroniser explicitement la branche de validation avec son mergeBase.
|
|
224
|
+
// gitSyncPR préserve metadata.files et ne doit pas regonfler le scope PR.
|
|
225
|
+
await (0, git_1.gitSyncPR)(this.git, branch, user);
|
|
221
226
|
}
|
|
222
227
|
catch (error) {
|
|
223
228
|
console.warn(`⚠️ Échec de la synchronisation de ${branch}: ${error.message}`);
|
|
@@ -257,7 +262,8 @@ class GitHealthManager {
|
|
|
257
262
|
catch (repairError) {
|
|
258
263
|
console.error(`❌ Impossible de réparer ${branch}: ${repairError.message}`);
|
|
259
264
|
console.error(`📝 Cette branche nécessite une intervention manuelle`);
|
|
260
|
-
|
|
265
|
+
failedBranches.push(branch);
|
|
266
|
+
continue;
|
|
261
267
|
}
|
|
262
268
|
}
|
|
263
269
|
}
|
|
@@ -285,6 +291,9 @@ class GitHealthManager {
|
|
|
285
291
|
console.warn(`⚠️ Erreur lors du retour à la branche originale: ${error}`);
|
|
286
292
|
}
|
|
287
293
|
}
|
|
294
|
+
if (failedBranches.length > 0) {
|
|
295
|
+
console.warn(`⚠️ Synchronisation incomplète (${failedBranches.length} branche(s) ignorée(s)): ${failedBranches.join(', ')}`);
|
|
296
|
+
}
|
|
288
297
|
}
|
|
289
298
|
/**
|
|
290
299
|
* Migrates Git notes from the first commit of validation branches to the last one.
|
|
@@ -299,7 +308,7 @@ class GitHealthManager {
|
|
|
299
308
|
* @returns GitPrNoteMigrationReport avec le nombre de notes migrées, déjà OK, et perdues
|
|
300
309
|
*/
|
|
301
310
|
async migrateNotes() {
|
|
302
|
-
const {
|
|
311
|
+
const { draftBranch, validationPrefix, gitNotes } = this.config;
|
|
303
312
|
if (!validationPrefix || !gitNotes.namespace) {
|
|
304
313
|
console.error("Erreur: Le préfixe de validation ou le namespace des notes n'est pas configuré.");
|
|
305
314
|
return { migrated: 0, alreadyOk: 0, lost: [] };
|
|
@@ -315,22 +324,30 @@ class GitHealthManager {
|
|
|
315
324
|
for (const branch of validationBranches) {
|
|
316
325
|
try {
|
|
317
326
|
const lastCommit = (await this.git.revparse(branch)).trim();
|
|
327
|
+
const prNumber = parseInt(branch.split('-').pop() || '0', 10);
|
|
318
328
|
// 1. Check if note is already on the last commit
|
|
319
329
|
const noteOnHead = await (0, git_1.gitReadNote)(this.git, lastCommit, gitNotes.namespace, 1);
|
|
320
330
|
if (noteOnHead) {
|
|
321
|
-
|
|
322
|
-
|
|
331
|
+
// Valider que la note HEAD correspond bien à la PR de la branche.
|
|
332
|
+
// Sinon, continuer la recherche dans l'historique pour éviter les faux "already OK".
|
|
333
|
+
if (noteOnHead.id === prNumber) {
|
|
334
|
+
alreadyOkCount++;
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
console.log(` ⚠️ ${branch}: Note HEAD id=${noteOnHead.id}, attendu id=${prNumber} - recherche historique`);
|
|
323
338
|
}
|
|
324
339
|
// 2. Scan the branch history for a note
|
|
325
|
-
|
|
340
|
+
// IMPORTANT:
|
|
341
|
+
// Les branches de validation sont créées depuis rule-editor (draftBranch),
|
|
342
|
+
// pas depuis main. Utiliser main élargit artificiellement l'historique scanné
|
|
343
|
+
// et peut faire remonter des notes d'autres PR (faux positifs).
|
|
344
|
+
const mergeBase = (await this.git.raw('merge-base', draftBranch, branch)).trim();
|
|
326
345
|
if (!mergeBase) {
|
|
327
|
-
console.log(`⚠️ ${branch}: Impossible de trouver un point de merge avec '${
|
|
346
|
+
console.log(`⚠️ ${branch}: Impossible de trouver un point de merge avec '${draftBranch}'. Ignoré.`);
|
|
328
347
|
continue;
|
|
329
348
|
}
|
|
330
349
|
const revListOutput = await this.git.raw('rev-list', `${mergeBase}..${branch}`);
|
|
331
350
|
const branchCommits = revListOutput.split('\n').filter(Boolean);
|
|
332
|
-
// Extract PR number from branch name (e.g., rule-validation-31 -> 31)
|
|
333
|
-
const prNumber = parseInt(branch.split('-').pop() || '0', 10);
|
|
334
351
|
let oldNoteCommit = null;
|
|
335
352
|
for (const commitHash of branchCommits) {
|
|
336
353
|
const note = await (0, git_1.gitReadNote)(this.git, commitHash, gitNotes.namespace, 1);
|
|
@@ -424,15 +441,20 @@ class GitHealthManager {
|
|
|
424
441
|
// Lister tous les fichiers .md dans la branche
|
|
425
442
|
const allFiles = await (0, git_1.gitListFilesInBranch)(this.git, branchName);
|
|
426
443
|
const mdFiles = allFiles.filter(f => f.endsWith('.md'));
|
|
444
|
+
const seenIDs = new Map();
|
|
427
445
|
// Variables pour suivre les problèmes dans cette branche
|
|
428
446
|
let branchHasIssues = false;
|
|
429
447
|
const branchIssues = [];
|
|
430
448
|
for (const file of mdFiles) {
|
|
431
449
|
report.scanned++;
|
|
432
450
|
try {
|
|
433
|
-
//
|
|
434
|
-
|
|
435
|
-
const
|
|
451
|
+
// Lecture fraîche depuis Git: évite les faux positifs liés à un cache matter obsolète.
|
|
452
|
+
const fileData = await (0, git_1.gitGetFileContent)(this.git, file, branchName);
|
|
453
|
+
const parsed = (0, utils_matter_1.matterParse)(fileData?.content || '');
|
|
454
|
+
const matter = {
|
|
455
|
+
id: parsed.matter?.id,
|
|
456
|
+
title: parsed.matter?.title
|
|
457
|
+
};
|
|
436
458
|
// Vérification de l'ID et du titre
|
|
437
459
|
let hasValidID = false;
|
|
438
460
|
let hasValidTitle = false;
|
|
@@ -449,19 +471,15 @@ class GitHealthManager {
|
|
|
449
471
|
}
|
|
450
472
|
else {
|
|
451
473
|
hasValidID = true;
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
474
|
+
const firstFile = seenIDs.get(matter.id);
|
|
475
|
+
if (firstFile && firstFile !== file) {
|
|
476
|
+
report.duplicateID.push(`${fileRef} (id=${matter.id})`);
|
|
477
|
+
hasValidID = false;
|
|
478
|
+
branchHasIssues = true;
|
|
479
|
+
branchIssues.push(` ⚠️ ${file}: ID ${matter.id} déjà utilisé`);
|
|
456
480
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
// Compter les doublons mais ne pas les ajouter aux erreurs (seront réparés)
|
|
460
|
-
report.duplicateID.push(`${fileRef} (id=${matter.id})`);
|
|
461
|
-
hasValidID = false;
|
|
462
|
-
branchHasIssues = true;
|
|
463
|
-
branchIssues.push(` ⚠️ ${file}: ID ${matter.id} déjà utilisé`);
|
|
464
|
-
}
|
|
481
|
+
else {
|
|
482
|
+
seenIDs.set(matter.id, file);
|
|
465
483
|
}
|
|
466
484
|
}
|
|
467
485
|
// Vérification du titre
|
|
@@ -551,42 +569,38 @@ class GitHealthManager {
|
|
|
551
569
|
];
|
|
552
570
|
let fixed = 0;
|
|
553
571
|
const fixedFiles = new Set();
|
|
554
|
-
|
|
555
|
-
// Cela permet à getFileID() de trouver l'ID existant du fichier
|
|
572
|
+
const usedIdsByBranch = new Map();
|
|
556
573
|
if (!dryRun && filesToFix.length > 0) {
|
|
557
|
-
|
|
558
|
-
const allBranches = await (0, git_1.gitGetAllBranches)(this.git);
|
|
559
|
-
const uniqueFiles = new Set();
|
|
574
|
+
const uniqueBranches = new Set();
|
|
560
575
|
for (const fileRef of filesToFix) {
|
|
561
|
-
const [
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
// Mettre en cache la liste des fichiers par branche pour éviter les appels répétés
|
|
566
|
-
const branchFilesCache = new Map();
|
|
567
|
-
for (const branch of allBranches) {
|
|
568
|
-
try {
|
|
569
|
-
const filesInBranch = await (0, git_1.gitListFilesInBranch)(this.git, branch);
|
|
570
|
-
branchFilesCache.set(branch, filesInBranch);
|
|
571
|
-
}
|
|
572
|
-
catch (e) {
|
|
573
|
-
// Ignorer silencieusement les erreurs (branche peut être inaccessible)
|
|
574
|
-
branchFilesCache.set(branch, []);
|
|
576
|
+
const [branchName] = fileRef.split(':');
|
|
577
|
+
if (branchName) {
|
|
578
|
+
uniqueBranches.add(branchName);
|
|
575
579
|
}
|
|
576
580
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
581
|
+
for (const branchName of uniqueBranches) {
|
|
582
|
+
const usedIds = new Set();
|
|
583
|
+
try {
|
|
584
|
+
const filesInBranch = await (0, git_1.gitListFilesInBranch)(this.git, branchName);
|
|
585
|
+
const mdFiles = filesInBranch.filter((f) => f.endsWith('.md'));
|
|
586
|
+
for (const file of mdFiles) {
|
|
587
|
+
try {
|
|
588
|
+
const data = await (0, git_1.gitGetFileContent)(this.git, file, branchName);
|
|
589
|
+
const parsed = (0, utils_matter_1.matterParse)(data?.content || '');
|
|
590
|
+
const id = parsed.matter?.id;
|
|
591
|
+
if (typeof id === 'number' && id > 999) {
|
|
592
|
+
usedIds.add(id);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
catch {
|
|
596
|
+
// Ignorer les fichiers inaccessibles ponctuellement
|
|
584
597
|
}
|
|
585
|
-
}
|
|
586
|
-
catch (e) {
|
|
587
|
-
// Ignorer silencieusement les erreurs (fichier peut ne pas exister)
|
|
588
598
|
}
|
|
589
599
|
}
|
|
600
|
+
catch {
|
|
601
|
+
// Ignorer les branches inaccessibles ponctuellement
|
|
602
|
+
}
|
|
603
|
+
usedIdsByBranch.set(branchName, usedIds);
|
|
590
604
|
}
|
|
591
605
|
}
|
|
592
606
|
for (const fileRef of filesToFix) {
|
|
@@ -608,12 +622,29 @@ class GitHealthManager {
|
|
|
608
622
|
// Parser le matter complet
|
|
609
623
|
const parsed = (0, utils_matter_1.matterParse)(fileData.content);
|
|
610
624
|
const { matter: fullMatter, content } = parsed;
|
|
611
|
-
// ✅ CORRECTION: En cas de doublon détecté, éviter la réutilisation via cache fichier
|
|
612
|
-
// pour forcer une réallocation d'ID. Sinon, garder la logique d'identité stable.
|
|
613
625
|
const isDuplicateIssue = report.duplicateID.some((entry) => entry.startsWith(fileRef));
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
626
|
+
// Convergence stable:
|
|
627
|
+
// - Cas normal: arbitrage par fichier (branch+file) pour réutiliser l'ID stable.
|
|
628
|
+
// - Cas doublon réel: allouer un nouvel ID libre dans la branche et l'enregistrer explicitement.
|
|
629
|
+
let matterWithID;
|
|
630
|
+
if (isDuplicateIssue) {
|
|
631
|
+
const usedIds = usedIdsByBranch.get(branchName) || new Set();
|
|
632
|
+
let nextId = Math.max(1000, ...Array.from(usedIds)) + 20;
|
|
633
|
+
while (usedIds.has(nextId)) {
|
|
634
|
+
nextId += 20;
|
|
635
|
+
}
|
|
636
|
+
fullMatter.id = nextId;
|
|
637
|
+
if (!fullMatter.title || fullMatter.title.trim() === '') {
|
|
638
|
+
fullMatter.title = file.replace(/\.md$/, '');
|
|
639
|
+
}
|
|
640
|
+
(0, git_1.gitRegisterExistingID)(fullMatter, branchName, file, this.config);
|
|
641
|
+
usedIds.add(nextId);
|
|
642
|
+
usedIdsByBranch.set(branchName, usedIds);
|
|
643
|
+
matterWithID = fullMatter;
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
matterWithID = (0, git_1.gitEnsureMatterID)(fullMatter, this.config, branchName, file);
|
|
647
|
+
}
|
|
617
648
|
if (matterWithID.id !== fullMatter.id) {
|
|
618
649
|
if (fullMatter.id && fullMatter.id > 999) {
|
|
619
650
|
console.log(` 🔧 ${file}: ID ${fullMatter.id} remplacé par ID existant ${matterWithID.id}`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { lock, unlock, gitLoad, isValidInt, gitIsFileMerged, gitLastCommit, gitListFilesInBranch, gitListFilesOutsideRepo, gitFileExistsInBranch, gitGetFilesSummary, gitGetFileContent, gitGetFilePreview, gitGetFileHistory, gitReadFileOutsideRepo, gitGetUnmergedBranchesForFile, gitGetAllBranches, gitGetDiffFiles, gitReadNote, gitWriteNote, gitDeleteNote, } from './repo.tools';
|
|
2
|
-
export { gitInit, gitEnsureRepositoryConfiguration, gitEnsureRemoteConfiguration, gitSetupRepository, gitShowConfiguration, gitCheckConfiguration, gitGetBranchHealth, gitGetValidationBranchHealth, gitCreateOrEditFile,
|
|
2
|
+
export { gitInit, gitEnsureRepositoryConfiguration, gitEnsureRemoteConfiguration, gitSetupRepository, gitShowConfiguration, gitCheckConfiguration, gitGetBranchHealth, gitGetValidationBranchHealth, gitCreateOrEditFile, gitUpdateMatter, gitRenameFile, gitDeleteFile, gitEditFile, gitGenerateNextID, gitAllocateNextPRNumber, gitFileStrictMatter, gitGetMatterCache, gitSetMatterCache, GitDocumentMutationOptions, gitReloadIDRegistry, gitRegisterExistingID, gitEnsureMatterID, gitIDRegistryExists, gitIDRegistryRename, } from './repo';
|
|
3
3
|
export { gitSyncPR, gitIsPRClosed, gitIsPRClosedRobust, gitGetPRMetadata, gitGetAllPR, gitGetClosedPRs, gitLoadPR, gitPRUpdateComments, gitClosePR, gitClosePRRobust, gitNewValidationRequest, gitNewPR, } from './repo.pr';
|
|
4
4
|
export * from './git.e2e.helper';
|
|
5
5
|
export * from './git.health';
|