@yemi33/minions 0.1.641 → 0.1.643
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 +9 -1
- package/dashboard/js/render-pipelines.js +4 -2
- package/dashboard/js/render-work-items.js +31 -1
- package/dashboard.js +29 -2
- package/engine.js +32 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.643 (2026-04-09)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
- track notes, plans, and PRDs as work item artifacts
|
|
7
|
+
|
|
8
|
+
## 0.1.642 (2026-04-09)
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
- show artifact links in work item detail modal
|
|
4
12
|
|
|
5
13
|
### Fixes
|
|
6
14
|
- clear _retryCount on successful completion
|
|
@@ -372,9 +372,11 @@ async function _abortPipeline(id, btn) {
|
|
|
372
372
|
try {
|
|
373
373
|
var res = await fetch('/api/pipelines/abort', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: id }) });
|
|
374
374
|
if (res.ok) {
|
|
375
|
-
|
|
375
|
+
var d = await res.json().catch(function() { return {}; });
|
|
376
|
+
showToast('cmd-toast', 'Pipeline aborted — ' + (d.cancelledWorkItems || 0) + ' work items cancelled', true);
|
|
376
377
|
if (btn) { btn.textContent = '\u2713 Aborted'; btn.style.color = 'var(--red)'; }
|
|
377
|
-
|
|
378
|
+
refresh();
|
|
379
|
+
openPipelineDetail(id);
|
|
378
380
|
} else { var d = await res.json().catch(function() { return {}; }); alert('Abort failed: ' + (d.error || 'unknown')); if (btn) { btn.textContent = 'Abort'; btn.style.pointerEvents = ''; btn.style.opacity = ''; } }
|
|
379
381
|
} catch (e) { alert('Error: ' + e.message); if (btn) { btn.textContent = 'Abort'; btn.style.pointerEvents = ''; btn.style.opacity = ''; } }
|
|
380
382
|
}
|
|
@@ -444,6 +444,20 @@ function openWorkItemDetail(id) {
|
|
|
444
444
|
if (item.references?.length) html += field('References', item.references.map(r => '<a href="' + escHtml(r.url) + '" target="_blank" style="color:var(--blue)">' + escHtml(r.title || r.url) + '</a>' + (r.type ? ' <span style="color:var(--muted);font-size:10px">(' + escHtml(r.type) + ')</span>' : '')).join('<br>'));
|
|
445
445
|
if (item._humanFeedback) html += field('Human Feedback', (item._humanFeedback.rating === 'up' ? '👍' : '👎') + (item._humanFeedback.comment ? ' — ' + escHtml(item._humanFeedback.comment) : ''));
|
|
446
446
|
if (item._pr) html += field('Pull Request', '<a href="' + escHtml(item._prUrl || '#') + '" target="_blank" style="color:var(--blue)">' + escHtml(item._pr) + '</a>');
|
|
447
|
+
|
|
448
|
+
// Artifacts — output log, branch, skills, etc.
|
|
449
|
+
var arts = item._artifacts || {};
|
|
450
|
+
var artPills = '';
|
|
451
|
+
var pillStyle = 'display:inline-flex;align-items:center;gap:3px;padding:2px 8px;border-radius:10px;font-size:10px;cursor:pointer;background:var(--surface2);border:1px solid var(--border);color:var(--text)';
|
|
452
|
+
if (arts.outputLog) artPills += '<span onclick="viewAgentOutput(\'' + escHtml(arts.outputLog) + '\')" style="' + pillStyle + '">📄 Output Log</span> ';
|
|
453
|
+
if (arts.branch) artPills += '<span style="' + pillStyle + ';cursor:default">🌿 ' + escHtml(arts.branch) + '</span> ';
|
|
454
|
+
if (arts.plan) artPills += '<span onclick="planView(\'' + escHtml(arts.plan) + '\')" style="' + pillStyle + '">📋 Plan</span> ';
|
|
455
|
+
if (arts.prd) artPills += '<span onclick="planView(\'' + escHtml(arts.prd) + '\')" style="' + pillStyle + '">📄 PRD</span> ';
|
|
456
|
+
if (arts.sourcePlan) artPills += '<span onclick="planView(\'' + escHtml(arts.sourcePlan) + '\')" style="' + pillStyle + '">📋 Source Plan</span> ';
|
|
457
|
+
if (arts.notes && arts.notes.length > 0) arts.notes.forEach(function(n) { artPills += '<span onclick="closeModal();switchPage(\'inbox\')" style="' + pillStyle + '">📝 ' + escHtml(n.replace(/\.md$/, '').slice(0, 30)) + '</span> '; });
|
|
458
|
+
if (arts.skills && arts.skills.length > 0) arts.skills.forEach(function(s) { artPills += '<span onclick="openSkill(\'' + escHtml(s) + '\',\'minions\',\'\')" style="' + pillStyle + '">⚙ ' + escHtml(s) + '</span> '; });
|
|
459
|
+
if (artPills) html += field('Artifacts', '<div style="display:flex;flex-wrap:wrap;gap:4px">' + artPills + '</div>');
|
|
460
|
+
|
|
447
461
|
if (item._totalCostUsd != null) html += field('Cumulative Cost', '$' + Number(item._totalCostUsd).toFixed(4));
|
|
448
462
|
if (item._totalInputTokens) html += field('Total Input Tokens', Number(item._totalInputTokens).toLocaleString());
|
|
449
463
|
if (item._totalOutputTokens) html += field('Total Output Tokens', Number(item._totalOutputTokens).toLocaleString());
|
|
@@ -466,4 +480,20 @@ function openAllWorkItems() {
|
|
|
466
480
|
document.getElementById('modal').classList.add('open');
|
|
467
481
|
}
|
|
468
482
|
|
|
469
|
-
|
|
483
|
+
function viewAgentOutput(logPath) {
|
|
484
|
+
document.getElementById('modal-title').textContent = 'Agent Output';
|
|
485
|
+
document.getElementById('modal-body').innerHTML = '<p style="color:var(--muted)">Loading...</p>';
|
|
486
|
+
document.getElementById('modal-body').style.fontFamily = 'Consolas, monospace';
|
|
487
|
+
document.getElementById('modal-body').style.whiteSpace = 'pre-wrap';
|
|
488
|
+
document.getElementById('modal').classList.add('open');
|
|
489
|
+
fetch('/api/agent-output?file=' + encodeURIComponent(logPath))
|
|
490
|
+
.then(function(r) { return r.text(); })
|
|
491
|
+
.then(function(content) {
|
|
492
|
+
document.getElementById('modal-body').textContent = content;
|
|
493
|
+
})
|
|
494
|
+
.catch(function() {
|
|
495
|
+
document.getElementById('modal-body').innerHTML = '<p style="color:var(--red)">Failed to load output.</p>';
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
window.MinionsWork = { wiRow, renderWorkItems, editWorkItem, submitWorkItemEdit, deleteWorkItem, archiveWorkItem, toggleWorkItemArchive, retryWorkItem, wiPrev, wiNext, feedbackWorkItem, submitFeedback, openCreateWorkItemModal, openWorkItemDetail, openAllWorkItems, viewAgentOutput };
|
package/dashboard.js
CHANGED
|
@@ -3883,7 +3883,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3883
3883
|
|
|
3884
3884
|
{ method: 'POST', path: '/api/plans/create', desc: 'Create a plan from user-provided content', params: 'title, content, project?', handler: async (req, res) => {
|
|
3885
3885
|
const body = await readBody(req);
|
|
3886
|
-
const { title, content, project: projectName } = body;
|
|
3886
|
+
const { title, content, project: projectName, meetingId } = body;
|
|
3887
3887
|
if (!title || !content) return jsonReply(res, 400, { error: 'title and content required' });
|
|
3888
3888
|
|
|
3889
3889
|
const plansDir = path.join(MINIONS_DIR, 'plans');
|
|
@@ -3895,6 +3895,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3895
3895
|
|
|
3896
3896
|
const header = `# ${title}\n\n` +
|
|
3897
3897
|
(projectName ? `**Project:** ${projectName}\n` : '') +
|
|
3898
|
+
(meetingId ? `**Source Meeting:** ${meetingId}\n` : '') +
|
|
3898
3899
|
`**Created:** ${date}\n**By:** human teammate\n\n---\n\n`;
|
|
3899
3900
|
safeWrite(filePath, header + content);
|
|
3900
3901
|
|
|
@@ -3935,6 +3936,15 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3935
3936
|
{ method: 'GET', path: /^\/api\/agent\/([\w-]+)\/live(?:\?.*)?$/, desc: 'Tail live output for a working agent', params: 'tail? (bytes, default 8192)', handler: handleAgentLive },
|
|
3936
3937
|
{ method: 'GET', path: /^\/api\/agent\/([\w-]+)\/output(?:\?.*)?$/, desc: 'Fetch final output.log for an agent', handler: handleAgentOutput },
|
|
3937
3938
|
{ method: 'GET', path: /^\/api\/agent\/([\w-]+)$/, desc: 'Get detailed agent info', handler: handleAgentDetail },
|
|
3939
|
+
{ method: 'GET', path: '/api/agent-output', desc: 'Read agent output log file', params: 'file', handler: async (req, res) => {
|
|
3940
|
+
const file = new URL(req.url, 'http://localhost').searchParams.get('file');
|
|
3941
|
+
if (!file || file.includes('..') || file.includes('\0') || !file.startsWith('agents/')) return jsonReply(res, 400, { error: 'invalid file' });
|
|
3942
|
+
const content = safeRead(path.join(MINIONS_DIR, file));
|
|
3943
|
+
if (content === null) return jsonReply(res, 404, { error: 'not found' });
|
|
3944
|
+
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
3945
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
3946
|
+
res.end(content);
|
|
3947
|
+
} },
|
|
3938
3948
|
|
|
3939
3949
|
// Knowledge base
|
|
3940
3950
|
{ method: 'GET', path: '/api/knowledge', desc: 'List all knowledge base entries grouped by category', handler: handleKnowledgeList },
|
|
@@ -4062,8 +4072,25 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
4062
4072
|
const run = getActiveRun(body.id);
|
|
4063
4073
|
if (!run) return jsonReply(res, 404, { error: 'No active run to abort' });
|
|
4064
4074
|
completeRun(body.id, run.runId, 'failed');
|
|
4075
|
+
// Cancel pending/active work items and dispatches spawned by this run
|
|
4076
|
+
let cancelled = 0;
|
|
4077
|
+
const wiPaths = [path.join(MINIONS_DIR, 'work-items.json'), ...PROJECTS.map(p => shared.projectWorkItemsPath(p))];
|
|
4078
|
+
for (const wiPath of wiPaths) {
|
|
4079
|
+
try {
|
|
4080
|
+
mutateWorkItems(wiPath, items => {
|
|
4081
|
+
for (const w of items) {
|
|
4082
|
+
if (w._pipelineRun === run.runId && w.status !== shared.WI_STATUS.DONE && w.status !== shared.WI_STATUS.CANCELLED) {
|
|
4083
|
+
w.status = shared.WI_STATUS.CANCELLED;
|
|
4084
|
+
w._cancelledBy = 'pipeline-abort';
|
|
4085
|
+
cancelled++;
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
});
|
|
4089
|
+
} catch {}
|
|
4090
|
+
}
|
|
4091
|
+
const dispatchCleaned = cleanDispatchEntries(d => d.meta?.item?._pipelineRun === run.runId);
|
|
4065
4092
|
invalidateStatusCache();
|
|
4066
|
-
return jsonReply(res, 200, { ok: true, runId: run.runId });
|
|
4093
|
+
return jsonReply(res, 200, { ok: true, runId: run.runId, cancelledWorkItems: cancelled, cancelledDispatches: dispatchCleaned });
|
|
4067
4094
|
}},
|
|
4068
4095
|
{ method: 'POST', path: '/api/pipelines/retrigger', desc: 'Abort active run (if any) and start a new one', params: 'id', handler: async (req, res) => {
|
|
4069
4096
|
const body = await readBody(req);
|
package/engine.js
CHANGED
|
@@ -786,6 +786,38 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
786
786
|
|
|
787
787
|
log('info', `Agent ${agentId} completed. Output saved to ${archivePath}`);
|
|
788
788
|
|
|
789
|
+
// Track artifacts on the work item for dashboard display
|
|
790
|
+
if (dispatchItem.meta?.item?.id) {
|
|
791
|
+
try {
|
|
792
|
+
const artWiPath = resolveWorkItemPath(dispatchItem.meta);
|
|
793
|
+
if (artWiPath) {
|
|
794
|
+
// Collect inbox notes written by this agent today
|
|
795
|
+
const _artToday = shared.dateStamp();
|
|
796
|
+
const _artInboxDir = path.join(MINIONS_DIR, 'notes', 'inbox');
|
|
797
|
+
let _artNotes = [];
|
|
798
|
+
try { _artNotes = shared.safeReadDir(_artInboxDir).filter(f => f.includes(agentId) && f.includes(_artToday)); } catch {}
|
|
799
|
+
|
|
800
|
+
mutateJsonFileLocked(artWiPath, data => {
|
|
801
|
+
if (!Array.isArray(data)) return data;
|
|
802
|
+
const wi = data.find(i => i.id === dispatchItem.meta.item.id);
|
|
803
|
+
if (!wi) return data;
|
|
804
|
+
const arts = wi._artifacts || {};
|
|
805
|
+
arts.outputLog = `agents/${agentId}/output-${id}.log`;
|
|
806
|
+
if (dispatchItem.meta.branch) arts.branch = dispatchItem.meta.branch;
|
|
807
|
+
if (wi._pr) arts.pr = wi._pr;
|
|
808
|
+
if (wi._prUrl) arts.prUrl = wi._prUrl;
|
|
809
|
+
if (_artNotes.length > 0) arts.notes = _artNotes;
|
|
810
|
+
// Track plan/PRD artifacts from dispatch metadata
|
|
811
|
+
if (dispatchItem.meta.item?.planFile) arts.plan = dispatchItem.meta.item.planFile;
|
|
812
|
+
if (dispatchItem.meta.item?._prdFilename) arts.prd = dispatchItem.meta.item._prdFilename;
|
|
813
|
+
if (dispatchItem.meta.item?.sourcePlan) arts.sourcePlan = dispatchItem.meta.item.sourcePlan;
|
|
814
|
+
wi._artifacts = arts;
|
|
815
|
+
return data;
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
} catch (err) { log('warn', `Artifact tracking: ${err.message}`); }
|
|
819
|
+
}
|
|
820
|
+
|
|
789
821
|
// Clean up temp agent directory
|
|
790
822
|
if (tempAgents.has(agentId)) {
|
|
791
823
|
tempAgents.delete(agentId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.643",
|
|
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"
|