@yemi33/minions 0.1.323 → 0.1.324

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,8 +1,9 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.323 (2026-04-03)
3
+ ## 0.1.324 (2026-04-03)
4
4
 
5
5
  ### Fixes
6
+ - 7 bugs from engine audit — runtime crashes, race conditions, stale state
6
7
  - scan skips NugetCache, OneDrive, .vs, packages + validates .git/HEAD
7
8
 
8
9
  ### Other
package/dashboard.js CHANGED
@@ -878,18 +878,22 @@ const server = http.createServer(async (req, res) => {
878
878
  }
879
879
  if (!wiPath) return jsonReply(res, 404, { error: 'source not found' });
880
880
 
881
- const items = JSON.parse(safeRead(wiPath) || '[]');
882
- const item = items.find(i => i.id === id);
883
- if (!item) return jsonReply(res, 404, { error: 'item not found' });
884
-
885
- item.status = 'pending';
886
- item._retryCount = 0; // Reset retry counter on manual retry
887
- delete item.dispatched_at;
888
- delete item.dispatched_to;
889
- delete item.failReason;
890
- delete item.failedAt;
891
- delete item.fanOutAgents;
892
- safeWrite(wiPath, items);
881
+ let found = false;
882
+ mutateJsonFileLocked(wiPath, (items) => {
883
+ if (!Array.isArray(items)) items = [];
884
+ const item = items.find(i => i.id === id);
885
+ if (!item) return items;
886
+ found = true;
887
+ item.status = 'pending';
888
+ item._retryCount = 0; // Reset retry counter on manual retry
889
+ delete item.dispatched_at;
890
+ delete item.dispatched_to;
891
+ delete item.failReason;
892
+ delete item.failedAt;
893
+ delete item.fanOutAgents;
894
+ return items;
895
+ });
896
+ if (!found) return jsonReply(res, 404, { error: 'item not found' });
893
897
 
894
898
  // Clear completed dispatch entries so the engine doesn't dedup this item
895
899
  const dispatchPath = path.join(MINIONS_DIR, 'engine', 'dispatch.json');
@@ -108,9 +108,11 @@ function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', res
108
108
  ? path.join(MINIONS_DIR, 'work-items.json')
109
109
  : item.meta.project?.name ? projectWorkItemsPath({ name: item.meta.project.name, localPath: item.meta.project.localPath }) : null;
110
110
  if (wiPath) {
111
- const items = safeJson(wiPath) || [];
112
- const wi = items.find(i => i.id === item.meta.item.id);
113
- if (wi) retries = wi._retryCount || 0;
111
+ const items = safeJson(wiPath);
112
+ if (items && Array.isArray(items)) {
113
+ const wi = items.find(i => i.id === item.meta.item.id);
114
+ if (wi) retries = wi._retryCount || 0;
115
+ }
114
116
  }
115
117
  } catch (e) { log('warn', 'read retry count: ' + e.message); }
116
118
  const maxRetries = ENGINE_DEFAULTS.maxRetries;
@@ -809,7 +809,7 @@ async function handlePostMerge(pr, project, config, newStatus) {
809
809
  if (item && item.status !== 'done') {
810
810
  log('info', `Post-merge: marking work item ${mergedItemId} as done (was ${item.status}) for ${pr.id}`);
811
811
  item.status = 'done';
812
- item.completedAt = e.ts();
812
+ item.completedAt = ts();
813
813
  item._mergedVia = pr.id;
814
814
  shared.safeWrite(wiPath, items);
815
815
  break;
@@ -1221,7 +1221,7 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
1221
1221
  return d.includes(branchSlug) && fs.statSync(path.join(worktreeRoot, d)).isDirectory();
1222
1222
  });
1223
1223
  // Only remove if no other active dispatch uses this branch
1224
- const dispatch = e.getDispatch();
1224
+ const dispatch = getDispatch();
1225
1225
  const otherActive = ((dispatch.active || []).concat(dispatch.pending || [])).some(d =>
1226
1226
  d.id !== dispatchItem.id && d.meta?.branch && shared.sanitizeBranch && shared.sanitizeBranch(d.meta.branch) === branchSlug
1227
1227
  );
@@ -1229,7 +1229,7 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
1229
1229
  for (const dir of dirs) {
1230
1230
  const wtPath = path.join(worktreeRoot, dir);
1231
1231
  try {
1232
- shared.exec(`git worktree remove "${wtPath}" --force`, { cwd: rootDir, stdio: 'pipe', timeout: 15000, windowsHide: true });
1232
+ execSilent(`git worktree remove "${wtPath}" --force`, { cwd: rootDir, timeout: 15000 });
1233
1233
  log('info', `Post-completion: removed worktree ${dir}`);
1234
1234
  } catch (err) {
1235
1235
  log('warn', `Post-completion: failed to remove worktree ${dir}: ${err.message}`);
@@ -1269,10 +1269,10 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
1269
1269
  wi._retryCount = retries + 1;
1270
1270
  delete wi.dispatched_at;
1271
1271
  delete wi.dispatched_to;
1272
- e.log('info', `Auto-retry ${retries + 1}/${maxR} for ${meta.item.id} (no PR created)`);
1272
+ log('info', `Auto-retry ${retries + 1}/${maxR} for ${meta.item.id} (no PR created)`);
1273
1273
  } else {
1274
1274
  wi.status = WI_STATUS.FAILED;
1275
- e.log('warn', `${meta.item.id} failed after ${maxR} retries — no PR created`);
1275
+ log('warn', `${meta.item.id} failed after ${maxR} retries — no PR created`);
1276
1276
  }
1277
1277
  shared.safeWrite(wiPath, items);
1278
1278
  }
package/engine.js CHANGED
@@ -591,6 +591,11 @@ function spawnAgent(dispatchItem, config) {
591
591
  // Re-attach to existing tracking
592
592
  activeProcesses.set(id, { proc: resumeProc, agentId, startedAt: procInfo.startedAt, sessionId: steerSessionId });
593
593
 
594
+ // Reset output buffers so post-completion parsing only sees the resumed session
595
+ stdout = '';
596
+ stderr = '';
597
+ lastOutputAt = Date.now();
598
+
594
599
  // Re-wire stdout/stderr handlers (same as original)
595
600
  resumeProc.stdout.on('data', (data) => {
596
601
  const chunk = data.toString();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.323",
3
+ "version": "0.1.324",
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"