@yemi33/minions 0.1.321 → 0.1.323

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,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.323 (2026-04-03)
4
+
5
+ ### Fixes
6
+ - scan skips NugetCache, OneDrive, .vs, packages + validates .git/HEAD
7
+
8
+ ### Other
9
+ - refactor: final magic string replacements in engine, lifecycle, cleanup
10
+
3
11
  ## 0.1.321 (2026-04-03)
4
12
 
5
13
  ### Fixes
package/dashboard.js CHANGED
@@ -2897,7 +2897,9 @@ What would you like to discuss or change? When you're happy, say "approve" and I
2897
2897
 
2898
2898
  // Find git repos recursively (same logic as minions.js findGitRepos)
2899
2899
  const skipDirs = new Set(['node_modules', '.git', '.hg', 'AppData', '$Recycle.Bin', 'Windows',
2900
- 'Program Files', 'Program Files (x86)', '.cache', '.npm', '.yarn', '.nuget', 'worktrees', '.minions', '.squad']);
2900
+ 'Program Files', 'Program Files (x86)', '.cache', '.npm', '.yarn', '.nuget', 'NugetCache',
2901
+ 'worktrees', '.minions', '.squad', '.vs', '.vscode', 'obj', 'bin', 'packages',
2902
+ 'OneDrive', 'OneDrive - Microsoft', '.copilot', 'marketplace-cache']);
2901
2903
  const repos = [];
2902
2904
  function walk(dir, depth) {
2903
2905
  if (depth > maxDepth) return;
@@ -2905,7 +2907,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
2905
2907
  try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
2906
2908
  for (const e of entries) {
2907
2909
  if (!e.isDirectory()) continue;
2908
- if (e.name === '.git') { repos.push(dir); return; }
2910
+ if (e.name === '.git') {
2911
+ // Validate it's a real repo — must have HEAD file
2912
+ try { if (fs.existsSync(path.join(dir, '.git', 'HEAD'))) repos.push(dir); } catch {}
2913
+ return;
2914
+ }
2909
2915
  if (e.name.startsWith('.') || skipDirs.has(e.name)) continue;
2910
2916
  walk(path.join(dir, e.name), depth + 1);
2911
2917
  }
package/engine/cleanup.js CHANGED
@@ -111,7 +111,7 @@ function runCleanup(config, verbose = false) {
111
111
  const prs = safeJson(projectPrPath(project)) || [];
112
112
  const mergedBranches = new Set();
113
113
  for (const pr of prs) {
114
- if (pr.status === 'merged' || pr.status === 'abandoned' || pr.status === 'completed') {
114
+ if (pr.status === shared.PR_STATUS.MERGED || pr.status === shared.PR_STATUS.ABANDONED || pr.status === shared.PLAN_STATUS.COMPLETED) {
115
115
  if (pr.branch) mergedBranches.add(pr.branch);
116
116
  }
117
117
  }
@@ -223,7 +223,7 @@ function runCleanup(config, verbose = false) {
223
223
  const freshPrs = safeJson(projectPrPath(project)) || [];
224
224
  const freshMergedBranches = new Set();
225
225
  for (const pr of freshPrs) {
226
- if (pr.status === 'merged' || pr.status === 'abandoned' || pr.status === 'completed') {
226
+ if (pr.status === shared.PR_STATUS.MERGED || pr.status === shared.PR_STATUS.ABANDONED || pr.status === shared.PLAN_STATUS.COMPLETED) {
227
227
  if (pr.branch) freshMergedBranches.add(pr.branch);
228
228
  }
229
229
  }
@@ -197,7 +197,7 @@ function checkPlanCompletion(meta, config) {
197
197
  const prs = (safeJson(shared.projectPrPath(p)) || [])
198
198
  .filter(pr => {
199
199
  const linkedId = prLinks[pr.id];
200
- return pr.status === 'active' && linkedId && doneItems.find(w => w.id === linkedId);
200
+ return pr.status === PR_STATUS.ACTIVE && linkedId && doneItems.find(w => w.id === linkedId);
201
201
  });
202
202
  if (prs.length > 0) {
203
203
  projectPrs[p.name] = { project: p, prs, mainBranch: p.mainBranch || 'main' };
@@ -693,7 +693,7 @@ function updatePrAfterReview(agentId, pr, project) {
693
693
  // Record the reviewer — actual verdict comes from ADO/GitHub votes via pollPrStatus.
694
694
  // Set to 'waiting' so pollPrStatus updates it with the real vote on next cycle.
695
695
  const dispatch = getDispatch();
696
- const completedEntry = (dispatch.completed || []).find(d => d.agent === agentId && d.type === 'review');
696
+ const completedEntry = (dispatch.completed || []).find(d => d.agent === agentId && d.type === WORK_TYPE.REVIEW);
697
697
 
698
698
  // Set reviewStatus to 'waiting' (single source of truth — synced from ADO/GitHub votes on next poll)
699
699
  target.reviewStatus = 'waiting';
@@ -988,7 +988,7 @@ function updateMetrics(agentId, dispatchItem, result, taskUsage, prsCreatedCount
988
988
  if (result === DISPATCH_RESULT.SUCCESS) {
989
989
  m.tasksCompleted++;
990
990
  if (prsCreatedCount > 0) m.prsCreated = (m.prsCreated || 0) + prsCreatedCount;
991
- if (dispatchItem.type === 'review') m.reviewsDone++;
991
+ if (dispatchItem.type === WORK_TYPE.REVIEW) m.reviewsDone++;
992
992
  } else if (result === 'retry') {
993
993
  // Auto-retry: count cost but not as a final outcome
994
994
  m.tasksRetried = (m.tasksRetried || 0) + 1;
@@ -1243,7 +1243,7 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
1243
1243
  }
1244
1244
 
1245
1245
  // Detect implement tasks that completed without creating a PR
1246
- if (isSuccess && (type === 'implement' || type === 'implement:large' || type === 'fix') && prsCreatedCount === 0 && meta?.item?.id) {
1246
+ if (isSuccess && (type === WORK_TYPE.IMPLEMENT || type === WORK_TYPE.IMPLEMENT_LARGE || type === WORK_TYPE.FIX) && prsCreatedCount === 0 && meta?.item?.id) {
1247
1247
  // Check if a PR already exists linked to this work item (from a previous attempt)
1248
1248
  const projects = shared.getProjects(config);
1249
1249
  const existingPrFound = Object.values(getPrLinks()).includes(meta.item.id);
@@ -1280,8 +1280,8 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
1280
1280
  }
1281
1281
  }
1282
1282
 
1283
- if (type === 'review') updatePrAfterReview(agentId, meta?.pr, meta?.project);
1284
- if (type === 'fix') updatePrAfterFix(meta?.pr, meta?.project, meta?.source);
1283
+ if (type === WORK_TYPE.REVIEW) updatePrAfterReview(agentId, meta?.pr, meta?.project);
1284
+ if (type === WORK_TYPE.FIX) updatePrAfterFix(meta?.pr, meta?.project, meta?.source);
1285
1285
  checkForLearnings(agentId, config.agents[agentId], dispatchItem.task);
1286
1286
  if (isSuccess) extractSkillsFromOutput(stdout, agentId, dispatchItem, config);
1287
1287
  updateAgentHistory(agentId, dispatchItem, result);
@@ -1311,14 +1311,14 @@ function syncPrdFromPrs(config) {
1311
1311
  for (const project of allProjects) {
1312
1312
  const wiPath = projectWorkItemsPath(project);
1313
1313
  const items = safeJson(wiPath) || [];
1314
- const hasPending = items.some(wi => wi.status === 'pending' && !wi._pr);
1314
+ const hasPending = items.some(wi => wi.status === WI_STATUS.PENDING && !wi._pr);
1315
1315
  if (!hasPending) continue;
1316
1316
  const reconciled = reconcileItemsWithPrs(items, allPrs);
1317
1317
  if (reconciled > 0) {
1318
1318
  safeWrite(wiPath, items);
1319
1319
  // Sync done status to PRD JSON for each newly reconciled item
1320
1320
  for (const wi of items) {
1321
- if (wi.status === 'done') syncPrdItemStatus(wi.id, 'done', wi.sourcePlan);
1321
+ if (wi.status === WI_STATUS.DONE) syncPrdItemStatus(wi.id, 'done', wi.sourcePlan);
1322
1322
  }
1323
1323
  totalReconciled += reconciled;
1324
1324
  }
package/engine.js CHANGED
@@ -1319,7 +1319,7 @@ function discoverFromPrs(config, project) {
1319
1319
  }
1320
1320
 
1321
1321
  // PRs with build failures — route to author (has session context from implementing)
1322
- if (pr.status === 'active' && pr.buildStatus === 'failing') {
1322
+ if (pr.status === PR_STATUS.ACTIVE && pr.buildStatus === 'failing') {
1323
1323
  const key = `build-fix-${project?.name || 'default'}-${pr.id}`;
1324
1324
  if (isAlreadyDispatched(key) || isOnCooldown(key, cooldownMs)) continue;
1325
1325
  const agentId = resolveAgent('fix', config, pr.agent);
@@ -1638,7 +1638,7 @@ function materializeSpecsAsWorkItems(config, project) {
1638
1638
 
1639
1639
  const prs = getPrs(project);
1640
1640
  const mergedPrs = prs.filter(pr =>
1641
- (pr.status === 'merged' || pr.status === 'completed') &&
1641
+ (pr.status === PR_STATUS.MERGED || pr.status === PLAN_STATUS.COMPLETED) &&
1642
1642
  !tracker.processedPrs[pr.id]
1643
1643
  );
1644
1644
 
package/minions.js CHANGED
@@ -257,7 +257,9 @@ function findGitRepos(rootDir, maxDepth = 3) {
257
257
  // Skip common non-project dirs
258
258
  const base = path.basename(dir);
259
259
  if (['node_modules', '.git', '.hg', 'AppData', '$Recycle.Bin', 'Windows', 'Program Files',
260
- 'Program Files (x86)', '.cache', '.npm', '.yarn', '.nuget', 'worktrees', '.minions'].includes(base)) return;
260
+ 'Program Files (x86)', '.cache', '.npm', '.yarn', '.nuget', 'NugetCache',
261
+ 'worktrees', '.minions', '.squad', '.vs', '.vscode', 'obj', 'bin', 'packages',
262
+ 'OneDrive', 'OneDrive - Microsoft', '.copilot', 'marketplace-cache'].includes(base)) return;
261
263
  // Skip minions home directory itself
262
264
  if (path.resolve(dir) === path.resolve(MINIONS_HOME)) return;
263
265
 
@@ -271,6 +273,8 @@ function findGitRepos(rootDir, maxDepth = 3) {
271
273
  if (content.trimStart().startsWith('gitdir:')) return; // worktree, skip
272
274
  } catch {}
273
275
  }
276
+ // Validate it's a real repo — must have HEAD file
277
+ if (stat.isDirectory() && !fs.existsSync(path.join(gitDir, 'HEAD'))) return;
274
278
  repos.push(dir);
275
279
  return; // Don't recurse into git repos (they may have nested submodules)
276
280
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.321",
3
+ "version": "0.1.323",
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"