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,11 +53,14 @@ exports.gitRenameFile_fs = gitRenameFile_fs;
|
|
|
53
53
|
exports.gitRenameFile = gitRenameFile;
|
|
54
54
|
exports.gitDeleteFile = gitDeleteFile;
|
|
55
55
|
exports.gitGetBranchHealth = gitGetBranchHealth;
|
|
56
|
+
exports.gitGetValidationBranchHealth = gitGetValidationBranchHealth;
|
|
56
57
|
const fs_1 = require("fs");
|
|
57
58
|
const errors_1 = require("../errors");
|
|
58
59
|
const path_1 = require("path");
|
|
59
60
|
const fs = __importStar(require("fs/promises"));
|
|
60
61
|
const repo_tools_1 = require("./repo.tools");
|
|
62
|
+
const repo_pr_1 = require("./repo.pr");
|
|
63
|
+
const utils_matter_1 = require("../utils.matter");
|
|
61
64
|
/**
|
|
62
65
|
* Service singleton pour gérer le registre d'IDs en mémoire
|
|
63
66
|
*/
|
|
@@ -70,16 +73,32 @@ class IDRegistryService {
|
|
|
70
73
|
static get() {
|
|
71
74
|
return IDRegistryService.instance || (IDRegistryService.instance = new IDRegistryService());
|
|
72
75
|
}
|
|
76
|
+
toString() {
|
|
77
|
+
return JSON.stringify(this.registry, null, 2);
|
|
78
|
+
}
|
|
79
|
+
clear() {
|
|
80
|
+
this.registry = null;
|
|
81
|
+
this.repoPath = '';
|
|
82
|
+
this.isDirty = false;
|
|
83
|
+
}
|
|
73
84
|
/**
|
|
74
85
|
* Initialise le service avec le chemin du repository
|
|
75
86
|
*/
|
|
76
87
|
init(repoPath) {
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (this.repoPath !== repoPath || !this.registry) {
|
|
88
|
+
// ✅ BUGFIX: Toujours mettre à jour repoPath même si le registre existe déjà
|
|
89
|
+
// Car gitReloadIDRegistry peut être appelé avec un nouveau repoPath pour un nouveau test
|
|
90
|
+
// et le repo précédent peut avoir été nettoyé
|
|
91
|
+
if (this.repoPath !== repoPath) {
|
|
82
92
|
this.repoPath = repoPath;
|
|
93
|
+
// Si le registre existe déjà, le recharger depuis le nouveau chemin
|
|
94
|
+
if (this.registry) {
|
|
95
|
+
this.registry = this.loadFromDisk();
|
|
96
|
+
this.isDirty = false;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Si le registre n'existe pas encore, le créer
|
|
101
|
+
if (!this.registry) {
|
|
83
102
|
this.registry = this.loadFromDisk();
|
|
84
103
|
this.isDirty = false;
|
|
85
104
|
}
|
|
@@ -172,16 +191,64 @@ class IDRegistryService {
|
|
|
172
191
|
this.isDirty = true;
|
|
173
192
|
return newID;
|
|
174
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Obtient l'ID d'un fichier (peu importe la branche)
|
|
196
|
+
*
|
|
197
|
+
* Un fichier a UN SEUL ID quelque soit la branche où il se trouve.
|
|
198
|
+
* Cette fonction cherche le fichier dans toutes les branches du cache.
|
|
199
|
+
*
|
|
200
|
+
* @param file Nom du fichier (ex: 'procedure.md')
|
|
201
|
+
* @returns L'ID du fichier ou undefined si le fichier n'a pas d'ID
|
|
202
|
+
*/
|
|
203
|
+
getFileID(file) {
|
|
204
|
+
if (!this.registry) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
// Chercher le fichier dans toutes les branches
|
|
208
|
+
for (const [cacheKey, matter] of Object.entries(this.registry.matters)) {
|
|
209
|
+
const [, cachedFile] = cacheKey.split(':');
|
|
210
|
+
if (cachedFile === file) {
|
|
211
|
+
return matter.id;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
175
216
|
/**
|
|
176
217
|
* Vérifie si un ID appartient déjà à un fichier spécifique
|
|
218
|
+
*
|
|
219
|
+
* Un fichier garde son ID peu importe la branche, donc cette fonction
|
|
220
|
+
* cherche dans toutes les branches pour vérifier la propriété.
|
|
221
|
+
*
|
|
222
|
+
* @param id L'ID à vérifier
|
|
223
|
+
* @param file Nom du fichier
|
|
224
|
+
* @returns true si ce fichier possède cet ID
|
|
177
225
|
*/
|
|
178
|
-
isIDOwnedByFile(id,
|
|
226
|
+
isIDOwnedByFile(id, file) {
|
|
179
227
|
if (!this.registry) {
|
|
180
228
|
return false;
|
|
181
229
|
}
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
return
|
|
230
|
+
// Chercher le fichier dans toutes les branches
|
|
231
|
+
const fileID = this.getFileID(file);
|
|
232
|
+
return fileID === id;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Trouve quel fichier possède un ID donné
|
|
236
|
+
*
|
|
237
|
+
* @param id L'ID à chercher
|
|
238
|
+
* @returns Le nom du fichier qui possède cet ID, ou undefined
|
|
239
|
+
*/
|
|
240
|
+
getFileByID(id) {
|
|
241
|
+
if (!this.registry) {
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
// Chercher l'ID dans le cache
|
|
245
|
+
for (const [cacheKey, matter] of Object.entries(this.registry.matters)) {
|
|
246
|
+
if (matter.id === id) {
|
|
247
|
+
const [, file] = cacheKey.split(':');
|
|
248
|
+
return file;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return undefined;
|
|
185
252
|
}
|
|
186
253
|
/**
|
|
187
254
|
* Enregistre un ID existant
|
|
@@ -198,7 +265,8 @@ class IDRegistryService {
|
|
|
198
265
|
// Si l'ID est déjà utilisé
|
|
199
266
|
if (this.registry.used.includes(id)) {
|
|
200
267
|
// Vérifier si c'est le même fichier (pas un doublon)
|
|
201
|
-
|
|
268
|
+
// Note: isIDOwnedByFile() cherche maintenant dans TOUTES les branches
|
|
269
|
+
if (file && this.isIDOwnedByFile(id, file)) {
|
|
202
270
|
// Même fichier → OK, pas d'erreur
|
|
203
271
|
return;
|
|
204
272
|
}
|
|
@@ -212,6 +280,10 @@ class IDRegistryService {
|
|
|
212
280
|
if (id > this.registry.last) {
|
|
213
281
|
this.registry.last = id;
|
|
214
282
|
}
|
|
283
|
+
// ⚠️ IMPORTANT: Ne PAS créer d'entrée dans matters ici
|
|
284
|
+
// setMatterCache() DOIT être appelé AVANT registerExistingID() par l'appelant
|
|
285
|
+
// car registerExistingID() n'a pas le contexte complet (title, service)
|
|
286
|
+
// Si branch/file sont fournis mais que le cache n'existe pas, c'est une erreur de programmation
|
|
215
287
|
this.isDirty = true;
|
|
216
288
|
}
|
|
217
289
|
/**
|
|
@@ -348,6 +420,9 @@ function gitGenerateNextID(config) {
|
|
|
348
420
|
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
349
421
|
idRegistryService.init(gitConf.repoPath);
|
|
350
422
|
const newID = idRegistryService.generateNextID();
|
|
423
|
+
// FIXME: Double save - gitGenerateNextID() et gitRegisterExistingID() appellent tous deux save()
|
|
424
|
+
// Refactorisation future: Retirer save() d'ici et le faire uniquement dans gitRegisterExistingID()
|
|
425
|
+
// quand matter est fourni (processus atomique: generate → setCache → register → save)
|
|
351
426
|
idRegistryService.save();
|
|
352
427
|
return newID;
|
|
353
428
|
}
|
|
@@ -370,13 +445,16 @@ function gitGenerateNextID(config) {
|
|
|
370
445
|
*/
|
|
371
446
|
function gitReloadIDRegistry(config) {
|
|
372
447
|
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
448
|
+
if (config?.reset) {
|
|
449
|
+
idRegistryService.clear();
|
|
450
|
+
}
|
|
373
451
|
idRegistryService.init(gitConf.repoPath);
|
|
374
452
|
idRegistryService.forceReload();
|
|
375
453
|
}
|
|
376
454
|
/**
|
|
377
455
|
* Enregistre un ID existant dans le registre
|
|
378
456
|
*
|
|
379
|
-
* @param
|
|
457
|
+
* @param matter Le matter contenant l'ID à enregistrer
|
|
380
458
|
* @param branch Branche du fichier (optionnel, pour vérification de propriété)
|
|
381
459
|
* @param file Nom du fichier (optionnel, pour vérification de propriété)
|
|
382
460
|
* @param config Configuration Git optionnelle
|
|
@@ -384,11 +462,36 @@ function gitReloadIDRegistry(config) {
|
|
|
384
462
|
*
|
|
385
463
|
* **Note:** Si `branch` et `file` sont fournis, la fonction vérifie si l'ID appartient
|
|
386
464
|
* déjà au même fichier. Si oui, aucune erreur n'est levée (cas de re-scan).
|
|
465
|
+
*
|
|
466
|
+
* **Note:** `setMatterCache()` est appelé automatiquement avant l'enregistrement
|
|
467
|
+
* pour garantir la cohérence du cache.
|
|
387
468
|
*/
|
|
388
|
-
function gitRegisterExistingID(
|
|
469
|
+
function gitRegisterExistingID(matter, branch, file, config) {
|
|
389
470
|
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
390
471
|
idRegistryService.init(gitConf.repoPath);
|
|
391
|
-
|
|
472
|
+
// ✅ Validation: matter doit avoir id et title
|
|
473
|
+
if (!matter.id || !matter.title) {
|
|
474
|
+
throw new errors_1.GitOperationError('Le matter est invalide (id et title requis)', 'invalid_matter');
|
|
475
|
+
}
|
|
476
|
+
// ✅ IMPORTANT: Vérifier AVANT de mettre à jour le cache
|
|
477
|
+
// Si l'ID existe déjà, vérifier qu'il appartient au MÊME fichier
|
|
478
|
+
const registry = idRegistryService.getRegistry();
|
|
479
|
+
if (registry.used.includes(matter.id)) {
|
|
480
|
+
const ownerFile = idRegistryService.getFileByID(matter.id);
|
|
481
|
+
if (ownerFile && ownerFile !== file) {
|
|
482
|
+
// ID utilisé par un AUTRE fichier → Erreur
|
|
483
|
+
const error = new Error(`ID ${matter.id} est déjà utilisé`);
|
|
484
|
+
error.code = 'id_already_used';
|
|
485
|
+
throw error;
|
|
486
|
+
}
|
|
487
|
+
// Même fichier OU pas de fichier dans le cache → OK, continuer
|
|
488
|
+
}
|
|
489
|
+
// ✅ Mettre à jour le cache (maintenant qu'on sait que l'ID est valide)
|
|
490
|
+
if (branch && file) {
|
|
491
|
+
idRegistryService.setMatterCache(branch, file, matter);
|
|
492
|
+
}
|
|
493
|
+
// ✅ Enregistrer l'ID dans used[] si pas déjà dedans
|
|
494
|
+
idRegistryService.registerExistingID(matter.id, branch, file);
|
|
392
495
|
idRegistryService.save();
|
|
393
496
|
}
|
|
394
497
|
/**
|
|
@@ -418,25 +521,61 @@ function gitIDRegistryRename(oldFile, newFile, branch, config) {
|
|
|
418
521
|
}
|
|
419
522
|
/**
|
|
420
523
|
* Valide et assure qu'un matter a un ID valide
|
|
524
|
+
*
|
|
525
|
+
* **Principe d'identité stable**: Un fichier conserve son ID à travers les branches.
|
|
526
|
+
* Cette fonction cherche d'abord si le fichier a déjà un ID dans le registre
|
|
527
|
+
* (peu importe la branche), et le réutilise pour garantir la cohérence.
|
|
528
|
+
*
|
|
421
529
|
* @param matter Le matter à valider/compléter
|
|
422
530
|
* @param config Configuration Git optionnelle
|
|
531
|
+
* @param branch Branche du fichier (optionnel, peut être undefined lors de createPullRequest)
|
|
532
|
+
* @param file Nom du fichier (requis pour chercher l'ID existant)
|
|
423
533
|
* @returns Le matter mis à jour avec un ID valide
|
|
424
534
|
*/
|
|
425
|
-
function gitEnsureMatterID(matter, config) {
|
|
426
|
-
|
|
535
|
+
function gitEnsureMatterID(matter, config, branch, file) {
|
|
536
|
+
const gitConf = (0, repo_tools_1.gitLoad)(config);
|
|
537
|
+
idRegistryService.init(gitConf.repoPath);
|
|
538
|
+
// ✅ NOUVEAU: Chercher d'abord l'ID existant du fichier (peu importe la branche)
|
|
539
|
+
const existingFileID = file ? idRegistryService.getFileID(file) : undefined;
|
|
540
|
+
if (existingFileID) {
|
|
541
|
+
// Le fichier a déjà un ID → le réutiliser pour garantir la cohérence
|
|
542
|
+
matter.id = existingFileID;
|
|
543
|
+
// Mettre à jour le cache si branch est définie (sinon ce sera fait plus tard)
|
|
544
|
+
if (branch && file && matter.title) {
|
|
545
|
+
idRegistryService.setMatterCache(branch, file, {
|
|
546
|
+
id: matter.id,
|
|
547
|
+
title: matter.title,
|
|
548
|
+
service: matter.service
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
return matter;
|
|
552
|
+
}
|
|
553
|
+
// Si matter a un ID fourni manuellement (ex: import ou test)
|
|
427
554
|
// FIXME 999 should be a constant in config
|
|
428
555
|
if (matter.id && typeof matter.id === 'number' && matter.id > 999) {
|
|
429
556
|
try {
|
|
430
|
-
//
|
|
431
|
-
gitRegisterExistingID(matter
|
|
557
|
+
// Essayer d'enregistrer cet ID
|
|
558
|
+
gitRegisterExistingID(matter, branch, file, config);
|
|
432
559
|
return matter;
|
|
433
560
|
}
|
|
434
561
|
catch (error) {
|
|
435
|
-
// ID déjà utilisé
|
|
562
|
+
// ID déjà utilisé par un AUTRE fichier → générer un nouveau
|
|
563
|
+
// (Si même fichier, registerExistingID ne lève pas d'erreur)
|
|
436
564
|
}
|
|
437
565
|
}
|
|
438
|
-
// Générer un nouvel ID
|
|
566
|
+
// Générer un nouvel ID (si aucun ID fourni ou ID invalide)
|
|
439
567
|
matter.id = gitGenerateNextID(config);
|
|
568
|
+
// FIXME: Double save ici - gitGenerateNextID() fait save() puis gitRegisterExistingID() fait save() aussi
|
|
569
|
+
// Refactorisation future: Retirer save() de gitGenerateNextID() et le faire uniquement dans gitRegisterExistingID()
|
|
570
|
+
// quand matter est fourni (processus atomique: generate → setCache → register → save)
|
|
571
|
+
// ✅ Mettre à jour le cache avec le nouvel ID (seulement si branch est définie)
|
|
572
|
+
if (branch && file && matter.title) {
|
|
573
|
+
idRegistryService.setMatterCache(branch, file, {
|
|
574
|
+
id: matter.id,
|
|
575
|
+
title: matter.title,
|
|
576
|
+
service: matter.service
|
|
577
|
+
});
|
|
578
|
+
}
|
|
440
579
|
return matter;
|
|
441
580
|
}
|
|
442
581
|
/**
|
|
@@ -862,13 +1001,20 @@ async function gitCheckConfiguration(git) {
|
|
|
862
1001
|
return check;
|
|
863
1002
|
}
|
|
864
1003
|
/**
|
|
865
|
-
* Crée un fichier dans
|
|
866
|
-
*
|
|
1004
|
+
* Crée ou modifie un fichier dans une branche Git et fait un commit automatique
|
|
1005
|
+
*
|
|
1006
|
+
* @param git Instance SimpleGit
|
|
867
1007
|
* @param filePath Chemin du fichier
|
|
868
1008
|
* @param PR Nom de la branche de Pull Request
|
|
869
1009
|
* @param content Contenu du fichier
|
|
870
|
-
* @param user Utilisateur qui
|
|
871
|
-
* @param config Configuration Git
|
|
1010
|
+
* @param user Utilisateur qui fait le commit
|
|
1011
|
+
* @param config Configuration Git optionnelle
|
|
1012
|
+
* @returns Historique du commit créé
|
|
1013
|
+
*
|
|
1014
|
+
* @fixme Ajouter un paramètre optionnel `commitMessage?: string` pour permettre de personnaliser
|
|
1015
|
+
* le message de commit au lieu d'utiliser toujours `commit: ${filePath}` par défaut.
|
|
1016
|
+
* Cela permettrait aux callers de spécifier des messages plus descriptifs.
|
|
1017
|
+
*
|
|
872
1018
|
* @throws GitOperationError si la création échoue
|
|
873
1019
|
*/
|
|
874
1020
|
async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
@@ -882,24 +1028,52 @@ async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
|
882
1028
|
if (gitConf.withID) {
|
|
883
1029
|
try {
|
|
884
1030
|
// Import dynamique pour éviter les dépendances circulaires
|
|
885
|
-
const
|
|
886
|
-
const parsed = matterParse(content);
|
|
1031
|
+
const parsed = (0, utils_matter_1.matterParse)(content);
|
|
887
1032
|
if (gitConf.strictID) {
|
|
888
1033
|
// Mode strict : valider que l'ID existe et est correct
|
|
889
1034
|
validateMatter(parsed.matter);
|
|
1035
|
+
// ✅ gitRegisterExistingID() appelle maintenant setMatterCache() automatiquement si matter est fourni
|
|
890
1036
|
// Enregistrer l'ID existant
|
|
891
1037
|
// ✅ Passer branch (PR) et file (filePath) pour vérifier propriété
|
|
892
|
-
gitRegisterExistingID(parsed.matter
|
|
1038
|
+
gitRegisterExistingID(parsed.matter, PR, filePath, gitConf);
|
|
893
1039
|
}
|
|
894
1040
|
else {
|
|
895
1041
|
// Mode permissif : générer l'ID si manquant
|
|
896
|
-
|
|
897
|
-
//
|
|
898
|
-
|
|
1042
|
+
// ✅ IMPORTANT: Ne PAS ré-assigner l'ID s'il est déjà valide
|
|
1043
|
+
// Le contenu a peut-être déjà été traité par gitEnsureMatterID() dans le caller
|
|
1044
|
+
const hasValidID = parsed.matter.id &&
|
|
1045
|
+
typeof parsed.matter.id === 'number' &&
|
|
1046
|
+
parsed.matter.id > 999;
|
|
1047
|
+
if (!hasValidID) {
|
|
1048
|
+
// Seulement si l'ID est manquant ou invalide
|
|
1049
|
+
parsed.matter = gitEnsureMatterID(parsed.matter, gitConf, PR, filePath);
|
|
1050
|
+
// Re-sérialiser avec l'ID mis à jour
|
|
1051
|
+
content = (0, utils_matter_1.matterSerialize)(parsed.content, parsed.matter);
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
// ✅ L'ID existe déjà : gitRegisterExistingID() appelle maintenant setMatterCache() automatiquement
|
|
1055
|
+
try {
|
|
1056
|
+
gitRegisterExistingID(parsed.matter, PR, filePath, gitConf);
|
|
1057
|
+
}
|
|
1058
|
+
catch (error) {
|
|
1059
|
+
// Si l'ID est déjà utilisé par un autre fichier, générer un nouvel ID
|
|
1060
|
+
if (error.code === 'id_already_used') {
|
|
1061
|
+
// En mode non-strict, générer un nouvel ID si doublon détecté
|
|
1062
|
+
parsed.matter = gitEnsureMatterID(parsed.matter, gitConf, PR, filePath);
|
|
1063
|
+
content = (0, utils_matter_1.matterSerialize)(parsed.content, parsed.matter);
|
|
1064
|
+
// ✅ Ne pas propager l'erreur, continuer avec le nouvel ID généré
|
|
1065
|
+
}
|
|
1066
|
+
else {
|
|
1067
|
+
throw error;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
899
1071
|
}
|
|
900
1072
|
}
|
|
901
1073
|
catch (error) {
|
|
902
|
-
|
|
1074
|
+
// En mode strict, propager l'erreur id_already_used
|
|
1075
|
+
// En mode permissif, cette erreur devrait déjà être gérée dans le bloc ci-dessus
|
|
1076
|
+
if (error.code === 'id_already_used' && gitConf.strictID) {
|
|
903
1077
|
throw new errors_1.GitOperationError(`Impossible de créer le fichier: ${error.message}`, 'duplicate_id', { filePath, error });
|
|
904
1078
|
}
|
|
905
1079
|
// Propager les autres erreurs de validation
|
|
@@ -923,13 +1097,15 @@ async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
|
923
1097
|
//
|
|
924
1098
|
// without note the branche is not a valid PR branch
|
|
925
1099
|
const oldNote = await (0, repo_tools_1.gitReadNote)(git, PR, gitConf.gitNotes.namespace, 10);
|
|
1100
|
+
if (oldNote && gitConf.verbose) {
|
|
1101
|
+
console.debug('[gitCreateOrEditFile] metadata.files before update:', oldNote.files);
|
|
1102
|
+
}
|
|
926
1103
|
const commit = await _writeFileAndCommit(git, filePath, content, user, gitConf, `commit: ${filePath}`);
|
|
927
1104
|
// ✅ Mettre à jour le cache du matter après sauvegarde
|
|
928
1105
|
// NOTE: Le cache est TOUJOURS mis à jour, indépendamment de withID
|
|
929
1106
|
// C'est une opération de cache, pas une validation
|
|
930
1107
|
try {
|
|
931
|
-
const
|
|
932
|
-
const parsed = matterParse(content);
|
|
1108
|
+
const parsed = (0, utils_matter_1.matterParse)(content);
|
|
933
1109
|
idRegistryService.init(gitConf.repoPath);
|
|
934
1110
|
idRegistryService.setMatterCache(PR, filePath, {
|
|
935
1111
|
id: parsed.matter.id,
|
|
@@ -958,11 +1134,15 @@ async function gitCreateOrEditFile(git, filePath, PR, content, user, config) {
|
|
|
958
1134
|
}
|
|
959
1135
|
// Determine the current head after the commit attempt.
|
|
960
1136
|
const newHead = (await git.revparse(['HEAD'])).trim();
|
|
961
|
-
|
|
1137
|
+
let newFiles = await (0, repo_tools_1.gitGetDiffFiles)(git, PR, oldNote.mergeBase);
|
|
1138
|
+
newFiles = await sanitizePRFiles(git, newFiles, PR);
|
|
962
1139
|
const updatedNote = {
|
|
963
1140
|
...oldNote,
|
|
964
1141
|
files: newFiles,
|
|
965
1142
|
};
|
|
1143
|
+
if (gitConf.verbose) {
|
|
1144
|
+
console.debug('[gitCreateOrEditFile] metadata.files after update:', updatedNote.files);
|
|
1145
|
+
}
|
|
966
1146
|
// Write the note to the current HEAD of the PR branch.
|
|
967
1147
|
// If no commit was made, this overwrites the note on oldHead.
|
|
968
1148
|
// If a new commit was made, this writes the note on newHead.
|
|
@@ -1165,12 +1345,24 @@ async function gitRenameFile(git, oldFileName, newFileName, branch, user, config
|
|
|
1165
1345
|
//
|
|
1166
1346
|
//FIXME: add a check about the branch (file can be NEW and on branch)
|
|
1167
1347
|
const fullOldPath = (0, path_1.join)(gitConf.uploadPath, oldFileName);
|
|
1348
|
+
let result;
|
|
1168
1349
|
if ((0, fs_1.existsSync)(fullOldPath)) {
|
|
1169
|
-
|
|
1350
|
+
result = await gitRenameFile_fs(git, oldFileName, newFileName, branch, user, config);
|
|
1170
1351
|
}
|
|
1171
1352
|
else {
|
|
1172
|
-
|
|
1353
|
+
result = await gitRenameFile_git(git, oldFileName, newFileName, branch, user, config);
|
|
1354
|
+
if (branch !== 'NEW' && branch.startsWith(gitConf.validationPrefix)) {
|
|
1355
|
+
try {
|
|
1356
|
+
await (0, repo_pr_1.gitPRReplaceFile)(git, branch, { remove: oldFileName, add: newFileName }, gitConf);
|
|
1357
|
+
}
|
|
1358
|
+
catch (metadataError) {
|
|
1359
|
+
if (gitConf.verbose) {
|
|
1360
|
+
console.warn('⚠️ Failed to update PR metadata after rename:', metadataError);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1173
1364
|
}
|
|
1365
|
+
return result;
|
|
1174
1366
|
}
|
|
1175
1367
|
catch (error) {
|
|
1176
1368
|
// Pour les vraies branches Git, ne pas faire de fallback filesystem
|
|
@@ -1398,6 +1590,81 @@ async function gitGetBranchHealth(git, branch) {
|
|
|
1398
1590
|
(health.clean || (health.modifiedFiles === 0 && health.stagedFiles === 0));
|
|
1399
1591
|
return health;
|
|
1400
1592
|
}
|
|
1593
|
+
/**
|
|
1594
|
+
* Version optimisée de gitGetBranchHealth pour les branches de validation
|
|
1595
|
+
*
|
|
1596
|
+
* Cette fonction utilise une approche en cascade pour diagnostiquer rapidement
|
|
1597
|
+
* l'état d'une branche de validation sans effectuer de checkout coûteux.
|
|
1598
|
+
*
|
|
1599
|
+
* Tests effectués dans l'ordre (arrêt au premier échec) :
|
|
1600
|
+
* 1. Branche existe ? (git rev-parse --verify)
|
|
1601
|
+
* 2. Note Git présente sur HEAD ? (gitReadNote avec maxCommit=1)
|
|
1602
|
+
* 3. Note correspond au bon PR ? (note.id === prNumber)
|
|
1603
|
+
*
|
|
1604
|
+
* Avantages :
|
|
1605
|
+
* - ✅ Pas de checkout (gain 80-90% performance)
|
|
1606
|
+
* - ✅ Lecture directe du HEAD sans changer de branche
|
|
1607
|
+
* - ✅ Tests cascade (arrêt dès qu'un problème est détecté)
|
|
1608
|
+
* - ✅ Optimisé pour branches temporaires (validations)
|
|
1609
|
+
*
|
|
1610
|
+
* @param git Instance SimpleGit
|
|
1611
|
+
* @param branch Nom de la branche de validation à diagnostiquer
|
|
1612
|
+
* @param config Configuration Git optionnelle
|
|
1613
|
+
* @returns GitHealthStatus avec diagnostic optimisé
|
|
1614
|
+
*/
|
|
1615
|
+
async function gitGetValidationBranchHealth(git, branch, config) {
|
|
1616
|
+
const gitConfig = config || (0, repo_tools_1.gitLoad)();
|
|
1617
|
+
const health = {
|
|
1618
|
+
branch,
|
|
1619
|
+
exists: false,
|
|
1620
|
+
accessible: false,
|
|
1621
|
+
clean: true, // Par défaut OK pour validation (pas de working dir check)
|
|
1622
|
+
mergeInProgress: false,
|
|
1623
|
+
conflictedFiles: 0,
|
|
1624
|
+
modifiedFiles: 0,
|
|
1625
|
+
untrackedFiles: 0,
|
|
1626
|
+
stagedFiles: 0,
|
|
1627
|
+
indexReadable: true,
|
|
1628
|
+
healthy: false,
|
|
1629
|
+
issues: [],
|
|
1630
|
+
recommendations: []
|
|
1631
|
+
};
|
|
1632
|
+
try {
|
|
1633
|
+
// 1. CASCADE: Vérifier existence (très rapide, sans checkout)
|
|
1634
|
+
const branchExists = await git.raw(['rev-parse', '--verify', branch])
|
|
1635
|
+
.then(() => true)
|
|
1636
|
+
.catch(() => false);
|
|
1637
|
+
if (!branchExists) {
|
|
1638
|
+
health.issues.push(`Branche '${branch}' n'existe pas`);
|
|
1639
|
+
health.recommendations.push(`Créer la branche ou vérifier le nom`);
|
|
1640
|
+
return health;
|
|
1641
|
+
}
|
|
1642
|
+
health.exists = true;
|
|
1643
|
+
health.accessible = true; // Si rev-parse OK, branche accessible
|
|
1644
|
+
// 2. CASCADE: Vérifier note sur HEAD (rapide, sans checkout)
|
|
1645
|
+
const headCommit = await git.raw(['rev-parse', branch]).then(s => s.trim());
|
|
1646
|
+
const note = await (0, repo_tools_1.gitReadNote)(git, headCommit, gitConfig.gitNotes.namespace, 1);
|
|
1647
|
+
if (!note) {
|
|
1648
|
+
health.issues.push(`Pas de note Git sur HEAD de '${branch}'`);
|
|
1649
|
+
health.recommendations.push(`Note perdue, utiliser git-notes-list-lost.sh pour la localiser`);
|
|
1650
|
+
return health;
|
|
1651
|
+
}
|
|
1652
|
+
// 3. CASCADE: Vérifier que la note correspond au PR
|
|
1653
|
+
const prNumber = parseInt(branch.split('-').pop() || '0', 10);
|
|
1654
|
+
if (note.id !== prNumber) {
|
|
1655
|
+
health.issues.push(`Note incorrecte: ID=${note.id}, attendu PR=${prNumber}`);
|
|
1656
|
+
health.recommendations.push(`Corriger la note ou supprimer la branche`);
|
|
1657
|
+
return health;
|
|
1658
|
+
}
|
|
1659
|
+
// ✅ Tous les tests essentiels passés
|
|
1660
|
+
health.healthy = true;
|
|
1661
|
+
}
|
|
1662
|
+
catch (error) {
|
|
1663
|
+
health.issues.push(`Erreur diagnostic: ${error.message}`);
|
|
1664
|
+
health.recommendations.push(`Vérifier l'état du dépôt Git`);
|
|
1665
|
+
}
|
|
1666
|
+
return health;
|
|
1667
|
+
}
|
|
1401
1668
|
/**
|
|
1402
1669
|
* Fonction helper pour créer un statut unhealthy
|
|
1403
1670
|
*/
|
|
@@ -1422,9 +1689,7 @@ function createUnhealthyStatus(branch, exists, accessible, clean, mergeInProgres
|
|
|
1422
1689
|
* Vérifie si un merge est en cours en regardant les fichiers Git
|
|
1423
1690
|
*/
|
|
1424
1691
|
async function checkMergeInProgress(repoPath) {
|
|
1425
|
-
const
|
|
1426
|
-
const { join } = await Promise.resolve().then(() => __importStar(require('path')));
|
|
1427
|
-
const gitDir = join(repoPath, '.git');
|
|
1692
|
+
const gitDir = (0, path_1.join)(repoPath, '.git');
|
|
1428
1693
|
const mergeFiles = [
|
|
1429
1694
|
'MERGE_HEAD',
|
|
1430
1695
|
'MERGE_MODE',
|
|
@@ -1432,5 +1697,27 @@ async function checkMergeInProgress(repoPath) {
|
|
|
1432
1697
|
'CHERRY_PICK_HEAD',
|
|
1433
1698
|
'REVERT_HEAD'
|
|
1434
1699
|
];
|
|
1435
|
-
return mergeFiles.some(file => existsSync(join(gitDir, file)));
|
|
1700
|
+
return mergeFiles.some(file => (0, fs_1.existsSync)((0, path_1.join)(gitDir, file)));
|
|
1701
|
+
}
|
|
1702
|
+
async function sanitizePRFiles(git, files, branch) {
|
|
1703
|
+
if (!files || files.length === 0) {
|
|
1704
|
+
return [];
|
|
1705
|
+
}
|
|
1706
|
+
const checks = await Promise.all(files.map(async (file) => ({
|
|
1707
|
+
file,
|
|
1708
|
+
exists: await (0, repo_tools_1.gitFileExistsInBranch)(git, file, branch)
|
|
1709
|
+
})));
|
|
1710
|
+
const sanitized = [];
|
|
1711
|
+
const seen = new Set();
|
|
1712
|
+
for (const { file, exists } of checks) {
|
|
1713
|
+
if (!exists) {
|
|
1714
|
+
continue;
|
|
1715
|
+
}
|
|
1716
|
+
if (seen.has(file)) {
|
|
1717
|
+
continue;
|
|
1718
|
+
}
|
|
1719
|
+
seen.add(file);
|
|
1720
|
+
sanitized.push(file);
|
|
1721
|
+
}
|
|
1722
|
+
return sanitized;
|
|
1436
1723
|
}
|
|
@@ -79,6 +79,14 @@ export declare function gitGetAllPR(git: SimpleGit, options?: {
|
|
|
79
79
|
*/
|
|
80
80
|
export declare function gitGetClosedPRs(git: SimpleGit, gitConfig: RulesGitConfig): Promise<PRInfo[]>;
|
|
81
81
|
export declare function gitLoadPR(git: SimpleGit, branch: string): Promise<PRInfo>;
|
|
82
|
+
/**
|
|
83
|
+
* Remplace explicitement un nom de fichier dans les métadonnées d'une PR.
|
|
84
|
+
* Utilisé pour refléter immédiatement les renames sans dépendre de git diff.
|
|
85
|
+
*/
|
|
86
|
+
export declare function gitPRReplaceFile(git: SimpleGit, branch: string, options?: {
|
|
87
|
+
remove?: string;
|
|
88
|
+
add?: string;
|
|
89
|
+
}, config?: RulesGitConfig): Promise<void>;
|
|
82
90
|
export declare function gitPRUpdateComments(git: SimpleGit, branch: string, details: RulePullRequestDetails, config?: RulesGitConfig): Promise<PRInfo>;
|
|
83
91
|
/**
|
|
84
92
|
* DEPRECATED: use gitClosePRRobust instead
|