@yemi33/minions 0.1.361 → 0.1.362
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 +2 -1
- package/dashboard/js/settings.js +1 -1
- package/dashboard.js +6 -6
- package/engine/shared.js +8 -2
- package/engine.js +6 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.362 (2026-04-06)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
|
+
- Null-safe safeJson wrappers + dashboard crash guards (#203)
|
|
6
7
|
- pipeline plan stage uses LLM to generate structured plan from meeting
|
|
7
8
|
- show 'Converting to PRD' status instead of 'In Progress' during plan conversion
|
|
8
9
|
|
package/dashboard/js/settings.js
CHANGED
|
@@ -25,7 +25,7 @@ async function openSettings() {
|
|
|
25
25
|
'<h3 style="font-size:13px;color:var(--blue);margin-bottom:8px">Engine</h3>' +
|
|
26
26
|
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:16px">' +
|
|
27
27
|
settingsField('Tick Interval', 'set-tickInterval', e.tickInterval || 60000, 'ms', 'How often the engine runs discovery + dispatch') +
|
|
28
|
-
settingsField('Max Concurrent Agents', 'set-maxConcurrent', e.maxConcurrent ||
|
|
28
|
+
settingsField('Max Concurrent Agents', 'set-maxConcurrent', e.maxConcurrent || 5, '', 'Max agents working simultaneously') +
|
|
29
29
|
settingsField('Consolidation Threshold', 'set-inboxConsolidateThreshold', e.inboxConsolidateThreshold || 5, 'notes', 'Inbox notes before auto-consolidation') +
|
|
30
30
|
settingsField('Agent Timeout', 'set-agentTimeout', e.agentTimeout || 18000000, 'ms', 'Kill agent after this duration') +
|
|
31
31
|
settingsField('Max Turns', 'set-maxTurns', e.maxTurns || 100, '', 'Claude CLI --max-turns per agent') +
|
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, safeUnlink, mutateJsonFileLocked, getProjects: _getProjects, DONE_STATUSES } = shared;
|
|
17
|
+
const { safeRead, safeReadDir, safeWrite, safeJson, safeJsonObj, safeJsonArr, safeUnlink, mutateJsonFileLocked, getProjects: _getProjects, DONE_STATUSES } = shared;
|
|
18
18
|
const { getAgents, getAgentDetail, getPrdInfo, getWorkItems, getDispatchQueue,
|
|
19
19
|
getSkills, getInbox, getNotesWithMeta, getPullRequests,
|
|
20
20
|
getEngineLog, getMetrics, getKnowledgeBaseEntries, timeSince,
|
|
@@ -1206,7 +1206,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1206
1206
|
const planPath = resolvePlanPath(body.source);
|
|
1207
1207
|
if (!fs.existsSync(planPath)) return jsonReply(res, 404, { error: 'plan file not found' });
|
|
1208
1208
|
// Pre-check: verify item exists before taking the lock
|
|
1209
|
-
const preCheck =
|
|
1209
|
+
const preCheck = safeJsonObj(planPath);
|
|
1210
1210
|
const preItem = (preCheck.missing_features || []).find(f => f.id === body.itemId);
|
|
1211
1211
|
if (!preItem) return jsonReply(res, 404, { error: 'item not found in plan' });
|
|
1212
1212
|
|
|
@@ -1258,7 +1258,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1258
1258
|
if (!body.source || !body.itemId) return jsonReply(res, 400, { error: 'source and itemId required' });
|
|
1259
1259
|
const planPath = resolvePlanPath(body.source);
|
|
1260
1260
|
if (!fs.existsSync(planPath)) return jsonReply(res, 404, { error: 'plan file not found' });
|
|
1261
|
-
const plan =
|
|
1261
|
+
const plan = safeJsonObj(planPath);
|
|
1262
1262
|
const idx = (plan.missing_features || []).findIndex(f => f.id === body.itemId);
|
|
1263
1263
|
if (idx < 0) return jsonReply(res, 404, { error: 'item not found in plan' });
|
|
1264
1264
|
|
|
@@ -2048,7 +2048,7 @@ If nothing to do: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
2048
2048
|
if (!body.source) return jsonReply(res, 400, { error: 'source required' });
|
|
2049
2049
|
const planPath = resolvePlanPath(body.source);
|
|
2050
2050
|
if (!fs.existsSync(planPath)) return jsonReply(res, 404, { error: 'plan file not found' });
|
|
2051
|
-
const plan =
|
|
2051
|
+
const plan = safeJsonObj(planPath);
|
|
2052
2052
|
const planItems = plan.missing_features || [];
|
|
2053
2053
|
|
|
2054
2054
|
let reset = 0, kept = 0, newCount = 0;
|
|
@@ -2127,7 +2127,7 @@ If nothing to do: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
2127
2127
|
}
|
|
2128
2128
|
for (const wiPath of wiPaths) {
|
|
2129
2129
|
try {
|
|
2130
|
-
const items =
|
|
2130
|
+
const items = safeJsonArr(wiPath);
|
|
2131
2131
|
const filtered = items.filter(w => w.sourcePlan !== body.file);
|
|
2132
2132
|
if (filtered.length < items.length) {
|
|
2133
2133
|
cleaned += items.length - filtered.length;
|
|
@@ -2834,7 +2834,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
2834
2834
|
if (!fs.existsSync(target)) return jsonReply(res, 400, { error: 'Directory not found: ' + target });
|
|
2835
2835
|
|
|
2836
2836
|
const configPath = path.join(MINIONS_DIR, 'config.json');
|
|
2837
|
-
const config =
|
|
2837
|
+
const config = safeJsonObj(configPath);
|
|
2838
2838
|
if (!config.projects) config.projects = [];
|
|
2839
2839
|
|
|
2840
2840
|
// Check if already linked
|
package/engine/shared.js
CHANGED
|
@@ -73,6 +73,12 @@ function safeJson(p) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/** Null-safe safeJson wrapper — returns {} when file is missing/corrupt. */
|
|
77
|
+
function safeJsonObj(p) { return safeJson(p) || {}; }
|
|
78
|
+
|
|
79
|
+
/** Null-safe safeJson wrapper — returns [] when file is missing/corrupt. */
|
|
80
|
+
function safeJsonArr(p) { return safeJson(p) || []; }
|
|
81
|
+
|
|
76
82
|
/**
|
|
77
83
|
* Monotonic counter for generating unique temp file names within this process.
|
|
78
84
|
* Assumes single-thread execution (no worker_threads). If worker_threads are
|
|
@@ -422,7 +428,7 @@ const DEFAULT_AGENTS = {
|
|
|
422
428
|
|
|
423
429
|
const DEFAULT_CLAUDE = {
|
|
424
430
|
binary: 'claude',
|
|
425
|
-
outputFormat: 'json',
|
|
431
|
+
outputFormat: 'stream-json',
|
|
426
432
|
allowedTools: 'Edit,Write,Read,Bash,Glob,Grep,Agent,WebFetch,WebSearch',
|
|
427
433
|
};
|
|
428
434
|
|
|
@@ -623,7 +629,7 @@ module.exports = {
|
|
|
623
629
|
log,
|
|
624
630
|
safeRead,
|
|
625
631
|
safeReadDir,
|
|
626
|
-
safeJson,
|
|
632
|
+
safeJson, safeJsonObj, safeJsonArr,
|
|
627
633
|
safeWrite,
|
|
628
634
|
safeUnlink,
|
|
629
635
|
withFileLock,
|
package/engine.js
CHANGED
|
@@ -1416,7 +1416,12 @@ function discoverFromWorkItems(config, project) {
|
|
|
1416
1416
|
// This protects against persisted state drift from old runtime versions.
|
|
1417
1417
|
try {
|
|
1418
1418
|
mutateDispatch((dp) => {
|
|
1419
|
-
|
|
1419
|
+
const prev = Array.isArray(dp.completed) ? dp.completed : [];
|
|
1420
|
+
const next = [];
|
|
1421
|
+
for (let i = 0; i < prev.length; i++) {
|
|
1422
|
+
if (prev[i].meta?.dispatchKey !== key) next.push(prev[i]);
|
|
1423
|
+
}
|
|
1424
|
+
dp.completed = next;
|
|
1420
1425
|
return dp;
|
|
1421
1426
|
});
|
|
1422
1427
|
dispatchCooldowns.delete(key);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.362",
|
|
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"
|