@yemi33/minions 0.1.110 → 0.1.112
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 +13 -0
- package/dashboard/js/live-stream.js +1 -1
- package/dashboard/js/modal-qa.js +2 -2
- package/dashboard/js/utils.js +4 -1
- package/dashboard.js +35 -40
- package/engine/ado.js +13 -1
- package/engine/github.js +13 -1
- package/engine/lifecycle.js +4 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.112 (2026-04-01)
|
|
4
|
+
|
|
5
|
+
### Engine
|
|
6
|
+
- engine/ado.js
|
|
7
|
+
- engine/github.js
|
|
8
|
+
- engine/lifecycle.js
|
|
9
|
+
|
|
10
|
+
### Dashboard
|
|
11
|
+
- dashboard.js
|
|
12
|
+
- dashboard/js/live-stream.js
|
|
13
|
+
- dashboard/js/modal-qa.js
|
|
14
|
+
- dashboard/js/utils.js
|
|
15
|
+
|
|
3
16
|
## 0.1.110 (2026-04-01)
|
|
4
17
|
|
|
5
18
|
### Dashboard
|
|
@@ -19,7 +19,7 @@ function renderLiveChatMessage(raw) {
|
|
|
19
19
|
if (block.type === 'tool_use') {
|
|
20
20
|
el.innerHTML += '<div style="background:var(--surface);border:1px solid var(--border);padding:4px 8px;border-radius:4px;margin:2px 0;font-size:10px;color:var(--muted);cursor:pointer" onclick="this.nextElementSibling.style.display=this.nextElementSibling.style.display===\'none\'?\'block\':\'none\'">' +
|
|
21
21
|
'\u{1F527} ' + escHtml(block.name || 'tool') + '</div>' +
|
|
22
|
-
'<div style="display:none;background:var(--bg);padding:4px 8px;border-radius:4px;margin:0 0 4px;font-size:10px;font-family:monospace;white-space:pre-wrap;max-height:200px;overflow-y:auto;color:var(--muted)">' + escHtml(JSON.stringify(block.input, null, 2).slice(0, 500)) + '</div>';
|
|
22
|
+
'<div style="display:none;background:var(--bg);padding:4px 8px;border-radius:4px;margin:0 0 4px;font-size:10px;font-family:monospace;white-space:pre-wrap;max-height:200px;overflow-y:auto;color:var(--muted)">' + escHtml(JSON.stringify(block.input || {}, null, 2).slice(0, 500)) + '</div>';
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
package/dashboard/js/modal-qa.js
CHANGED
|
@@ -227,8 +227,8 @@ async function _processQaMessage(message, selection) {
|
|
|
227
227
|
_modalDocContext.content = display;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
// If editing paused an active PRD, show re-execute actions
|
|
231
|
-
if (data.pausedPrd && capturedFilePath) {
|
|
230
|
+
// If editing paused an active PRD, show re-execute actions (only for plan .md files)
|
|
231
|
+
if (data.pausedPrd && capturedFilePath && /^plans\/.*\.md$/.test(capturedFilePath)) {
|
|
232
232
|
const planFile = capturedFilePath.replace(/^plans\//, '');
|
|
233
233
|
const esc = planFile.replace(/'/g, "\\'");
|
|
234
234
|
const actionDiv = document.createElement('div');
|
package/dashboard/js/utils.js
CHANGED
|
@@ -67,7 +67,10 @@ function renderMd(s) {
|
|
|
67
67
|
html = html.replace(/(?<!\w)\*([^*\n]+)\*(?!\w)/g, '<em>$1</em>');
|
|
68
68
|
html = html.replace(/(?<!\w)_([^_\n]+)_(?!\w)/g, '<em>$1</em>');
|
|
69
69
|
html = html.replace(/~~(.+?)~~/g, '<s>$1</s>');
|
|
70
|
-
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g,
|
|
70
|
+
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, function(_, text, href) {
|
|
71
|
+
if (/^(javascript|data|vbscript):/i.test(href)) return text;
|
|
72
|
+
return '<a href="' + href + '" target="_blank" rel="noopener" style="color:var(--blue)">' + text + '</a>';
|
|
73
|
+
});
|
|
71
74
|
|
|
72
75
|
// 3. Block-level processing (line by line)
|
|
73
76
|
var lines = html.split('\n');
|
package/dashboard.js
CHANGED
|
@@ -1453,55 +1453,50 @@ const server = http.createServer(async (req, res) => {
|
|
|
1453
1453
|
manifest.push({ category: e.cat, file: e.file, title: e.title, agent: e.agent, date: e.date, content: content.slice(0, 3000) });
|
|
1454
1454
|
}
|
|
1455
1455
|
|
|
1456
|
-
const
|
|
1456
|
+
const { callLLM, trackEngineUsage } = require('./engine/llm');
|
|
1457
|
+
const BATCH_SIZE = 30; // ~30 entries per batch to stay within Haiku context
|
|
1458
|
+
const batches = [];
|
|
1459
|
+
for (let i = 0; i < manifest.length; i += BATCH_SIZE) {
|
|
1460
|
+
batches.push(manifest.slice(i, i + BATCH_SIZE));
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
const plan = { duplicates: [], reclassify: [], remove: [] };
|
|
1464
|
+
for (let b = 0; b < batches.length; b++) {
|
|
1465
|
+
const batch = batches[b];
|
|
1466
|
+
const offset = b * BATCH_SIZE;
|
|
1467
|
+
const prompt = `You are a knowledge base curator. Analyze these ${batch.length} entries (batch ${b + 1}/${batches.length}, indices ${offset}-${offset + batch.length - 1}) and produce a cleanup plan.
|
|
1457
1468
|
|
|
1458
1469
|
## Entries
|
|
1459
1470
|
|
|
1460
|
-
${
|
|
1461
|
-
${m.title}
|
|
1462
|
-
${m.content.slice(0, 1500)}
|
|
1463
|
-
</entry>`).join('\n\n')}
|
|
1471
|
+
${batch.map((m, i) => `[${offset + i}] ${m.category}/${m.file} | ${m.title} | ${m.date} | ${m.agent || '?'} | ${(m.content || '').slice(0, 200).replace(/\n/g, ' ')}`).join('\n')}
|
|
1464
1472
|
|
|
1465
1473
|
## Instructions
|
|
1466
1474
|
|
|
1467
|
-
1. **Find duplicates**: entries with substantially the same content
|
|
1468
|
-
|
|
1469
|
-
|
|
1475
|
+
1. **Find duplicates**: entries with substantially the same content (same findings, different agents/runs). List pairs by index. Prefer keeping the more recent entry.
|
|
1476
|
+
2. **Find misclassified**: entries in the wrong category.
|
|
1477
|
+
3. **Find stale/empty**: entries with no actionable content (boilerplate, bail-out notes, "no changes needed").
|
|
1470
1478
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
## Output Format
|
|
1474
|
-
|
|
1475
|
-
Respond with ONLY valid JSON (no markdown fences, no preamble):
|
|
1476
|
-
|
|
1477
|
-
{
|
|
1478
|
-
"duplicates": [
|
|
1479
|
-
{ "keep": 0, "remove": [1, 5], "reason": "same PR review findings" }
|
|
1480
|
-
],
|
|
1481
|
-
"reclassify": [
|
|
1482
|
-
{ "index": 3, "from": "conventions", "to": "build-reports", "reason": "..." }
|
|
1483
|
-
],
|
|
1484
|
-
"remove": [
|
|
1485
|
-
{ "index": 7, "reason": "empty bail-out note" }
|
|
1486
|
-
]
|
|
1487
|
-
}
|
|
1479
|
+
Respond with ONLY valid JSON: { "duplicates": [{ "keep": N, "remove": [N], "reason": "..." }], "reclassify": [{ "index": N, "from": "cat", "to": "cat", "reason": "..." }], "remove": [{ "index": N, "reason": "..." }] }
|
|
1480
|
+
If nothing to do: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
1488
1481
|
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
timeout: 180000, label: 'kb-sweep', model: 'haiku', maxTurns: 1
|
|
1494
|
-
});
|
|
1495
|
-
trackEngineUsage('kb-sweep', result.usage);
|
|
1482
|
+
const result = await callLLM(prompt, 'Output only JSON.', {
|
|
1483
|
+
timeout: 120000, label: 'kb-sweep', model: 'haiku', maxTurns: 1
|
|
1484
|
+
});
|
|
1485
|
+
trackEngineUsage('kb-sweep', result.usage);
|
|
1496
1486
|
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1487
|
+
let batchPlan;
|
|
1488
|
+
try {
|
|
1489
|
+
let jsonStr = (result.text || '').trim();
|
|
1490
|
+
const fenceMatch = jsonStr.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
1491
|
+
if (fenceMatch) jsonStr = fenceMatch[1].trim();
|
|
1492
|
+
batchPlan = JSON.parse(jsonStr);
|
|
1493
|
+
} catch {
|
|
1494
|
+
console.log(`[kb-sweep] batch ${b + 1}/${batches.length} returned invalid JSON, skipping`);
|
|
1495
|
+
continue;
|
|
1496
|
+
}
|
|
1497
|
+
if (batchPlan.duplicates) plan.duplicates.push(...batchPlan.duplicates);
|
|
1498
|
+
if (batchPlan.reclassify) plan.reclassify.push(...batchPlan.reclassify);
|
|
1499
|
+
if (batchPlan.remove) plan.remove.push(...batchPlan.remove);
|
|
1505
1500
|
}
|
|
1506
1501
|
|
|
1507
1502
|
let removed = 0, reclassified = 0, merged = 0;
|
package/engine/ado.js
CHANGED
|
@@ -389,7 +389,19 @@ async function reconcilePrs(config) {
|
|
|
389
389
|
e.log('info', `PR reconciliation: added ${prId} (branch: ${branch}${confirmedItemId ? ', linked to ' + confirmedItemId : ''}) to ${project.name}`);
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
|
|
392
|
+
// Backfill prdItems from pr-links for any PR with empty array
|
|
393
|
+
const prLinks = shared.getPrLinks();
|
|
394
|
+
let backfilled = 0;
|
|
395
|
+
for (const pr of existingPrs) {
|
|
396
|
+
const linked = prLinks[pr.id];
|
|
397
|
+
if (linked && !(pr.prdItems || []).includes(linked)) {
|
|
398
|
+
pr.prdItems = Array.isArray(pr.prdItems) ? pr.prdItems : [];
|
|
399
|
+
pr.prdItems.push(linked);
|
|
400
|
+
backfilled++;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (projectAdded > 0 || projectUpdated > 0 || backfilled > 0) {
|
|
393
405
|
shared.safeWrite(prPath, existingPrs);
|
|
394
406
|
totalAdded += projectAdded;
|
|
395
407
|
if (projectUpdated > 0) e.log('info', `PR reconciliation: linked ${projectUpdated} existing PR(s) to PRD items in ${project.name}`);
|
package/engine/github.js
CHANGED
|
@@ -376,7 +376,19 @@ async function reconcilePrs(config) {
|
|
|
376
376
|
e.log('info', `GitHub PR reconciliation: added ${prId} (branch: ${branch}${confirmedItemId ? ', linked to ' + confirmedItemId : ''}) to ${project.name}`);
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
-
|
|
379
|
+
// Backfill prdItems from pr-links for any PR with empty array
|
|
380
|
+
const prLinks = getPrLinks();
|
|
381
|
+
let backfilled = 0;
|
|
382
|
+
for (const pr of existingPrs) {
|
|
383
|
+
const linked = prLinks[pr.id];
|
|
384
|
+
if (linked && !(pr.prdItems || []).includes(linked)) {
|
|
385
|
+
pr.prdItems = Array.isArray(pr.prdItems) ? pr.prdItems : [];
|
|
386
|
+
pr.prdItems.push(linked);
|
|
387
|
+
backfilled++;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (projectAdded > 0 || backfilled > 0) {
|
|
380
392
|
safeWrite(prPath, existingPrs);
|
|
381
393
|
totalAdded += projectAdded;
|
|
382
394
|
}
|
package/engine/lifecycle.js
CHANGED
|
@@ -724,6 +724,9 @@ async function handlePostMerge(pr, project, config, newStatus) {
|
|
|
724
724
|
|
|
725
725
|
if (newStatus !== 'merged') return;
|
|
726
726
|
|
|
727
|
+
// Mark review as approved since it was merged
|
|
728
|
+
pr.reviewStatus = 'approved';
|
|
729
|
+
|
|
727
730
|
// Resolve linked work item from pr-links or PR branch name
|
|
728
731
|
let mergedItemId = getPrLinks()[pr.id];
|
|
729
732
|
if (!mergedItemId && pr.branch) {
|
|
@@ -761,7 +764,7 @@ async function handlePostMerge(pr, project, config, newStatus) {
|
|
|
761
764
|
if (item && item.status !== 'done') {
|
|
762
765
|
log('info', `Post-merge: marking work item ${mergedItemId} as done (was ${item.status}) for ${pr.id}`);
|
|
763
766
|
item.status = 'done';
|
|
764
|
-
item.completedAt =
|
|
767
|
+
item.completedAt = ts();
|
|
765
768
|
item._mergedVia = pr.id;
|
|
766
769
|
shared.safeWrite(wiPath, items);
|
|
767
770
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.112",
|
|
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"
|