@yemi33/minions 0.1.322 → 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 +5 -1
- package/dashboard.js +24 -14
- package/engine/dispatch.js +5 -3
- package/engine/lifecycle.js +5 -5
- package/engine.js +5 -0
- package/minions.js +5 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.324 (2026-04-03)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
- 7 bugs from engine audit — runtime crashes, race conditions, stale state
|
|
7
|
+
- scan skips NugetCache, OneDrive, .vs, packages + validates .git/HEAD
|
|
4
8
|
|
|
5
9
|
### Other
|
|
6
10
|
- refactor: final magic string replacements in engine, lifecycle, cleanup
|
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
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
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');
|
|
@@ -2897,7 +2901,9 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
2897
2901
|
|
|
2898
2902
|
// Find git repos recursively (same logic as minions.js findGitRepos)
|
|
2899
2903
|
const skipDirs = new Set(['node_modules', '.git', '.hg', 'AppData', '$Recycle.Bin', 'Windows',
|
|
2900
|
-
'Program Files', 'Program Files (x86)', '.cache', '.npm', '.yarn', '.nuget', '
|
|
2904
|
+
'Program Files', 'Program Files (x86)', '.cache', '.npm', '.yarn', '.nuget', 'NugetCache',
|
|
2905
|
+
'worktrees', '.minions', '.squad', '.vs', '.vscode', 'obj', 'bin', 'packages',
|
|
2906
|
+
'OneDrive', 'OneDrive - Microsoft', '.copilot', 'marketplace-cache']);
|
|
2901
2907
|
const repos = [];
|
|
2902
2908
|
function walk(dir, depth) {
|
|
2903
2909
|
if (depth > maxDepth) return;
|
|
@@ -2905,7 +2911,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
2905
2911
|
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
2906
2912
|
for (const e of entries) {
|
|
2907
2913
|
if (!e.isDirectory()) continue;
|
|
2908
|
-
if (e.name === '.git') {
|
|
2914
|
+
if (e.name === '.git') {
|
|
2915
|
+
// Validate it's a real repo — must have HEAD file
|
|
2916
|
+
try { if (fs.existsSync(path.join(dir, '.git', 'HEAD'))) repos.push(dir); } catch {}
|
|
2917
|
+
return;
|
|
2918
|
+
}
|
|
2909
2919
|
if (e.name.startsWith('.') || skipDirs.has(e.name)) continue;
|
|
2910
2920
|
walk(path.join(dir, e.name), depth + 1);
|
|
2911
2921
|
}
|
package/engine/dispatch.js
CHANGED
|
@@ -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
|
-
|
|
113
|
-
|
|
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;
|
package/engine/lifecycle.js
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/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', '
|
|
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.
|
|
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"
|