agentic-api 2.0.646 → 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.
Files changed (59) hide show
  1. package/dist/src/agents/prompts.d.ts +2 -3
  2. package/dist/src/agents/prompts.js +21 -118
  3. package/dist/src/agents/reducer.loaders.d.ts +103 -1
  4. package/dist/src/agents/reducer.loaders.js +164 -2
  5. package/dist/src/agents/reducer.types.d.ts +34 -3
  6. package/dist/src/agents/simulator.d.ts +32 -2
  7. package/dist/src/agents/simulator.executor.d.ts +15 -5
  8. package/dist/src/agents/simulator.executor.js +134 -67
  9. package/dist/src/agents/simulator.js +251 -8
  10. package/dist/src/agents/simulator.prompts.d.ts +55 -10
  11. package/dist/src/agents/simulator.prompts.js +305 -61
  12. package/dist/src/agents/simulator.types.d.ts +62 -1
  13. package/dist/src/agents/simulator.types.js +5 -0
  14. package/dist/src/agents/subagent.d.ts +128 -0
  15. package/dist/src/agents/subagent.js +231 -0
  16. package/dist/src/agents/worker.executor.d.ts +48 -0
  17. package/dist/src/agents/worker.executor.js +152 -0
  18. package/dist/src/execute/helpers.d.ts +3 -0
  19. package/dist/src/execute/helpers.js +222 -16
  20. package/dist/src/execute/responses.js +81 -55
  21. package/dist/src/execute/shared.d.ts +5 -0
  22. package/dist/src/execute/shared.js +27 -0
  23. package/dist/src/index.d.ts +2 -1
  24. package/dist/src/index.js +3 -1
  25. package/dist/src/llm/openai.js +8 -1
  26. package/dist/src/llm/pricing.js +2 -0
  27. package/dist/src/llm/xai.js +11 -6
  28. package/dist/src/prompts.d.ts +14 -0
  29. package/dist/src/prompts.js +41 -1
  30. package/dist/src/rag/rag.manager.d.ts +18 -3
  31. package/dist/src/rag/rag.manager.js +114 -12
  32. package/dist/src/rag/types.d.ts +3 -1
  33. package/dist/src/rules/git/git.e2e.helper.js +51 -4
  34. package/dist/src/rules/git/git.health.js +89 -56
  35. package/dist/src/rules/git/index.d.ts +2 -2
  36. package/dist/src/rules/git/index.js +22 -5
  37. package/dist/src/rules/git/repo.d.ts +64 -6
  38. package/dist/src/rules/git/repo.js +572 -141
  39. package/dist/src/rules/git/repo.pr.d.ts +11 -18
  40. package/dist/src/rules/git/repo.pr.js +82 -94
  41. package/dist/src/rules/git/repo.tools.d.ts +5 -0
  42. package/dist/src/rules/git/repo.tools.js +6 -1
  43. package/dist/src/rules/types.d.ts +0 -2
  44. package/dist/src/rules/utils.matter.js +1 -5
  45. package/dist/src/scrapper.d.ts +138 -25
  46. package/dist/src/scrapper.js +538 -160
  47. package/dist/src/stategraph/stategraph.d.ts +6 -2
  48. package/dist/src/stategraph/stategraph.js +21 -6
  49. package/dist/src/stategraph/types.d.ts +14 -6
  50. package/dist/src/types.d.ts +22 -0
  51. package/dist/src/utils.d.ts +24 -0
  52. package/dist/src/utils.js +84 -86
  53. package/package.json +3 -2
  54. package/dist/src/agents/semantic.d.ts +0 -4
  55. package/dist/src/agents/semantic.js +0 -19
  56. package/dist/src/execute/legacy.d.ts +0 -46
  57. package/dist/src/execute/legacy.js +0 -460
  58. package/dist/src/pricing.llm.d.ts +0 -5
  59. package/dist/src/pricing.llm.js +0 -14
@@ -3,6 +3,9 @@ import { RulesGitConfig, PRMetadata, PRInfo, RuleUser, PRMergeResult, RulePullRe
3
3
  /**
4
4
  * Synchronise une branche PR avec son mergeBase pour corriger les références orphelines
5
5
  *
6
+ * @deprecated Utiliser le workflow applicatif (RulesWorkflow.syncPullRequest) et
7
+ * les opérations robustes orientées Git Notes. API conservée pour compatibilité.
8
+ *
6
9
  * FIXME: Cette fonction a un problème de gestion des métadonnées manquantes.
7
10
  *
8
11
  * PROBLÈME IDENTIFIÉ:
@@ -38,12 +41,14 @@ export declare function gitSyncPR(git: SimpleGit, branch: string, user: RuleUser
38
41
  * @param branch Nom de la branche PR
39
42
  * @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
40
43
  * @returns true si le PR est fermé (dernier commit contient le token de validation)
41
- * DEPRECATED: use gitIsPRClosedRobust instead
44
+ * @deprecated Utiliser gitIsPRClosedRobust.
42
45
  */
43
46
  export declare function gitIsPRClosed(git: SimpleGit, branch: string): Promise<boolean>;
44
47
  /**
45
48
  * Vérifie si une branche PR est fermée en utilisant Git Notes (avec fallback)
46
49
  * Version robuste qui remplace gitIsPRClosed
50
+ * @deprecated Préférer gitLoadPR() pour récupérer l'état PR complet.
51
+ *
47
52
  * @param git Instance Git
48
53
  * @param branch Nom de la branche PR
49
54
  * @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
@@ -73,23 +78,18 @@ export declare function gitGetAllPR(git: SimpleGit, options?: {
73
78
  }): Promise<PRInfo[]>;
74
79
  /**
75
80
  * Finds closed PRs by scanning the history of the draft branch for merge commits with PR metadata.
81
+ * @deprecated Utiliser gitGetAllPR(git, { closed: true }) pour unifier le listing PR.
82
+ *
76
83
  * @param git SimpleGit instance
77
84
  * @param gitConfig The git configuration
78
85
  * @returns A list of PRInfo objects for closed PRs.
79
86
  */
80
87
  export declare function gitGetClosedPRs(git: SimpleGit, gitConfig: RulesGitConfig): Promise<PRInfo[]>;
81
88
  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>;
90
89
  export declare function gitPRUpdateComments(git: SimpleGit, branch: string, details: RulePullRequestDetails, config?: RulesGitConfig): Promise<PRInfo>;
91
90
  /**
92
- * DEPRECATED: use gitClosePRRobust instead
91
+ * @deprecated Utiliser gitClosePRRobust.
92
+ *
93
93
  * Ferme un PR en ajoutant un commit avec le token de validation
94
94
  * @param git Instance Git
95
95
  * @param branch Branche PR à fermer
@@ -111,13 +111,6 @@ export declare function gitClosePR(git: SimpleGit, branch: string, author: RuleU
111
111
  * @returns Hash du commit de fermeture ou référence de la note
112
112
  */
113
113
  export declare function gitClosePRRobust(git: SimpleGit, branch: string, closedBy: RuleUser, message?: string, config?: RulesGitConfig): Promise<PRMergeResult>;
114
- /**
115
- * Trouve le prochain numéro de PR disponible (protégé contre la concurrence)
116
- * @param git Instance Git
117
- * @param validationPrefix Préfixe des branches de validation
118
- * @returns Prochain numéro de PR
119
- */
120
- export declare function gitGetNextPRNumber(git: SimpleGit, validationPrefix: string): Promise<number>;
121
114
  /**
122
115
  * Crée une nouvelle branche de validation (pour un PR vers rule-editor)
123
116
  * à partir d'une branche source (typiquement rule-editor).
@@ -136,7 +129,7 @@ export declare function gitNewValidationRequest(git: SimpleGit, files: string[],
136
129
  validationBranchPrefix?: string;
137
130
  }): Promise<PRInfo>;
138
131
  /**
139
- * DEPRECATED
132
+ * @deprecated Utiliser gitNewValidationRequest().
140
133
  */
141
134
  export declare function gitNewPR(git: SimpleGit, files: string[], description: string, author: RuleUser, options?: {
142
135
  content?: string[];
@@ -40,20 +40,23 @@ exports.gitGetPRMetadata = gitGetPRMetadata;
40
40
  exports.gitGetAllPR = gitGetAllPR;
41
41
  exports.gitGetClosedPRs = gitGetClosedPRs;
42
42
  exports.gitLoadPR = gitLoadPR;
43
- exports.gitPRReplaceFile = gitPRReplaceFile;
44
43
  exports.gitPRUpdateComments = gitPRUpdateComments;
45
44
  exports.gitClosePR = gitClosePR;
46
45
  exports.gitClosePRRobust = gitClosePRRobust;
47
- exports.gitGetNextPRNumber = gitGetNextPRNumber;
48
46
  exports.gitNewValidationRequest = gitNewValidationRequest;
49
47
  exports.gitNewPR = gitNewPR;
50
48
  const errors_1 = require("../errors");
51
49
  const path_1 = require("path");
52
50
  const fs = __importStar(require("fs/promises"));
53
51
  const repo_tools_1 = require("./repo.tools");
52
+ const repo_1 = require("./repo");
53
+ const utils_matter_1 = require("../utils.matter");
54
54
  /**
55
55
  * Synchronise une branche PR avec son mergeBase pour corriger les références orphelines
56
56
  *
57
+ * @deprecated Utiliser le workflow applicatif (RulesWorkflow.syncPullRequest) et
58
+ * les opérations robustes orientées Git Notes. API conservée pour compatibilité.
59
+ *
57
60
  * FIXME: Cette fonction a un problème de gestion des métadonnées manquantes.
58
61
  *
59
62
  * PROBLÈME IDENTIFIÉ:
@@ -152,9 +155,12 @@ async function gitSyncPR(git, branch, user) {
152
155
  throw new errors_1.GitOperationError(`Failed to sync PR ${branch} with merge base ${mergeBase}: ${mergeError}`, 'pr_sync', { branch, mergeBase, mergeError, fallbackError });
153
156
  }
154
157
  }
155
- // Préserver les fichiers originaux de la PR (ne pas recalculer après merge)
156
- // Note: Après un merge réussi, gitGetDiffFiles retournera [] car il n'y a plus de différences
157
- // Les fichiers de la PR sont une information historique qui doit être préservée
158
+ // Préserver les fichiers originaux de la PR (ne pas recalculer après merge).
159
+ // NOTE IMPORTANTE:
160
+ // - gitSyncPR fait un merge technique pour maintenir la branche de validation à jour.
161
+ // - Ce merge NE DOIT PAS redéfinir le scope métier de la PR.
162
+ // - metadata.files reste la source de vérité (pilotée par add/edit/rename/delete).
163
+ // - Un diff Git post-merge peut être ambigu (base mouvante, merge commit, refs).
158
164
  const updatedMetadata = {
159
165
  ...pr.metadata,
160
166
  files: pr.metadata.files, // Préserver les fichiers originaux
@@ -191,7 +197,7 @@ async function gitSyncPR(git, branch, user) {
191
197
  * @param branch Nom de la branche PR
192
198
  * @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
193
199
  * @returns true si le PR est fermé (dernier commit contient le token de validation)
194
- * DEPRECATED: use gitIsPRClosedRobust instead
200
+ * @deprecated Utiliser gitIsPRClosedRobust.
195
201
  */
196
202
  async function gitIsPRClosed(git, branch) {
197
203
  return gitIsPRClosedRobust(git, branch);
@@ -199,6 +205,8 @@ async function gitIsPRClosed(git, branch) {
199
205
  /**
200
206
  * Vérifie si une branche PR est fermée en utilisant Git Notes (avec fallback)
201
207
  * Version robuste qui remplace gitIsPRClosed
208
+ * @deprecated Préférer gitLoadPR() pour récupérer l'état PR complet.
209
+ *
202
210
  * @param git Instance Git
203
211
  * @param branch Nom de la branche PR
204
212
  * @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
@@ -232,13 +240,33 @@ async function gitIsPRClosedRobust(git, branch, config) {
232
240
  async function gitGetPRMetadata(git, branch, config) {
233
241
  const gitConf = (0, repo_tools_1.gitLoad)(config);
234
242
  try {
235
- // D'abord essayer de chercher sur le commit HEAD de la branche
236
- let metadata = await (0, repo_tools_1.gitReadNote)(git, branch, gitConf.gitNotes.namespace, 20);
237
- // DEPRECATED: we now we always use the last 20 commits to find the metadata
238
- // if (!metadata) {
239
- // metadata = await gitReadNote(git, branch, gitConf.gitNotes.namespace, 20);
240
- // }
241
- return metadata;
243
+ const expectedPRId = Number.parseInt(branch.replace(gitConf.validationPrefix, ''), 10);
244
+ // D'abord essayer de chercher rapidement sur HEAD / historique récent.
245
+ const metadata = await (0, repo_tools_1.gitReadNote)(git, branch, gitConf.gitNotes.namespace, 20);
246
+ if (metadata) {
247
+ // Si ce n'est pas une branche de validation standard, on retourne tel quel.
248
+ if (Number.isNaN(expectedPRId)) {
249
+ return metadata;
250
+ }
251
+ // Branche de validation: rejeter une note dont l'ID ne correspond pas.
252
+ if (metadata.id === expectedPRId) {
253
+ return metadata;
254
+ }
255
+ if (gitConf.verbose) {
256
+ console.warn(`⚠️ gitGetPRMetadata(${branch}): note récente id=${metadata.id}, attendu=${expectedPRId}. Recherche approfondie...`);
257
+ }
258
+ }
259
+ // Fallback robuste: scanner plus large et ne retenir que la note du bon PR.
260
+ if (!Number.isNaN(expectedPRId)) {
261
+ const log = await git.log(['-n', '200', branch]);
262
+ for (const commit of log.all) {
263
+ const note = await (0, repo_tools_1.gitReadNote)(git, commit.hash, gitConf.gitNotes.namespace, 1);
264
+ if (note && note.id === expectedPRId) {
265
+ return note;
266
+ }
267
+ }
268
+ }
269
+ return null;
242
270
  }
243
271
  catch (error) {
244
272
  // This typically happens if the branch doesn't exist, which is a valid case.
@@ -287,6 +315,8 @@ async function gitGetAllPR(git, options = {}) {
287
315
  }
288
316
  /**
289
317
  * Finds closed PRs by scanning the history of the draft branch for merge commits with PR metadata.
318
+ * @deprecated Utiliser gitGetAllPR(git, { closed: true }) pour unifier le listing PR.
319
+ *
290
320
  * @param git SimpleGit instance
291
321
  * @param gitConfig The git configuration
292
322
  * @returns A list of PRInfo objects for closed PRs.
@@ -317,12 +347,13 @@ async function gitGetClosedPRs(git, gitConfig) {
317
347
  }
318
348
  async function gitLoadPR(git, branch) {
319
349
  try {
350
+ const gitConf = (0, repo_tools_1.gitLoad)();
320
351
  // Load metadata from
321
352
  const metadata = await gitGetPRMetadata(git, branch);
322
353
  if (!metadata) {
323
354
  throw new errors_1.GitOperationError(`PR not found for branch ${branch}`, 'pr_load', { branch });
324
355
  }
325
- const files = metadata.files || (await (0, repo_tools_1.gitGetDiffFiles)(git, branch, metadata?.mergeBase, '.md'));
356
+ const files = metadata.files || (await (0, repo_tools_1.gitGetDiffFiles)(git, branch, metadata?.mergeBase || gitConf.draftBranch, '.md'));
326
357
  // Récupérer les infos du dernier commit de la branche
327
358
  const log = await git.log({ from: branch, to: branch, maxCount: 1 });
328
359
  const lastCommit = log.latest;
@@ -342,49 +373,6 @@ async function gitLoadPR(git, branch) {
342
373
  throw new errors_1.GitOperationError(`Failed to retrieve PR: ${error}`, 'pr_load');
343
374
  }
344
375
  }
345
- /**
346
- * Remplace explicitement un nom de fichier dans les métadonnées d'une PR.
347
- * Utilisé pour refléter immédiatement les renames sans dépendre de git diff.
348
- */
349
- async function gitPRReplaceFile(git, branch, options = {}, config) {
350
- const { remove, add } = options;
351
- if (!remove && !add) {
352
- return;
353
- }
354
- const gitConf = (0, repo_tools_1.gitLoad)(config);
355
- const metadata = await gitGetPRMetadata(git, branch, config);
356
- if (!metadata) {
357
- return;
358
- }
359
- const currentFiles = Array.isArray(metadata.files) ? metadata.files : [];
360
- const seen = new Set();
361
- const nextFiles = [];
362
- for (const file of currentFiles) {
363
- if (!file) {
364
- continue;
365
- }
366
- if (remove && file === remove) {
367
- continue;
368
- }
369
- if (seen.has(file)) {
370
- continue;
371
- }
372
- seen.add(file);
373
- nextFiles.push(file);
374
- }
375
- if (add && !seen.has(add)) {
376
- nextFiles.push(add);
377
- seen.add(add);
378
- }
379
- const changed = currentFiles.length !== nextFiles.length ||
380
- currentFiles.some((file, idx) => file !== nextFiles[idx]);
381
- if (!changed) {
382
- return;
383
- }
384
- metadata.files = nextFiles;
385
- const headCommitHash = (await git.revparse([branch])).trim();
386
- await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, metadata, gitConf.gitNotes.namespace);
387
- }
388
376
  async function gitPRUpdateComments(git, branch, details, config) {
389
377
  const gitConf = (0, repo_tools_1.gitLoad)(config);
390
378
  const metadata = await gitGetPRMetadata(git, branch, config);
@@ -405,7 +393,8 @@ async function gitPRUpdateComments(git, branch, details, config) {
405
393
  return gitLoadPR(git, branch);
406
394
  }
407
395
  /**
408
- * DEPRECATED: use gitClosePRRobust instead
396
+ * @deprecated Utiliser gitClosePRRobust.
397
+ *
409
398
  * Ferme un PR en ajoutant un commit avec le token de validation
410
399
  * @param git Instance Git
411
400
  * @param branch Branche PR à fermer
@@ -483,6 +472,7 @@ async function gitClosePRRobust(git, branch, closedBy, message, config) {
483
472
  // Note: "ours" dans ce contexte signifie "garder la version de la branche qu'on merge" (la PR)
484
473
  // car nous sommes sur rule-editor et on merge la PR
485
474
  let mergeResult;
475
+ let mergeCommitHash;
486
476
  try {
487
477
  mergeResult = await git
488
478
  .env({
@@ -498,6 +488,7 @@ async function gitClosePRRobust(git, branch, closedBy, message, config) {
498
488
  '-m', `Merge branch '${branch}'`,
499
489
  branch
500
490
  ]);
491
+ mergeCommitHash = (await git.revparse(['HEAD'])).trim();
501
492
  }
502
493
  catch (mergeError) {
503
494
  if (gitConf.verbose) {
@@ -537,15 +528,22 @@ async function gitClosePRRobust(git, branch, closedBy, message, config) {
537
528
  }
538
529
  }
539
530
  // Amend le commit de merge avec les bons contenus
540
- await git.commit('--amend', ['--no-edit'], {
531
+ const amendResult = await git.commit('--amend', ['--no-edit'], {
541
532
  '--author': `${closedBy.name} <${closedBy.email}>`
542
533
  });
534
+ mergeCommitHash = amendResult.commit || (await git.revparse(['HEAD'])).trim();
543
535
  mergeResult = { summary: { changes: prFiles.length } };
544
536
  }
545
537
  catch (fallbackError) {
546
538
  throw new errors_1.PRClosureError(`Failed to merge PR ${branch} with all strategies: ${mergeError}\nFallback error: ${fallbackError}`, branch, 'merge_failed');
547
539
  }
548
540
  }
541
+ // 6. Attacher aussi la note au commit de merge sur la branche cible.
542
+ // Cela garantit la traçabilité même si la branche PR est supprimée.
543
+ if (!mergeCommitHash) {
544
+ mergeCommitHash = (await git.revparse(['HEAD'])).trim();
545
+ }
546
+ await (0, repo_tools_1.gitWriteNote)(git, mergeCommitHash, finalMetadata, gitConf.gitNotes.namespace);
549
547
  const result = {
550
548
  metadata: finalMetadata,
551
549
  failed: false,
@@ -583,37 +581,6 @@ async function gitClosePRRobust(git, branch, closedBy, message, config) {
583
581
  (0, repo_tools_1.unlock)('checkout');
584
582
  }
585
583
  }
586
- /**
587
- * Trouve le prochain numéro de PR disponible (protégé contre la concurrence)
588
- * @param git Instance Git
589
- * @param validationPrefix Préfixe des branches de validation
590
- * @returns Prochain numéro de PR
591
- */
592
- async function gitGetNextPRNumber(git, validationPrefix) {
593
- await (0, repo_tools_1.lock)('pr-number');
594
- try {
595
- const allBranches = await (0, repo_tools_1.gitGetAllBranches)(git);
596
- const prBranches = allBranches.filter((branch) => branch.startsWith(validationPrefix));
597
- if (prBranches.length === 0) {
598
- return 1;
599
- }
600
- const numbers = prBranches
601
- .map((branch) => {
602
- const numberPart = branch.substring(validationPrefix.length);
603
- // Gérer les cas où le nom de branche contient plus que le numéro après le préfixe
604
- const actualNumber = parseInt(numberPart.split('-')[0], 10);
605
- return actualNumber;
606
- })
607
- .filter((num) => !isNaN(num));
608
- return numbers.length > 0 ? Math.max(...numbers) + 1 : 1;
609
- }
610
- catch (error) {
611
- throw new errors_1.GitOperationError(`Failed to get next PR number: ${error}`, 'pr_number', { validationPrefix, originalError: error });
612
- }
613
- finally {
614
- (0, repo_tools_1.unlock)('pr-number');
615
- }
616
- }
617
584
  /**
618
585
  * Crée une nouvelle branche de validation (pour un PR vers rule-editor)
619
586
  * à partir d'une branche source (typiquement rule-editor).
@@ -639,6 +606,20 @@ async function gitNewValidationRequest(git, files, description, author, options
639
606
  throw new errors_1.PRCreationError('Author is required', undefined, files);
640
607
  }
641
608
  try {
609
+ // Vérification atomique côté API: empêcher plusieurs PR ouvertes sur les mêmes fichiers.
610
+ const openPRs = await gitGetAllPR(git, {
611
+ closed: false,
612
+ validationPrefix: validationBranchPrefix
613
+ });
614
+ const conflictingPRs = openPRs.filter((pr) => pr.files.some((prFile) => files.includes(prFile)));
615
+ if (conflictingPRs.length > 0) {
616
+ const conflictingFiles = files.filter((file) => conflictingPRs.some((pr) => pr.files.includes(file)));
617
+ if (gitConfig.verbose) {
618
+ console.log('🌶️ DEBUG: gitNewValidationRequest -- conflicting PRs:', conflictingPRs.map((pr) => pr.branch));
619
+ }
620
+ const firstConflict = conflictingPRs[0].branch;
621
+ throw new errors_1.PRCreationError(`Le fichier est déjà dans la validation en cours #${firstConflict.replace(validationBranchPrefix, '')}`, firstConflict, conflictingFiles);
622
+ }
642
623
  // ✅ CLEANUP: Nettoyer tout état de merge corrompu avant de créer la branche
643
624
  // Cela évite l'erreur "you need to resolve your current index first"
644
625
  try {
@@ -654,7 +635,7 @@ async function gitNewValidationRequest(git, files, description, author, options
654
635
  console.warn('Cleanup before branch creation failed (non-critical):', cleanupError);
655
636
  }
656
637
  }
657
- const nextID = await gitGetNextPRNumber(git, validationBranchPrefix);
638
+ const nextID = await (0, repo_1.gitAllocateNextPRNumber)(git, validationBranchPrefix, gitConfig);
658
639
  const newBranchName = `${validationBranchPrefix}${nextID}`;
659
640
  await git.checkoutBranch(newBranchName, sourceBranch);
660
641
  const initialCommitMessage = `#${nextID} ${description}`;
@@ -663,9 +644,16 @@ async function gitNewValidationRequest(git, files, description, author, options
663
644
  if (!content[idx]) {
664
645
  continue;
665
646
  }
666
- // console.log('writeFile',files[idx],content[idx]);
647
+ let fileContent = content[idx];
648
+ // Assurer l'ID documentaire lors de la création d'une PR avec contenus fournis.
649
+ if (gitConfig.withID !== false) {
650
+ const parsed = (0, utils_matter_1.matterParse)(fileContent);
651
+ parsed.matter = (0, repo_1.gitEnsureMatterID)(parsed.matter, gitConfig, newBranchName, files[idx]);
652
+ fileContent = (0, utils_matter_1.matterSerialize)(parsed.content, parsed.matter);
653
+ }
654
+ // console.log('writeFile',files[idx],fileContent);
667
655
  // This writeFile is in-memory and stages the file.
668
- await _writeFileAndCommit(git, files[idx], content[idx], author, gitConfig, initialCommitMessage);
656
+ await _writeFileAndCommit(git, files[idx], fileContent, author, gitConfig, initialCommitMessage);
669
657
  }
670
658
  const metadata = {
671
659
  id: nextID,
@@ -710,7 +698,7 @@ async function gitNewValidationRequest(git, files, description, author, options
710
698
  }
711
699
  }
712
700
  /**
713
- * DEPRECATED
701
+ * @deprecated Utiliser gitNewValidationRequest().
714
702
  */
715
703
  async function gitNewPR(git, files, description, author, options = {}) {
716
704
  return gitNewValidationRequest(git, files, description, author, options);
@@ -123,6 +123,11 @@ export declare function gitGetAllBranches(git: SimpleGit, options?: {
123
123
  * @param baseBranch Branche de base (par défaut: main)
124
124
  * @param filter Filtre optionnel (ex: '.md')
125
125
  * @returns Liste des fichiers modifiés
126
+ *
127
+ * NOTE IMPORTANTE:
128
+ * - Ce résultat est une vue TECHNIQUE du diff Git (commits/références), pas une vérité métier PR.
129
+ * - Ne pas utiliser seul pour reconstruire metadata.files d'une PR.
130
+ * - La source de vérité métier reste metadata.files maintenu par add/edit/rename/delete.
126
131
  */
127
132
  export declare function gitGetDiffFiles(git: SimpleGit, targetBranch: string, baseBranch?: string, filter?: string): Promise<string[]>;
128
133
  /**
@@ -129,7 +129,7 @@ function gitLoad(defaultConfig) {
129
129
  }
130
130
  // console.log(`🌶️ gitLoad: First Loading git config`,defaultConfig);
131
131
  const verbose = defaultConfig?.verbose || process.env.GIT_VERBOSE === 'true';
132
- const remoteUrl = defaultConfig?.remoteUrl || process.env.GIT_REMOTE_URL;
132
+ const remoteUrl = defaultConfig?.remoteUrl ?? process.env.GIT_REMOTE_URL;
133
133
  const repoPath = defaultConfig?.repoPath || process.env.GIT_REPO_PATH;
134
134
  const uploadPath = defaultConfig?.uploadPath || process.env.GIT_UPLOAD_PATH;
135
135
  const draftBranch = defaultConfig?.draftBranch || process.env.DEFAULT_BRANCH_DRAFT;
@@ -625,6 +625,11 @@ async function gitGetAllBranches(git, options = {}) {
625
625
  * @param baseBranch Branche de base (par défaut: main)
626
626
  * @param filter Filtre optionnel (ex: '.md')
627
627
  * @returns Liste des fichiers modifiés
628
+ *
629
+ * NOTE IMPORTANTE:
630
+ * - Ce résultat est une vue TECHNIQUE du diff Git (commits/références), pas une vérité métier PR.
631
+ * - Ne pas utiliser seul pour reconstruire metadata.files d'une PR.
632
+ * - La source de vérité métier reste metadata.files maintenu par add/edit/rename/delete.
628
633
  */
629
634
  async function gitGetDiffFiles(git, targetBranch, baseBranch, filter) {
630
635
  const gitConfig = gitLoad();
@@ -122,8 +122,6 @@ export interface FrontMatter {
122
122
  id?: number;
123
123
  /** Titre descriptif de la règle */
124
124
  title: string;
125
- /** Ancien nom de fichier (pour renommage) */
126
- oldfile?: string;
127
125
  /** Auteur original de la règle (format git: "Name <email>") */
128
126
  author?: string;
129
127
  /** Email du validateur assigné à cette règle */
@@ -121,13 +121,9 @@ function matterParse(markdown) {
121
121
  * ✅ NOUVELLE FONCTION pour reconstruire le contenu complet
122
122
  */
123
123
  function matterSerializeFromRule(rule) {
124
- // Créer un objet propre pour le front-matter (exclure oldfile)
125
- const matter = { ...rule.matter };
126
- delete matter.oldfile;
127
- return matterSerialize(rule.content, matter);
124
+ return matterSerialize(rule.content, rule.matter);
128
125
  }
129
126
  function matterSerialize(content, matter) {
130
- // Créer un objet propre pour le front-matter (exclure oldfile)
131
127
  const cleanMatter = { ...matter };
132
128
  const result = Object.keys(cleanMatter).reduce((acc, key) => {
133
129
  const value = cleanMatter[key];
@@ -1,48 +1,161 @@
1
1
  import { FrontMatter } from "./rules/types";
2
- export declare function extractCaptcha(base64Image: string, openai: any): Promise<{
3
- number: any;
4
- cost: number;
5
- }>;
2
+ /** Raw image data extracted from a PDF page. */
3
+ export interface PageImage {
4
+ /** Raw pixel buffer: RGBA, RGB, grayscale bytes, or JPEG-encoded bytes. */
5
+ data: Buffer;
6
+ /** Pixel format / encoding of the buffer content. */
7
+ type: 'jpeg' | 'rgb' | 'rgba' | 'grayscale';
8
+ width: number;
9
+ height: number;
10
+ }
11
+ /** Dimensions of a single GFM table detected on a page. */
12
+ export interface PageTable {
13
+ /** Number of data rows (header and separator lines excluded). */
14
+ rows: number;
15
+ /** Number of columns inferred from the header line. */
16
+ cols: number;
17
+ }
18
+ /** Structured representation of a single PDF page. */
19
+ export interface Page {
20
+ pageNumber: number;
21
+ /** Cleaned body text, with reconstructed GFM tables. Running headers/footers removed. */
22
+ text: string;
23
+ /**
24
+ * Running page header detected across ≥ 3 consecutive pages (e.g. chapter title,
25
+ * magazine section name). Undefined for the poppler engine or single-page PDFs.
26
+ */
27
+ header?: string;
28
+ /**
29
+ * Running page footer detected across ≥ 3 consecutive pages (e.g. folio number,
30
+ * document title). Undefined for the poppler engine or single-page PDFs.
31
+ */
32
+ footer?: string;
33
+ /**
34
+ * Dimensions of each GFM table found in `text`.
35
+ * Used by `callLLMForParsingPDF` to select an appropriate model:
36
+ * pages with wide (cols > 3) or long (rows > 10) tables are upgraded
37
+ * from `LOW-fast` to `MEDIUM-fast` automatically.
38
+ */
39
+ tables: PageTable[];
40
+ /** Images extracted from the page. Always empty for the poppler engine. */
41
+ images: PageImage[];
42
+ }
43
+ /** Extraction backend selection. */
44
+ export type PdftotextEngine = 'poppler' | 'mupdf';
6
45
  /**
7
- * Calls GPT to parse a PDF file and convert it to markdown format.
46
+ * Converts extracted PDF content to clean Markdown via LLM.
8
47
  *
9
- * @param {string} inputfile - The name of the PDF file being processed
10
- * @param {any} pdfData - The extracted content from the PDF file
11
- * @param {any[]} links - Optional array of links extracted from the PDF to be integrated into the markdown
12
- * @param {string} model - The model to use for parsing (default: "MEDIUM-fast")
13
- * @returns {Promise<{markdown: string, cost: number}>} - The parsed markdown content and the cost of the API call
48
+ * Two paths depending on the `pdfData` type:
49
+ *
50
+ * **`Page[]` (mupdf path)** `MapLLM.reduce`, one page per chunk.
51
+ * Each page is processed by `mupdfPagePrompt` (heading normalisation, broken-cell
52
+ * fusion, repeated-header removal). No frontmatter is added here; the caller
53
+ * (`pdf2markdown`) prepends the single YAML block.
54
+ *
55
+ * NOTE: `finalReduce` is intentionally disabled — it is reserved for a future
56
+ * "N-page light summary" feature where a second LLM pass synthesises the whole
57
+ * document into a shorter version.
58
+ *
59
+ * A raw `string` (e.g. from `html2markdown`) is automatically wrapped into a
60
+ * single `Page` so both callers share the exact same code path.
61
+ *
62
+ * @param inputfile - Original file path (used for logging only).
63
+ * @param pdfData - Either a `Page[]` array (mupdf) or a raw string.
64
+ * @param links - External links appended as `## Liens` footer (string path).
65
+ * @param model - LLM model alias (default: `'MEDIUM-fast'`).
14
66
  */
15
- export declare function callLLMForParsingPDF(inputfile: string, pdfData: any, links?: any[], model?: string): Promise<{
67
+ export declare function callLLMForParsingPDF(inputfile: string, pdfData: Page[] | string, links?: {
68
+ text: string;
69
+ href: string;
70
+ }[], model?: string): Promise<{
16
71
  markdown: string;
17
72
  cost: number;
18
73
  }>;
19
74
  /**
20
- * Parses an HTML file and converts it to markdown using GPT.
75
+ * Parses an HTML file and converts it to markdown using LLM.
21
76
  *
22
77
  * @param {string} output - The directory path where the output markdown file will be saved.
23
78
  * @param {string} file - The path to the HTML file to be parsed.
24
79
  * @param {string} service - The service name used as part of the output filename output.
25
80
  * @param {string} model - The model to use for parsing (default: "MEDIUM-fast")
26
- * @returns {Promise<{markdown: string, cost: number}>} - The generated markdown content and the cost of the GPT API call.
81
+ * @returns {Promise<{markdown: string, cost: number}>} - The generated markdown content and the cost of the API call.
27
82
  */
28
83
  export declare function html2markdown(output: string, file: string, service: string, model?: string): Promise<{
29
84
  markdown: string;
30
85
  cost: number;
31
86
  }>;
32
87
  /**
33
- * Parse un PDF en effectuant :
34
- * 1. Le nettoyage du PDF avec Ghostscript.
35
- * 2. Sa conversion en XML via pdftohtml.
36
- * 3. (Optionnellement) Le passage du contenu converti au modèle LLM pour analyser la structure.
37
- *
38
- * @param {string} outputDir - Dossier de sortie pour le fichier markdown.
39
- * @param {string} pdf - Chemin vers le fichier PDF à analyser.
40
- * @param {FrontMatter|null} matter - Métadonnées du document (title, service, author, role). Si null, utilise le nom du PDF pour le titre.
41
- * @param {string} model - Le modèle à utiliser (défaut: "MEDIUM-fast").
42
- * @returns {Promise<{markdown: string, cost: number, outputPath: string}>} - Le markdown structuré, le coût et le chemin du fichier de sortie.
88
+ * Extracts plain text from a PDF using the system `pdftotext` binary (poppler-utils).
89
+ *
90
+ * - Pages are delimited by form-feed (\f) characters in the binary's output.
91
+ * - Excessive blank lines are normalised (3+ 2).
92
+ * - Images are NOT extracted (always []).
93
+ *
94
+ * NOTE: Better alternative is `pdftotext_pdfjs` which uses Mozilla's PDF engine
95
+ * to extract text + images + links in a single Node.js-native pass, with better
96
+ * table reconstruction for complex layouts. See `pdftotext_pdfjs` for details.
97
+ *
98
+ * @param {string} pdfPath - Absolute path to the PDF file.
99
+ * @param {string} outputDir - Directory used for temporary files.
100
+ * @returns {Promise<Page[]>} One `Page` per PDF page, text-only.
101
+ */
102
+ export declare function pdftotext_poppler(pdfPath: string, outputDir: string): Promise<Page[]>;
103
+ /**
104
+ * Extracts text, reconstructed tables, links, and optionally page-raster images
105
+ * from a PDF using the **mupdf** npm package (WASM build of the MuPDF C library).
106
+ *
107
+ * Key advantages over the poppler engine:
108
+ * - `table-hunt` detects tables geometrically even in **untagged** PDFs.
109
+ * - `segment` splits the page into logical reading-order blocks.
110
+ * - Significantly faster than pdfjs for large documents.
111
+ * - No shell binary dependency (pure WASM, runs anywhere Node.js does).
112
+ *
113
+ * Images (opt-in via `withImages: true`): each page is rasterised at 1.5× scale
114
+ * (≈ 113 DPI). The `imageFormat` option controls encoding:
115
+ *
116
+ * | format | size/page (base64) | notes |
117
+ * |-------------|-------------------|--------------------------------|
118
+ * | `'rgb'` | ≈ 4.4 MB | raw RGB, lossless, large |
119
+ * | `'gray'` | ≈ 1.5 MB | raw grayscale, 3× smaller |
120
+ * | `'jpeg'` | ≈ 100–200 KB | JPEG quality 75, 31× smaller |
121
+ *
122
+ * Disabled by default because image data quickly exhausts stdout buffers for
123
+ * large documents. Use `jpeg` for production with vision models.
124
+ *
125
+ * NOTE: `mupdf` is ESM-only. Extraction is delegated to a standalone
126
+ * `mupdf-extract.mjs` worker spawned via `execAsync`, which avoids any
127
+ * ESM/CJS interoperability issues in the main process and under ts-jest.
128
+ *
129
+ * @param {string} pdfPath - Absolute path to the PDF file.
130
+ * @param {object} [options]
131
+ * @param {boolean} [options.withImages=false] - Rasterise each page.
132
+ * @param {'rgb'|'gray'|'jpeg'} [options.imageFormat='rgb'] - Pixel encoding.
133
+ * @returns {Promise<Page[]>} One `Page` per PDF page with text, GFM tables, and optional images.
43
134
  */
44
- export declare function pdf2markdown(outputDir: string, pdf: string, matter: FrontMatter | null, model?: string): Promise<{
135
+ export declare function pdftotext_mupdf(pdfPath: string, options?: {
136
+ withImages?: boolean;
137
+ imageFormat?: 'rgb' | 'gray' | 'jpeg';
138
+ }): Promise<Page[]>;
139
+ /**
140
+ * Converts a PDF to a structured Markdown file.
141
+ *
142
+ * Pipeline:
143
+ * 1. `pdftotext_mupdf` (or poppler) → `Page[]`
144
+ * 2. `callLLMForParsingPDF` — MapLLM.reduce, one page per chunk
145
+ * 3. Prepend a **single** YAML frontmatter block and write to `outputDir`.
146
+ *
147
+ * Model choice: `LOW-fast` is sufficient — mupdf output is already clean GFM;
148
+ * the LLM only normalises headings and removes repeated headers/footers.
149
+ * Use `MEDIUM-fast` for complex layouts that need heavier restructuring.
150
+ *
151
+ * @param outputDir - Directory for the output `.md` file.
152
+ * @param pdf - Absolute path to the PDF file.
153
+ * @param matter - Document metadata; defaults derived from filename.
154
+ * @param model - LLM model alias (default: `'LOW-fast'`).
155
+ * @param engine - Extraction backend (default: `'mupdf'`).
156
+ * @returns `{ markdown, outputPath }` — frontmatter-prefixed markdown and output path.
157
+ */
158
+ export declare function pdf2markdown(outputDir: string, pdf: string, matter: FrontMatter | null, model?: string, engine?: PdftotextEngine): Promise<{
45
159
  markdown: string;
46
- cost: number;
47
160
  outputPath: string;
48
161
  }>;