# How the Bundled‑Node Launcher Solves the nvm/PATH Problem for Agent Hooks

> Discover how the bundled-node launcher resolves nvm PATH issues for agent hooks by using absolute paths, eliminating system dependencies for Electron binaries.

- Repository: [Chaitanya Giri/munder-difflin](https://github.com/chaitanyagiri/munder-difflin)
- Tags: how-to-guide
- Published: 2026-08-29

---

**The bundled‑node launcher eliminates nvm/PATH problems by forcing the Electron binary to execute agent hooks via absolute paths, completely bypassing system PATH dependencies.**

Munder‑Difflin executes agent hooks through a specialized **bundled‑node launcher** that guarantees a working Node runtime regardless of how the user installed Node. When **nvm** (Node Version Manager) places the `node` executable only in interactive shell PATHs, standard hook invocations fail with exit 127. The launcher solves this by wrapping the application’s own Electron binary to execute hook scripts, ensuring reliable agent communication even in minimal shell environments.

## The nvm/PATH Problem in Hook Execution

When users install Node via **nvm**, the binary is typically added to PATH only within interactive login shells. However, Munder‑Difflin’s hive spawns hook processes using `/bin/sh -c` with a minimal environment (`/usr/bin:/bin:/usr/sbin:/sbin`). In this context, the `node` command is unavailable, causing hooks to fail immediately with exit 127. This prevents agents from reporting status or receiving stop signals, effectively breaking the agent lifecycle.

## How the Bundled‑Node Launcher Works

The solution resides in [`src/main/hive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts), which generates a platform‑specific launcher script at `<hiveRoot>/bin/hive-node` (or `hive-node.cmd` on Windows). This wrapper forces the Electron runtime (the app’s bundled Node) to execute hook code with an absolute path, rendering the system PATH irrelevant.

### Launcher Generation via writeNodeLauncher()

During each bootstrap, the `writeNodeLauncher()` function creates or updates the launcher script. This ensures the launcher stays current after application upgrades.

```ts
// src/main/hive.ts – writeNodeLauncher()
if (process.platform === 'win32') {
  writeFileSync(p,
    `@echo off\r\nset ELECTRON_RUN_AS_NODE=1\r\n"${process.execPath}" %*\r\n`,
    'utf8');
} else {
  writeFileSync(p,
    `#!/bin/sh\nELECTRON_RUN_AS_NODE=1 exec "${process.execPath}" "$@"\n`,
    'utf8');
  chmodSync(p, 0o755);
}

```

The script sets `ELECTRON_RUN_AS_NODE=1` to force Electron to run as a Node.js process, then executes the absolute path stored in `process.execPath`.

### Hook Command Construction via nodeRun()

When building command strings for agent hooks, the `nodeRun()` method substitutes the launcher for a bare `node` command:

```ts
// src/main/hive.ts – nodeRun()
const launcher = this.nodeLauncher();               // absolute path or null
return [launcher ? `"${launcher}"` : 'node',
        `"${script}"`, ...args].join(' ');

```

If the launcher exists, the method returns an absolute command like `"/path/to/hive/bin/hive-node" "agents/1234/hook.cjs" …`. Otherwise, it falls back to the system `node` for backward compatibility.

### Cross‑Platform Path Resolution

The `nodeLauncherPath()` and `nodeLauncher()` methods determine the absolute path to the launcher. By using absolute paths, the system avoids Windows‑specific expansion issues where `$HIVE_NODE` variables fail in `cmd.exe` or PowerShell. This ensures consistent behavior across POSIX and Windows systems without shell‑specific quirks.

## Practical Implementation Example

When instantiating a Hive and generating a hook command:

```ts
const hive = new Hive();
const hookCmd = hive.nodeRun(
  join(agentDir, 'my-hook.cjs'),
  '--option', 'value'
);
// Output: "/abs/path/to/hive/bin/hive-node" "…/my-hook.cjs" "--option" "value"

```

The generated POSIX launcher looks like:

```sh
#!/bin/sh
ELECTRON_RUN_AS_NODE=1 exec "/Applications/Munder‑Difflin.app/Contents/MacOS/Electron" "$@"

```

On Windows:

```cmd
@echo off
set ELECTRON_RUN_AS_NODE=1
"C:\Program Files\Munder‑Difflin\Electron.exe" %*

```

## Summary

- The **bundled‑node launcher** guarantees executable Node access by wrapping the Electron binary at [`src/main/hive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts).
- It bypasses **nvm** shell‑only PATH restrictions that cause exit 127 failures in minimal `/bin/sh` environments.
- Absolute paths eliminate cross‑platform expansion issues on Windows and POSIX systems.
- Automatic regeneration during bootstrap ensures the launcher stays synchronized with application updates.
- The `nodeRun()` method in [`src/main/hive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts) transparently selects the launcher over system Node when available.

## Frequently Asked Questions

### Why does nvm cause hook failures if Node is installed?

nvm modifies PATH only in interactive login shells. Hook processes spawned via `/bin/sh -c` receive a minimal PATH (`/usr/bin:/bin:/usr/sbin:/sbin`) that excludes nvm’s Node directory, resulting in "command not found" (exit 127) errors when hooks attempt to invoke `node`.

### Where is the bundled-node launcher stored?

The launcher is written to `<hiveRoot>/bin/hive-node` on POSIX systems or `hive-node.cmd` on Windows, located within the hive’s installation directory. This path is calculated by `nodeLauncherPath()` in [`src/main/hive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts).

### How does the launcher use Electron instead of system Node?

The wrapper script sets `ELECTRON_RUN_AS_NODE=1` and invokes `process.execPath` (the absolute path to the Electron binary). This forces Electron to run in Node.js mode, providing a stable runtime independent of system Node installations or nvm shims.

### What happens if the launcher file is missing?

If `nodeLauncher()` returns null (file does not exist), `nodeRun()` falls back to the bare `node` command, preserving backward compatibility while risking PATH-related failures on nvm-managed systems. The launcher regenerates at each bootstrap to prevent this scenario.