@yemi33/minions 0.1.232 → 0.1.234
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 +10 -0
- package/bin/minions.js +71 -25
- package/engine/cleanup.js +2 -0
- package/engine/lifecycle.js +25 -14
- package/minions.js +1 -102
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.234 (2026-04-03)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
- clean _pendingReason on all status→done transitions, not just updateWorkItemStatus
|
|
7
|
+
|
|
8
|
+
## 0.1.233 (2026-04-03)
|
|
9
|
+
|
|
10
|
+
### Fixes
|
|
11
|
+
- combine nuke and reset into single 'minions nuke --confirm'
|
|
12
|
+
|
|
3
13
|
## 0.1.232 (2026-04-03)
|
|
4
14
|
|
|
5
15
|
### Fixes
|
package/bin/minions.js
CHANGED
|
@@ -458,7 +458,7 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
|
458
458
|
minions spawn <agent> <prompt> Manually spawn an agent
|
|
459
459
|
minions plan <file|text> [proj] Run a plan
|
|
460
460
|
minions cleanup Clean temp files, worktrees, zombies
|
|
461
|
-
minions
|
|
461
|
+
minions nuke --confirm Factory reset (delete state, reset config to defaults)
|
|
462
462
|
|
|
463
463
|
Dashboard:
|
|
464
464
|
minions dash Start web dashboard (default :7331)
|
|
@@ -497,68 +497,114 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
|
497
497
|
dashProc.unref();
|
|
498
498
|
console.log(` Dashboard started (PID: ${dashProc.pid})`);
|
|
499
499
|
console.log(' Dashboard: http://localhost:7331\n');
|
|
500
|
-
} else if (cmd === '
|
|
500
|
+
} else if (cmd === 'nuke') {
|
|
501
501
|
ensureInstalled();
|
|
502
502
|
if (!rest.includes('--confirm')) {
|
|
503
503
|
console.log(`
|
|
504
|
-
|
|
505
|
-
|
|
504
|
+
Factory reset — kills all processes and deletes all runtime state.
|
|
505
|
+
|
|
506
|
+
DELETED:
|
|
507
|
+
- Work items, dispatch queue, PRDs, plans, pipelines
|
|
506
508
|
- Agent history, sessions, output logs
|
|
507
509
|
- Notes, knowledge base, pinned notes
|
|
508
|
-
- Metrics, cooldowns, schedules
|
|
510
|
+
- Metrics, cooldowns, schedules, meetings
|
|
511
|
+
- Project state (PR tracking, per-project work items)
|
|
512
|
+
|
|
513
|
+
RESET to defaults:
|
|
514
|
+
- config.json (engine settings, agents — project links removed)
|
|
515
|
+
- routing.md
|
|
509
516
|
|
|
510
|
-
|
|
517
|
+
PRESERVED:
|
|
518
|
+
- Agent charters
|
|
519
|
+
- Playbooks
|
|
511
520
|
|
|
512
|
-
Run: minions
|
|
521
|
+
Run: minions nuke --confirm
|
|
513
522
|
`);
|
|
514
523
|
process.exit(0);
|
|
515
524
|
}
|
|
516
|
-
|
|
525
|
+
|
|
526
|
+
console.log('\n Minions Factory Reset\n');
|
|
527
|
+
|
|
528
|
+
// 1. Kill all processes
|
|
517
529
|
try { execSync(`node "${path.join(MINIONS_HOME, 'engine.js')}" stop`, { stdio: 'ignore', cwd: MINIONS_HOME }); } catch {}
|
|
530
|
+
// Kill dashboard
|
|
531
|
+
try {
|
|
532
|
+
if (process.platform === 'win32') {
|
|
533
|
+
const out = execSync('wmic process where "name=\'node.exe\'" get processid,commandline /format:csv', { encoding: 'utf8', timeout: 10000, windowsHide: true });
|
|
534
|
+
for (const line of out.split('\n')) {
|
|
535
|
+
if (line.includes('minions') && (line.includes('engine.js') || line.includes('dashboard.js') || line.includes('spawn-agent.js'))) {
|
|
536
|
+
const pid = line.split(',').pop()?.trim();
|
|
537
|
+
if (pid && pid !== String(process.pid)) {
|
|
538
|
+
try { process.kill(parseInt(pid)); } catch {}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
} else {
|
|
543
|
+
try { execSync('lsof -ti:7331 | xargs kill -9 2>/dev/null', { timeout: 5000 }); } catch {}
|
|
544
|
+
}
|
|
545
|
+
} catch {}
|
|
546
|
+
console.log(' Killed all processes');
|
|
547
|
+
|
|
548
|
+
// 2. Delete runtime state
|
|
518
549
|
const glob = (dir, pattern) => { try { return fs.readdirSync(dir).filter(f => pattern.test(f)).map(f => path.join(dir, f)); } catch { return []; } };
|
|
519
550
|
const rm = (f) => { try { fs.unlinkSync(f); } catch {} };
|
|
520
551
|
const rmDir = (d) => { try { fs.rmSync(d, { recursive: true, force: true }); } catch {} };
|
|
521
552
|
const engineDir = path.join(MINIONS_HOME, 'engine');
|
|
553
|
+
|
|
522
554
|
// Engine state
|
|
523
|
-
for (const f of ['dispatch.json', 'control.json', 'log.json', 'metrics.json', 'cooldowns.json', 'schedule-runs.json', 'kb-checkpoint.json', 'cc-session.json', 'doc-sessions.json']) rm(path.join(engineDir, f));
|
|
555
|
+
for (const f of ['dispatch.json', 'control.json', 'log.json', 'metrics.json', 'cooldowns.json', 'schedule-runs.json', 'kb-checkpoint.json', 'cc-session.json', 'doc-sessions.json', 'pipeline-runs.json']) rm(path.join(engineDir, f));
|
|
524
556
|
glob(engineDir, /^pid-.*\.pid$/).forEach(rm);
|
|
525
557
|
rmDir(path.join(engineDir, 'tmp'));
|
|
558
|
+
|
|
526
559
|
// Work items + PRs
|
|
527
560
|
rm(path.join(MINIONS_HOME, 'work-items.json'));
|
|
528
561
|
rm(path.join(MINIONS_HOME, 'work-items-archive.json'));
|
|
529
562
|
rm(path.join(MINIONS_HOME, 'pull-requests.json'));
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
rmDir(path.join(MINIONS_HOME, 'prd'
|
|
534
|
-
rmDir(path.join(MINIONS_HOME, '
|
|
563
|
+
|
|
564
|
+
// Plans + PRDs + Pipelines + Meetings
|
|
565
|
+
rmDir(path.join(MINIONS_HOME, 'plans'));
|
|
566
|
+
rmDir(path.join(MINIONS_HOME, 'prd'));
|
|
567
|
+
rmDir(path.join(MINIONS_HOME, 'pipelines'));
|
|
568
|
+
rmDir(path.join(MINIONS_HOME, 'meetings'));
|
|
569
|
+
fs.mkdirSync(path.join(MINIONS_HOME, 'plans'), { recursive: true });
|
|
570
|
+
fs.mkdirSync(path.join(MINIONS_HOME, 'prd'), { recursive: true });
|
|
571
|
+
|
|
535
572
|
// Notes + KB
|
|
536
573
|
rm(path.join(MINIONS_HOME, 'notes.md'));
|
|
537
574
|
rm(path.join(MINIONS_HOME, 'pinned.md'));
|
|
538
|
-
rmDir(path.join(MINIONS_HOME, 'notes'
|
|
539
|
-
rmDir(path.join(MINIONS_HOME, '
|
|
575
|
+
rmDir(path.join(MINIONS_HOME, 'notes'));
|
|
576
|
+
rmDir(path.join(MINIONS_HOME, 'knowledge'));
|
|
540
577
|
fs.mkdirSync(path.join(MINIONS_HOME, 'notes', 'inbox'), { recursive: true });
|
|
541
|
-
|
|
542
|
-
for (const cat of ['architecture', 'conventions', 'project-notes', 'build-reports', 'reviews']) {
|
|
543
|
-
const catDir = path.join(MINIONS_HOME, 'knowledge', cat);
|
|
544
|
-
glob(catDir, /\.md$/).forEach(rm);
|
|
545
|
-
}
|
|
578
|
+
|
|
546
579
|
// Agent state (preserve charters)
|
|
547
580
|
const agentsDir = path.join(MINIONS_HOME, 'agents');
|
|
548
581
|
try {
|
|
549
582
|
for (const agent of fs.readdirSync(agentsDir)) {
|
|
550
583
|
const agentDir = path.join(agentsDir, agent);
|
|
551
584
|
if (!fs.statSync(agentDir).isDirectory()) continue;
|
|
552
|
-
for (const f of
|
|
553
|
-
|
|
585
|
+
for (const f of fs.readdirSync(agentDir)) {
|
|
586
|
+
if (f === 'charter.md') continue;
|
|
587
|
+
rm(path.join(agentDir, f));
|
|
588
|
+
}
|
|
554
589
|
}
|
|
555
590
|
} catch {}
|
|
591
|
+
|
|
556
592
|
// Projects state
|
|
557
593
|
rmDir(path.join(MINIONS_HOME, 'projects'));
|
|
558
594
|
fs.mkdirSync(path.join(MINIONS_HOME, 'projects'), { recursive: true });
|
|
559
595
|
|
|
560
|
-
|
|
561
|
-
|
|
596
|
+
// 3. Reset config.json and routing.md to defaults
|
|
597
|
+
const tmplPath = path.join(MINIONS_HOME, 'config.template.json');
|
|
598
|
+
const configPath = path.join(MINIONS_HOME, 'config.json');
|
|
599
|
+
if (fs.existsSync(tmplPath)) {
|
|
600
|
+
fs.copyFileSync(tmplPath, configPath);
|
|
601
|
+
} else {
|
|
602
|
+
fs.writeFileSync(configPath, JSON.stringify({ projects: [], engine: {}, claude: {}, agents: {} }, null, 2));
|
|
603
|
+
}
|
|
604
|
+
// Re-run init to populate defaults (agents, engine settings)
|
|
605
|
+
try { execSync(`node "${path.join(MINIONS_HOME, 'minions.js')}" init --skip-scan`, { stdio: 'inherit' }); } catch {}
|
|
606
|
+
|
|
607
|
+
console.log('\n Factory reset complete. Run "minions init" to link projects and start fresh.\n');
|
|
562
608
|
} else if (cmd === 'doctor') {
|
|
563
609
|
ensureInstalled();
|
|
564
610
|
const { doctor } = require(path.join(MINIONS_HOME, 'engine', 'preflight'));
|
package/engine/cleanup.js
CHANGED
|
@@ -364,6 +364,7 @@ function runCleanup(config, verbose = false) {
|
|
|
364
364
|
for (const item of items) {
|
|
365
365
|
if (LEGACY_DONE_STATUSES.has(item.status)) {
|
|
366
366
|
item.status = 'done';
|
|
367
|
+
delete item._pendingReason;
|
|
367
368
|
migrated++;
|
|
368
369
|
}
|
|
369
370
|
}
|
|
@@ -381,6 +382,7 @@ function runCleanup(config, verbose = false) {
|
|
|
381
382
|
for (const item of centralItems) {
|
|
382
383
|
if (LEGACY_DONE_STATUSES.has(item.status)) {
|
|
383
384
|
item.status = 'done';
|
|
385
|
+
delete item._pendingReason;
|
|
384
386
|
migrated++;
|
|
385
387
|
}
|
|
386
388
|
}
|
package/engine/lifecycle.js
CHANGED
|
@@ -284,23 +284,33 @@ function checkPlanCompletion(meta, config) {
|
|
|
284
284
|
shared.safeWrite(planPath, plan);
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
-
// Also archive the source .md plan if it exists
|
|
287
|
+
// Also archive the source .md plan if it exists (use source_plan field, not content matching)
|
|
288
288
|
const planArchiveDir = path.join(PLANS_DIR, 'archive');
|
|
289
289
|
if (!fs.existsSync(planArchiveDir)) fs.mkdirSync(planArchiveDir, { recursive: true });
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
fs.renameSync(path.join(PLANS_DIR, md), path.join(planArchiveDir, md));
|
|
298
|
-
log('info', `Archived source plan: plans/archive/${md}`);
|
|
299
|
-
} catch (err) { log('warn', `Failed to archive plan ${md}: ${err.message}`); }
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
290
|
+
if (plan.source_plan) {
|
|
291
|
+
const mdPath = path.join(PLANS_DIR, plan.source_plan);
|
|
292
|
+
if (fs.existsSync(mdPath)) {
|
|
293
|
+
try {
|
|
294
|
+
fs.renameSync(mdPath, path.join(planArchiveDir, plan.source_plan));
|
|
295
|
+
log('info', `Archived source plan: plans/archive/${plan.source_plan}`);
|
|
296
|
+
} catch (err) { log('warn', `Failed to archive source plan ${plan.source_plan}: ${err.message}`); }
|
|
302
297
|
}
|
|
303
|
-
}
|
|
298
|
+
} else {
|
|
299
|
+
// Fallback: scan for matching .md files (legacy PRDs without source_plan)
|
|
300
|
+
try {
|
|
301
|
+
const mdFiles = fs.readdirSync(PLANS_DIR).filter(f => f.endsWith('.md'));
|
|
302
|
+
for (const md of mdFiles) {
|
|
303
|
+
const mdContent = shared.safeRead(path.join(PLANS_DIR, md)) || '';
|
|
304
|
+
if (mdContent.includes(projectName) || mdContent.includes(plan.plan_summary?.slice(0, 40) || '___nomatch___')) {
|
|
305
|
+
try {
|
|
306
|
+
fs.renameSync(path.join(PLANS_DIR, md), path.join(planArchiveDir, md));
|
|
307
|
+
log('info', `Archived source plan: plans/archive/${md}`);
|
|
308
|
+
} catch (err) { log('warn', `Failed to archive plan ${md}: ${err.message}`); }
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
} catch (err) { log('warn', `Plan archive scan: ${err.message}`); }
|
|
313
|
+
}
|
|
304
314
|
|
|
305
315
|
// 6. Clean up ALL worktrees created for this plan's work items (shared-branch + per-item)
|
|
306
316
|
try {
|
|
@@ -791,6 +801,7 @@ async function handlePostMerge(pr, project, config, newStatus) {
|
|
|
791
801
|
log('info', `Post-merge: marking work item ${mergedItemId} as done (was ${item.status}) for ${pr.id}`);
|
|
792
802
|
item.status = 'done';
|
|
793
803
|
item.completedAt = ts();
|
|
804
|
+
delete item._pendingReason;
|
|
794
805
|
item._mergedVia = pr.id;
|
|
795
806
|
found = true;
|
|
796
807
|
}
|
package/minions.js
CHANGED
|
@@ -465,105 +465,6 @@ async function initMinions({ skipScan = false, scanRoot, scanDepth } = {}) {
|
|
|
465
465
|
}
|
|
466
466
|
}
|
|
467
467
|
|
|
468
|
-
function nukeMinions() {
|
|
469
|
-
console.log('\n Minions Factory Reset');
|
|
470
|
-
console.log(' ===================\n');
|
|
471
|
-
|
|
472
|
-
// 1. Kill engine process
|
|
473
|
-
const controlPath = path.join(MINIONS_HOME, 'engine', 'control.json');
|
|
474
|
-
try {
|
|
475
|
-
const control = JSON.parse(fs.readFileSync(controlPath, 'utf8'));
|
|
476
|
-
if (control.pid) {
|
|
477
|
-
try {
|
|
478
|
-
process.kill(control.pid);
|
|
479
|
-
console.log(` Killed engine (PID: ${control.pid})`);
|
|
480
|
-
} catch { console.log(` Engine process ${control.pid} already dead`); }
|
|
481
|
-
}
|
|
482
|
-
} catch {}
|
|
483
|
-
|
|
484
|
-
// 2. Kill dashboard (port 7331)
|
|
485
|
-
try {
|
|
486
|
-
if (process.platform === 'win32') {
|
|
487
|
-
const { execSync } = require('child_process');
|
|
488
|
-
const out = execSync('netstat -ano | findstr :7331 | findstr LISTENING', { encoding: 'utf8', timeout: 5000, windowsHide: true });
|
|
489
|
-
const pids = [...new Set(out.split('\n').map(l => l.trim().split(/\s+/).pop()).filter(p => p && p !== '0'))];
|
|
490
|
-
for (const pid of pids) {
|
|
491
|
-
try { execSync(`taskkill /F /PID ${pid}`, { windowsHide: true, timeout: 5000 }); console.log(` Killed dashboard (PID: ${pid})`); } catch {}
|
|
492
|
-
}
|
|
493
|
-
} else {
|
|
494
|
-
const { execSync } = require('child_process');
|
|
495
|
-
try { execSync('lsof -ti:7331 | xargs kill -9 2>/dev/null', { timeout: 5000 }); console.log(' Killed dashboard'); } catch {}
|
|
496
|
-
}
|
|
497
|
-
} catch {}
|
|
498
|
-
|
|
499
|
-
// 3. Kill all agent processes (PID files in engine/tmp/)
|
|
500
|
-
const pidDir = path.join(MINIONS_HOME, 'engine', 'tmp');
|
|
501
|
-
try {
|
|
502
|
-
const pidFiles = fs.readdirSync(pidDir).filter(f => f.endsWith('.pid'));
|
|
503
|
-
for (const f of pidFiles) {
|
|
504
|
-
try {
|
|
505
|
-
const pid = parseInt(fs.readFileSync(path.join(pidDir, f), 'utf8').trim());
|
|
506
|
-
if (pid) { try { process.kill(pid); console.log(` Killed agent process (PID: ${pid})`); } catch {} }
|
|
507
|
-
} catch {}
|
|
508
|
-
}
|
|
509
|
-
} catch {}
|
|
510
|
-
|
|
511
|
-
// 4. Kill any remaining minions-related node processes
|
|
512
|
-
try {
|
|
513
|
-
const { execSync } = require('child_process');
|
|
514
|
-
if (process.platform === 'win32') {
|
|
515
|
-
// Find node processes with minions in their command line
|
|
516
|
-
const out = execSync('wmic process where "name=\'node.exe\'" get processid,commandline /format:csv', { encoding: 'utf8', timeout: 10000, windowsHide: true });
|
|
517
|
-
for (const line of out.split('\n')) {
|
|
518
|
-
if (line.includes('minions') && (line.includes('engine.js') || line.includes('dashboard.js') || line.includes('spawn-agent.js'))) {
|
|
519
|
-
const pid = line.split(',').pop()?.trim();
|
|
520
|
-
if (pid && pid !== String(process.pid)) {
|
|
521
|
-
try { execSync(`taskkill /F /PID ${pid}`, { windowsHide: true, timeout: 5000 }); console.log(` Killed minions process (PID: ${pid})`); } catch {}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
} catch {}
|
|
527
|
-
|
|
528
|
-
// 5. Delete runtime state (NOT the source code)
|
|
529
|
-
console.log('\n Cleaning runtime state...');
|
|
530
|
-
const runtimeDirs = ['projects', 'plans', 'prd', 'knowledge', 'skills', 'notes', 'identity'];
|
|
531
|
-
const runtimeFiles = ['config.json', 'work-items.json', 'notes.md', 'routing.md'];
|
|
532
|
-
const engineRuntimeFiles = ['control.json', 'dispatch.json', 'log.json', 'metrics.json', 'cooldowns.json', 'kb-checkpoint.json', 'cc-session.json', 'doc-sessions.json'];
|
|
533
|
-
|
|
534
|
-
for (const dir of runtimeDirs) {
|
|
535
|
-
const p = path.join(MINIONS_HOME, dir);
|
|
536
|
-
if (fs.existsSync(p)) { try { fs.rmSync(p, { recursive: true, force: true }); console.log(` Deleted ${dir}/`); } catch {} }
|
|
537
|
-
}
|
|
538
|
-
for (const f of runtimeFiles) {
|
|
539
|
-
const p = path.join(MINIONS_HOME, f);
|
|
540
|
-
if (fs.existsSync(p)) { try { fs.unlinkSync(p); console.log(` Deleted ${f}`); } catch {} }
|
|
541
|
-
}
|
|
542
|
-
const engineDir = path.join(MINIONS_HOME, 'engine');
|
|
543
|
-
for (const f of engineRuntimeFiles) {
|
|
544
|
-
const p = path.join(engineDir, f);
|
|
545
|
-
if (fs.existsSync(p)) { try { fs.unlinkSync(p); console.log(` Deleted engine/${f}`); } catch {} }
|
|
546
|
-
}
|
|
547
|
-
// Clean engine/tmp/
|
|
548
|
-
const tmpDir = path.join(engineDir, 'tmp');
|
|
549
|
-
if (fs.existsSync(tmpDir)) { try { fs.rmSync(tmpDir, { recursive: true, force: true }); console.log(' Deleted engine/tmp/'); } catch {} }
|
|
550
|
-
// Clean agent history and output logs (preserve charters)
|
|
551
|
-
const agentsDir = path.join(MINIONS_HOME, 'agents');
|
|
552
|
-
if (fs.existsSync(agentsDir)) {
|
|
553
|
-
for (const agent of fs.readdirSync(agentsDir)) {
|
|
554
|
-
const agentDir = path.join(agentsDir, agent);
|
|
555
|
-
try { if (!fs.statSync(agentDir).isDirectory()) continue; } catch { continue; }
|
|
556
|
-
for (const f of fs.readdirSync(agentDir)) {
|
|
557
|
-
if (f === 'charter.md') continue; // preserve charters
|
|
558
|
-
try { fs.unlinkSync(path.join(agentDir, f)); } catch {}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
console.log(' Cleaned agent state (charters preserved)');
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
console.log(' Factory reset complete. Run "minions init" to start fresh.\n');
|
|
565
|
-
rl.close();
|
|
566
|
-
}
|
|
567
468
|
|
|
568
469
|
const commands = {
|
|
569
470
|
init: () => {
|
|
@@ -586,7 +487,6 @@ const commands = {
|
|
|
586
487
|
list: () => listProjects(),
|
|
587
488
|
scan: () => scanAndAdd({ root: rest[0], depth: rest[1] })
|
|
588
489
|
.catch(e => { console.error(e); process.exit(1); }),
|
|
589
|
-
nuke: () => nukeMinions(),
|
|
590
490
|
};
|
|
591
491
|
|
|
592
492
|
if (cmd && commands[cmd]) {
|
|
@@ -599,7 +499,6 @@ if (cmd && commands[cmd]) {
|
|
|
599
499
|
console.log(' scan [dir] [depth] Scan for git repos and multi-select to add');
|
|
600
500
|
console.log(' add <project-dir> Link a single project');
|
|
601
501
|
console.log(' remove <project-dir> Unlink a project');
|
|
602
|
-
console.log(' list List linked projects');
|
|
603
|
-
console.log(' nuke Factory reset — kill all processes, delete ~/.minions/\n');
|
|
502
|
+
console.log(' list List linked projects\n');
|
|
604
503
|
}
|
|
605
504
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.234",
|
|
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"
|