@yemi33/minions 0.1.156 → 0.1.158

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,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.158 (2026-04-02)
4
+
5
+ ### Engine
6
+ - engine/shared.js
7
+
8
+ ## 0.1.157 (2026-04-02)
9
+
10
+ ### Engine
11
+ - engine.js
12
+ - engine/ado.js
13
+ - engine/github.js
14
+ - engine/shared.js
15
+
16
+ ### Other
17
+ - pipelines/daily-standup.json
18
+ - test/unit.test.js
19
+
3
20
  ## 0.1.156 (2026-04-02)
4
21
 
5
22
  ### Engine
package/engine/ado.js CHANGED
@@ -386,19 +386,7 @@ async function reconcilePrs(config) {
386
386
  log('info', `PR reconciliation: added ${prId} (branch: ${branch}${confirmedItemId ? ', linked to ' + confirmedItemId : ''}) to ${project.name}`);
387
387
  }
388
388
 
389
- // Backfill prdItems from pr-links for any PR with empty array
390
- const prLinks = shared.getPrLinks();
391
- let backfilled = 0;
392
- for (const pr of existingPrs) {
393
- const linked = prLinks[pr.id];
394
- if (linked && !(pr.prdItems || []).includes(linked)) {
395
- pr.prdItems = Array.isArray(pr.prdItems) ? pr.prdItems : [];
396
- pr.prdItems.push(linked);
397
- backfilled++;
398
- }
399
- }
400
-
401
- if (projectAdded > 0 || projectUpdated > 0 || backfilled > 0) {
389
+ if (projectAdded > 0 || projectUpdated > 0) {
402
390
  shared.safeWrite(prPath, existingPrs);
403
391
  totalAdded += projectAdded;
404
392
  if (projectUpdated > 0) log('info', `PR reconciliation: linked ${projectUpdated} existing PR(s) to PRD items in ${project.name}`);
package/engine/github.js CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  const shared = require('./shared');
8
- const { exec, getProjects, projectPrPath, projectWorkItemsPath, safeJson, safeWrite, MINIONS_DIR, addPrLink, getPrLinks, log, dateStamp } = shared;
8
+ const { exec, getProjects, projectPrPath, projectWorkItemsPath, safeJson, safeWrite, MINIONS_DIR, addPrLink, log, dateStamp } = shared;
9
9
  const { getPrs } = require('./queries');
10
10
  const path = require('path');
11
11
 
@@ -371,19 +371,7 @@ async function reconcilePrs(config) {
371
371
  log('info', `GitHub PR reconciliation: added ${prId} (branch: ${branch}${confirmedItemId ? ', linked to ' + confirmedItemId : ''}) to ${project.name}`);
372
372
  }
373
373
 
374
- // Backfill prdItems from pr-links for any PR with empty array
375
- const prLinks = getPrLinks();
376
- let backfilled = 0;
377
- for (const pr of existingPrs) {
378
- const linked = prLinks[pr.id];
379
- if (linked && !(pr.prdItems || []).includes(linked)) {
380
- pr.prdItems = Array.isArray(pr.prdItems) ? pr.prdItems : [];
381
- pr.prdItems.push(linked);
382
- backfilled++;
383
- }
384
- }
385
-
386
- if (projectAdded > 0 || backfilled > 0) {
374
+ if (projectAdded > 0) {
387
375
  safeWrite(prPath, existingPrs);
388
376
  totalAdded += projectAdded;
389
377
  }
package/engine/shared.js CHANGED
@@ -404,10 +404,26 @@ function projectStateDir(project) {
404
404
  return dir;
405
405
  }
406
406
 
407
+ const CENTRAL_WI_PATH = path.join(MINIONS_DIR, 'work-items.json');
408
+
407
409
  function projectWorkItemsPath(project) {
408
410
  return path.join(projectStateDir(project), 'work-items.json');
409
411
  }
410
412
 
413
+ /**
414
+ * Resolve work-items.json path from dispatch meta.
415
+ * Central items → CENTRAL_WI_PATH; project items → projects/<name>/work-items.json.
416
+ */
417
+ function resolveWiPath(meta) {
418
+ if (meta.source === 'central-work-item' || meta.source === 'central-work-item-fanout') {
419
+ return CENTRAL_WI_PATH;
420
+ }
421
+ if (meta.project?.name) {
422
+ return path.join(MINIONS_DIR, 'projects', meta.project.name, 'work-items.json');
423
+ }
424
+ return null;
425
+ }
426
+
411
427
  function projectPrPath(project) {
412
428
  return path.join(projectStateDir(project), 'pull-requests.json');
413
429
  }
@@ -501,10 +517,11 @@ function parseSkillFrontmatter(content, filename) {
501
517
  // Never touched by polling loops — only written when a PR is first linked to a PRD item.
502
518
 
503
519
  function getPrLinks() {
504
- // Derive from PR.prdItems (single source of truth) + legacy pr-links.json as fallback
520
+ // Derive from PR.prdItems (single source of truth)
505
521
  const links = {};
506
522
  try {
507
- const projects = getProjects();
523
+ const config = safeJson(path.join(MINIONS_DIR, 'config.json')) || {};
524
+ const projects = getProjects(config);
508
525
  for (const project of projects) {
509
526
  const prs = safeJson(projectPrPath(project)) || [];
510
527
  for (const pr of prs) {
@@ -514,22 +531,16 @@ function getPrLinks() {
514
531
  }
515
532
  }
516
533
  } catch { /* optional */ }
517
- // Merge legacy pr-links.json for items not yet in PR.prdItems
518
- try {
519
- const legacy = JSON.parse(require('fs').readFileSync(PR_LINKS_PATH, 'utf8'));
520
- for (const [prId, itemId] of Object.entries(legacy)) {
521
- if (!links[prId]) links[prId] = itemId;
522
- }
523
- } catch { /* optional */ }
524
534
  return links;
525
535
  }
526
536
 
527
537
  function addPrLink(prId, itemId) {
528
538
  if (!prId || !itemId) return;
529
- const links = getPrLinks();
530
- if (links[prId] === itemId) return; // already correct, no write needed
531
- links[prId] = itemId;
532
- safeWrite(PR_LINKS_PATH, links);
539
+ try {
540
+ const config = safeJson(path.join(MINIONS_DIR, 'config.json')) || {};
541
+ const projects = getProjects(config);
542
+ for (const project of projects) { linkPrToItem(project, prId, itemId); }
543
+ } catch { /* optional */ }
533
544
  }
534
545
 
535
546
  /**
@@ -592,7 +603,9 @@ module.exports = {
592
603
  getProjects,
593
604
  projectRoot,
594
605
  projectStateDir,
606
+ CENTRAL_WI_PATH,
595
607
  projectWorkItemsPath,
608
+ resolveWiPath,
596
609
  projectPrPath,
597
610
  getPrLinks,
598
611
  addPrLink,
package/engine.js CHANGED
@@ -24,7 +24,7 @@
24
24
  const fs = require('fs');
25
25
  const path = require('path');
26
26
  const shared = require('./engine/shared');
27
- const { exec, execSilent, runFile, ENGINE_DEFAULTS: DEFAULTS } = shared;
27
+ const { exec, execSilent, runFile, ENGINE_DEFAULTS: DEFAULTS, CENTRAL_WI_PATH, resolveWiPath } = shared;
28
28
  const queries = require('./engine/queries');
29
29
 
30
30
  // ─── Paths ──────────────────────────────────────────────────────────────────
@@ -879,7 +879,7 @@ const { COOLDOWN_PATH, dispatchCooldowns, loadCooldowns, saveCooldowns,
879
879
  // Auto-clean pending/failed work items for a PRD so they re-materialize with updated plan data
880
880
  function autoCleanPrdWorkItems(prdFile, config) {
881
881
  const allProjects = getProjects(config);
882
- const wiPaths = [path.join(MINIONS_DIR, 'work-items.json')];
882
+ const wiPaths = [CENTRAL_WI_PATH];
883
883
  for (const proj of allProjects) wiPaths.push(projectWorkItemsPath(proj));
884
884
  const deletedIds = [];
885
885
  for (const wiPath of wiPaths) {
@@ -1001,8 +1001,7 @@ function materializePlansAsWorkItems(config) {
1001
1001
  const allProjects = getProjects(config);
1002
1002
  const targetProject = allProjects.find(p => p.name?.toLowerCase() === projectName.toLowerCase()) || allProjects[0];
1003
1003
  if (targetProject) {
1004
- const centralWiPath = path.join(MINIONS_DIR, 'work-items.json');
1005
- const centralItems = safeJson(centralWiPath) || [];
1004
+ const centralItems = safeJson(CENTRAL_WI_PATH) || [];
1006
1005
  const alreadyQueued = centralItems.some(w =>
1007
1006
  w.type === 'plan-to-prd' && w.planFile === plan.source_plan && (w.status === 'pending' || w.status === 'dispatched')
1008
1007
  );
@@ -1070,7 +1069,7 @@ function materializePlansAsWorkItems(config) {
1070
1069
  }
1071
1070
  }
1072
1071
  // Also check central work-items.json
1073
- for (const w of (safeJson(path.join(MINIONS_DIR, 'work-items.json')) || [])) {
1072
+ for (const w of (safeJson(CENTRAL_WI_PATH) || [])) {
1074
1073
  if (w.id) allExistingWiIds.add(w.id);
1075
1074
  }
1076
1075
  const items = plan.missing_features.filter(f =>
@@ -1115,7 +1114,7 @@ function materializePlansAsWorkItems(config) {
1115
1114
 
1116
1115
  let totalCreated = 0;
1117
1116
  for (const [projName, { project, items: projItems }] of itemsByProject) {
1118
- const wiPath = project ? projectWorkItemsPath(project) : path.join(MINIONS_DIR, 'work-items.json');
1117
+ const wiPath = project ? projectWorkItemsPath(project) : CENTRAL_WI_PATH;
1119
1118
  const existingItems = safeJson(wiPath) || [];
1120
1119
  let created = 0;
1121
1120
  const newlyCreatedIds = new Set(); // tracks IDs created in this pass for reconciliation scoping
@@ -1768,7 +1767,7 @@ function extractSpecInfo(filePath, projectRoot_) {
1768
1767
  * Uses the shared work-item.md playbook with multi-project context injected.
1769
1768
  */
1770
1769
  function discoverCentralWorkItems(config) {
1771
- const centralPath = path.join(MINIONS_DIR, 'work-items.json');
1770
+ const centralPath = CENTRAL_WI_PATH;
1772
1771
  const items = safeJson(centralPath) || [];
1773
1772
  const projects = getProjects(config);
1774
1773
  const newWork = [];
@@ -2080,7 +2079,7 @@ function discoverWork(config) {
2080
2079
  const { discoverScheduledWork } = require('./engine/scheduler');
2081
2080
  const scheduledWork = discoverScheduledWork(config);
2082
2081
  if (scheduledWork.length > 0) {
2083
- const centralPath = path.join(MINIONS_DIR, 'work-items.json');
2082
+ const centralPath = CENTRAL_WI_PATH;
2084
2083
  const items = safeJson(centralPath) || [];
2085
2084
  let added = 0;
2086
2085
  for (const item of scheduledWork) {
@@ -2411,9 +2410,7 @@ async function tickInner() {
2411
2410
  // Defensive: ensure the work item is re-queued if completeDispatch didn't fire
2412
2411
  if (item.meta?.item?.id) {
2413
2412
  try {
2414
- const wiPath = item.meta.source === 'central-work-item' || item.meta.source === 'central-work-item-fanout'
2415
- ? path.join(ENGINE_DIR, '..', 'work-items.json')
2416
- : item.meta.project?.name ? projectWorkItemsPath({ name: item.meta.project.name, localPath: item.meta.project.localPath }) : null;
2413
+ const wiPath = resolveWiPath(item.meta);
2417
2414
  if (wiPath) {
2418
2415
  const items = safeJson(wiPath) || [];
2419
2416
  const wi = items.find(i => i.id === item.meta.item.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.156",
3
+ "version": "0.1.158",
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"
@@ -0,0 +1,19 @@
1
+ {
2
+ "id": "daily-standup",
3
+ "title": "Daily Squad Stand-up",
4
+ "stages": [
5
+ {
6
+ "id": "standup",
7
+ "type": "meeting",
8
+ "title": "Daily Squad Stand-up � 2026-04-01",
9
+ "agenda": "Daily weekday meeting for all agents to review squad setup improvements and discuss progress on agentic harness engineering.\n\n1. What shipped since yesterday's meeting (Track A/B/C progress)?\n2. Any blockers on current work items?\n3. Review recent agentic harness engineering best practices � are there new Anthropic articles or model improvements that change our assumptions?\n4. Propose 1 concrete improvement each agent wants to make to the squad harness this week.\n5. Agree on today's priorities and any re-sequencing needed.\n\nOutput: a bulleted list of decisions made, action items with owners, and any updated priorities.",
10
+ "participants": [
11
+ "all"
12
+ ]
13
+ }
14
+ ],
15
+ "trigger": {
16
+ "cron": "0 9 1,2,3,4,5"
17
+ },
18
+ "enabled": true
19
+ }