@yemi33/minions 0.1.287 → 0.1.288
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/.gitattributes +2 -0
- package/CHANGELOG.md +2 -1
- package/bin/minions.js +1 -1
- package/dashboard.js +2 -2
- package/engine/cleanup.js +1 -1
- package/engine/consolidation.js +1 -3
- package/engine/lifecycle.js +5 -3
- package/engine/llm.js +2 -2
- package/engine/queries.js +3 -2
- package/engine/shared.js +28 -0
- package/engine/timeout.js +4 -8
- package/package.json +1 -1
package/.gitattributes
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.288 (2026-04-03)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
6
|
- all doc-chats use Sonnet with full tools (agent change)
|
|
7
7
|
|
|
8
8
|
### Fixes
|
|
9
|
+
- cross-platform compatibility — signal handling, paths, home dir
|
|
9
10
|
- engine sidebar badge only triggers on new dispatch errors
|
|
10
11
|
|
|
11
12
|
## 0.1.285 (2026-04-03)
|
package/bin/minions.js
CHANGED
|
@@ -666,7 +666,7 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
|
666
666
|
|
|
667
667
|
// 2. Remove minions-authored skills from ~/.claude/skills/
|
|
668
668
|
try {
|
|
669
|
-
const claudeSkills = path.join(
|
|
669
|
+
const claudeSkills = path.join(os.homedir(), '.claude', 'skills');
|
|
670
670
|
if (fs.existsSync(claudeSkills)) {
|
|
671
671
|
for (const dir of fs.readdirSync(claudeSkills)) {
|
|
672
672
|
const skillFile = path.join(claudeSkills, dir, 'SKILL.md');
|
package/dashboard.js
CHANGED
|
@@ -163,7 +163,7 @@ function getEngineState() { return queries.getControl(); }
|
|
|
163
163
|
|
|
164
164
|
function getMcpServers() {
|
|
165
165
|
try {
|
|
166
|
-
const home =
|
|
166
|
+
const home = os.homedir();
|
|
167
167
|
const claudeJsonPath = path.join(home, '.claude.json');
|
|
168
168
|
const data = JSON.parse(safeRead(claudeJsonPath) || '{}');
|
|
169
169
|
const servers = data.mcpServers || {};
|
|
@@ -2736,7 +2736,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
2736
2736
|
}
|
|
2737
2737
|
if (!content) {
|
|
2738
2738
|
// Fallback: search Claude Code skills, then project skills
|
|
2739
|
-
const home =
|
|
2739
|
+
const home = os.homedir();
|
|
2740
2740
|
const claudePath = path.join(home, '.claude', 'skills', file.replace('.md', '').replace('SKILL', ''), 'SKILL.md');
|
|
2741
2741
|
content = safeRead(claudePath) || '';
|
|
2742
2742
|
if (!content) {
|
package/engine/cleanup.js
CHANGED
|
@@ -230,7 +230,7 @@ function runCleanup(config, verbose = false) {
|
|
|
230
230
|
const activeIds = new Set((dispatch.active || []).map(d => d.id));
|
|
231
231
|
for (const [id, info] of activeProcesses.entries()) {
|
|
232
232
|
if (!activeIds.has(id)) {
|
|
233
|
-
|
|
233
|
+
shared.killImmediate(info.proc);
|
|
234
234
|
activeProcesses.delete(id);
|
|
235
235
|
cleaned.zombies++;
|
|
236
236
|
}
|
package/engine/consolidation.js
CHANGED
|
@@ -179,10 +179,8 @@ function consolidateWithLLM(items, existingNotes, files, config) {
|
|
|
179
179
|
|
|
180
180
|
const timeout = setTimeout(() => {
|
|
181
181
|
log('warn', 'LLM consolidation timed out after 3m — killing and falling back to regex');
|
|
182
|
-
|
|
183
|
-
// Escalate to SIGKILL after 10s if process doesn't exit
|
|
182
|
+
shared.killGracefully(proc, 10000);
|
|
184
183
|
setTimeout(() => {
|
|
185
|
-
try { proc.kill('SIGKILL'); } catch { /* process may be dead */ }
|
|
186
184
|
if (_consolidationInFlight) {
|
|
187
185
|
_consolidationInFlight = false;
|
|
188
186
|
_processingFiles.clear();
|
package/engine/lifecycle.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
8
9
|
const shared = require('./shared');
|
|
9
10
|
const { safeRead, safeJson, safeWrite, safeReadDir, execSilent, projectPrPath, getPrLinks, addPrLink,
|
|
10
11
|
mutateJsonFileLocked, log, ts, dateStamp } = shared;
|
|
@@ -200,11 +201,12 @@ function checkPlanCompletion(meta, config) {
|
|
|
200
201
|
|
|
201
202
|
// Build per-project checkout commands: one worktree, merge all PR branches into it
|
|
202
203
|
const checkoutBlocks = Object.entries(projectPrs).map(([name, { project: p, prs, mainBranch }]) => {
|
|
203
|
-
const
|
|
204
|
+
const localPath = p.localPath.replace(/\\/g, '/');
|
|
205
|
+
const wtPath = `${localPath}/../worktrees/verify-${name}-${planSlug}-${shared.uid()}`;
|
|
204
206
|
const branches = prs.map(pr => pr.branch).filter(Boolean);
|
|
205
207
|
const lines = [
|
|
206
208
|
`# ${name} — merge ${branches.length} PR branch(es) into one worktree`,
|
|
207
|
-
`cd "${
|
|
209
|
+
`cd "${localPath}"`,
|
|
208
210
|
`git fetch origin ${branches.map(b => `"${b}"`).join(' ')} "${mainBranch}"`,
|
|
209
211
|
`git worktree add "${wtPath}" "origin/${mainBranch}" 2>/dev/null || (cd "${wtPath}" && git checkout "${mainBranch}" && git pull origin "${mainBranch}")`,
|
|
210
212
|
`cd "${wtPath}"`,
|
|
@@ -901,7 +903,7 @@ function extractSkillsFromOutput(output, agentId, dispatchItem, config) {
|
|
|
901
903
|
}
|
|
902
904
|
} else {
|
|
903
905
|
// Write in Claude Code native format: ~/.claude/skills/<name>/SKILL.md
|
|
904
|
-
const claudeSkillsDir = path.join(
|
|
906
|
+
const claudeSkillsDir = path.join(os.homedir(), '.claude', 'skills');
|
|
905
907
|
const skillDir = path.join(claudeSkillsDir, name.replace(/[^a-z0-9-]/g, '-'));
|
|
906
908
|
const skillPath = path.join(skillDir, 'SKILL.md');
|
|
907
909
|
if (!fs.existsSync(skillPath)) {
|
package/engine/llm.js
CHANGED
|
@@ -72,7 +72,7 @@ function callLLM(promptText, sysPromptText, { timeout = 120000, label = 'llm', m
|
|
|
72
72
|
proc.stdout.on('data', d => { stdout += d.toString(); });
|
|
73
73
|
proc.stderr.on('data', d => { stderr += d.toString(); });
|
|
74
74
|
|
|
75
|
-
const timer = setTimeout(() => {
|
|
75
|
+
const timer = setTimeout(() => { shared.killImmediate(proc); }, timeout);
|
|
76
76
|
|
|
77
77
|
proc.on('close', (code) => {
|
|
78
78
|
clearTimeout(timer);
|
|
@@ -180,7 +180,7 @@ function callLLMStreaming(promptText, sysPromptText, { timeout = 120000, label =
|
|
|
180
180
|
});
|
|
181
181
|
proc.stderr.on('data', d => { stderr += d.toString(); });
|
|
182
182
|
|
|
183
|
-
const timer = setTimeout(() => {
|
|
183
|
+
const timer = setTimeout(() => { shared.killImmediate(proc); }, timeout);
|
|
184
184
|
|
|
185
185
|
proc.on('close', (code) => {
|
|
186
186
|
clearTimeout(timer);
|
package/engine/queries.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
+
const os = require('os');
|
|
9
10
|
const shared = require('./shared');
|
|
10
11
|
|
|
11
12
|
const { safeRead, safeReadDir, safeJson, safeWrite, getProjects,
|
|
@@ -19,7 +20,7 @@ const ENGINE_DIR = path.join(MINIONS_DIR, 'engine');
|
|
|
19
20
|
const INBOX_DIR = path.join(MINIONS_DIR, 'notes', 'inbox');
|
|
20
21
|
const PLANS_DIR = path.join(MINIONS_DIR, 'plans');
|
|
21
22
|
const PRD_DIR = path.join(MINIONS_DIR, 'prd');
|
|
22
|
-
const SKILLS_DIR = path.join(
|
|
23
|
+
const SKILLS_DIR = path.join(os.homedir(), '.claude', 'skills');
|
|
23
24
|
const KNOWLEDGE_DIR = path.join(MINIONS_DIR, 'knowledge');
|
|
24
25
|
const ARCHIVE_DIR = path.join(MINIONS_DIR, 'notes', 'archive');
|
|
25
26
|
|
|
@@ -332,7 +333,7 @@ function collectSkillFiles(config) {
|
|
|
332
333
|
const seen = new Set(); // dedup by name
|
|
333
334
|
|
|
334
335
|
// 1. Claude Code native skills: ~/.claude/skills/<name>/SKILL.md
|
|
335
|
-
const homeDir =
|
|
336
|
+
const homeDir = os.homedir();
|
|
336
337
|
const claudeSkillsDir = path.join(homeDir, '.claude', 'skills');
|
|
337
338
|
try {
|
|
338
339
|
const dirs = fs.readdirSync(claudeSkillsDir).filter(d => {
|
package/engine/shared.js
CHANGED
|
@@ -536,6 +536,32 @@ function addPrLink(prId, itemId) {
|
|
|
536
536
|
safeWrite(PR_LINKS_PATH, links);
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
+
// ─── Cross-Platform Process Kill Helpers ─────────────────────────────────────
|
|
540
|
+
|
|
541
|
+
function killGracefully(proc, graceMs = 5000) {
|
|
542
|
+
if (!proc || !proc.pid) return;
|
|
543
|
+
if (process.platform === 'win32') {
|
|
544
|
+
try { _execSync(`taskkill /PID ${proc.pid} /T`, { stdio: 'pipe', timeout: 3000, windowsHide: true }); } catch { /* process may be dead */ }
|
|
545
|
+
setTimeout(() => {
|
|
546
|
+
try { _execSync(`taskkill /PID ${proc.pid} /F /T`, { stdio: 'pipe', timeout: 3000, windowsHide: true }); } catch { /* process may be dead */ }
|
|
547
|
+
}, graceMs);
|
|
548
|
+
} else {
|
|
549
|
+
try { proc.kill('SIGTERM'); } catch { /* process may be dead */ }
|
|
550
|
+
setTimeout(() => {
|
|
551
|
+
try { proc.kill('SIGKILL'); } catch { /* process may be dead */ }
|
|
552
|
+
}, graceMs);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function killImmediate(proc) {
|
|
557
|
+
if (!proc || !proc.pid) return;
|
|
558
|
+
if (process.platform === 'win32') {
|
|
559
|
+
try { _execSync(`taskkill /PID ${proc.pid} /F /T`, { stdio: 'pipe', timeout: 3000, windowsHide: true }); } catch { /* process may be dead */ }
|
|
560
|
+
} else {
|
|
561
|
+
try { proc.kill('SIGKILL'); } catch { /* process may be dead */ }
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
539
565
|
module.exports = {
|
|
540
566
|
MINIONS_DIR,
|
|
541
567
|
PR_LINKS_PATH,
|
|
@@ -580,6 +606,8 @@ module.exports = {
|
|
|
580
606
|
validatePid,
|
|
581
607
|
parseSkillFrontmatter,
|
|
582
608
|
sleepMs,
|
|
609
|
+
killGracefully,
|
|
610
|
+
killImmediate,
|
|
583
611
|
LOCK_STALE_MS,
|
|
584
612
|
};
|
|
585
613
|
|
package/engine/timeout.js
CHANGED
|
@@ -72,7 +72,7 @@ function checkSteering(config) {
|
|
|
72
72
|
log('info', `Steering: killing ${info.agentId} (${id}) for session resume with human message`);
|
|
73
73
|
|
|
74
74
|
// Kill current process
|
|
75
|
-
|
|
75
|
+
shared.killImmediate(info.proc);
|
|
76
76
|
|
|
77
77
|
// Store steering context for re-spawn on close
|
|
78
78
|
info._steeringMessage = message;
|
|
@@ -97,10 +97,7 @@ function checkTimeouts(config) {
|
|
|
97
97
|
const elapsed = Date.now() - new Date(info.startedAt).getTime();
|
|
98
98
|
if (elapsed > itemTimeout) {
|
|
99
99
|
log('warn', `Agent ${info.agentId} (${id}) hit hard timeout after ${Math.round(elapsed / 1000)}s — killing`);
|
|
100
|
-
|
|
101
|
-
setTimeout(() => {
|
|
102
|
-
try { info.proc.kill('SIGKILL'); } catch { /* process may be dead */ }
|
|
103
|
-
}, 5000);
|
|
100
|
+
shared.killGracefully(info.proc, 5000);
|
|
104
101
|
}
|
|
105
102
|
}
|
|
106
103
|
|
|
@@ -149,7 +146,7 @@ function checkTimeouts(config) {
|
|
|
149
146
|
runPostCompletionHooks(item, item.agent, isSuccess ? 0 : 1, liveLog, config);
|
|
150
147
|
|
|
151
148
|
if (hasProcess) {
|
|
152
|
-
|
|
149
|
+
shared.killImmediate(activeProcesses.get(item.id)?.proc);
|
|
153
150
|
activeProcesses.delete(item.id);
|
|
154
151
|
}
|
|
155
152
|
continue; // Skip orphan/hung detection — we handled it
|
|
@@ -208,8 +205,7 @@ function checkTimeouts(config) {
|
|
|
208
205
|
log('warn', `Hung agent: ${item.agent} (${item.id}) — process exists but no output for ${silentSec}s${isBlocking ? ' (blocking timeout exceeded)' : ''}`);
|
|
209
206
|
const procInfo = activeProcesses.get(item.id);
|
|
210
207
|
if (procInfo) {
|
|
211
|
-
|
|
212
|
-
setTimeout(() => { try { procInfo.proc.kill('SIGKILL'); } catch { /* process may be dead */ } }, 5000);
|
|
208
|
+
shared.killGracefully(procInfo.proc, 5000);
|
|
213
209
|
activeProcesses.delete(item.id);
|
|
214
210
|
}
|
|
215
211
|
deadItems.push({ item, reason: `Hung — no output for ${silentSec}s` });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.288",
|
|
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"
|