@yemi33/minions 0.1.1553 → 0.1.1554
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 +5 -0
- package/dashboard/js/render-plans.js +16 -11
- package/dashboard.js +40 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -601,13 +601,12 @@ async function planArchive(file, btn) {
|
|
|
601
601
|
if (!confirm(confirmMsg)) return;
|
|
602
602
|
_stopPlanPoll();
|
|
603
603
|
markDeleted('plan:' + file);
|
|
604
|
-
// Also optimistically hide the linked source plan (server archives both)
|
|
605
604
|
if (isPrd) {
|
|
606
605
|
var linkedPlan = (window._lastPlans || []).find(function(p) { return p.file === file && p.sourcePlan; });
|
|
607
606
|
if (linkedPlan) markDeleted('plan:' + linkedPlan.sourcePlan);
|
|
608
607
|
}
|
|
609
608
|
try { closeModal(); } catch { /* may not be open */ }
|
|
610
|
-
showToast('cmd-toast', '
|
|
609
|
+
showToast('cmd-toast', 'Archived', true);
|
|
611
610
|
try {
|
|
612
611
|
const res = await fetch('/api/plans/archive', {
|
|
613
612
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
@@ -617,16 +616,18 @@ async function planArchive(file, btn) {
|
|
|
617
616
|
if (!ct.includes('json')) { refresh(); return; }
|
|
618
617
|
const d = await res.json().catch(() => ({}));
|
|
619
618
|
if (res.ok && d.ok) {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
619
|
+
if (d.archivedSource || d.cancelledItems) {
|
|
620
|
+
var msg = 'Archived';
|
|
621
|
+
if (d.archivedSource) msg += ' PRD + source plan';
|
|
622
|
+
if (d.cancelledItems) msg += ', cancelled ' + d.cancelledItems + ' pending item(s)';
|
|
623
|
+
showToast('cmd-toast', msg, true);
|
|
624
|
+
}
|
|
624
625
|
refresh();
|
|
625
626
|
} else {
|
|
626
|
-
|
|
627
|
-
|
|
627
|
+
showToast('cmd-toast', 'Archive failed: ' + (d.error || 'unknown'), false);
|
|
628
|
+
refresh();
|
|
628
629
|
}
|
|
629
|
-
} catch (e) {
|
|
630
|
+
} catch (e) { showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
630
631
|
}
|
|
631
632
|
|
|
632
633
|
async function planPause(file, btn) {
|
|
@@ -793,8 +794,12 @@ async function planUnarchive(file, btn) {
|
|
|
793
794
|
body: JSON.stringify({ file })
|
|
794
795
|
});
|
|
795
796
|
if (res.ok) { refreshPlans(); refresh(); }
|
|
796
|
-
else {
|
|
797
|
-
|
|
797
|
+
else {
|
|
798
|
+
const d = await res.json().catch(() => ({}));
|
|
799
|
+
showToast('cmd-toast', 'Unarchive failed: ' + (d.error || 'unknown'), false);
|
|
800
|
+
refresh();
|
|
801
|
+
}
|
|
802
|
+
} catch (e) { showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
798
803
|
}
|
|
799
804
|
|
|
800
805
|
window.MinionsPlans = { openCreatePlanModal, refreshPlans, derivePlanStatus, renderPlans, openArchivedPlansModal, planExecute, planSubmitRevise, planShowRevise, planHideRevise, planView, planApprove, planArchive, planUnarchive, planDelete, planPause, planReject, planDiscuss, planOpenInDocChat, planRegeneratePRD, openVerifyGuide, triggerVerify };
|
package/dashboard.js
CHANGED
|
@@ -3341,46 +3341,69 @@ If nothing to do: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
3341
3341
|
try {
|
|
3342
3342
|
const body = await readBody(req);
|
|
3343
3343
|
if (!body.file) return jsonReply(res, 400, { error: 'file required' });
|
|
3344
|
-
|
|
3344
|
+
const isPrd = body.file.endsWith('.json');
|
|
3345
|
+
shared.sanitizePath(body.file, isPrd ? PRD_DIR : PLANS_DIR);
|
|
3345
3346
|
const planPath = resolvePlanPath(body.file);
|
|
3346
3347
|
if (!fs.existsSync(planPath)) return jsonReply(res, 404, { error: 'plan not found' });
|
|
3347
3348
|
|
|
3348
|
-
|
|
3349
|
-
const archiveDir = body.file.endsWith('.json') ? path.join(PRD_DIR, 'archive') : path.join(PLANS_DIR, 'archive');
|
|
3349
|
+
const archiveDir = isPrd ? path.join(PRD_DIR, 'archive') : path.join(PLANS_DIR, 'archive');
|
|
3350
3350
|
if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
|
|
3351
3351
|
const archivePath = path.join(archiveDir, body.file);
|
|
3352
3352
|
fs.renameSync(planPath, archivePath);
|
|
3353
3353
|
|
|
3354
|
-
// Mark archived in JSON if PRD
|
|
3355
3354
|
let archivedSource = null;
|
|
3356
|
-
|
|
3355
|
+
let plan = {};
|
|
3356
|
+
if (isPrd) {
|
|
3357
3357
|
try {
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
safeWrite(archivePath,
|
|
3362
|
-
//
|
|
3363
|
-
|
|
3364
|
-
|
|
3358
|
+
plan = safeJsonObj(archivePath) || {};
|
|
3359
|
+
plan.status = 'archived';
|
|
3360
|
+
plan.archivedAt = new Date().toISOString();
|
|
3361
|
+
safeWrite(archivePath, plan);
|
|
3362
|
+
// Without removing the .backup sidecar, safeJson would auto-restore the
|
|
3363
|
+
// pre-completion snapshot on engine restart, re-triggering plan completion
|
|
3364
|
+
// and spawning duplicate verify tasks (regression of #f28162b0).
|
|
3365
|
+
const backupPath = planPath + '.backup';
|
|
3366
|
+
try { fs.unlinkSync(backupPath); } catch {
|
|
3367
|
+
try { fs.writeFileSync(backupPath, JSON.stringify({ status: 'archived' })); } catch { /* best-effort */ }
|
|
3368
|
+
}
|
|
3369
|
+
if (plan.source_plan) {
|
|
3370
|
+
const mdPath = path.join(PLANS_DIR, plan.source_plan);
|
|
3365
3371
|
if (fs.existsSync(mdPath)) {
|
|
3366
3372
|
const planArchive = path.join(PLANS_DIR, 'archive');
|
|
3367
3373
|
if (!fs.existsSync(planArchive)) fs.mkdirSync(planArchive, { recursive: true });
|
|
3368
|
-
fs.renameSync(mdPath, path.join(planArchive,
|
|
3369
|
-
archivedSource =
|
|
3374
|
+
fs.renameSync(mdPath, path.join(planArchive, plan.source_plan));
|
|
3375
|
+
archivedSource = plan.source_plan;
|
|
3370
3376
|
}
|
|
3371
3377
|
}
|
|
3372
3378
|
} catch { /* optional */ }
|
|
3373
3379
|
}
|
|
3374
3380
|
|
|
3375
|
-
//
|
|
3381
|
+
// Cancel pending work items linked to this plan so the engine stops
|
|
3382
|
+
// dispatching for an archived plan. Done items are preserved as history.
|
|
3383
|
+
let cancelledItems = 0;
|
|
3384
|
+
const wiPaths = [path.join(MINIONS_DIR, 'work-items.json'), ...PROJECTS.map(p => shared.projectWorkItemsPath(p))];
|
|
3385
|
+
for (const wiPath of wiPaths) {
|
|
3386
|
+
try {
|
|
3387
|
+
mutateWorkItems(wiPath, items => {
|
|
3388
|
+
for (const w of items) {
|
|
3389
|
+
if (w.sourcePlan !== body.file) continue;
|
|
3390
|
+
if (w.status === WI_STATUS.PENDING || w.status === WI_STATUS.QUEUED) {
|
|
3391
|
+
w.status = WI_STATUS.CANCELLED;
|
|
3392
|
+
w._cancelledBy = 'plan-archived';
|
|
3393
|
+
cancelledItems++;
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
});
|
|
3397
|
+
} catch (e) { console.error('plan archive cancel:', e.message); }
|
|
3398
|
+
}
|
|
3399
|
+
|
|
3376
3400
|
try {
|
|
3377
|
-
const plan = body.file.endsWith('.json') ? (safeJsonObj(archivePath) || {}) : {};
|
|
3378
3401
|
const { cleanupPlanWorktrees } = require('./engine/lifecycle');
|
|
3379
3402
|
cleanupPlanWorktrees(body.file, plan, PROJECTS, getConfig());
|
|
3380
3403
|
} catch (e) { console.error('plan worktree cleanup:', e.message); }
|
|
3381
3404
|
|
|
3382
3405
|
invalidateStatusCache();
|
|
3383
|
-
return jsonReply(res, 200, { ok: true, archived: body.file, archivedSource });
|
|
3406
|
+
return jsonReply(res, 200, { ok: true, archived: body.file, archivedSource, cancelledItems });
|
|
3384
3407
|
} catch (e) { return jsonReply(res, 400, { error: e.message }); }
|
|
3385
3408
|
}
|
|
3386
3409
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1554",
|
|
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"
|