llxprt-code: the install says Node 24, the launcher runs Bun
An open-source multi-provider AI assisted CLI development tool. Use whatever LLM you want to code in your terminal.
At a glance
- What is it?
- vybestack/llxprt-code is a terminal AI coding assistant distributed as an npm package, a Homebrew tap, and a Zed integration, which requires Node 24 and Bun 1.3.14 at the same time, resolves its bundled Bun through a three-level lookup that deliberately refuses to climb past the package boundary, works around npm 12 disabling dependency install scripts with sixteen platform tarballs, and fails with a documented exit code 43 when no runtime candidate survives.
- Who is it for?
- llxprt-code is doing something most assistant CLIs leave to chance: it treats the runtime as a distribution problem and writes the resolution rules down. Three-level lookup, `.bin` symlinks excluded, exact version pins enforced, and a documented macOS exception with a linked issue explaining the credential disruption it avoids.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The install path never starts a Node process
The documentation is careful to say two things that look contradictory. The prerequisite is Node.js 24 or newer, and a note immediately below it explains that the CLI runs on the Bun runtime under the covers, that Node.js remains the compatibility target for invocation, and that the published package bundles Bun as a dependency so most users never install it separately. The manifest requires both, with `node >=24` and `bun >=1.3.14`. What the launcher actually does is narrower than either statement: the platform-native binary at `packages/cli/bin/llxprt` resolves the bundled Bun and execs the TypeScript entrypoint at `packages/cli/index.ts` directly. There is no Node process on the installed command path, and no pre-compiled `dist/` artefact is required, nor is the retired `bundle/llxprt.js` one. So Node is there so that `npx` and `npm` behave, not so that your code runs on it.
The Bun lookup stops at the package boundary on purpose
The resolution order is spelled out in three levels, each with a fallback. First, the package-local copy at `<package>/node_modules/bun/bin/bun.exe`, falling back to an `@oven/bun-<platform>` variant. Second, a hoisted copy in the enclosing `node_modules`, which the documentation says stops at the enclosing boundary and never climbs into consumer ancestors. Third, the workspace root, and only when the package is not under `node_modules` at all and the root is a verified workspace whose manifest references this package. Two constraints make this more than a search path. The launcher never scans `.bin` symlinks, so it cannot be redirected by a shim. And when the package declares an exact Bun pin, a candidate whose own `package.json` or version is missing or mismatched is rejected outright. On macOS there is one deliberate exception, a Bun already on `PATH` that meets the version floor wins, cited to issue 2962 as a way to avoid npm re-extracting a running executable.
Sixteen platform tarballs exist because npm 12 blocks install scripts
The `bun` package normally ships its binary through a `postinstall` script that moves it out of an optional platform dependency into place. npm v12, under RFC 0054, disables dependency install scripts by default, which means that on a default-deny install the binary simply never appears. The workaround is to stop depending on the script. LLxprt Code declares all sixteen `@oven/bun-<platform>` packages as its own `optionalDependencies`, and those tarballs contain only the binary with no install scripts at all, so they materialise regardless of the policy. The launcher and the TypeScript resolver then fall back to them when the expected path is absent. One detail is worth noting for anyone profiling installs: host detection for CPU features and the ABI runs only on this fallback path, so a normal install never forks detection subprocesses. The cost of the approach is sixteen platform entries in the dependency list, which is the price of not depending on a lifecycle hook.
When every candidate is rejected the exit code is 43
Only after each level and each of its platform fallbacks has been probed and rejected, plus the macOS `PATH` preference, does the launcher stop. The error is specific and the exit code is documented:
LLxprt Code: bundled Bun runtime was not found. Reinstall the package with "npm install @vybestack/llxprt-code" to restore the bundled Bun dependency, or visit https://bun.shExit code 43 is worth remembering because it is greppable, and because a generic launcher failure here would otherwise be indistinguishable from a corrupt download. The documented recovery differs by channel: npm users re-run the install, global or local, to restore the bundled dependency, while Homebrew users are pointed at a different route. That split matters in practice, because the two channels get their binaries from different places and only the npm one has a bundled runtime to restore.
Sixteen workspaces, a private root, and an image tag tied to the version
The root manifest lists sixteen workspaces: tools, storage, auth, settings, telemetry, ide-integration, policy, mcp, core, lsp, providers, agents, zed-acp, cli, a2a-server, test-utils, and a VS Code IDE companion. That is the shape of the product in one list, since providers, agents, auth, and storage are separate packages rather than directories inside one. Two details in the same file are easy to miss. The root is marked `"private": true` while carrying the published package name, so the version in it is the version of the source tree, not something a consumer installs. And the `config` block pins the sandbox image to `ghcr.io/vybestack/llxprt-code/sandbox:0.12.0`, which matches that tree version, while the newest release tag is `v0.11.0`. The branch is therefore a minor ahead of anything published, and the image tag follows the manifest rather than the tag.
Sixteen trusted install scripts, thirteen of them ast-grep grammars
The manifest carries a `trustedDependencies` allowlist, which is the mechanism a Bun-installer project uses to say which packages may run scripts. Sixteen entries are listed, and the pattern is lopsided: thirteen are `@ast-grep/lang-*` packages for C, C++, C#, Go, Java, JSON, Kotlin, PHP, Python, Ruby, Rust, Scala, Swift, and Bash, plus `tree-sitter-bash`. The two that are not grammars are `bun` itself and `@lvce-editor/ripgrep`. Two things follow. First, the structural code-editing engine this CLI depends on is ast-grep, and its language support arrives as one installable package per language, which is why the list is thirteen entries long. Second, allowing scripts for sixteen named packages is a narrower grant than allowing them for everything, and the fact that `ripgrep` is on it means the project expects to compile or fetch a search binary during install rather than shelling out to whatever the host happens to have.
The sandbox image is a general dev box with a second Bun in it
The container starts from `node:24-slim` and installs a broad toolset: `python3`, `make`, `g++`, `ripgrep`, `rsync`, `jq`, `less`, `socat`, `lsof`, `git-lfs`, `openssl-client`, `procps`, and `man-db`, among others. That is a general development environment rather than a minimal image, which fits a sandbox an agent runs code in. Then it installs Bun, by piping the official install script into bash:
BUN_VERSION="$(tr -d '[:space:]' < /tmp/.bun-version)" && \
curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" && \
ln -sf /usr/local/bun/bin/bun /usr/local/bin/bun && \
bun --versionTwo details are handled properly. The version is read from the repository's own `.bun-version` file so the image, the release, and CI cannot drift, and the shell is switched to bash with pipefail for that one instruction so a failed `curl` fails the build instead of succeeding silently, a hadolint DL4006 case the comment names explicitly. The image then drops to the non-root `node` user with a global npm prefix chowned to it. The visible oddity is that this sandbox installs its own Bun while the npm package bundles another, which is two runtimes for one product.
make release is declared but has no recipe
The Makefile is a thin wrapper and reading it is faster than reading the package scripts. Eleven targets, and every one of them except two is a single `npm run` passthrough: `install`, `build`, `build-all`, `test`, `lint`, `format`, `preflight`, `clean`, `start`, and `debug`. The two exceptions are the interesting ones. `create-alias` runs `scripts/create_alias.sh`, and `run-npx` is:
npx https://github.com/vybestack/llxprt-codeA bare repository URL handed to npx, which is a compact way to exercise the published package path without installing it. Then there is the gap worth knowing about. `.PHONY` lists `build-sandbox` and `release`, and neither has a recipe, so `make release` succeeds having done nothing. Both of those jobs live in npm scripts instead. It is a small thing, but it is the kind of thing that wastes an afternoon when someone trusts the Makefile's own help text, which advertises every target as if it did work.
Editorial conclusion
llxprt-code is doing something most assistant CLIs leave to chance: it treats the runtime as a distribution problem and writes the resolution rules down. Three-level lookup, `.bin` symlinks excluded, exact version pins enforced, and a documented macOS exception with a linked issue explaining the credential disruption it avoids. If you have ever had an installed assistant binary stop working after a package manager re-extracted a running executable, that section is worth reading on its own. Two things to weigh before you adopt it. The provider story is the product, and several of its flows take a subscription or a key, so read which routes store credentials where before you wire it into a script. And the root manifest is a private sixteen-workspace monorepo versioned at 0.12.0 while the newest release tag is 0.11.0, so what you install from npm and what you would build from the branch are not the same tree.
Frequently asked questions
What is llxprt-code?
An open-source terminal AI coding assistant that works with any LLM provider, from Anthropic and Gemini through Kimi and OpenAI-compatible endpoints, with local models supported through LM Studio or llama.cpp. It is distributed as an npm package, a Homebrew tap, and a native Zed integration.
Do I need Node or Bun to run llxprt-code?
Both are declared: the manifest requires `node >=24` and `bun >=1.3.14`. Node.js is the compatibility target for invocation, but the CLI runs on Bun underneath and the published package bundles Bun as a dependency, so most users never install it separately. The launcher execs the TypeScript entrypoint directly with no Node process on the installed path.
How do I authenticate llxprt-code with a subscription I already pay for?
From the REPL, use `/auth anthropic enable` then `/provider anthropic` for a Claude subscription, or `/auth codex enable` then `/provider codex` for ChatGPT Plus or Pro, and `/auth gemini enable` for Gemini. Kimi uses `/provider kimi` with a `/key` argument. The tool also supports multiple accounts with automatic failover on rate limits.
How does llxprt-code cope with npm 12 blocking install scripts?
The `bun` package normally moves its binary into place with a postinstall script, which npm v12 disables by default under RFC 0054. LLxprt Code declares all sixteen `@oven/bun-<platform>` packages as its own optional dependencies, whose tarballs hold only the binary and no install scripts, so they are present even under default-deny and the launcher falls back to them.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vybestack-llxprt-code)