@yemi33/minions 0.1.199 → 0.1.201

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 CHANGED
@@ -1,13 +1,15 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.199 (2026-04-02)
3
+ ## 0.1.201 (2026-04-02)
4
4
 
5
5
  ### Engine
6
6
  - engine/ado.js
7
7
  - engine/github.js
8
8
  - engine/lifecycle.js
9
+ - engine/preflight.js
9
10
  - engine/queries.js
10
11
  - engine/shared.js
12
+ - engine/spawn-agent.js
11
13
 
12
14
  ### Dashboard
13
15
  - dashboard.js
@@ -14,26 +14,50 @@ const { execSync } = require('child_process');
14
14
  */
15
15
  function findClaudeBinary() {
16
16
  const searchPaths = [
17
- path.join(process.env.npm_config_prefix || '', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
18
- path.join(process.env.APPDATA || '', 'npm', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
17
+ // npm global (npm_config_prefix)
18
+ process.env.npm_config_prefix ? path.join(process.env.npm_config_prefix, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js') : '',
19
+ // Windows: %APPDATA%\npm
20
+ process.env.APPDATA ? path.join(process.env.APPDATA, 'npm', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js') : '',
21
+ // Unix global
19
22
  '/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js',
20
23
  '/usr/lib/node_modules/@anthropic-ai/claude-code/cli.js',
21
- ];
24
+ // Homebrew (macOS)
25
+ '/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js',
26
+ // nvm (current node version)
27
+ path.join(path.dirname(process.execPath), '..', 'lib', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
28
+ // fnm / volta — sibling to the node binary
29
+ path.join(path.dirname(process.execPath), 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
30
+ ].filter(Boolean);
22
31
  for (const p of searchPaths) {
23
- if (p && fs.existsSync(p)) return p;
32
+ try { if (fs.existsSync(p)) return p; } catch {}
24
33
  }
25
- // Fallback: parse the shell wrapper
34
+ // Fallback: which/where resolve wrapper to cli.js
26
35
  try {
27
- const which = execSync('bash -c "which claude"', { encoding: 'utf8', windowsHide: true, timeout: 5000 }).trim();
28
- const whichNative = which.replace(/^\/([a-zA-Z])\//, (_, d) => d.toUpperCase() + ':/').replace(/\//g, path.sep);
29
- const wrapper = fs.readFileSync(whichNative, 'utf8');
30
- const m = wrapper.match(/node_modules\/@anthropic-ai\/claude-code\/cli\.js/);
31
- if (m) {
32
- const basedir = path.dirname(whichNative);
33
- const resolved = path.join(basedir, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
34
- if (fs.existsSync(resolved)) return resolved;
36
+ const isWin = process.platform === 'win32';
37
+ const cmd = isWin ? 'where claude 2>NUL' : 'which claude 2>/dev/null';
38
+ const which = execSync(cmd, { encoding: 'utf8', windowsHide: true, timeout: 5000 }).trim().split('\n')[0].trim();
39
+ if (which) {
40
+ const whichNative = isWin ? which : which.replace(/^\/([a-zA-Z])\//, (_, d) => d.toUpperCase() + ':/').replace(/\//g, path.sep);
41
+ try {
42
+ const resolved = fs.realpathSync(whichNative);
43
+ if (resolved.endsWith('cli.js') && fs.existsSync(resolved)) return resolved;
44
+ } catch {}
45
+ try {
46
+ const wrapper = fs.readFileSync(whichNative, 'utf8');
47
+ const m = wrapper.match(/node_modules[\\/]@anthropic-ai[\\/]claude-code[\\/]cli\.js/);
48
+ if (m) {
49
+ const candidate = path.join(path.dirname(whichNative), 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
50
+ if (fs.existsSync(candidate)) return candidate;
51
+ }
52
+ } catch {}
35
53
  }
36
54
  } catch { /* optional */ }
55
+ // Last resort: npm root -g
56
+ try {
57
+ const globalRoot = execSync('npm root -g', { encoding: 'utf8', windowsHide: true, timeout: 5000 }).trim();
58
+ const candidate = path.join(globalRoot, '@anthropic-ai', 'claude-code', 'cli.js');
59
+ if (fs.existsSync(candidate)) return candidate;
60
+ } catch { /* optional */ }
37
61
  return null;
38
62
  }
39
63
 
@@ -22,30 +22,60 @@ const sysPrompt = fs.readFileSync(sysPromptFile, 'utf8');
22
22
 
23
23
  const env = cleanChildEnv();
24
24
 
25
- // Resolve claude binary — find the actual JS entry point
25
+ // Resolve claude binary — supports both npm install (cli.js) and native installer (binary on PATH)
26
26
  let claudeBin;
27
- const searchPaths = [
28
- // npm global install locations (platform-adaptive)
29
- path.join(process.env.npm_config_prefix || '', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
30
- path.join(process.env.APPDATA || '', 'npm', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
31
- // Unix global locations
32
- '/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js',
33
- '/usr/lib/node_modules/@anthropic-ai/claude-code/cli.js',
34
- ];
35
- for (const p of searchPaths) {
36
- if (p && fs.existsSync(p)) { claudeBin = p; break; }
27
+ let claudeIsNative = false; // true = native binary, false = node cli.js
28
+
29
+ // Strategy 1: Check if `claude` is on PATH (native installer or npm global bin)
30
+ try {
31
+ const isWin = process.platform === 'win32';
32
+ const cmd = isWin ? 'where claude 2>NUL' : 'which claude 2>/dev/null';
33
+ const which = exec(cmd, { encoding: 'utf8', env, timeout: 10000 }).trim().split('\n')[0].trim();
34
+ if (which) {
35
+ const whichNative = isWin ? which : which.replace(/^\/([a-zA-Z])\//, (_, d) => d.toUpperCase() + ':/').replace(/\//g, path.sep);
36
+ // Check if it's a node wrapper (npm install) or native binary
37
+ try {
38
+ const content = fs.readFileSync(whichNative, 'utf8');
39
+ // npm wrapper scripts reference cli.js — extract the path
40
+ const m = content.match(/node_modules[\\/]@anthropic-ai[\\/]claude-code[\\/]cli\.js/);
41
+ if (m) {
42
+ const candidate = path.join(path.dirname(whichNative), 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
43
+ if (fs.existsSync(candidate)) { claudeBin = candidate; }
44
+ }
45
+ } catch {
46
+ // Can't read as text — it's a compiled binary
47
+ }
48
+ if (!claudeBin) {
49
+ // Native binary or wrapper without cli.js reference — use directly
50
+ claudeBin = whichNative;
51
+ claudeIsNative = true;
52
+ }
53
+ }
54
+ } catch { /* optional */ }
55
+
56
+ // Strategy 2: Known node_modules locations (npm global installs)
57
+ if (!claudeBin) {
58
+ const searchPaths = [
59
+ process.env.npm_config_prefix ? path.join(process.env.npm_config_prefix, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js') : '',
60
+ process.env.APPDATA ? path.join(process.env.APPDATA, 'npm', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js') : '',
61
+ '/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js',
62
+ '/usr/lib/node_modules/@anthropic-ai/claude-code/cli.js',
63
+ '/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js',
64
+ path.join(path.dirname(process.execPath), '..', 'lib', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
65
+ path.join(path.dirname(process.execPath), 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
66
+ path.join(__dirname, '..', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
67
+ ].filter(Boolean);
68
+ for (const p of searchPaths) {
69
+ try { if (fs.existsSync(p)) { claudeBin = p; break; } } catch {}
70
+ }
37
71
  }
38
- // Fallback: parse the shell wrapper
72
+
73
+ // Strategy 3: npm root -g
39
74
  if (!claudeBin) {
40
75
  try {
41
- const which = exec('bash -c "which claude"', { encoding: 'utf8', env, timeout: 10000 }).trim();
42
- const whichNative = which.replace(/^\/([a-zA-Z])\//, (_, d) => d.toUpperCase() + ':/').replace(/\//g, path.sep);
43
- const wrapper = fs.readFileSync(whichNative, 'utf8');
44
- const m = wrapper.match(/node_modules\/@anthropic-ai\/claude-code\/cli\.js/);
45
- if (m) {
46
- const basedir = path.dirname(whichNative);
47
- claudeBin = path.join(basedir, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
48
- }
76
+ const globalRoot = exec('npm root -g', { encoding: 'utf8', env, timeout: 10000 }).trim();
77
+ const candidate = path.join(globalRoot, '@anthropic-ai', 'claude-code', 'cli.js');
78
+ if (fs.existsSync(candidate)) claudeBin = candidate;
49
79
  } catch { /* optional */ }
50
80
  }
51
81
 
@@ -53,7 +83,7 @@ if (!claudeBin) {
53
83
  const tmpDir = path.join(__dirname, 'tmp');
54
84
  if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir, { recursive: true });
55
85
  const debugPath = path.join(tmpDir, 'spawn-debug.log');
56
- fs.writeFileSync(debugPath, `spawn-agent.js at ${new Date().toISOString()}\nclaudeBin=${claudeBin || 'not found'}\nprompt=${promptFile}\nsysPrompt=${sysPromptFile}\nextraArgs=${extraArgs.join(' ')}\n`);
86
+ fs.writeFileSync(debugPath, `spawn-agent.js at ${new Date().toISOString()}\nclaudeBin=${claudeBin || 'not found'}\nnative=${claudeIsNative}\nprompt=${promptFile}\nsysPrompt=${sysPromptFile}\nextraArgs=${extraArgs.join(' ')}\n`);
57
87
 
58
88
  // When resuming a session, skip system prompt (it's baked into the session)
59
89
  const isResume = extraArgs.includes('--resume');
@@ -85,7 +115,9 @@ try {
85
115
  if (_sysPromptFileSupported === null) {
86
116
  try {
87
117
  const { spawnSync } = require('child_process');
88
- const testResult = spawnSync(process.execPath, [claudeBin, '--help'], { encoding: 'utf8', timeout: 10000, windowsHide: true });
118
+ const testResult = claudeIsNative
119
+ ? spawnSync(claudeBin, ['--help'], { encoding: 'utf8', timeout: 10000, windowsHide: true })
120
+ : spawnSync(process.execPath, [claudeBin, '--help'], { encoding: 'utf8', timeout: 10000, windowsHide: true });
89
121
  _sysPromptFileSupported = (testResult.stdout || '').includes('system-prompt-file');
90
122
  try { fs.writeFileSync(capsCachePath, JSON.stringify({ claudeBin, sysPromptFile: _sysPromptFileSupported, checkedAt: new Date().toISOString() })); } catch { /* optional */ }
91
123
  } catch { _sysPromptFileSupported = true; /* assume supported */ }
@@ -111,10 +143,9 @@ if (!isResume) try {
111
143
  // If help check fails, try file approach anyway
112
144
  }
113
145
 
114
- const proc = runFile(process.execPath, [claudeBin, ...actualArgs], {
115
- stdio: ['pipe', 'pipe', 'pipe'],
116
- env
117
- });
146
+ const proc = claudeIsNative
147
+ ? runFile(claudeBin, actualArgs, { stdio: ['pipe', 'pipe', 'pipe'], env })
148
+ : runFile(process.execPath, [claudeBin, ...actualArgs], { stdio: ['pipe', 'pipe', 'pipe'], env });
118
149
 
119
150
  fs.appendFileSync(debugPath, `PID=${proc.pid || 'none'}\nargs=${actualArgs.join(' ').slice(0, 500)}\n`);
120
151
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.199",
3
+ "version": "0.1.201",
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"