fix: use bun-runner to find Bun on fresh install (#818)
On fresh installations, smart-install.js installs Bun to ~/.bun/bin/bun but Bun isn't in PATH until terminal restart. Subsequent hooks fail because they try to run `bun ...` directly. This fix introduces bun-runner.js - a Node.js script that finds Bun in common install locations (not just PATH) and runs commands with it. All hooks now use `node bun-runner.js ...` instead of `bun ...`. The bun-runner checks: - PATH (via which/where) - ~/.bun/bin/bun (default install location) - /usr/local/bin/bun - /opt/homebrew/bin/bun (macOS Homebrew) - /home/linuxbrew/.linuxbrew/bin/bun (Linuxbrew) - Windows: %LOCALAPPDATA%\bun or fallback paths Fixes #818 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+31
-6
@@ -19,12 +19,22 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/smart-install.js\" && bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" stop && bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code context",
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/smart-install.js\"",
|
||||||
"timeout": 300
|
"timeout": 300
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code user-message",
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||||
|
"timeout": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code context",
|
||||||
|
"timeout": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code user-message",
|
||||||
"timeout": 60
|
"timeout": 60
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -35,7 +45,12 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code session-init",
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||||
|
"timeout": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code session-init",
|
||||||
"timeout": 60
|
"timeout": 60
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -47,8 +62,13 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code observation",
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||||
"timeout": 30
|
"timeout": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code observation",
|
||||||
|
"timeout": 120
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -58,7 +78,12 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code summarize",
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
|
||||||
|
"timeout": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/bun-runner.js\" \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" hook claude-code summarize",
|
||||||
"timeout": 120
|
"timeout": 120
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Bun Runner - Finds and executes Bun even when not in PATH
|
||||||
|
*
|
||||||
|
* This script solves the fresh install problem where:
|
||||||
|
* 1. smart-install.js installs Bun to ~/.bun/bin/bun
|
||||||
|
* 2. But Bun isn't in PATH until terminal restart
|
||||||
|
* 3. Subsequent hooks fail because they can't find `bun`
|
||||||
|
*
|
||||||
|
* Usage: node bun-runner.js <script> [args...]
|
||||||
|
*
|
||||||
|
* Fixes #818: Worker fails to start on fresh install
|
||||||
|
*/
|
||||||
|
import { spawnSync, spawn } from 'child_process';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { homedir } from 'os';
|
||||||
|
|
||||||
|
const IS_WINDOWS = process.platform === 'win32';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find Bun executable - checks PATH first, then common install locations
|
||||||
|
*/
|
||||||
|
function findBun() {
|
||||||
|
// Try PATH first
|
||||||
|
const pathCheck = spawnSync(IS_WINDOWS ? 'where' : 'which', ['bun'], {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
shell: IS_WINDOWS
|
||||||
|
});
|
||||||
|
|
||||||
|
if (pathCheck.status === 0 && pathCheck.stdout.trim()) {
|
||||||
|
return 'bun'; // Found in PATH
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check common installation paths (handles fresh installs before PATH reload)
|
||||||
|
// Windows: Bun installs to ~/.bun/bin/bun.exe (same as smart-install.js)
|
||||||
|
// Unix: Check default location plus common package manager paths
|
||||||
|
const bunPaths = IS_WINDOWS
|
||||||
|
? [join(homedir(), '.bun', 'bin', 'bun.exe')]
|
||||||
|
: [
|
||||||
|
join(homedir(), '.bun', 'bin', 'bun'),
|
||||||
|
'/usr/local/bin/bun',
|
||||||
|
'/opt/homebrew/bin/bun',
|
||||||
|
'/home/linuxbrew/.linuxbrew/bin/bun'
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const bunPath of bunPaths) {
|
||||||
|
if (existsSync(bunPath)) {
|
||||||
|
return bunPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get args: node bun-runner.js <script> [args...]
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (args.length === 0) {
|
||||||
|
console.error('Usage: node bun-runner.js <script> [args...]');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bunPath = findBun();
|
||||||
|
|
||||||
|
if (!bunPath) {
|
||||||
|
console.error('Error: Bun not found. Please install Bun: https://bun.sh');
|
||||||
|
console.error('After installation, restart your terminal.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn Bun with the provided script and args
|
||||||
|
// Use spawn (not spawnSync) to properly handle stdio
|
||||||
|
const child = spawn(bunPath, args, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: IS_WINDOWS,
|
||||||
|
env: process.env
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('error', (err) => {
|
||||||
|
console.error(`Failed to start Bun: ${err.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', (code) => {
|
||||||
|
process.exit(code || 0);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user