@yemi33/minions 0.1.287 → 0.1.289

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 ADDED
@@ -0,0 +1,2 @@
1
+ * text=auto eol=lf
2
+ *.ps1 text eol=crlf
package/CHANGELOG.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.287 (2026-04-03)
3
+ ## 0.1.289 (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
+ - ' not 'Evaluate:'
10
+ - cross-platform compatibility — signal handling, paths, home dir
9
11
  - engine sidebar badge only triggers on new dispatch errors
10
12
 
11
13
  ## 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(process.env.HOME || process.env.USERPROFILE || '', '.claude', 'skills');
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 = process.env.USERPROFILE || process.env.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 = process.env.HOME || process.env.USERPROFILE || '';
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
- try { if (info.proc) info.proc.kill('SIGTERM'); } catch { /* process may be dead */ }
233
+ shared.killImmediate(info.proc);
234
234
  activeProcesses.delete(id);
235
235
  cleaned.zombies++;
236
236
  }
@@ -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
- try { proc.kill('SIGTERM'); } catch { /* process may be dead */ }
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();
@@ -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 wtPath = `${p.localPath}/../worktrees/verify-${name}-${planSlug}-${shared.uid()}`;
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 "${p.localPath}"`,
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(process.env.HOME || process.env.USERPROFILE || '', '.claude', 'skills');
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)) {
@@ -1275,7 +1277,7 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
1275
1277
  const parentItem = items.find(i => i.id === evalTargetId);
1276
1278
  const evalItem = {
1277
1279
  id: 'W-' + shared.uid(),
1278
- title: `Evaluate: ${parentItem?.title || meta.item.title || evalTargetId}`,
1280
+ title: `Review: ${parentItem?.title || meta.item.title || evalTargetId}`,
1279
1281
  type: 'review',
1280
1282
  priority: meta.item.priority || 'high',
1281
1283
  status: 'pending',
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(() => { try { proc.kill('SIGTERM'); } catch { /* process may be dead */ } }, timeout);
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(() => { try { proc.kill('SIGTERM'); } catch {} }, timeout);
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(process.env.HOME || process.env.USERPROFILE || '', '.claude', 'skills');
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 = process.env.HOME || process.env.USERPROFILE || '';
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
- try { info.proc.kill('SIGTERM'); } catch { /* process may be dead */ }
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
- try { info.proc.kill('SIGTERM'); } catch { /* process may be dead */ }
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
- try { activeProcesses.get(item.id)?.proc.kill('SIGTERM'); } catch { /* process may be dead */ }
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
- try { procInfo.proc.kill('SIGTERM'); } catch { /* process may be dead */ }
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.287",
3
+ "version": "0.1.289",
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"