@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 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 reset --confirm Factory reset (delete all state, keep config)
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 === 'reset') {
500
+ } else if (cmd === 'nuke') {
501
501
  ensureInstalled();
502
502
  if (!rest.includes('--confirm')) {
503
503
  console.log(`
504
- This will DELETE all runtime state:
505
- - Work items, dispatch queue, PRDs, plans
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
- Config.json and agent charters are PRESERVED.
517
+ PRESERVED:
518
+ - Agent charters
519
+ - Playbooks
511
520
 
512
- Run: minions reset --confirm
521
+ Run: minions nuke --confirm
513
522
  `);
514
523
  process.exit(0);
515
524
  }
516
- // Stop engine + dashboard
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
- // Plans + PRDs
531
- glob(path.join(MINIONS_HOME, 'plans'), /\.md$/).forEach(rm);
532
- glob(path.join(MINIONS_HOME, 'prd'), /\.json$/).forEach(rm);
533
- rmDir(path.join(MINIONS_HOME, 'prd', 'archive'));
534
- rmDir(path.join(MINIONS_HOME, 'prd', 'guides'));
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', 'inbox'));
539
- rmDir(path.join(MINIONS_HOME, 'notes', 'archive'));
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
- fs.mkdirSync(path.join(MINIONS_HOME, 'notes', 'archive'), { recursive: true });
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 ['live-output.log', 'session.json', 'history.md', 'steer.md', 'status.json']) rm(path.join(agentDir, f));
553
- glob(agentDir, /^output.*\.log$/).forEach(rm);
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
- console.log('\n Reset complete. Config and charters preserved.');
561
- console.log(' Run: minions up\n');
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
  }
@@ -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
- try {
291
- const mdFiles = fs.readdirSync(PLANS_DIR).filter(f => f.endsWith('.md'));
292
- for (const md of mdFiles) {
293
- const mdContent = shared.safeRead(path.join(PLANS_DIR, md)) || '';
294
- // Match by project name or plan summary appearing in the .md content
295
- if (mdContent.includes(projectName) || mdContent.includes(plan.plan_summary?.slice(0, 40) || '___nomatch___')) {
296
- try {
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
- } catch (err) { log('warn', `Plan archive scan: ${err.message}`); }
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.232",
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"