@yemi33/minions 0.1.375 → 0.1.377
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 +9 -1
- package/dashboard.js +50 -44
- package/engine.js +4 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.377 (2026-04-06)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
- Fix 3 crash bugs: fanAgentId, null dereferences, cleanDispatchEntries (#228)
|
|
7
|
+
|
|
8
|
+
## 0.1.376 (2026-04-06)
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
- dashboard write locking for work-items.json (#227)
|
|
4
12
|
|
|
5
13
|
### Fixes
|
|
6
14
|
- show red notification dot on Notes & KB when note is created
|
package/dashboard.js
CHANGED
|
@@ -14,7 +14,7 @@ const shared = require('./engine/shared');
|
|
|
14
14
|
const queries = require('./engine/queries');
|
|
15
15
|
const os = require('os');
|
|
16
16
|
|
|
17
|
-
const { safeRead, safeReadDir, safeWrite, safeJson, safeJsonObj, safeJsonArr, safeUnlink, mutateJsonFileLocked, getProjects: _getProjects, DONE_STATUSES } = shared;
|
|
17
|
+
const { safeRead, safeReadDir, safeWrite, safeJson, safeJsonObj, safeJsonArr, safeUnlink, mutateJsonFileLocked, getProjects: _getProjects, DONE_STATUSES, WI_STATUS } = shared;
|
|
18
18
|
const { getAgents, getAgentDetail, getPrdInfo, getWorkItems, getDispatchQueue,
|
|
19
19
|
getSkills, getInbox, getNotesWithMeta, getPullRequests,
|
|
20
20
|
getEngineLog, getMetrics, getKnowledgeBaseEntries, timeSince,
|
|
@@ -972,7 +972,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
972
972
|
}, { defaultValue: [] });
|
|
973
973
|
if (!found) return jsonReply(res, 404, { error: 'item not found' });
|
|
974
974
|
|
|
975
|
-
// Clean dispatch entries + kill running agent
|
|
975
|
+
// Clean dispatch entries + kill running agent (outside lock)
|
|
976
976
|
const dispatchRemoved = cleanDispatchEntries(d =>
|
|
977
977
|
d.meta?.item?.id === id ||
|
|
978
978
|
d.meta?.dispatchKey?.endsWith(id)
|
|
@@ -1011,21 +1011,24 @@ const server = http.createServer(async (req, res) => {
|
|
|
1011
1011
|
}
|
|
1012
1012
|
if (!wiPath) return jsonReply(res, 404, { error: 'source not found' });
|
|
1013
1013
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1014
|
+
let archivedItem = null;
|
|
1015
|
+
mutateJsonFileLocked(wiPath, (items) => {
|
|
1016
|
+
if (!Array.isArray(items)) items = [];
|
|
1017
|
+
const idx = items.findIndex(i => i.id === id);
|
|
1018
|
+
if (idx === -1) return items;
|
|
1019
|
+
archivedItem = items.splice(idx, 1)[0];
|
|
1020
|
+
archivedItem.archivedAt = new Date().toISOString();
|
|
1021
|
+
return items;
|
|
1022
|
+
});
|
|
1023
|
+
if (!archivedItem) return jsonReply(res, 404, { error: 'item not found' });
|
|
1020
1024
|
|
|
1021
|
-
// Append to archive file
|
|
1025
|
+
// Append to archive file (outside lock)
|
|
1022
1026
|
const archivePath = wiPath.replace('.json', '-archive.json');
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
safeWrite(wiPath, items);
|
|
1027
|
+
mutateJsonFileLocked(archivePath, (archive) => {
|
|
1028
|
+
if (!Array.isArray(archive)) archive = [];
|
|
1029
|
+
archive.push(archivedItem);
|
|
1030
|
+
return archive;
|
|
1031
|
+
}, { defaultValue: [] });
|
|
1029
1032
|
|
|
1030
1033
|
// Clean dispatch entries for archived item
|
|
1031
1034
|
const sourcePrefix = (!source || source === 'central') ? 'central-work-' : `work-${source}-`;
|
|
@@ -1069,14 +1072,11 @@ const server = http.createServer(async (req, res) => {
|
|
|
1069
1072
|
// Write to central queue — agent decides which project
|
|
1070
1073
|
wiPath = path.join(MINIONS_DIR, 'work-items.json');
|
|
1071
1074
|
}
|
|
1072
|
-
let items = [];
|
|
1073
|
-
const existing = safeRead(wiPath);
|
|
1074
|
-
if (existing) { try { items = JSON.parse(existing); } catch {} }
|
|
1075
1075
|
const id = 'W-' + shared.uid();
|
|
1076
1076
|
const item = {
|
|
1077
1077
|
id, title: body.title, type: body.type || 'implement',
|
|
1078
1078
|
priority: body.priority || 'medium', description: body.description || '',
|
|
1079
|
-
status:
|
|
1079
|
+
status: WI_STATUS.PENDING, created: new Date().toISOString(), createdBy: 'dashboard',
|
|
1080
1080
|
};
|
|
1081
1081
|
if (body.scope) item.scope = body.scope;
|
|
1082
1082
|
if (body.agent) item.agent = body.agent;
|
|
@@ -1084,8 +1084,11 @@ const server = http.createServer(async (req, res) => {
|
|
|
1084
1084
|
if (body.references) item.references = body.references;
|
|
1085
1085
|
if (body.acceptanceCriteria) item.acceptanceCriteria = body.acceptanceCriteria;
|
|
1086
1086
|
if (body.skipPr) item.skipPr = true;
|
|
1087
|
-
items
|
|
1088
|
-
|
|
1087
|
+
mutateJsonFileLocked(wiPath, (items) => {
|
|
1088
|
+
if (!Array.isArray(items)) items = [];
|
|
1089
|
+
items.push(item);
|
|
1090
|
+
return items;
|
|
1091
|
+
});
|
|
1089
1092
|
return jsonReply(res, 200, { ok: true, id });
|
|
1090
1093
|
} catch (e) { return jsonReply(res, 400, { error: e.message }); }
|
|
1091
1094
|
}
|
|
@@ -1107,29 +1110,32 @@ const server = http.createServer(async (req, res) => {
|
|
|
1107
1110
|
}
|
|
1108
1111
|
if (!wiPath) return jsonReply(res, 404, { error: 'source not found' });
|
|
1109
1112
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
return jsonReply(res,
|
|
1113
|
+
let result = null;
|
|
1114
|
+
let agentChanged = false;
|
|
1115
|
+
mutateJsonFileLocked(wiPath, (items) => {
|
|
1116
|
+
if (!Array.isArray(items)) items = [];
|
|
1117
|
+
const item = items.find(i => i.id === id);
|
|
1118
|
+
if (!item) { result = { code: 404, body: { error: 'item not found' } }; return items; }
|
|
1119
|
+
if (item.status === WI_STATUS.DISPATCHED) { result = { code: 400, body: { error: 'Cannot edit dispatched items' } }; return items; }
|
|
1120
|
+
|
|
1121
|
+
if (title !== undefined) item.title = title;
|
|
1122
|
+
if (description !== undefined) item.description = description;
|
|
1123
|
+
if (type !== undefined) item.type = type;
|
|
1124
|
+
if (priority !== undefined) item.priority = priority;
|
|
1125
|
+
if (agent !== undefined) {
|
|
1126
|
+
item.agent = agent || null;
|
|
1127
|
+
agentChanged = true;
|
|
1128
|
+
}
|
|
1129
|
+
if (body.references !== undefined) item.references = body.references;
|
|
1130
|
+
if (body.acceptanceCriteria !== undefined) item.acceptanceCriteria = body.acceptanceCriteria;
|
|
1131
|
+
item.updatedAt = new Date().toISOString();
|
|
1132
|
+
result = { code: 200, body: { ok: true, item } };
|
|
1133
|
+
return items;
|
|
1134
|
+
});
|
|
1135
|
+
if (!result) return jsonReply(res, 500, { error: 'unexpected state' });
|
|
1136
|
+
// Clear stale pending dispatch entries outside lock
|
|
1137
|
+
if (agentChanged) cleanDispatchEntries(d => d.meta?.item?.id === id);
|
|
1138
|
+
return jsonReply(res, result.code, result.body);
|
|
1133
1139
|
} catch (e) { return jsonReply(res, 400, { error: e.message }); }
|
|
1134
1140
|
}
|
|
1135
1141
|
|
package/engine.js
CHANGED
|
@@ -911,8 +911,10 @@ function autoCleanPrdWorkItems(prdFile, config) {
|
|
|
911
911
|
const deletedSet = new Set(deletedIds);
|
|
912
912
|
mutateDispatch((dispatch) => {
|
|
913
913
|
const pred = d => deletedSet.has(d.meta?.item?.id) && d.meta?.item?.sourcePlan === prdFile;
|
|
914
|
-
|
|
915
|
-
|
|
914
|
+
for (const queue of ['pending', 'active', 'completed']) {
|
|
915
|
+
if (!Array.isArray(dispatch[queue])) continue;
|
|
916
|
+
dispatch[queue] = dispatch[queue].filter(d => !pred(d));
|
|
917
|
+
}
|
|
916
918
|
return dispatch;
|
|
917
919
|
});
|
|
918
920
|
log('info', `Plan sync: cleared ${deletedIds.length} pending/failed work items for ${prdFile}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.377",
|
|
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"
|