agentic-api 2.0.314 → 2.0.585
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/README.md +37 -34
- package/dist/src/agents/prompts.d.ts +1 -1
- package/dist/src/agents/prompts.js +9 -7
- package/dist/src/agents/reducer.core.js +2 -2
- package/dist/src/agents/simulator.d.ts +33 -4
- package/dist/src/agents/simulator.dashboard.d.ts +140 -0
- package/dist/src/agents/simulator.dashboard.js +344 -0
- package/dist/src/agents/simulator.executor.d.ts +9 -3
- package/dist/src/agents/simulator.executor.js +43 -17
- package/dist/src/agents/simulator.js +103 -19
- package/dist/src/agents/simulator.prompts.d.ts +9 -8
- package/dist/src/agents/simulator.prompts.js +68 -62
- package/dist/src/agents/simulator.types.d.ts +39 -4
- package/dist/src/agents/simulator.utils.d.ts +22 -1
- package/dist/src/agents/simulator.utils.js +27 -2
- package/dist/src/execute/helpers.d.ts +75 -0
- package/dist/src/execute/helpers.js +139 -0
- package/dist/src/execute/index.d.ts +11 -0
- package/dist/src/execute/index.js +44 -0
- package/dist/src/execute/legacy.d.ts +46 -0
- package/dist/src/{execute.js → execute/legacy.js} +130 -232
- package/dist/src/execute/modelconfig.d.ts +29 -0
- package/dist/src/execute/modelconfig.js +72 -0
- package/dist/src/execute/responses.d.ts +55 -0
- package/dist/src/execute/responses.js +595 -0
- package/dist/src/execute/shared.d.ts +83 -0
- package/dist/src/execute/shared.js +188 -0
- package/dist/src/index.d.ts +5 -1
- package/dist/src/index.js +21 -2
- package/dist/src/llm/config.d.ts +25 -0
- package/dist/src/llm/config.js +38 -0
- package/dist/src/llm/index.d.ts +48 -0
- package/dist/src/llm/index.js +115 -0
- package/dist/src/llm/openai.d.ts +6 -0
- package/dist/src/llm/openai.js +154 -0
- package/dist/src/llm/pricing.d.ts +26 -0
- package/dist/src/llm/pricing.js +129 -0
- package/dist/src/llm/xai.d.ts +17 -0
- package/dist/src/llm/xai.js +90 -0
- package/dist/src/pricing.llm.d.ts +3 -15
- package/dist/src/pricing.llm.js +10 -230
- package/dist/src/prompts.d.ts +0 -1
- package/dist/src/prompts.js +51 -118
- package/dist/src/rag/embeddings.d.ts +5 -1
- package/dist/src/rag/embeddings.js +23 -7
- package/dist/src/rag/parser.js +1 -1
- package/dist/src/rag/rag.manager.d.ts +33 -2
- package/dist/src/rag/rag.manager.js +159 -61
- package/dist/src/rag/types.d.ts +2 -0
- package/dist/src/rag/usecase.js +8 -11
- package/dist/src/rules/git/git.e2e.helper.js +21 -2
- package/dist/src/rules/git/git.health.d.ts +4 -2
- package/dist/src/rules/git/git.health.js +113 -16
- package/dist/src/rules/git/index.d.ts +1 -1
- package/dist/src/rules/git/index.js +3 -2
- package/dist/src/rules/git/repo.d.ts +57 -7
- package/dist/src/rules/git/repo.js +326 -39
- package/dist/src/rules/git/repo.pr.d.ts +8 -0
- package/dist/src/rules/git/repo.pr.js +161 -13
- package/dist/src/rules/git/repo.tools.d.ts +5 -1
- package/dist/src/rules/git/repo.tools.js +54 -7
- package/dist/src/rules/types.d.ts +25 -0
- package/dist/src/rules/utils.matter.d.ts +0 -20
- package/dist/src/rules/utils.matter.js +58 -81
- package/dist/src/scrapper.js +3 -2
- package/dist/src/stategraph/stategraph.d.ts +26 -1
- package/dist/src/stategraph/stategraph.js +43 -2
- package/dist/src/stategraph/stategraph.storage.js +4 -0
- package/dist/src/stategraph/types.d.ts +5 -0
- package/dist/src/types.d.ts +42 -7
- package/dist/src/types.js +8 -7
- package/dist/src/usecase.js +1 -1
- package/dist/src/utils.d.ts +0 -8
- package/dist/src/utils.js +26 -29
- package/package.json +9 -7
- package/dist/src/execute.d.ts +0 -63
|
@@ -53,12 +53,19 @@ class RAGManager {
|
|
|
53
53
|
DEFAULT_RAG_INSTANCE[defaultName] = this;
|
|
54
54
|
}
|
|
55
55
|
static get(config) {
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
// Normaliser le baseDir pour éviter les doublons dus aux trailing slashes
|
|
57
|
+
// ou aux variations de paths (relatif/absolu, symlinks, etc.)
|
|
58
|
+
const normalizedBaseDir = path_1.default.resolve(config.baseDir).replace(/\/+$/, '');
|
|
59
|
+
// Vérifier si une instance existe déjà pour ce baseDir normalisé
|
|
60
|
+
if (DEFAULT_RAG_INSTANCE[normalizedBaseDir]) {
|
|
61
|
+
return DEFAULT_RAG_INSTANCE[normalizedBaseDir];
|
|
62
|
+
}
|
|
63
|
+
// Créer une nouvelle instance avec le baseDir normalisé
|
|
64
|
+
const normalizedConfig = {
|
|
65
|
+
...config,
|
|
66
|
+
baseDir: normalizedBaseDir
|
|
67
|
+
};
|
|
68
|
+
return new RAGManager(normalizedConfig);
|
|
62
69
|
}
|
|
63
70
|
/**
|
|
64
71
|
* Nettoie le cache des instances (utile pour les tests)
|
|
@@ -231,31 +238,40 @@ class RAGManager {
|
|
|
231
238
|
*/
|
|
232
239
|
notifyUpdate(name, opts) {
|
|
233
240
|
const embeddingData = this.loadedEmbeddings.get(name);
|
|
241
|
+
console.log(`\n🔔 notifyUpdate('${name}') appelé`);
|
|
242
|
+
console.log(` Embedding en cache: ${embeddingData ? 'OUI' : 'NON'}`);
|
|
243
|
+
// Si l'embedding n'est pas encore chargé, rien à faire
|
|
244
|
+
// Il sera chargé avec la nouvelle config au prochain load()
|
|
234
245
|
if (!embeddingData) {
|
|
235
|
-
|
|
236
|
-
console.log(`ℹ️ Aucun embedding chargé pour '${name}'`);
|
|
246
|
+
console.log(` ⏭️ Skip: sera chargé avec la nouvelle config au prochain load()`);
|
|
237
247
|
return;
|
|
238
248
|
}
|
|
249
|
+
// Charger la nouvelle configuration depuis le registre
|
|
250
|
+
const entry = this.getEntry(name);
|
|
251
|
+
if (!entry) {
|
|
252
|
+
console.log(` ⚠️ Entry '${name}' introuvable dans le registre`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
console.log(` 📁 entry.configPath: ${entry.configPath}`);
|
|
256
|
+
// L'embedding est chargé, forcer son rechargement via update()
|
|
257
|
+
// update() modifie l'instance EN PLACE, donc tous ceux qui ont une référence voient la nouvelle version
|
|
239
258
|
const { embedding } = embeddingData;
|
|
240
259
|
try {
|
|
241
|
-
// Charger la nouvelle configuration
|
|
242
|
-
const entry = this.getEntry(name);
|
|
243
|
-
if (!entry) {
|
|
244
|
-
throw new Error(`RAG '${name}' n'existe plus dans le registre`);
|
|
245
|
-
}
|
|
246
260
|
const newConfig = this.loadConfig(entry.configPath);
|
|
261
|
+
console.log(` 🔄 Appel de embedding.update() avec baseDir: ${newConfig.baseDir}`);
|
|
247
262
|
// FIXME: Vérifier si l'embedding est busy avant de faire l'update
|
|
248
|
-
//
|
|
263
|
+
// update() va recharger l'index, les métadonnées et le mapping depuis newConfig.baseDir
|
|
264
|
+
// update() modifie l'instance en place, donc tous les utilisateurs voient la nouvelle version
|
|
249
265
|
embedding.update(newConfig);
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
console.log(`✅ Embedding '${name}' notifié de la mise à jour (${action})`);
|
|
255
|
-
}
|
|
266
|
+
// Mettre à jour le timestamp du cache
|
|
267
|
+
embeddingData.loadTime = Date.now();
|
|
268
|
+
console.log(` ✅ Embedding rechargé avec succès`);
|
|
269
|
+
console.log(` ✅ embedding.isReady(): ${embedding.isReady()}`);
|
|
256
270
|
}
|
|
257
271
|
catch (error) {
|
|
258
|
-
console.error(
|
|
272
|
+
console.error(` ❌ Erreur lors de la mise à jour de l'embedding '${name}':`, error);
|
|
273
|
+
// En cas d'erreur, update() a déjà fait un rollback de sa propre config
|
|
274
|
+
// L'instance continue de fonctionner avec l'ancienne config
|
|
259
275
|
// Ne pas throw pour éviter de bloquer le flow
|
|
260
276
|
}
|
|
261
277
|
}
|
|
@@ -486,18 +502,31 @@ class RAGManager {
|
|
|
486
502
|
if (!entry) {
|
|
487
503
|
throw new Error(`RAG '${name}' n'existe pas`);
|
|
488
504
|
}
|
|
489
|
-
// if (status.status !== 'building') {
|
|
490
|
-
// throw new Error(`RAG '${name}' n'est pas en construction (status: ${status.status})`);
|
|
491
|
-
// }
|
|
492
505
|
const buildPath = entry.configPath;
|
|
493
506
|
if (!(0, fs_1.existsSync)(buildPath)) {
|
|
494
507
|
throw new Error(`Le dossier de construction n'existe pas: ${buildPath}`);
|
|
495
508
|
}
|
|
496
509
|
const filePath = path_1.default.join(buildPath, filename);
|
|
497
|
-
|
|
498
|
-
//
|
|
510
|
+
// ✅ Sauvegarder le fichier source uniquement
|
|
511
|
+
// Le .sha est écrit par markDocumentProcessed() APRÈS génération de .enhanced.md et .query.json
|
|
499
512
|
(0, fs_1.writeFileSync)(filePath, content, 'utf8');
|
|
500
|
-
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Marque un document comme entièrement traité (enhanced + query.json générés)
|
|
516
|
+
* Le .sha est écrit APRÈS le traitement complet pour garantir la cohérence
|
|
517
|
+
*
|
|
518
|
+
* @param name Nom du RAG
|
|
519
|
+
* @param filename Nom du fichier (ex: 'procedure.md')
|
|
520
|
+
* @param content Contenu du document traité
|
|
521
|
+
*/
|
|
522
|
+
markDocumentProcessed(name, filename, content) {
|
|
523
|
+
const entry = this.getEntry(name);
|
|
524
|
+
if (!entry) {
|
|
525
|
+
throw new Error(`RAG '${name}' n'existe pas`);
|
|
526
|
+
}
|
|
527
|
+
const buildPath = entry.configPath;
|
|
528
|
+
const shaPath = path_1.default.join(buildPath, filename + '.sha');
|
|
529
|
+
// ✅ Sauvegarder le fingerprint pour marquer le document comme traité
|
|
501
530
|
const sha = (0, usecase_1.calculateFingerprint)(content);
|
|
502
531
|
(0, fs_1.writeFileSync)(shaPath, sha, 'utf8');
|
|
503
532
|
}
|
|
@@ -677,6 +706,72 @@ class RAGManager {
|
|
|
677
706
|
const queryFile = baseName + '.query.json';
|
|
678
707
|
return (0, usecase_1.loadUseCases)(queryFile, { baseDir: configPath });
|
|
679
708
|
}
|
|
709
|
+
/**
|
|
710
|
+
* Charge tous les use cases de tous les documents d'un RAG
|
|
711
|
+
*
|
|
712
|
+
* Utilise le fichier rag-metadata.json pour obtenir la liste des documents valides
|
|
713
|
+
* puis charge chaque fichier .query.json correspondant
|
|
714
|
+
*
|
|
715
|
+
* @param name Nom du RAG
|
|
716
|
+
* @returns Objet contenant tous les use cases par document
|
|
717
|
+
*
|
|
718
|
+
* @example
|
|
719
|
+
* ```typescript
|
|
720
|
+
* const allQueries = ragManager.loadAllUseCases('my-rag');
|
|
721
|
+
* console.log(`${Object.keys(allQueries.documents).length} documents avec use cases`);
|
|
722
|
+
* console.log(`${allQueries.totalQueries} questions au total`);
|
|
723
|
+
* ```
|
|
724
|
+
*/
|
|
725
|
+
loadAllUseCases(name) {
|
|
726
|
+
const entry = this.getEntry(name);
|
|
727
|
+
if (!entry) {
|
|
728
|
+
throw new Error(`RAG '${name}' n'existe pas`);
|
|
729
|
+
}
|
|
730
|
+
//
|
|
731
|
+
// Charger le metadata pour obtenir la liste des documents
|
|
732
|
+
const metadataFile = path_1.default.join(entry.configPath, types_1.RAG_FILES.METADATA);
|
|
733
|
+
if (!(0, fs_1.existsSync)(metadataFile)) {
|
|
734
|
+
throw new Error(`Fichier metadata manquant: ${metadataFile}`);
|
|
735
|
+
}
|
|
736
|
+
let metadata;
|
|
737
|
+
try {
|
|
738
|
+
metadata = JSON.parse((0, fs_1.readFileSync)(metadataFile, 'utf8'));
|
|
739
|
+
}
|
|
740
|
+
catch (error) {
|
|
741
|
+
throw new Error(`Erreur lors du chargement du metadata: ${error}`);
|
|
742
|
+
}
|
|
743
|
+
//
|
|
744
|
+
// Extraire les filenames uniques depuis metadata.documents
|
|
745
|
+
const uniqueFilenames = new Set();
|
|
746
|
+
for (const docRef of Object.values(metadata.documents)) {
|
|
747
|
+
if (docRef.filename) {
|
|
748
|
+
uniqueFilenames.add(docRef.filename);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
//
|
|
752
|
+
// Charger les use cases pour chaque document
|
|
753
|
+
const documents = {};
|
|
754
|
+
let totalQueries = 0;
|
|
755
|
+
const filenames = [];
|
|
756
|
+
for (const filename of uniqueFilenames) {
|
|
757
|
+
const baseName = filename.replace(/\.md$/, '');
|
|
758
|
+
const queryFile = path_1.default.join(entry.configPath, baseName + '.query.json');
|
|
759
|
+
if ((0, fs_1.existsSync)(queryFile)) {
|
|
760
|
+
try {
|
|
761
|
+
const queries = JSON.parse((0, fs_1.readFileSync)(queryFile, 'utf8'));
|
|
762
|
+
documents[filename] = queries;
|
|
763
|
+
totalQueries += queries.queries?.length || 0;
|
|
764
|
+
filenames.push(filename);
|
|
765
|
+
}
|
|
766
|
+
catch (error) {
|
|
767
|
+
//
|
|
768
|
+
// Log l'erreur mais continue avec les autres documents
|
|
769
|
+
console.warn(`⚠️ Erreur chargement use cases pour ${filename}:`, error.message);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return { documents, totalQueries };
|
|
774
|
+
}
|
|
680
775
|
/**
|
|
681
776
|
* Construit un RAG de manière atomique
|
|
682
777
|
*
|
|
@@ -777,6 +872,9 @@ class RAGManager {
|
|
|
777
872
|
this.cachedRegistry = { ...registry };
|
|
778
873
|
this.saveRegistry(registry);
|
|
779
874
|
console.log(`✅ RAG '${name}' construit avec succès`);
|
|
875
|
+
console.log(` 📁 configPath final: ${entry.configPath}`);
|
|
876
|
+
console.log(` 📦 Embeddings chargés en cache: ${this.loadedEmbeddings.size}`);
|
|
877
|
+
console.log(` 🔍 Embedding '${name}' dans le cache: ${this.loadedEmbeddings.has(name) ? 'OUI' : 'NON'}`);
|
|
780
878
|
// Notifier les embeddings chargés de se mettre à jour
|
|
781
879
|
this.notifyUpdate(name);
|
|
782
880
|
}
|
|
@@ -869,28 +967,34 @@ class RAGManager {
|
|
|
869
967
|
*/
|
|
870
968
|
async delete(name, archiveFlag = true) {
|
|
871
969
|
if (!this.exists(name)) {
|
|
872
|
-
|
|
970
|
+
console.warn(`⚠️ RAG '${name}' n'existe pas, skip delete`);
|
|
971
|
+
return new Error(`RAG '${name}' n'existe pas`);
|
|
873
972
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
(0, fs_1.
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
973
|
+
try {
|
|
974
|
+
const entry = this.getEntry(name);
|
|
975
|
+
const registry = this.getRegistry();
|
|
976
|
+
// Archiver si demandé
|
|
977
|
+
if (archiveFlag) {
|
|
978
|
+
await this.archive(name);
|
|
979
|
+
}
|
|
980
|
+
else if ((0, fs_1.existsSync)(entry.configPath)) {
|
|
981
|
+
(0, fs_1.rmSync)(entry.configPath, { recursive: true, force: true });
|
|
982
|
+
}
|
|
983
|
+
// Supprimer du registre
|
|
984
|
+
delete registry.registries[name];
|
|
985
|
+
// Supprimer de la map des embeddings chargés
|
|
986
|
+
this.loadedEmbeddings.delete(name);
|
|
987
|
+
// Changer le défaut si nécessaire
|
|
988
|
+
if (registry.defaultName === name) {
|
|
989
|
+
const remainingRAGs = Object.keys(registry.registries);
|
|
990
|
+
registry.defaultName = remainingRAGs.length > 0 ? remainingRAGs[0] : 'RAGRegistry';
|
|
991
|
+
}
|
|
992
|
+
this.saveRegistry(registry);
|
|
993
|
+
console.log(`✅ RAG supprimé: ${name}`);
|
|
994
|
+
}
|
|
995
|
+
catch (error) {
|
|
996
|
+
return error;
|
|
891
997
|
}
|
|
892
|
-
this.saveRegistry(registry);
|
|
893
|
-
console.log(`✅ RAG supprimé: ${name}`);
|
|
894
998
|
}
|
|
895
999
|
/**
|
|
896
1000
|
* Renomme un document dans un RAG existant
|
|
@@ -1029,7 +1133,8 @@ class RAGManager {
|
|
|
1029
1133
|
}
|
|
1030
1134
|
}
|
|
1031
1135
|
/**
|
|
1032
|
-
* Renomme un RAG existant
|
|
1136
|
+
* Renomme un RAG existant (nom dans le registre uniquement)
|
|
1137
|
+
* Le dossier physique n'est PAS modifié, seul le nom dans la config change.
|
|
1033
1138
|
*
|
|
1034
1139
|
* @param from Nom actuel du RAG
|
|
1035
1140
|
* @param to Nouveau nom du RAG
|
|
@@ -1039,6 +1144,7 @@ class RAGManager {
|
|
|
1039
1144
|
* @example
|
|
1040
1145
|
* ```typescript
|
|
1041
1146
|
* await ragManager.rename('old-name', 'new-name', 'Description mise à jour');
|
|
1147
|
+
* // Le dossier reste /path/to/old-name mais le RAG s'appelle 'new-name'
|
|
1042
1148
|
* ```
|
|
1043
1149
|
*/
|
|
1044
1150
|
async rename(from, to, description) {
|
|
@@ -1061,20 +1167,14 @@ class RAGManager {
|
|
|
1061
1167
|
throw new Error(`Le nom '${to}' est réservé et ne peut pas être utilisé`);
|
|
1062
1168
|
}
|
|
1063
1169
|
const sourceEntry = this.getEntry(from);
|
|
1064
|
-
const sourcePath = sourceEntry.configPath;
|
|
1065
|
-
const destPath = path_1.default.join(this.config.baseDir, to);
|
|
1066
1170
|
try {
|
|
1067
|
-
//
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
if (this.config.verbose)
|
|
1071
|
-
console.log(`📁 Dossier renommé: ${sourcePath} → ${destPath}`);
|
|
1072
|
-
}
|
|
1073
|
-
// Mettre à jour le registre
|
|
1171
|
+
//
|
|
1172
|
+
// Ne PAS renommer le dossier physique - juste le nom dans le registre
|
|
1173
|
+
// Le configPath reste inchangé (pointe vers le même dossier)
|
|
1074
1174
|
const registry = this.getRegistry();
|
|
1075
1175
|
registry.registries[to] = {
|
|
1076
1176
|
name: to,
|
|
1077
|
-
configPath:
|
|
1177
|
+
configPath: sourceEntry.configPath, // ✅ Garde le même chemin
|
|
1078
1178
|
status: sourceEntry.status,
|
|
1079
1179
|
description: description || sourceEntry.description
|
|
1080
1180
|
};
|
|
@@ -1088,12 +1188,10 @@ class RAGManager {
|
|
|
1088
1188
|
if (embeddingData) {
|
|
1089
1189
|
this.loadedEmbeddings.set(to, embeddingData);
|
|
1090
1190
|
this.loadedEmbeddings.delete(from);
|
|
1091
|
-
// Notifier l'embedding de se mettre à jour avec le nouveau chemin
|
|
1092
|
-
this.notifyUpdate(to);
|
|
1093
1191
|
}
|
|
1094
1192
|
// Mettre à jour le cache
|
|
1095
1193
|
this.saveRegistry(registry);
|
|
1096
|
-
console.log(`✅ RAG renommé: ${from} → ${to}`);
|
|
1194
|
+
console.log(`✅ RAG renommé: ${from} → ${to} (dossier inchangé: ${sourceEntry.configPath})`);
|
|
1097
1195
|
}
|
|
1098
1196
|
catch (error) {
|
|
1099
1197
|
console.error(`❌ Erreur lors du renommage de '${from}' en '${to}':`, error);
|
package/dist/src/rag/types.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export declare const RAG_FILES: {
|
|
|
5
5
|
readonly QUERIES: "rag-queries.json";
|
|
6
6
|
};
|
|
7
7
|
export interface RAGConfig {
|
|
8
|
+
/** Provider de l'embedding (default openai) */
|
|
9
|
+
provider?: string;
|
|
8
10
|
/** Répertoire de base pour les documents et index */
|
|
9
11
|
baseDir: string;
|
|
10
12
|
/** Nom du fichier de vecteurs (sans extension) */
|
package/dist/src/rag/usecase.js
CHANGED
|
@@ -132,25 +132,22 @@ async function extractAndSaveDocumentUseCases(document, baseDir, version, tag, o
|
|
|
132
132
|
const existingQueries = JSON.parse((0, fs_1.readFileSync)(queryFile, 'utf8'));
|
|
133
133
|
// Comparer le fingerprint
|
|
134
134
|
if (existingQueries.fingerprint === fingerprint) {
|
|
135
|
-
|
|
136
|
-
console.log(`⏭️ Use cases à jour (fingerprint identique): ${document.filename}`);
|
|
137
|
-
}
|
|
135
|
+
console.log(`⏭️ Skip queries: ${document.filename}`);
|
|
138
136
|
return existingQueries;
|
|
139
137
|
}
|
|
140
|
-
|
|
141
|
-
console.log(`🔄 Contenu modifié (fingerprint différent): ${document.filename}`);
|
|
142
|
-
}
|
|
138
|
+
console.log(`🔄 Source modifiée queries: ${document.filename}`);
|
|
143
139
|
}
|
|
144
140
|
catch (error) {
|
|
145
141
|
// Si erreur de lecture, on continue avec l'extraction
|
|
146
|
-
|
|
147
|
-
console.warn(`⚠️ Erreur lecture use cases existants: ${error}`);
|
|
148
|
-
}
|
|
142
|
+
console.warn(`⚠️ Erreur lecture use cases: ${error}`);
|
|
149
143
|
}
|
|
150
144
|
}
|
|
151
145
|
// Extraction nécessaire (fichier inexistant, fingerprint différent, ou force=true)
|
|
152
|
-
if (
|
|
153
|
-
console.log(`🔄
|
|
146
|
+
if (force) {
|
|
147
|
+
console.log(`🔄 Force queries: ${document.filename}`);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
console.log(`✨ Nouveau queries: ${document.filename}`);
|
|
154
151
|
}
|
|
155
152
|
const { queries, cost } = await extractDocumentUseCases(document);
|
|
156
153
|
const docQueries = {
|
|
@@ -82,15 +82,34 @@ class GitE2EHelper {
|
|
|
82
82
|
await fs_1.promises.mkdir(uploadDir, { recursive: true });
|
|
83
83
|
// Initialiser le repository Git
|
|
84
84
|
await this.git.init();
|
|
85
|
+
// FIXME: remove this hack that wait for the .git directory to be created
|
|
86
|
+
// ✅ Attendre que le répertoire .git soit complètement créé
|
|
87
|
+
const gitDir = (0, path_1.join)(this.tempRepoPath, '.git');
|
|
88
|
+
// let attempts = 0;
|
|
89
|
+
// while (!existsSync(gitDir) && attempts < 50) {
|
|
90
|
+
// await new Promise(resolve => setTimeout(resolve, 10));
|
|
91
|
+
// attempts++;
|
|
92
|
+
// }
|
|
93
|
+
if (!(0, fs_1.existsSync)(gitDir)) {
|
|
94
|
+
throw new Error('.git directory not created after git init');
|
|
95
|
+
}
|
|
85
96
|
// Configuration Git de base
|
|
86
97
|
await this.git.addConfig('user.name', 'E2E Test');
|
|
87
98
|
await this.git.addConfig('user.email', 'e2e@test.com');
|
|
88
99
|
await this.git.addConfig('init.defaultBranch', this.config.mainBranch);
|
|
89
100
|
// Créer la branche main directement
|
|
90
101
|
await this.git.checkout(['-b', this.config.mainBranch]);
|
|
91
|
-
// Créer un commit initial
|
|
102
|
+
// Créer un commit initial avec un README.md valide (avec front-matter pour les tests withID)
|
|
92
103
|
const readmePath = (0, path_1.join)(this.tempRepoPath, 'README.md');
|
|
93
|
-
|
|
104
|
+
const readmeContent = `---
|
|
105
|
+
id: 1000
|
|
106
|
+
title: E2E Test Repository
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
# E2E Test Repository
|
|
110
|
+
|
|
111
|
+
Temporary repository for E2E testing.`;
|
|
112
|
+
await fs_1.promises.writeFile(readmePath, readmeContent);
|
|
94
113
|
await this.git.add('README.md');
|
|
95
114
|
await this.git.commit('Initial commit');
|
|
96
115
|
// Créer la branche draft à partir de main
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RulesGitConfig, GitHealthStatus } from '../types';
|
|
1
|
+
import { RulesGitConfig, GitHealthStatus, GitPrNoteMigrationReport } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Rapport d'analyse des IDs
|
|
4
4
|
*/
|
|
@@ -73,8 +73,10 @@ export declare class GitHealthManager {
|
|
|
73
73
|
* - Notes orphelines sur anciens commits → copie vers HEAD + suppression
|
|
74
74
|
* - Branches sans historique de notes → ignore silencieusement
|
|
75
75
|
* - Erreurs de traitement par branche → continue avec les autres
|
|
76
|
+
*
|
|
77
|
+
* @returns GitPrNoteMigrationReport avec le nombre de notes migrées, déjà OK, et perdues
|
|
76
78
|
*/
|
|
77
|
-
migrateNotes(): Promise<
|
|
79
|
+
migrateNotes(): Promise<GitPrNoteMigrationReport>;
|
|
78
80
|
/**
|
|
79
81
|
* Analyse les IDs des documents Markdown dans une branche
|
|
80
82
|
*
|
|
@@ -31,8 +31,7 @@ class GitHealthManager {
|
|
|
31
31
|
await this.git.checkout(branch);
|
|
32
32
|
}
|
|
33
33
|
catch (checkoutError) {
|
|
34
|
-
// Si checkout normal échoue,
|
|
35
|
-
console.log(`🔧 Checkout forcé vers ${branch}...`, checkoutError);
|
|
34
|
+
// Si checkout normal échoue, continuer (forceRepair gérera l'erreur)
|
|
36
35
|
}
|
|
37
36
|
try {
|
|
38
37
|
// Use the robust health diagnostic function
|
|
@@ -142,12 +141,14 @@ class GitHealthManager {
|
|
|
142
141
|
const draftHealth = await (0, git_1.gitGetBranchHealth)(this.git, draftBranch);
|
|
143
142
|
results.push(draftHealth);
|
|
144
143
|
this.displayHealthSummary(draftHealth);
|
|
145
|
-
// Check all validation branches
|
|
144
|
+
// Check all validation branches with optimized function
|
|
146
145
|
const allBranches = await (0, git_1.gitGetAllBranches)(this.git);
|
|
147
146
|
const validationBranches = allBranches.filter(b => b.startsWith(validationPrefix));
|
|
148
147
|
if (validationBranches.length > 0) {
|
|
148
|
+
console.log(`🔍 Diagnostic optimisé de ${validationBranches.length} branche(s) de validation...`);
|
|
149
149
|
for (const branch of validationBranches) {
|
|
150
|
-
|
|
150
|
+
// ✅ OPTIMISATION: Utiliser la version légère pour les validations
|
|
151
|
+
const health = await (0, git_1.gitGetValidationBranchHealth)(this.git, branch, this.config);
|
|
151
152
|
results.push(health);
|
|
152
153
|
this.displayHealthSummary(health);
|
|
153
154
|
}
|
|
@@ -294,20 +295,23 @@ class GitHealthManager {
|
|
|
294
295
|
* - Notes orphelines sur anciens commits → copie vers HEAD + suppression
|
|
295
296
|
* - Branches sans historique de notes → ignore silencieusement
|
|
296
297
|
* - Erreurs de traitement par branche → continue avec les autres
|
|
298
|
+
*
|
|
299
|
+
* @returns GitPrNoteMigrationReport avec le nombre de notes migrées, déjà OK, et perdues
|
|
297
300
|
*/
|
|
298
301
|
async migrateNotes() {
|
|
299
302
|
const { mainBranch, validationPrefix, gitNotes } = this.config;
|
|
300
303
|
if (!validationPrefix || !gitNotes.namespace) {
|
|
301
304
|
console.error("Erreur: Le préfixe de validation ou le namespace des notes n'est pas configuré.");
|
|
302
|
-
return;
|
|
305
|
+
return { migrated: 0, alreadyOk: 0, lost: [] };
|
|
303
306
|
}
|
|
304
307
|
const allBranches = await (0, git_1.gitGetAllBranches)(this.git);
|
|
305
308
|
const validationBranches = allBranches.filter(b => b.startsWith(validationPrefix));
|
|
306
309
|
if (validationBranches.length === 0) {
|
|
307
|
-
return;
|
|
310
|
+
return { migrated: 0, alreadyOk: 0, lost: [] };
|
|
308
311
|
}
|
|
309
312
|
let migratedCount = 0;
|
|
310
313
|
let alreadyOkCount = 0;
|
|
314
|
+
const lostNotes = [];
|
|
311
315
|
for (const branch of validationBranches) {
|
|
312
316
|
try {
|
|
313
317
|
const lastCommit = (await this.git.revparse(branch)).trim();
|
|
@@ -325,12 +329,20 @@ class GitHealthManager {
|
|
|
325
329
|
}
|
|
326
330
|
const revListOutput = await this.git.raw('rev-list', `${mergeBase}..${branch}`);
|
|
327
331
|
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);
|
|
328
334
|
let oldNoteCommit = null;
|
|
329
335
|
for (const commitHash of branchCommits) {
|
|
330
336
|
const note = await (0, git_1.gitReadNote)(this.git, commitHash, gitNotes.namespace, 1);
|
|
331
337
|
if (note) {
|
|
332
|
-
|
|
333
|
-
|
|
338
|
+
// CRITICAL FIX: Verify the note belongs to this PR before migrating
|
|
339
|
+
if (note.id === prNumber) {
|
|
340
|
+
oldNoteCommit = commitHash;
|
|
341
|
+
break; // Found the correct note for this PR
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
console.log(` ⚠️ ${branch}: Note trouvée avec ID=${note.id}, attendu ID=${prNumber} - ignorée`);
|
|
345
|
+
}
|
|
334
346
|
}
|
|
335
347
|
}
|
|
336
348
|
// 3. If a note was found on an old commit, migrate it
|
|
@@ -341,6 +353,11 @@ class GitHealthManager {
|
|
|
341
353
|
console.log(`✅ ${branch}: Migration terminée`);
|
|
342
354
|
migratedCount++;
|
|
343
355
|
}
|
|
356
|
+
else {
|
|
357
|
+
// CRITICAL: Note perdue pour cette validation
|
|
358
|
+
console.error(`🚨 ${branch}: NOTE PERDUE - PR ${prNumber} n'a plus de note dans son historique`);
|
|
359
|
+
lostNotes.push({ branch, prNumber });
|
|
360
|
+
}
|
|
344
361
|
}
|
|
345
362
|
catch (error) {
|
|
346
363
|
console.error(`❌ ${branch}: Erreur lors de la migration:`, error);
|
|
@@ -348,9 +365,14 @@ class GitHealthManager {
|
|
|
348
365
|
}
|
|
349
366
|
}
|
|
350
367
|
// Summary only if there was work to do
|
|
351
|
-
if (migratedCount > 0 || alreadyOkCount !== validationBranches.length) {
|
|
352
|
-
console.log(`✅ Migration des notes terminée: ${migratedCount} migrées, ${alreadyOkCount} déjà OK`);
|
|
368
|
+
if (migratedCount > 0 || alreadyOkCount !== validationBranches.length || lostNotes.length > 0) {
|
|
369
|
+
console.log(`✅ Migration des notes terminée: ${migratedCount} migrées, ${alreadyOkCount} déjà OK${lostNotes.length > 0 ? `, ${lostNotes.length} perdues 🚨` : ''}`);
|
|
353
370
|
}
|
|
371
|
+
return {
|
|
372
|
+
migrated: migratedCount,
|
|
373
|
+
alreadyOk: alreadyOkCount,
|
|
374
|
+
lost: lostNotes
|
|
375
|
+
};
|
|
354
376
|
}
|
|
355
377
|
/**
|
|
356
378
|
* Analyse les IDs des documents Markdown dans une branche
|
|
@@ -428,9 +450,9 @@ class GitHealthManager {
|
|
|
428
450
|
else {
|
|
429
451
|
hasValidID = true;
|
|
430
452
|
// Enregistrer l'ID existant dans le registry
|
|
431
|
-
// ✅
|
|
453
|
+
// ✅ gitRegisterExistingID() appelle maintenant setMatterCache() automatiquement si matter est fourni
|
|
432
454
|
try {
|
|
433
|
-
(0, git_1.gitRegisterExistingID)(matter
|
|
455
|
+
(0, git_1.gitRegisterExistingID)(matter, branchName, file, this.config);
|
|
434
456
|
}
|
|
435
457
|
catch (error) {
|
|
436
458
|
if (error.code === 'id_already_used') {
|
|
@@ -529,6 +551,44 @@ class GitHealthManager {
|
|
|
529
551
|
];
|
|
530
552
|
let fixed = 0;
|
|
531
553
|
const fixedFiles = new Set();
|
|
554
|
+
// ✅ ÉTAPE CRITIQUE: Charger le cache pour toutes les branches avant la réparation
|
|
555
|
+
// Cela permet à getFileID() de trouver l'ID existant du fichier
|
|
556
|
+
if (!dryRun && filesToFix.length > 0) {
|
|
557
|
+
console.log(` 🔍 Chargement du cache pour toutes les branches...`);
|
|
558
|
+
const allBranches = await (0, git_1.gitGetAllBranches)(this.git);
|
|
559
|
+
const uniqueFiles = new Set();
|
|
560
|
+
for (const fileRef of filesToFix) {
|
|
561
|
+
const [, ...fileParts] = fileRef.split(':');
|
|
562
|
+
const file = fileParts.join(':');
|
|
563
|
+
uniqueFiles.add(file);
|
|
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, []);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
// Charger le cache pour chaque fichier dans les branches où il existe
|
|
578
|
+
for (const file of uniqueFiles) {
|
|
579
|
+
for (const branch of allBranches) {
|
|
580
|
+
try {
|
|
581
|
+
const filesInBranch = branchFilesCache.get(branch) || [];
|
|
582
|
+
if (filesInBranch.includes(file)) {
|
|
583
|
+
await (0, git_1.gitFileStrictMatter)(this.git, file, branch, this.config);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
catch (e) {
|
|
587
|
+
// Ignorer silencieusement les erreurs (fichier peut ne pas exister)
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
532
592
|
for (const fileRef of filesToFix) {
|
|
533
593
|
// Éviter de réparer deux fois le même fichier
|
|
534
594
|
if (fixedFiles.has(fileRef)) {
|
|
@@ -548,11 +608,44 @@ class GitHealthManager {
|
|
|
548
608
|
// Parser le matter complet
|
|
549
609
|
const parsed = (0, utils_matter_1.matterParse)(fileData.content);
|
|
550
610
|
const { matter: fullMatter, content } = parsed;
|
|
551
|
-
//
|
|
552
|
-
|
|
553
|
-
|
|
611
|
+
// ✅ CORRECTION: Utiliser gitEnsureMatterID() pour réutiliser l'ID existant du fichier
|
|
612
|
+
// Si le fichier existe déjà dans une autre branche, son ID sera réutilisé
|
|
613
|
+
// Sinon, un nouvel ID sera généré automatiquement
|
|
614
|
+
const matterWithID = (0, git_1.gitEnsureMatterID)(fullMatter, this.config, branchName, file);
|
|
615
|
+
if (matterWithID.id !== fullMatter.id) {
|
|
616
|
+
if (fullMatter.id && fullMatter.id > 999) {
|
|
617
|
+
console.log(` 🔧 ${file}: ID ${fullMatter.id} remplacé par ID existant ${matterWithID.id}`);
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
console.log(` 🔧 ${file}: Nouvel ID assigné: ${matterWithID.id}`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
fullMatter.id = matterWithID.id;
|
|
624
|
+
// Générer un titre si manquant (basé sur le nom du fichier ou le premier titre de section)
|
|
625
|
+
if (!fullMatter.title || fullMatter.title.trim() === '') {
|
|
626
|
+
// Essayer d'extraire le premier titre de section (# Title)
|
|
627
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
628
|
+
if (titleMatch) {
|
|
629
|
+
fullMatter.title = titleMatch[1].trim();
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
// Sinon, utiliser le nom du fichier sans extension
|
|
633
|
+
const fileName = file.replace(/\.md$/, '').replace(/[-_]/g, ' ');
|
|
634
|
+
fullMatter.title = fileName.split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
635
|
+
}
|
|
636
|
+
console.log(` 🔧 ${file}: Génération d'un titre: ${fullMatter.title}`);
|
|
637
|
+
}
|
|
554
638
|
// Re-sérialiser et sauvegarder
|
|
555
639
|
const updatedContent = (0, utils_matter_1.matterSerialize)(content, fullMatter);
|
|
640
|
+
// ✅ CRITIQUE: Vérifier si le contenu a réellement changé avant de créer un commit
|
|
641
|
+
// Si le contenu n'a pas changé, ne pas créer de commit pour éviter de déplacer les notes Git
|
|
642
|
+
if (updatedContent === fileData.content) {
|
|
643
|
+
// Le contenu n'a pas changé, juste mettre à jour le cache sans créer de commit
|
|
644
|
+
console.log(` ℹ️ ${file}: Aucun changement nécessaire (ID ${fullMatter.id} déjà correct)`);
|
|
645
|
+
fixed++;
|
|
646
|
+
fixedFiles.add(fileRef);
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
556
649
|
// ✅ IMPORTANT: Désactiver withID car l'ID est déjà généré et enregistré
|
|
557
650
|
// Évite la double validation qui cause "ID déjà utilisé"
|
|
558
651
|
await (0, repo_1.gitCreateOrEditFile)(this.git, file, branchName, updatedContent, { name: 'GitHealthManager', email: 'health@system' }, { ...this.config, canForce: true, withID: false });
|
|
@@ -618,6 +711,10 @@ class GitHealthManager {
|
|
|
618
711
|
if (autoFix && (report.missingID.length > 0 || report.missingTitle.length > 0 || report.invalidID.length > 0 || report.duplicateID.length > 0)) {
|
|
619
712
|
finalReport = await this.repairSafeIDs(report, { dryRun });
|
|
620
713
|
}
|
|
714
|
+
// Calculer le nombre de fichiers réparés
|
|
715
|
+
// C'est la différence entre les problèmes avant et après réparation
|
|
716
|
+
const fixed = (report.missingID.length + report.missingTitle.length + report.invalidID.length + report.duplicateID.length) -
|
|
717
|
+
(finalReport.missingID.length + finalReport.missingTitle.length + finalReport.invalidID.length + finalReport.duplicateID.length);
|
|
621
718
|
// Retourner dans l'ancien format pour compatibilité
|
|
622
719
|
return {
|
|
623
720
|
scanned: finalReport.scanned,
|
|
@@ -626,7 +723,7 @@ class GitHealthManager {
|
|
|
626
723
|
missingTitle: finalReport.missingTitle.length,
|
|
627
724
|
invalidID: finalReport.invalidID.length,
|
|
628
725
|
duplicateID: finalReport.duplicateID.length,
|
|
629
|
-
fixed:
|
|
726
|
+
fixed: Math.max(0, fixed), // S'assurer que fixed n'est pas négatif
|
|
630
727
|
errors: finalReport.errors
|
|
631
728
|
};
|
|
632
729
|
}
|
|
@@ -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, gitCreateOrEditFile, gitEditFile, gitRenameFile, gitDeleteFile, gitGenerateNextID, gitReloadIDRegistry, gitRegisterExistingID, gitEnsureMatterID, gitFileStrictMatter, gitIDRegistryExists, gitIDRegistryRename, } from './repo';
|
|
2
|
+
export { gitInit, gitEnsureRepositoryConfiguration, gitEnsureRemoteConfiguration, gitSetupRepository, gitShowConfiguration, gitCheckConfiguration, gitGetBranchHealth, gitGetValidationBranchHealth, gitCreateOrEditFile, gitEditFile, gitRenameFile, gitDeleteFile, gitGenerateNextID, gitReloadIDRegistry, gitRegisterExistingID, gitEnsureMatterID, gitFileStrictMatter, gitIDRegistryExists, gitIDRegistryRename, } from './repo';
|
|
3
3
|
export { gitSyncPR, gitIsPRClosed, gitIsPRClosedRobust, gitGetPRMetadata, gitGetAllPR, gitGetClosedPRs, gitLoadPR, gitPRUpdateComments, gitClosePR, gitClosePRRobust, gitGetNextPRNumber, gitNewValidationRequest, gitNewPR, } from './repo.pr';
|
|
4
4
|
export * from './git.e2e.helper';
|
|
5
5
|
export * from './git.health';
|