agentic-api 1.0.5 → 2.0.26

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 (104) hide show
  1. package/README.md +118 -22
  2. package/dist/src/agents/agents.example.d.ts +3 -0
  3. package/dist/src/agents/agents.example.js +38 -0
  4. package/dist/src/agents/authentication.js +2 -0
  5. package/dist/src/agents/digestor.js +25 -16
  6. package/dist/src/agents/prompts.d.ts +2 -2
  7. package/dist/src/agents/prompts.js +112 -49
  8. package/dist/src/agents/reducer.core.d.ts +12 -0
  9. package/dist/src/agents/reducer.core.js +207 -0
  10. package/dist/src/agents/reducer.d.ts +3 -0
  11. package/dist/src/agents/reducer.example.d.ts +28 -0
  12. package/dist/src/agents/reducer.example.js +118 -0
  13. package/dist/src/agents/reducer.js +19 -0
  14. package/dist/src/agents/reducer.loaders.d.ts +34 -0
  15. package/dist/src/agents/reducer.loaders.js +122 -0
  16. package/dist/src/agents/reducer.process.d.ts +16 -0
  17. package/dist/src/agents/reducer.process.js +143 -0
  18. package/dist/src/agents/reducer.tools.d.ts +29 -0
  19. package/dist/src/agents/reducer.tools.js +157 -0
  20. package/dist/src/agents/reducer.types.d.ts +50 -0
  21. package/dist/src/agents/reducer.types.js +5 -0
  22. package/dist/src/agents/simulator.d.ts +47 -0
  23. package/dist/src/agents/simulator.executor.d.ts +26 -0
  24. package/dist/src/agents/simulator.executor.js +132 -0
  25. package/dist/src/agents/simulator.js +205 -0
  26. package/dist/src/agents/simulator.prompts.d.ts +16 -0
  27. package/dist/src/agents/simulator.prompts.js +108 -0
  28. package/dist/src/agents/simulator.types.d.ts +42 -0
  29. package/dist/src/agents/simulator.types.js +2 -0
  30. package/dist/src/agents/simulator.utils.d.ts +20 -0
  31. package/dist/src/agents/simulator.utils.js +87 -0
  32. package/dist/src/execute.d.ts +14 -7
  33. package/dist/src/execute.js +359 -84
  34. package/dist/src/index.d.ts +9 -0
  35. package/dist/src/index.js +14 -0
  36. package/dist/src/princing.openai.d.ts +9 -2
  37. package/dist/src/princing.openai.js +16 -11
  38. package/dist/src/prompts.d.ts +3 -2
  39. package/dist/src/prompts.js +207 -72
  40. package/dist/src/rag/embeddings.d.ts +103 -0
  41. package/dist/src/rag/embeddings.js +466 -0
  42. package/dist/src/rag/index.d.ts +12 -0
  43. package/dist/src/rag/index.js +40 -0
  44. package/dist/src/rag/lucene.d.ts +45 -0
  45. package/dist/src/rag/lucene.js +227 -0
  46. package/dist/src/rag/parser.d.ts +68 -0
  47. package/dist/src/rag/parser.js +192 -0
  48. package/dist/src/rag/tools.d.ts +76 -0
  49. package/dist/src/rag/tools.js +196 -0
  50. package/dist/src/rag/types.d.ts +178 -0
  51. package/dist/src/rag/types.js +21 -0
  52. package/dist/src/rag/usecase.d.ts +16 -0
  53. package/dist/src/rag/usecase.js +79 -0
  54. package/dist/src/rules/errors.d.ts +60 -0
  55. package/dist/src/rules/errors.js +97 -0
  56. package/dist/src/rules/git/git.e2e.helper.d.ts +104 -0
  57. package/dist/src/rules/git/git.e2e.helper.js +488 -0
  58. package/dist/src/rules/git/git.health.d.ts +66 -0
  59. package/dist/src/rules/git/git.health.js +354 -0
  60. package/dist/src/rules/git/git.helper.d.ts +129 -0
  61. package/dist/src/rules/git/git.helper.js +53 -0
  62. package/dist/src/rules/git/index.d.ts +6 -0
  63. package/dist/src/rules/git/index.js +76 -0
  64. package/dist/src/rules/git/repo.d.ts +128 -0
  65. package/dist/src/rules/git/repo.js +900 -0
  66. package/dist/src/rules/git/repo.pr.d.ts +137 -0
  67. package/dist/src/rules/git/repo.pr.js +589 -0
  68. package/dist/src/rules/git/repo.tools.d.ts +134 -0
  69. package/dist/src/rules/git/repo.tools.js +730 -0
  70. package/dist/src/rules/index.d.ts +8 -0
  71. package/dist/src/rules/index.js +25 -0
  72. package/dist/src/rules/messages.d.ts +17 -0
  73. package/dist/src/rules/messages.js +21 -0
  74. package/dist/src/rules/types.ctrl.d.ts +28 -0
  75. package/dist/src/rules/types.ctrl.js +2 -0
  76. package/dist/src/rules/types.d.ts +510 -0
  77. package/dist/src/rules/types.helpers.d.ts +132 -0
  78. package/dist/src/rules/types.helpers.js +2 -0
  79. package/dist/src/rules/types.js +33 -0
  80. package/dist/src/rules/user.mapper.d.ts +61 -0
  81. package/dist/src/rules/user.mapper.js +160 -0
  82. package/dist/src/rules/utils/slug.d.ts +22 -0
  83. package/dist/src/rules/utils/slug.js +35 -0
  84. package/dist/src/rules/utils.matter.d.ts +66 -0
  85. package/dist/src/rules/utils.matter.js +208 -0
  86. package/dist/src/rules/utils.slug.d.ts +22 -0
  87. package/dist/src/rules/utils.slug.js +35 -0
  88. package/dist/src/scrapper.d.ts +3 -2
  89. package/dist/src/scrapper.js +33 -37
  90. package/dist/src/stategraph/index.d.ts +8 -0
  91. package/dist/src/stategraph/index.js +21 -0
  92. package/dist/src/stategraph/stategraph.d.ts +91 -0
  93. package/dist/src/stategraph/stategraph.js +241 -0
  94. package/dist/src/stategraph/stategraph.storage.d.ts +41 -0
  95. package/dist/src/stategraph/stategraph.storage.js +166 -0
  96. package/dist/src/stategraph/types.d.ts +139 -0
  97. package/dist/src/stategraph/types.js +19 -0
  98. package/dist/src/types.d.ts +68 -39
  99. package/dist/src/types.js +53 -89
  100. package/dist/src/usecase.d.ts +4 -0
  101. package/dist/src/usecase.js +44 -0
  102. package/dist/src/utils.d.ts +12 -5
  103. package/dist/src/utils.js +30 -13
  104. package/package.json +9 -3
@@ -0,0 +1,589 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.gitSyncPR = gitSyncPR;
37
+ exports.gitIsPRClosed = gitIsPRClosed;
38
+ exports.gitIsPRClosedRobust = gitIsPRClosedRobust;
39
+ exports.gitGetPRMetadata = gitGetPRMetadata;
40
+ exports.gitGetAllPR = gitGetAllPR;
41
+ exports.gitGetClosedPRs = gitGetClosedPRs;
42
+ exports.gitLoadPR = gitLoadPR;
43
+ exports.gitPRUpdateComments = gitPRUpdateComments;
44
+ exports.gitClosePR = gitClosePR;
45
+ exports.gitClosePRRobust = gitClosePRRobust;
46
+ exports.gitGetNextPRNumber = gitGetNextPRNumber;
47
+ exports.gitNewValidationRequest = gitNewValidationRequest;
48
+ exports.gitNewPR = gitNewPR;
49
+ const errors_1 = require("../errors");
50
+ const path_1 = require("path");
51
+ const fs = __importStar(require("fs/promises"));
52
+ const repo_tools_1 = require("./repo.tools");
53
+ /**
54
+ * Synchronise une branche PR avec son mergeBase pour corriger les références orphelines
55
+ *
56
+ * FIXME: Cette fonction a un problème de gestion des métadonnées manquantes.
57
+ *
58
+ * PROBLÈME IDENTIFIÉ:
59
+ * - Les branches de validation (rule-validation-*) DOIVENT avoir des métadonnées PR
60
+ * - Actuellement, si les métadonnées sont manquantes, la fonction fait un return silencieux
61
+ * - Ceci masque un vrai problème : pourquoi une branche de validation n'a pas de métadonnées ?
62
+ *
63
+ * CAUSES POSSIBLES:
64
+ * - Problème de namespace Git Notes (création vs lecture dans des namespaces différents)
65
+ * - Race condition entre création et lecture des Git Notes
66
+ * - Configuration E2E différente entre helper.newPR() et gitSyncPR()
67
+ * - Corruption des Git Notes lors de la simulation du "reset" dans le test
68
+ *
69
+ * SOLUTION RECOMMANDÉE:
70
+ * - Ajouter un diagnostic approfondi pour identifier pourquoi les métadonnées sont manquantes
71
+ * - Distinguer le traitement : strict pour branches de validation, tolérant pour autres branches
72
+ * - Corriger la cause racine au lieu de masquer le symptôme
73
+ *
74
+ * IMPACT MÉTIER:
75
+ * - Une branche de validation sans métadonnées indique un problème d'intégrité des données
76
+ * - Le comportement actuel (return silencieux) peut masquer des corruptions de données
77
+ * - Les utilisateurs pourraient perdre des informations de PR importantes
78
+ *
79
+ * @param git Instance Git
80
+ * @param branch Nom de la branche PR à synchroniser
81
+ * @param user Utilisateur qui effectue la synchronisation
82
+ * @returns Promise<void>
83
+ */
84
+ async function gitSyncPR(git, branch, user) {
85
+ const gitConfig = (0, repo_tools_1.gitLoad)();
86
+ await (0, repo_tools_1.lock)('checkout');
87
+ let currentBranch;
88
+ try {
89
+ currentBranch = await git.revparse(['--abbrev-ref', 'HEAD']);
90
+ // Vérifier que la branche PR existe
91
+ const allBranches = await (0, repo_tools_1.gitGetAllBranches)(git);
92
+ if (!allBranches.includes(branch)) {
93
+ throw new errors_1.BranchNotFoundError(branch);
94
+ }
95
+ // Charger les métadonnées de la PR
96
+ const pr = await gitLoadPR(git, branch);
97
+ if (!pr || !pr.metadata) {
98
+ throw new errors_1.GitOperationError(`PR metadata not found for branch ${branch}`, 'pr_sync', { branch });
99
+ }
100
+ if (pr.metadata.status === 'closed') {
101
+ return;
102
+ }
103
+ // Vérifier que le mergeBase existe et est accessible
104
+ const mergeBase = pr.metadata.mergeBase;
105
+ if (!mergeBase) {
106
+ throw new errors_1.GitOperationError(`No merge base found for PR ${branch}`, 'pr_sync', { branch });
107
+ }
108
+ try {
109
+ // Vérifier que le mergeBase est un commit valide
110
+ await git.raw(['rev-parse', '--verify', `${mergeBase}^{commit}`]);
111
+ }
112
+ catch (error) {
113
+ throw new errors_1.GitOperationError(`Merge base ${mergeBase} is not a valid commit for PR ${branch}`, 'pr_sync', { branch, mergeBase, error });
114
+ }
115
+ // Basculer vers la branche PR
116
+ await git.checkout(branch);
117
+ // Effectuer le merge avec stratégie "recursive" et option "theirs" pour gérer les conflits
118
+ try {
119
+ // const merge =await git.env({
120
+ // GIT_COMMITTER_NAME: user.name,
121
+ // GIT_COMMITTER_EMAIL: user.email
122
+ // }).raw([
123
+ //
124
+ // ours = (branche PR avec travail éditeur) → PRÉSERVÉ le travail éditeur
125
+ // theirs = (mergeBase avec modifications concurrentes) → IGNORÉ en cas de conflit
126
+ const merge = await git.raw([
127
+ 'merge',
128
+ mergeBase,
129
+ '--no-ff',
130
+ '--strategy', 'recursive',
131
+ '--strategy-option', 'ours',
132
+ '-m', `Sync: Merge ${mergeBase} into ${branch}`
133
+ ]);
134
+ //console.log('merge',merge);
135
+ }
136
+ catch (mergeError) {
137
+ console.log('mergeError', mergeError);
138
+ // FIXME: this is a fallback, we should not use it
139
+ // En cas d'échec du merge, tenter avec une stratégie plus agressive
140
+ try {
141
+ await git.raw(['merge', '--abort']).catch(() => { }); // Ignore si pas de merge en cours
142
+ await git.raw([
143
+ 'merge',
144
+ mergeBase,
145
+ '--no-ff',
146
+ '--strategy', 'ours',
147
+ '-m', `Sync: Force merge ${mergeBase} into ${branch} (conflicts resolved)`
148
+ ]);
149
+ }
150
+ catch (fallbackError) {
151
+ throw new errors_1.GitOperationError(`Failed to sync PR ${branch} with merge base ${mergeBase}: ${mergeError}`, 'pr_sync', { branch, mergeBase, mergeError, fallbackError });
152
+ }
153
+ }
154
+ // Préserver les fichiers originaux de la PR (ne pas recalculer après merge)
155
+ // Note: Après un merge réussi, gitGetDiffFiles retournera [] car il n'y a plus de différences
156
+ // Les fichiers de la PR sont une information historique qui doit être préservée
157
+ const updatedMetadata = {
158
+ ...pr.metadata,
159
+ files: pr.metadata.files, // Préserver les fichiers originaux
160
+ timestamp: new Date().toISOString(),
161
+ lastSync: new Date().toISOString(), // Ajouter timestamp de synchronisation
162
+ };
163
+ // Get the commit hash of the HEAD of the branch
164
+ const headCommitHash = (await git.revparse([branch])).trim();
165
+ await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, updatedMetadata, gitConfig.gitNotes.namespace);
166
+ }
167
+ catch (error) {
168
+ if (error instanceof errors_1.GitOperationError || error instanceof errors_1.BranchNotFoundError) {
169
+ throw error;
170
+ }
171
+ throw new errors_1.GitOperationError(`Failed to sync PR ${branch}: ${error}`, 'pr_sync', { branch, error });
172
+ }
173
+ finally {
174
+ if (currentBranch) {
175
+ try {
176
+ await git.checkout(currentBranch);
177
+ }
178
+ catch (checkoutError) {
179
+ if (gitConfig.verbose) {
180
+ console.error(`Failed to checkout back to ${currentBranch}: ${checkoutError}`);
181
+ }
182
+ }
183
+ }
184
+ (0, repo_tools_1.unlock)('checkout');
185
+ }
186
+ }
187
+ /**
188
+ * Vérifie si une branche PR est fermée en regardant le message du dernier commit
189
+ * @param git Instance Git
190
+ * @param branch Nom de la branche PR
191
+ * @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
192
+ * @returns true si le PR est fermé (dernier commit contient le token de validation)
193
+ * DEPRECATED: use gitIsPRClosedRobust instead
194
+ */
195
+ async function gitIsPRClosed(git, branch) {
196
+ return gitIsPRClosedRobust(git, branch);
197
+ }
198
+ /**
199
+ * Vérifie si une branche PR est fermée en utilisant Git Notes (avec fallback)
200
+ * Version robuste qui remplace gitIsPRClosed
201
+ * @param git Instance Git
202
+ * @param branch Nom de la branche PR
203
+ * @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
204
+ * @param config Configuration Git (optionnel, utilise la config globale)
205
+ * @returns true si le PR est fermé
206
+ */
207
+ async function gitIsPRClosedRobust(git, branch, config) {
208
+ const gitConfig = (0, repo_tools_1.gitLoad)(config);
209
+ try {
210
+ const note = await gitGetPRMetadata(git, branch, config);
211
+ // If the branch is not found, then METADATA is Null, it is closed
212
+ if (!note) {
213
+ return true;
214
+ }
215
+ return ['closed', 'merged'].includes(note.status);
216
+ }
217
+ catch (error) {
218
+ // If the branch is not found, or not compliant with METADATA, it is closed
219
+ return true;
220
+ }
221
+ }
222
+ /**
223
+ * Vérifie si une branche PR est fermée en utilisant Git Notes (avec fallback)
224
+ * Version robuste qui remplace gitIsPRClosed
225
+ * @param git Instance Git
226
+ * @param branch Nom de la branche PR
227
+ * @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
228
+ * @param config Configuration Git (optionnel, utilise la config globale)
229
+ * @returns true si le PR est fermé
230
+ */
231
+ async function gitGetPRMetadata(git, branch, config) {
232
+ const gitConf = (0, repo_tools_1.gitLoad)(config);
233
+ try {
234
+ // D'abord essayer de chercher sur le commit HEAD de la branche
235
+ let metadata = await (0, repo_tools_1.gitReadNote)(git, branch, gitConf.gitNotes.namespace, 20);
236
+ // DEPRECATED: we now we always use the last 20 commits to find the metadata
237
+ // if (!metadata) {
238
+ // metadata = await gitReadNote(git, branch, gitConf.gitNotes.namespace, 20);
239
+ // }
240
+ return metadata;
241
+ }
242
+ catch (error) {
243
+ // This typically happens if the branch doesn't exist, which is a valid case.
244
+ return null;
245
+ }
246
+ }
247
+ /**
248
+ * Récupère tous les pull requests (branches rule-validation-*) avec leur statut
249
+ * @param git Instance Gitno
250
+ * @param options Configuration pour filtrer les PRs (default: list open PRs)
251
+ * @returns Liste des PRs avec leur statut (au niveau de la branche)
252
+ */
253
+ async function gitGetAllPR(git, options = {}) {
254
+ try {
255
+ const gitConfig = (0, repo_tools_1.gitLoad)();
256
+ const { closed: listOnlyClosed, validationPrefix = gitConfig.validationPrefix, } = options;
257
+ const allLocalBranchesRaw = await (0, repo_tools_1.gitGetAllBranches)(git);
258
+ const prCandidateBranches = allLocalBranchesRaw.filter((branch) => branch.startsWith(validationPrefix));
259
+ const prs = [];
260
+ for (const branch of prCandidateBranches) {
261
+ try {
262
+ const pr = await gitLoadPR(git, branch);
263
+ const isOpen = pr.metadata.status === 'open';
264
+ if (listOnlyClosed) {
265
+ if (!isOpen) {
266
+ prs.push(pr);
267
+ }
268
+ }
269
+ else {
270
+ if (isOpen) {
271
+ prs.push(pr);
272
+ }
273
+ }
274
+ }
275
+ catch (error) {
276
+ if (gitConfig.verbose)
277
+ console.error(`[gitGetAllPR] Error loading PR ${branch}:`, error);
278
+ continue;
279
+ }
280
+ }
281
+ return prs;
282
+ }
283
+ catch (error) {
284
+ throw new errors_1.GitOperationError(`Failed to retrieve PRs: ${error}`, 'pr_listing', { options, originalError: error });
285
+ }
286
+ }
287
+ /**
288
+ * Finds closed PRs by scanning the history of the draft branch for merge commits with PR metadata.
289
+ * @param git SimpleGit instance
290
+ * @param gitConfig The git configuration
291
+ * @returns A list of PRInfo objects for closed PRs.
292
+ */
293
+ async function gitGetClosedPRs(git, gitConfig) {
294
+ const log = await git.log({
295
+ from: gitConfig.draftBranch,
296
+ '--merges': null, // Only show merge commits
297
+ });
298
+ const prs = [];
299
+ for (const commit of log.all) {
300
+ const note = await (0, repo_tools_1.gitReadNote)(git, commit.hash, gitConfig.gitNotes.namespace, 1);
301
+ if (note && note.status === 'closed' && note.id) {
302
+ const pr = {
303
+ branch: `${gitConfig.validationPrefix}${note.id}`, // Reconstruct original branch name
304
+ files: note.files || [],
305
+ open: false,
306
+ lastCommit: commit.hash,
307
+ lastCommitMessage: commit.message,
308
+ lastModified: new Date(commit.date),
309
+ metadata: note,
310
+ toString() { return this.branch; }
311
+ };
312
+ prs.push(pr);
313
+ }
314
+ }
315
+ return prs;
316
+ }
317
+ async function gitLoadPR(git, branch) {
318
+ try {
319
+ // Load metadata from
320
+ const metadata = await gitGetPRMetadata(git, branch);
321
+ if (!metadata) {
322
+ throw new errors_1.GitOperationError(`PR not found for branch ${branch}`, 'pr_load', { branch });
323
+ }
324
+ const files = metadata.files || await (0, repo_tools_1.gitGetDiffFiles)(git, branch, metadata?.mergeBase, '.md');
325
+ // Récupérer les infos du dernier commit de la branche
326
+ const log = await git.log({ from: branch, to: branch, maxCount: 1 });
327
+ const lastCommit = log.latest;
328
+ const pr = {
329
+ branch,
330
+ files,
331
+ open: metadata.status == 'open',
332
+ lastCommit: lastCommit?.hash,
333
+ lastCommitMessage: lastCommit?.message,
334
+ lastModified: lastCommit?.date ? new Date(lastCommit.date) : undefined,
335
+ metadata: metadata, // metadata should exist if we read a note
336
+ toString() { return this.branch; }
337
+ };
338
+ return pr;
339
+ }
340
+ catch (error) {
341
+ throw new errors_1.GitOperationError(`Failed to retrieve PR: ${error}`, 'pr_load');
342
+ }
343
+ }
344
+ async function gitPRUpdateComments(git, branch, details, config) {
345
+ const gitConf = (0, repo_tools_1.gitLoad)(config);
346
+ const metadata = await gitGetPRMetadata(git, branch, config);
347
+ if (!metadata) {
348
+ throw new errors_1.GitOperationError(`PR not found for branch ${branch}`, 'pr_load', { branch });
349
+ }
350
+ // FIXME: only the owner of the PR can update metadatas
351
+ metadata.comments = details.comments || metadata.comments;
352
+ metadata.readyForValidation = details.readyForValidation;
353
+ //
354
+ // update validator only if it is not already set
355
+ if (details.validator) {
356
+ metadata.validator = details.validator;
357
+ }
358
+ // Get the commit hash of the HEAD of the branch
359
+ const headCommitHash = (await git.revparse([branch])).trim();
360
+ await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, metadata, gitConf.gitNotes.namespace);
361
+ return gitLoadPR(git, branch);
362
+ }
363
+ /**
364
+ * DEPRECATED: use gitClosePRRobust instead
365
+ * Ferme un PR en ajoutant un commit avec le token de validation
366
+ * @param git Instance Git
367
+ * @param branch Branche PR à fermer
368
+ * @param author Utilisateur qui ferme le PR
369
+ * @param message Message optionnel à ajouter avant le token de validation
370
+ * @param validationToken Token utilisé pour marquer la fermeture (optionnel, utilise la config par défaut)
371
+ * @returns Hash du commit de fermeture
372
+ * DEPRECATED: use gitClosePRRobust instead
373
+ */
374
+ async function gitClosePR(git, branch, author, message, validationToken) {
375
+ return gitClosePRRobust(git, branch, author, message);
376
+ }
377
+ /**
378
+ * Ferme un PR en utilisant Git Notes (avec fallback vers commit)
379
+ * Version robuste qui remplace gitClosePR
380
+ * @param git Instance Git
381
+ * @param branch Branche PR à fermer
382
+ * @param closedBy Utilisateur qui ferme le PR
383
+ * @param message Message optionnel à ajouter avant le token de validation
384
+ * @param config Configuration Git (optionnel, utilise la config globale)
385
+ * @returns Hash du commit de fermeture ou référence de la note
386
+ */
387
+ async function gitClosePRRobust(git, branch, closedBy, message, config) {
388
+ const gitConf = (0, repo_tools_1.gitLoad)(config);
389
+ // Validation des paramètres
390
+ if (!branch) {
391
+ throw new errors_1.PRClosureError('Branch name is required', branch, 'invalid_parameters');
392
+ }
393
+ if (!closedBy) {
394
+ throw new errors_1.PRClosureError('Closed by is required', branch, 'invalid_parameters');
395
+ }
396
+ if (!branch.startsWith(gitConf.validationPrefix)) {
397
+ throw new errors_1.PRClosureError(`Branch must start with ${gitConf.validationPrefix}`, branch, 'invalid_branch');
398
+ }
399
+ //
400
+ // lock checkout is global
401
+ await (0, repo_tools_1.lock)('checkout');
402
+ let currentBranch;
403
+ try {
404
+ currentBranch = await git.revparse(['--abbrev-ref', 'HEAD']);
405
+ // 1. Read the existing metadata from the PR branch (includes search in recent commits)
406
+ const originalMetadata = await gitGetPRMetadata(git, branch, config);
407
+ if (!originalMetadata) {
408
+ throw new errors_1.PRClosureError(`Could not find metadata on branch ${branch}. Cannot close.`, branch, 'metadata_not_found');
409
+ }
410
+ // 3. Update the metadata note on the PR branch itself to mark it as closed
411
+ const finalMetadata = {
412
+ ...originalMetadata,
413
+ status: 'closed',
414
+ closedBy: closedBy,
415
+ closedAt: new Date().toISOString(),
416
+ comments: [...(originalMetadata.comments || []), message || ''],
417
+ };
418
+ // Get the commit hash of the HEAD of the branch
419
+ const headCommitHash = (await git.revparse([branch])).trim();
420
+ await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, finalMetadata, gitConf.gitNotes.namespace);
421
+ // 4. Now, merge the updated PR branch into the target branch
422
+ await git.checkout(originalMetadata.mergeBase || gitConf.draftBranch);
423
+ const mergeResult = await git
424
+ .env({
425
+ GIT_AUTHOR_NAME: closedBy.name,
426
+ GIT_AUTHOR_EMAIL: closedBy.email,
427
+ GIT_COMMITTER_NAME: closedBy.name,
428
+ GIT_COMMITTER_EMAIL: closedBy.email,
429
+ })
430
+ .merge([
431
+ '--no-ff', '-X', 'theirs', '-m', `Merge branch '${branch}'`, branch
432
+ ]);
433
+ const result = {
434
+ metadata: finalMetadata,
435
+ failed: false,
436
+ conflicts: mergeResult.conflicts,
437
+ raw: mergeResult,
438
+ };
439
+ return result;
440
+ }
441
+ catch (error) {
442
+ if (error instanceof errors_1.PRClosureError)
443
+ throw error;
444
+ throw new errors_1.PRClosureError(`Failed to close PR ${branch}: ${error}`, branch, 'unknown');
445
+ }
446
+ finally {
447
+ if (currentBranch) {
448
+ await git.checkout(currentBranch);
449
+ }
450
+ (0, repo_tools_1.unlock)('checkout');
451
+ }
452
+ }
453
+ /**
454
+ * Trouve le prochain numéro de PR disponible (protégé contre la concurrence)
455
+ * @param git Instance Git
456
+ * @param validationPrefix Préfixe des branches de validation
457
+ * @returns Prochain numéro de PR
458
+ */
459
+ async function gitGetNextPRNumber(git, validationPrefix) {
460
+ await (0, repo_tools_1.lock)('pr-number');
461
+ try {
462
+ const allBranches = await (0, repo_tools_1.gitGetAllBranches)(git);
463
+ const prBranches = allBranches.filter((branch) => branch.startsWith(validationPrefix));
464
+ if (prBranches.length === 0) {
465
+ return 1;
466
+ }
467
+ const numbers = prBranches
468
+ .map((branch) => {
469
+ const numberPart = branch.substring(validationPrefix.length);
470
+ // Gérer les cas où le nom de branche contient plus que le numéro après le préfixe
471
+ const actualNumber = parseInt(numberPart.split('-')[0], 10);
472
+ return actualNumber;
473
+ })
474
+ .filter((num) => !isNaN(num));
475
+ return numbers.length > 0 ? Math.max(...numbers) + 1 : 1;
476
+ }
477
+ catch (error) {
478
+ throw new errors_1.GitOperationError(`Failed to get next PR number: ${error}`, 'pr_number', { validationPrefix, originalError: error });
479
+ }
480
+ finally {
481
+ (0, repo_tools_1.unlock)('pr-number');
482
+ }
483
+ }
484
+ /**
485
+ * Crée une nouvelle branche de validation (pour un PR vers rule-editor)
486
+ * à partir d'une branche source (typiquement rule-editor).
487
+ * Fait un commit initial vide et écrit une note Git avec les métadonnées du travail à faire.
488
+ * @param git Instance SimpleGit
489
+ * @param files Liste des fichiers qui seront concernés par ce travail de validation
490
+ * @param description Description du travail ou du PR
491
+ * @param author Utilisateur qui initie ce travail
492
+ * @param options Options (content when file creation, editorBranch, validationBranchPrefix)
493
+ * @returns PRInfo contenant les informations de la branche créée et ses métadonnées initiales
494
+ */
495
+ async function gitNewValidationRequest(git, files, description, author, options = {}) {
496
+ await (0, repo_tools_1.lock)('new-pr');
497
+ let currentBranch;
498
+ try {
499
+ currentBranch = await git.revparse(['--abbrev-ref', 'HEAD']);
500
+ const gitConfig = (0, repo_tools_1.gitLoad)();
501
+ const { content = options.content || [], editorBranch: sourceBranch = options.editorBranch || gitConfig.draftBranch, validationBranchPrefix = options.validationBranchPrefix || gitConfig.validationPrefix } = options;
502
+ if ((!files || !files.length)) {
503
+ throw new errors_1.PRCreationError('At least one file must be specified for a new validation branch');
504
+ }
505
+ if (!author) {
506
+ throw new errors_1.PRCreationError('Author is required', undefined, files);
507
+ }
508
+ try {
509
+ const nextID = await gitGetNextPRNumber(git, validationBranchPrefix);
510
+ const newBranchName = `${validationBranchPrefix}${nextID}`;
511
+ await git.checkoutBranch(newBranchName, sourceBranch);
512
+ const initialCommitMessage = `#${nextID} ${description}`;
513
+ // create or update files
514
+ for (let idx = 0; idx < files.length; idx++) {
515
+ if (!content[idx]) {
516
+ continue;
517
+ }
518
+ // console.log('writeFile',files[idx],content[idx]);
519
+ // This writeFile is in-memory and stages the file.
520
+ await _writeFileAndCommit(git, files[idx], content[idx], author, gitConfig, initialCommitMessage);
521
+ }
522
+ const metadata = {
523
+ id: nextID,
524
+ status: 'open',
525
+ timestamp: new Date().toISOString(),
526
+ validator: '',
527
+ readyForValidation: false,
528
+ createdBy: author,
529
+ mergeBase: options.mergeBase || sourceBranch,
530
+ comments: [description || `Write description here...`],
531
+ files: files,
532
+ };
533
+ // Get the commit hash of the HEAD of the new branch
534
+ const headCommitHash = (await git.revparse([newBranchName])).trim();
535
+ await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, metadata, gitConfig.gitNotes.namespace);
536
+ const log = await git.log({ from: newBranchName, to: newBranchName, maxCount: 1 });
537
+ const lastCommit = log.latest;
538
+ return {
539
+ branch: newBranchName,
540
+ files: files,
541
+ open: true,
542
+ metadata: metadata,
543
+ lastCommit: lastCommit?.hash,
544
+ lastCommitMessage: lastCommit?.message,
545
+ lastModified: lastCommit?.date ? new Date(lastCommit.date) : undefined,
546
+ toString() { return this.branch; }
547
+ };
548
+ }
549
+ catch (error) {
550
+ if (error instanceof errors_1.PRCreationError || error instanceof errors_1.GitOperationError) {
551
+ throw error;
552
+ }
553
+ // Utiliser la variable "files" définie dans la portée externe du try
554
+ throw new errors_1.PRCreationError(`Failed to create validation branch: ${error}`, undefined, files);
555
+ }
556
+ }
557
+ finally {
558
+ if (currentBranch) {
559
+ await git.checkout(currentBranch);
560
+ }
561
+ (0, repo_tools_1.unlock)('new-pr');
562
+ }
563
+ }
564
+ /**
565
+ * DEPRECATED
566
+ */
567
+ async function gitNewPR(git, files, description, author, options = {}) {
568
+ return gitNewValidationRequest(git, files, description, author, options);
569
+ }
570
+ /**
571
+ * PRIVATE TOOLS
572
+ */
573
+ const _writeFileAndCommit = async (git, filePath, content, user, config, commitMsg) => {
574
+ const gitConf = config;
575
+ if (!gitConf || !gitConf.repoPath) {
576
+ throw new errors_1.GitOperationError('_writeFileAndCommit requires a config with a valid repoPath.', 'internal_error');
577
+ }
578
+ // Créer les répertoires parents si nécessaire
579
+ const fullPath = (0, path_1.join)(gitConf.repoPath, filePath);
580
+ const dir = (0, path_1.dirname)(fullPath);
581
+ await fs.mkdir(dir, { recursive: true });
582
+ // Écrire le fichier
583
+ await fs.writeFile(fullPath, content, { encoding: 'utf8', flag: 'w' });
584
+ await git.add(filePath);
585
+ const commit = await git.commit(commitMsg || `commit`, [filePath], {
586
+ '--author': `${user.name} <${user.email}>`
587
+ });
588
+ return commit;
589
+ };