@yemi33/minions 0.1.333 → 0.1.334
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/CHANGELOG.md +4 -1
- package/engine/ado.js +31 -12
- package/engine/github.js +35 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/engine/ado.js
CHANGED
|
@@ -7,6 +7,7 @@ const path = require('path');
|
|
|
7
7
|
const shared = require('./shared');
|
|
8
8
|
const { exec, getAdoOrgBase, addPrLink, log, ts, dateStamp, PR_STATUS } = shared;
|
|
9
9
|
const { getPrs } = require('./queries');
|
|
10
|
+
const { mutateJsonFileLocked } = shared;
|
|
10
11
|
|
|
11
12
|
// Lazy require to avoid circular dependency — only needed for engine().handlePostMerge
|
|
12
13
|
let _engine = null;
|
|
@@ -99,7 +100,15 @@ async function forEachActivePr(config, token, callback) {
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
if (projectUpdated > 0) {
|
|
102
|
-
|
|
103
|
+
mutateJsonFileLocked(shared.projectPrPath(project), (currentPrs) => {
|
|
104
|
+
// Merge updated PRs into the locked copy by ID
|
|
105
|
+
for (const updatedPr of prs) {
|
|
106
|
+
const idx = currentPrs.findIndex(p => p.id === updatedPr.id);
|
|
107
|
+
if (idx >= 0) currentPrs[idx] = updatedPr;
|
|
108
|
+
else currentPrs.push(updatedPr);
|
|
109
|
+
}
|
|
110
|
+
return currentPrs;
|
|
111
|
+
}, { defaultValue: [] });
|
|
103
112
|
totalUpdated += projectUpdated;
|
|
104
113
|
}
|
|
105
114
|
}
|
|
@@ -174,11 +183,11 @@ async function pollPrStatus(config) {
|
|
|
174
183
|
if (authorId) {
|
|
175
184
|
try {
|
|
176
185
|
const metricsPath = path.join(__dirname, 'metrics.json');
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
186
|
+
mutateJsonFileLocked(metricsPath, (metrics) => {
|
|
187
|
+
if (!metrics[authorId]) metrics[authorId] = {};
|
|
188
|
+
if (newReviewStatus === 'approved') metrics[authorId].prsApproved = (metrics[authorId].prsApproved || 0) + 1;
|
|
189
|
+
else metrics[authorId].prsRejected = (metrics[authorId].prsRejected || 0) + 1;
|
|
190
|
+
});
|
|
182
191
|
} catch (err) { log('warn', `Metrics update: ${err.message}`); }
|
|
183
192
|
}
|
|
184
193
|
}
|
|
@@ -196,8 +205,8 @@ async function pollPrStatus(config) {
|
|
|
196
205
|
|
|
197
206
|
const buildStatuses = [...latest.values()].filter(s => {
|
|
198
207
|
const ctx = ((s.context?.genre || '') + '/' + (s.context?.name || '')).toLowerCase();
|
|
199
|
-
return ctx
|
|
200
|
-
ctx
|
|
208
|
+
return /\bcodecoverage\b/.test(ctx) || /\bbuild\b/.test(ctx) ||
|
|
209
|
+
/\bdeploy\b/.test(ctx) || /(?:^|\/)ci(?:\/|$)/.test(ctx);
|
|
201
210
|
});
|
|
202
211
|
|
|
203
212
|
let buildStatus = 'none';
|
|
@@ -248,7 +257,8 @@ async function pollPrHumanComments(config) {
|
|
|
248
257
|
const threadsData = await adoFetch(threadsUrl, token);
|
|
249
258
|
const threads = threadsData.value || [];
|
|
250
259
|
|
|
251
|
-
const
|
|
260
|
+
const cutoffStr = pr.humanFeedback?.lastProcessedCommentDate || pr.created || '1970-01-01';
|
|
261
|
+
const cutoffMs = new Date(cutoffStr).getTime() || 0;
|
|
252
262
|
|
|
253
263
|
// Collect ALL human comments on the PR for full context
|
|
254
264
|
const allHumanComments = [];
|
|
@@ -269,7 +279,8 @@ async function pollPrHumanComments(config) {
|
|
|
269
279
|
allHumanComments.push(entry);
|
|
270
280
|
|
|
271
281
|
// Track which comments are new (for triggering — any new comment triggers a fix)
|
|
272
|
-
|
|
282
|
+
const commentMs = comment.publishedDate ? new Date(comment.publishedDate).getTime() : 0;
|
|
283
|
+
if (commentMs && commentMs > cutoffMs) {
|
|
273
284
|
newHumanComments.push(entry);
|
|
274
285
|
}
|
|
275
286
|
}
|
|
@@ -285,7 +296,7 @@ async function pollPrHumanComments(config) {
|
|
|
285
296
|
// Provide ALL comments as context — the agent needs full thread context to fix properly
|
|
286
297
|
const feedbackContent = allHumanComments
|
|
287
298
|
.map(c => {
|
|
288
|
-
const isNew = c.date >
|
|
299
|
+
const isNew = (new Date(c.date).getTime() || 0) > cutoffMs;
|
|
289
300
|
return `${isNew ? '**[NEW]** ' : ''}**${c.author}** (${c.date}):\n${c.content.replace(/@minions\s*/gi, '').trim()}`;
|
|
290
301
|
})
|
|
291
302
|
.join('\n\n---\n\n');
|
|
@@ -412,7 +423,15 @@ async function reconcilePrs(config) {
|
|
|
412
423
|
}
|
|
413
424
|
|
|
414
425
|
if (projectAdded > 0 || projectUpdated > 0 || backfilled > 0) {
|
|
415
|
-
|
|
426
|
+
mutateJsonFileLocked(prPath, (currentPrs) => {
|
|
427
|
+
// Merge reconciled PRs into the locked copy by ID
|
|
428
|
+
for (const pr of existingPrs) {
|
|
429
|
+
const idx = currentPrs.findIndex(p => p.id === pr.id);
|
|
430
|
+
if (idx >= 0) currentPrs[idx] = pr;
|
|
431
|
+
else currentPrs.push(pr);
|
|
432
|
+
}
|
|
433
|
+
return currentPrs;
|
|
434
|
+
}, { defaultValue: [] });
|
|
416
435
|
totalAdded += projectAdded;
|
|
417
436
|
if (projectUpdated > 0) log('info', `PR reconciliation: linked ${projectUpdated} existing PR(s) to PRD items in ${project.name}`);
|
|
418
437
|
}
|
package/engine/github.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const shared = require('./shared');
|
|
8
|
-
const { exec, getProjects, projectPrPath, projectWorkItemsPath, safeJson, safeWrite, MINIONS_DIR, addPrLink, getPrLinks, log, ts, dateStamp, PR_STATUS } = shared;
|
|
8
|
+
const { exec, getProjects, projectPrPath, projectWorkItemsPath, safeJson, safeWrite, mutateJsonFileLocked, MINIONS_DIR, addPrLink, getPrLinks, log, ts, dateStamp, PR_STATUS } = shared;
|
|
9
9
|
const { getPrs } = require('./queries');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
@@ -71,7 +71,14 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
if (projectUpdated > 0) {
|
|
74
|
-
|
|
74
|
+
mutateJsonFileLocked(projectPrPath(project), (currentPrs) => {
|
|
75
|
+
for (const updatedPr of prs) {
|
|
76
|
+
const idx = currentPrs.findIndex(p => p.id === updatedPr.id);
|
|
77
|
+
if (idx >= 0) currentPrs[idx] = updatedPr;
|
|
78
|
+
else currentPrs.push(updatedPr);
|
|
79
|
+
}
|
|
80
|
+
return currentPrs;
|
|
81
|
+
}, { defaultValue: [] });
|
|
75
82
|
totalUpdated += projectUpdated;
|
|
76
83
|
}
|
|
77
84
|
}
|
|
@@ -105,7 +112,14 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
105
112
|
}
|
|
106
113
|
}
|
|
107
114
|
if (centralUpdated > 0) {
|
|
108
|
-
|
|
115
|
+
mutateJsonFileLocked(centralPath, (currentPrs) => {
|
|
116
|
+
for (const updatedPr of centralPrs) {
|
|
117
|
+
const idx = currentPrs.findIndex(p => p.id === updatedPr.id);
|
|
118
|
+
if (idx >= 0) currentPrs[idx] = updatedPr;
|
|
119
|
+
else currentPrs.push(updatedPr);
|
|
120
|
+
}
|
|
121
|
+
return currentPrs;
|
|
122
|
+
}, { defaultValue: [] });
|
|
109
123
|
totalUpdated += centralUpdated;
|
|
110
124
|
}
|
|
111
125
|
|
|
@@ -182,11 +196,11 @@ async function pollPrStatus(config) {
|
|
|
182
196
|
if (authorId) {
|
|
183
197
|
try {
|
|
184
198
|
const metricsPath = path.join(__dirname, 'metrics.json');
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
199
|
+
mutateJsonFileLocked(metricsPath, (metrics) => {
|
|
200
|
+
if (!metrics[authorId]) metrics[authorId] = {};
|
|
201
|
+
if (newReviewStatus === 'approved') metrics[authorId].prsApproved = (metrics[authorId].prsApproved || 0) + 1;
|
|
202
|
+
else metrics[authorId].prsRejected = (metrics[authorId].prsRejected || 0) + 1;
|
|
203
|
+
});
|
|
190
204
|
} catch (err) { log('warn', `Metrics update: ${err.message}`); }
|
|
191
205
|
}
|
|
192
206
|
}
|
|
@@ -258,7 +272,8 @@ async function pollPrHumanComments(config) {
|
|
|
258
272
|
return true;
|
|
259
273
|
});
|
|
260
274
|
|
|
261
|
-
const
|
|
275
|
+
const cutoffStr = pr.humanFeedback?.lastProcessedCommentDate || pr.created || '1970-01-01';
|
|
276
|
+
const cutoffMs = new Date(cutoffStr).getTime() || 0;
|
|
262
277
|
|
|
263
278
|
// Collect ALL human comments for full context, track new ones for triggering
|
|
264
279
|
const allCommentEntries = [];
|
|
@@ -275,7 +290,8 @@ async function pollPrHumanComments(config) {
|
|
|
275
290
|
allCommentEntries.push(entry);
|
|
276
291
|
|
|
277
292
|
// Any new comment triggers a fix — no @minions filter needed
|
|
278
|
-
|
|
293
|
+
const dateMs = date ? new Date(date).getTime() : 0;
|
|
294
|
+
if (dateMs && dateMs > cutoffMs) {
|
|
279
295
|
newComments.push(entry);
|
|
280
296
|
}
|
|
281
297
|
}
|
|
@@ -290,7 +306,7 @@ async function pollPrHumanComments(config) {
|
|
|
290
306
|
// Provide ALL comments as context — the agent needs full thread context to fix properly
|
|
291
307
|
const feedbackContent = allCommentEntries
|
|
292
308
|
.map(c => {
|
|
293
|
-
const isNew = c.date >
|
|
309
|
+
const isNew = (new Date(c.date).getTime() || 0) > cutoffMs;
|
|
294
310
|
return `${isNew ? '**[NEW]** ' : ''}**${c.author}** (${c.date}):\n${c.content.replace(/@minions\s*/gi, '').trim()}`;
|
|
295
311
|
})
|
|
296
312
|
.join('\n\n---\n\n');
|
|
@@ -397,7 +413,14 @@ async function reconcilePrs(config) {
|
|
|
397
413
|
}
|
|
398
414
|
|
|
399
415
|
if (projectAdded > 0 || backfilled > 0) {
|
|
400
|
-
|
|
416
|
+
mutateJsonFileLocked(prPath, (currentPrs) => {
|
|
417
|
+
for (const pr of existingPrs) {
|
|
418
|
+
const idx = currentPrs.findIndex(p => p.id === pr.id);
|
|
419
|
+
if (idx >= 0) currentPrs[idx] = pr;
|
|
420
|
+
else currentPrs.push(pr);
|
|
421
|
+
}
|
|
422
|
+
return currentPrs;
|
|
423
|
+
}, { defaultValue: [] });
|
|
401
424
|
totalAdded += projectAdded;
|
|
402
425
|
}
|
|
403
426
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.334",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|