@yemi33/minions 0.1.478 → 0.1.480
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 +3 -1
- package/dashboard/js/command-center.js +11 -0
- package/dashboard.js +23 -0
- package/engine/cli.js +3 -0
- package/engine/shared.js +41 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.480 (2026-04-07)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
|
+
- file bugs as GitHub issues from CC or doc-chat
|
|
7
|
+
- Buffer log() writes to reduce lock contention
|
|
6
8
|
- Cache getStatus() JSON serialization and add mtime-based invalidation
|
|
7
9
|
- Add mtime-based caching to getPrdInfo()
|
|
8
10
|
- Optimize getAgentStatus() to read only head+tail of live-output.log
|
|
@@ -638,6 +638,17 @@ async function ccExecuteAction(action) {
|
|
|
638
638
|
status.style.color = 'var(--green)';
|
|
639
639
|
break;
|
|
640
640
|
}
|
|
641
|
+
case 'file-bug': {
|
|
642
|
+
const res = await _ccFetch('/api/issues/create', { title: action.title, description: action.description, project: action.project, labels: action.labels });
|
|
643
|
+
const d = await res.json();
|
|
644
|
+
if (d.url) {
|
|
645
|
+
status.innerHTML = '🐛 Bug filed: <a href="' + escHtml(d.url) + '" target="_blank" style="color:var(--blue)">' + escHtml(action.title) + '</a>';
|
|
646
|
+
} else {
|
|
647
|
+
status.innerHTML = '🐛 Bug filed: <strong>' + escHtml(action.title) + '</strong>';
|
|
648
|
+
}
|
|
649
|
+
status.style.color = 'var(--green)';
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
641
652
|
default:
|
|
642
653
|
status.innerHTML = '? Unknown action: ' + escHtml(action.type);
|
|
643
654
|
status.style.color = 'var(--muted)';
|
package/dashboard.js
CHANGED
|
@@ -576,6 +576,7 @@ Available action types:
|
|
|
576
576
|
- **link-pr**: Link an external PR for tracking. Fields: url (PR URL), title (optional), project (optional), autoObserve (bool, default true)
|
|
577
577
|
- **archive-meeting**: Archive a completed meeting. Fields: id (meeting ID)
|
|
578
578
|
- **update-routing**: Update the routing table. Fields: content (full routing.md content)
|
|
579
|
+
- **file-bug**: File a bug as a GitHub issue on the project repo. Fields: title (short bug title), description (markdown body with repro steps, expected vs actual behavior), project (optional, defaults to first project), labels (optional array, defaults to ["bug"]). Use when the user says "file a bug", "create an issue", "report this", etc.
|
|
579
580
|
|
|
580
581
|
## Rules
|
|
581
582
|
|
|
@@ -3184,6 +3185,25 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3184
3185
|
} catch (e) { return jsonReply(res, 500, { error: e.message }); }
|
|
3185
3186
|
}
|
|
3186
3187
|
|
|
3188
|
+
async function handleFileBug(req, res) {
|
|
3189
|
+
try {
|
|
3190
|
+
const body = await readBody(req);
|
|
3191
|
+
if (!body.title) return jsonReply(res, 400, { error: 'title required' });
|
|
3192
|
+
const project = shared.getProjects(CONFIG).find(p => body.project ? p.name === body.project : true);
|
|
3193
|
+
if (!project) return jsonReply(res, 400, { error: 'no project found' });
|
|
3194
|
+
|
|
3195
|
+
const labels = (body.labels || ['bug']).join(',');
|
|
3196
|
+
const bugBody = (body.description || '') + '\n\n---\n_Filed via Minions dashboard_';
|
|
3197
|
+
const slug = project.repoHost === 'github' ? `${project.adoOrg}/${project.repoName}` : null;
|
|
3198
|
+
if (!slug) return jsonReply(res, 400, { error: 'Bug filing currently supports GitHub repos only' });
|
|
3199
|
+
|
|
3200
|
+
const cmd = `gh issue create --repo "${slug}" --title "${body.title.replace(/"/g, '\\"')}" --body "${bugBody.replace(/"/g, '\\"')}" --label "${labels}" 2>&1`;
|
|
3201
|
+
const result = shared.exec(cmd, { encoding: 'utf-8', timeout: 30000, windowsHide: true });
|
|
3202
|
+
const urlMatch = result.match(/https:\/\/github\.com\/\S+/);
|
|
3203
|
+
return jsonReply(res, 200, { ok: true, url: urlMatch ? urlMatch[0] : null, output: result.trim() });
|
|
3204
|
+
} catch (e) { return jsonReply(res, 500, { error: e.message }); }
|
|
3205
|
+
}
|
|
3206
|
+
|
|
3187
3207
|
async function handleCommandCenterNewSession(req, res) {
|
|
3188
3208
|
ccSession = { sessionId: null, createdAt: null, lastActiveAt: null, turnCount: 0 };
|
|
3189
3209
|
ccInFlight = false; // Reset concurrency guard so a stuck request doesn't block new sessions
|
|
@@ -3877,6 +3897,9 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3877
3897
|
{ method: 'POST', path: '/api/projects/scan', desc: 'Scan a directory for git repos', params: 'path?, depth?', handler: handleProjectsScan },
|
|
3878
3898
|
{ method: 'POST', path: '/api/projects/add', desc: 'Auto-discover and add a project to config', params: 'path, name?', handler: handleProjectsAdd },
|
|
3879
3899
|
|
|
3900
|
+
// Bug Filing
|
|
3901
|
+
{ method: 'POST', path: '/api/issues/create', desc: 'File a bug as a GitHub issue', params: 'title, description?, project?, labels?', handler: handleFileBug },
|
|
3902
|
+
|
|
3880
3903
|
// Command Center
|
|
3881
3904
|
{ method: 'POST', path: '/api/command-center/new-session', desc: 'Clear active CC session', handler: handleCommandCenterNewSession },
|
|
3882
3905
|
{ method: 'POST', path: '/api/command-center', desc: 'Conversational command center with full minions context', params: 'message, sessionId?', handler: handleCommandCenter },
|
package/engine/cli.js
CHANGED
|
@@ -391,6 +391,7 @@ const commands = {
|
|
|
391
391
|
if (e.activeProcesses.size === 0) {
|
|
392
392
|
safeWrite(CONTROL_PATH, { state: 'stopped', stopped_at: e.ts() });
|
|
393
393
|
e.log('info', 'Graceful shutdown complete (no active agents)');
|
|
394
|
+
shared.flushLogs(); // drain buffered log entries before exit
|
|
394
395
|
console.log('No active agents — stopped.');
|
|
395
396
|
process.exit(0);
|
|
396
397
|
}
|
|
@@ -404,6 +405,7 @@ const commands = {
|
|
|
404
405
|
clearInterval(poll);
|
|
405
406
|
safeWrite(CONTROL_PATH, { state: 'stopped', stopped_at: e.ts() });
|
|
406
407
|
e.log('info', 'Graceful shutdown complete (all agents finished)');
|
|
408
|
+
shared.flushLogs(); // drain buffered log entries before exit
|
|
407
409
|
console.log('All agents finished — stopped.');
|
|
408
410
|
process.exit(0);
|
|
409
411
|
}
|
|
@@ -411,6 +413,7 @@ const commands = {
|
|
|
411
413
|
clearInterval(poll);
|
|
412
414
|
safeWrite(CONTROL_PATH, { state: 'stopped', stopped_at: e.ts() });
|
|
413
415
|
e.log('warn', `Graceful shutdown timed out after ${timeout / 1000}s with ${e.activeProcesses.size} agent(s) still active`);
|
|
416
|
+
shared.flushLogs(); // drain buffered log entries before exit
|
|
414
417
|
console.log(`Shutdown timeout (${timeout / 1000}s) — force exiting with ${e.activeProcesses.size} agent(s) still running.`);
|
|
415
418
|
process.exit(1);
|
|
416
419
|
}
|
package/engine/shared.js
CHANGED
|
@@ -18,20 +18,56 @@ function ts() { return new Date().toISOString(); }
|
|
|
18
18
|
function logTs() { return new Date().toLocaleTimeString(); }
|
|
19
19
|
function dateStamp() { return new Date().toISOString().slice(0, 10); }
|
|
20
20
|
|
|
21
|
+
// ── Log Buffering ──────────────────────────────────────────────────────────
|
|
22
|
+
// Buffer log entries in memory and flush to disk periodically to reduce lock
|
|
23
|
+
// contention (~139 calls/tick → 1 lock acquisition per flush).
|
|
24
|
+
const _logBuffer = [];
|
|
25
|
+
let _logFlushTimer = null;
|
|
26
|
+
|
|
21
27
|
function log(level, msg, meta = {}) {
|
|
22
28
|
const entry = { timestamp: ts(), level, message: msg, ...meta };
|
|
29
|
+
// Console output remains immediate
|
|
23
30
|
console.log(`[${logTs()}] [${level}] ${msg}`);
|
|
24
31
|
|
|
32
|
+
_logBuffer.push(entry);
|
|
33
|
+
|
|
34
|
+
// Start the flush timer lazily on first buffered entry
|
|
35
|
+
if (!_logFlushTimer) {
|
|
36
|
+
_logFlushTimer = setInterval(() => {
|
|
37
|
+
_flushLogBuffer();
|
|
38
|
+
}, ENGINE_DEFAULTS.logFlushInterval);
|
|
39
|
+
// Unref so the timer doesn't keep the process alive during shutdown
|
|
40
|
+
if (_logFlushTimer.unref) _logFlushTimer.unref();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Flush immediately when buffer exceeds threshold
|
|
44
|
+
if (_logBuffer.length >= ENGINE_DEFAULTS.logBufferSize) {
|
|
45
|
+
_flushLogBuffer();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function _flushLogBuffer() {
|
|
50
|
+
if (_logBuffer.length === 0) return;
|
|
51
|
+
const entries = _logBuffer.splice(0);
|
|
25
52
|
try {
|
|
26
53
|
mutateJsonFileLocked(LOG_PATH, (logData) => {
|
|
27
54
|
if (!Array.isArray(logData)) logData = logData?.entries || [];
|
|
28
|
-
logData.push(
|
|
55
|
+
logData.push(...entries);
|
|
29
56
|
if (logData.length >= 2500) logData.splice(0, logData.length - 2000);
|
|
30
57
|
return logData;
|
|
31
58
|
}, { defaultValue: [] });
|
|
32
59
|
} catch { /* logging should never crash the caller */ }
|
|
33
60
|
}
|
|
34
61
|
|
|
62
|
+
/** Flush buffered log entries to disk. Call during graceful shutdown to drain the buffer. */
|
|
63
|
+
function flushLogs() {
|
|
64
|
+
_flushLogBuffer();
|
|
65
|
+
if (_logFlushTimer) {
|
|
66
|
+
clearInterval(_logFlushTimer);
|
|
67
|
+
_logFlushTimer = null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
35
71
|
// ── File I/O ─────────────────────────────────────────────────────────────────
|
|
36
72
|
|
|
37
73
|
function safeRead(p) {
|
|
@@ -441,6 +477,8 @@ const ENGINE_DEFAULTS = {
|
|
|
441
477
|
pipelineApiRetries: 2, // max attempts for pipeline API calls
|
|
442
478
|
pipelineApiRetryDelay: 2000, // ms delay between pipeline API retries
|
|
443
479
|
versionCheckInterval: 3600000, // 1 hour — how often to check npm for updates (ms)
|
|
480
|
+
logFlushInterval: 5000, // 5s — how often to flush buffered log entries to disk
|
|
481
|
+
logBufferSize: 50, // flush immediately when buffer exceeds this many entries
|
|
444
482
|
};
|
|
445
483
|
|
|
446
484
|
// ─── Status & Type Constants ─────────────────────────────────────────────────
|
|
@@ -729,5 +767,7 @@ module.exports = {
|
|
|
729
767
|
killGracefully,
|
|
730
768
|
killImmediate,
|
|
731
769
|
LOCK_STALE_MS,
|
|
770
|
+
flushLogs,
|
|
771
|
+
_logBuffer, // exported for testing
|
|
732
772
|
};
|
|
733
773
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.480",
|
|
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"
|