@yemi33/minions 0.1.654 → 0.1.656

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,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.656 (2026-04-09)
4
+
5
+ ### Fixes
6
+ - bust require cache for package.json in engine start
7
+ - PRD discovery loop, github.js duplicate const, transcript null guards
8
+
9
+ ## 0.1.655 (2026-04-09)
10
+
11
+ ### Fixes
12
+ - skip PR reconciliation for projects with no PRs or work items
13
+
3
14
  ## 0.1.654 (2026-04-09)
4
15
 
5
16
  ### Fixes
@@ -197,7 +197,7 @@ function _renderMeetingDetail(m) {
197
197
 
198
198
  // Wire up doc-chat Q&A panel for the meeting transcript
199
199
  const transcript = (m.transcript || []).map(t =>
200
- '### ' + t.agent + ' (' + t.type + ', Round ' + t.round + ')\n\n' + (t.content || '')
200
+ '### ' + (t.agent || 'agent') + ' (' + (t.type || '') + ', Round ' + (t.round || '?') + ')\n\n' + (t.content || '')
201
201
  ).join('\n\n---\n\n');
202
202
  const meetingDoc = '# Meeting: ' + m.title + '\n\n**Agenda:** ' + m.agenda + '\n\n' + transcript;
203
203
  _modalDocContext = { title: 'Meeting: ' + m.title, content: meetingDoc, selection: '' };
@@ -418,7 +418,7 @@ async function _createPlanFromMeeting(id, btn) {
418
418
 
419
419
  // Use doc-chat to generate a structured plan from the meeting
420
420
  const transcript = (m.transcript || []).map(function(t) {
421
- return '### ' + t.agent + ' (' + t.type + ', Round ' + t.round + ')\n\n' + (t.content || '');
421
+ return '### ' + (t.agent || 'agent') + ' (' + (t.type || '') + ', Round ' + (t.round || '?') + ')\n\n' + (t.content || '');
422
422
  }).join('\n\n---\n\n');
423
423
  const meetingDoc = '# Meeting: ' + m.title + '\n\n**Agenda:** ' + m.agenda + '\n\n' + transcript;
424
424
 
@@ -455,7 +455,7 @@ function openWorkItemDetail(id) {
455
455
  if (arts.prd) artPills += '<span onclick="planView(\'' + escHtml(arts.prd) + '\')" style="' + pillStyle + '">📄 PRD</span> ';
456
456
  if (arts.sourcePlan) artPills += '<span onclick="planView(\'' + escHtml(arts.sourcePlan) + '\')" style="' + pillStyle + '">📋 Source Plan</span> ';
457
457
  if (arts.notes && arts.notes.length > 0) arts.notes.forEach(function(n) {
458
- var noteLabel = typeof n === 'object' ? (n.id || n.file || 'note').slice(0, 30) : String(n).replace(/\.md$/, '').slice(0, 30);
458
+ var noteLabel = (n && typeof n === 'object') ? (n.id || n.file || 'note').slice(0, 30) : String(n || 'note').replace(/\.md$/, '').slice(0, 30);
459
459
  artPills += '<span onclick="closeModal();switchPage(\'inbox\')" style="' + pillStyle + '">📝 ' + escHtml(noteLabel) + '</span> ';
460
460
  });
461
461
  if (arts.skills && arts.skills.length > 0) arts.skills.forEach(function(s) { artPills += '<span onclick="openSkill(\'' + escHtml(s) + '\',\'minions\',\'\')" style="' + pillStyle + '">⚙ ' + escHtml(s) + '</span> '; });
@@ -490,7 +490,7 @@ function viewAgentOutput(logPath) {
490
490
  document.getElementById('modal-body').style.whiteSpace = 'pre-wrap';
491
491
  document.getElementById('modal').classList.add('open');
492
492
  fetch('/api/agent-output?file=' + encodeURIComponent(logPath))
493
- .then(function(r) { return r.text(); })
493
+ .then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); })
494
494
  .then(function(content) {
495
495
  document.getElementById('modal-body').textContent = content;
496
496
  })
package/engine/cli.js CHANGED
@@ -85,9 +85,20 @@ const commands = {
85
85
  }
86
86
 
87
87
  // Record version + git commit so dashboard can detect stale engine code
88
+ // Bust require cache so npm updates are detected after minions restart
88
89
  let codeVersion = null;
89
- try { codeVersion = require('../package.json').version; } catch {}
90
- if (!codeVersion) { try { codeVersion = require('@yemi33/minions/package.json').version; } catch {} }
90
+ try {
91
+ const pkgPath = require.resolve('../package.json');
92
+ delete require.cache[pkgPath];
93
+ codeVersion = require('../package.json').version;
94
+ } catch {}
95
+ if (!codeVersion) {
96
+ try {
97
+ const pkgPath = require.resolve('@yemi33/minions/package.json');
98
+ delete require.cache[pkgPath];
99
+ codeVersion = require('@yemi33/minions/package.json').version;
100
+ } catch {}
101
+ }
91
102
  let codeCommit = null;
92
103
  try { codeCommit = require('child_process').execSync('git rev-parse --short HEAD', { cwd: path.resolve(__dirname, '..'), encoding: 'utf8', timeout: 5000, windowsHide: true }).trim(); } catch {}
93
104
  safeWrite(CONTROL_PATH, { state: 'running', pid: process.pid, started_at: e.ts(), codeVersion, codeCommit });
package/engine/github.js CHANGED
@@ -511,6 +511,16 @@ async function reconcilePrs(config) {
511
511
  // Skip projects in backoff (inaccessible repo)
512
512
  if (isSlugInBackoff(slug)) continue;
513
513
 
514
+ // Skip projects with no tracked PRs and no work items — nothing to reconcile
515
+ const existingPrs = getPrs(project);
516
+ if (existingPrs.length === 0) {
517
+ try {
518
+ const wiPath = projectWorkItemsPath(project);
519
+ const wis = safeJson(wiPath) || [];
520
+ if (wis.length === 0) continue;
521
+ } catch { continue; }
522
+ }
523
+
514
524
  // Fetch open PRs
515
525
  const prsData = await ghApi('/pulls?state=open&per_page=100', slug);
516
526
  if (!prsData || !Array.isArray(prsData)) {
@@ -527,8 +537,8 @@ async function reconcilePrs(config) {
527
537
  if (ghPrs.length === 0) continue;
528
538
 
529
539
  const prPath = projectPrPath(project);
530
- const existingPrs = safeJson(prPath) || [];
531
- const existingIds = new Set(existingPrs.map(p => p.id));
540
+ const currentPrs = safeJson(prPath) || [];
541
+ const existingIds = new Set(currentPrs.map(p => p.id));
532
542
  let projectAdded = 0;
533
543
 
534
544
  // Load work items to match branches
@@ -549,7 +559,7 @@ async function reconcilePrs(config) {
549
559
  if (existingIds.has(prId)) {
550
560
  if (confirmedItemId) {
551
561
  addPrLink(prId, confirmedItemId);
552
- const existing = existingPrs.find(p => p.id === prId);
562
+ const existing = currentPrs.find(p => p.id === prId);
553
563
  if (existing && !(existing.prdItems || []).includes(confirmedItemId)) {
554
564
  existing.prdItems = Array.isArray(existing.prdItems) ? existing.prdItems : [];
555
565
  existing.prdItems.push(confirmedItemId);
@@ -563,7 +573,7 @@ async function reconcilePrs(config) {
563
573
 
564
574
  const prUrl = project.prUrlBase ? project.prUrlBase + ghPr.number : ghPr.html_url || '';
565
575
 
566
- existingPrs.push({
576
+ currentPrs.push({
567
577
  id: prId,
568
578
  title: (ghPr.title || `PR #${ghPr.number}`).slice(0, 120),
569
579
  agent: (linkedItem?.dispatched_to || ghPr.user?.login || 'unknown').toLowerCase(),
@@ -584,7 +594,7 @@ async function reconcilePrs(config) {
584
594
  // Backfill prdItems from pr-links for any PR with empty array
585
595
  const prLinks = getPrLinks();
586
596
  let backfilled = 0;
587
- for (const pr of existingPrs) {
597
+ for (const pr of currentPrs) {
588
598
  const linked = prLinks[pr.id];
589
599
  if (linked && !(pr.prdItems || []).includes(linked)) {
590
600
  pr.prdItems = Array.isArray(pr.prdItems) ? pr.prdItems : [];
@@ -594,13 +604,13 @@ async function reconcilePrs(config) {
594
604
  }
595
605
 
596
606
  if (projectAdded > 0 || backfilled > 0) {
597
- mutateJsonFileLocked(prPath, (currentPrs) => {
598
- for (const pr of existingPrs) {
599
- const idx = currentPrs.findIndex(p => p.id === pr.id);
600
- if (idx >= 0) currentPrs[idx] = pr;
601
- else currentPrs.push(pr);
607
+ mutateJsonFileLocked(prPath, (lockedPrs) => {
608
+ for (const pr of currentPrs) {
609
+ const idx = lockedPrs.findIndex(p => p.id === pr.id);
610
+ if (idx >= 0) lockedPrs[idx] = pr;
611
+ else lockedPrs.push(pr);
602
612
  }
603
- return currentPrs;
613
+ return lockedPrs;
604
614
  }, { defaultValue: [] });
605
615
  totalAdded += projectAdded;
606
616
  }
@@ -603,21 +603,21 @@ function isStageComplete(stage, stageState, run, config) {
603
603
  const plans = artifacts.plans || [];
604
604
  const discoveredPrds = [];
605
605
  const discoveredWiIds = [];
606
+ const prdFiles = fs.existsSync(prdDir) ? safeReadDir(prdDir).filter(f => f.endsWith('.json')) : [];
606
607
  for (const planFile of plans) {
607
- const prdFiles = fs.existsSync(prdDir) ? safeReadDir(prdDir).filter(f => f.endsWith('.json')) : [];
608
608
  for (const pf of prdFiles) {
609
609
  const prd = safeJson(path.join(prdDir, pf));
610
610
  if (prd?.source_plan === planFile && !(artifacts.prds || []).includes(pf) && !discoveredPrds.includes(pf)) {
611
611
  discoveredPrds.push(pf);
612
612
  }
613
613
  }
614
- const allPrds = [...(artifacts.prds || []), ...discoveredPrds];
615
- for (const prdFile of allPrds) {
616
- const prdItems = all.filter(w => w.sourcePlan === prdFile && w.type !== WORK_TYPE.PLAN_TO_PRD);
617
- for (const wi of prdItems) {
618
- if (!(artifacts.workItems || []).includes(wi.id) && !discoveredWiIds.includes(wi.id)) {
619
- discoveredWiIds.push(wi.id);
620
- }
614
+ }
615
+ const allPrds = [...(artifacts.prds || []), ...discoveredPrds];
616
+ for (const prdFile of allPrds) {
617
+ const prdItems = all.filter(w => w.sourcePlan === prdFile && w.type !== WORK_TYPE.PLAN_TO_PRD);
618
+ for (const wi of prdItems) {
619
+ if (!(artifacts.workItems || []).includes(wi.id) && !discoveredWiIds.includes(wi.id)) {
620
+ discoveredWiIds.push(wi.id);
621
621
  }
622
622
  }
623
623
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.654",
3
+ "version": "0.1.656",
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"