We would love to accept your patches and contributions to this project.
By contributing to this project, you agree to provide a non-revocable Apache Software License 2.0 to Vybestack and the general community. You (or your employer) retain the copyright to your contribution while granting permission to use and redistribute your contributions as part of the project under the Apache License 2.0.
This project values respectful and constructive collaboration. We expect all contributors to maintain professional conduct and focus on improving the project for the benefit of all users.
Run the presubmit check from the repository root before opening a pull request:
bun run presubmitThis formats the repository and then runs linting, the build, type checking, and tests. (The build runs before type checking and tests because packages type-check and test against each other's built output.) Note: several root scripts including presubmit, preflight, and build:all still chain to npm run ... internally during the Bun migration transition — bun run presubmit delegates to npm under the covers. If your PR is large or you run into problems that are difficult to reproduce locally, you may also want to run the heavier CI-style preflight check:
bun run preflightpreflight performs a clean install and already includes formatting, CI linting, build, type checking, and CI tests, so you do not need to run those separately unless you are re-checking a specific failure after making fixes.
All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose.
To assign an issue to yourself, add a comment that contains only the text /assign (nothing else). A GitHub Action handles the request when:
- The issue is not already assigned.
- You are eligible: you have at least one merged PR in this repository, or you have a durable prior assignment (previously assigned to an issue here — current or past assignments in open or closed issues both qualify, recorded in a history index).
- You currently have fewer than 3 open issues assigned to you (hard cap to limit spam / hoarding).
Bot accounts are ignored. On success the issue is labeled auto-assigned and a feedback comment is posted. Assignment may fail for several reasons (the issue was assigned by a concurrent request, the eligibility check could not be verified, GitHub API errors, or GitHub rejected or ignored the assignment because the account is unavailable or otherwise unassignable). The automation verifies each assignment and posts feedback reporting any failure.
Auto-assignments older than 2 weeks with no qualifying linked PR activity are unassigned automatically. Only the login assigned by the /assign automation is removed — manual co-assignees are preserved. The maintainer acoliver is exempt from cleanup. Comment /assign again if you still intend to work on the issue.
To help us review and merge your PRs quickly, please follow these guidelines. PRs that do not meet these standards may be closed.
All PRs should be linked to an existing issue in our tracker. This ensures that every change has been discussed and is aligned with the project's goals before any code is written.
- For bug fixes: The PR should be linked to the bug report issue.
- For features: The PR should be linked to the feature request or proposal issue that has been approved by a maintainer.
If an issue for your change doesn't exist, please open one first and wait for feedback before you start coding.
We favor small, atomic PRs that address a single issue or add a single, self-contained feature.
- Do: Create a PR that fixes one specific bug or adds one specific feature.
- Don't: Bundle multiple unrelated changes (e.g., a bug fix, a new feature, and a refactor) into a single PR.
Large changes should be broken down into a series of smaller, logical PRs that can be reviewed and merged independently.
If you'd like to get early feedback on your work, please use GitHub's Draft Pull Request feature. This signals to the maintainers that the PR is not yet ready for a formal review but is open for discussion and initial feedback.
Before submitting your PR, ensure that the contributor readiness checks pass by running bun run presubmit. This command formats the repository and then runs linting, the build, type checking, and tests. (Note: several root scripts including presubmit, preflight, and build:all still chain to npm run ... internally during the migration transition — bun run presubmit delegates to npm under the covers.)
If your PR introduces a user-facing change (e.g., a new command, a modified flag, or a change in behavior), you must also update the relevant documentation in the /docs directory. See the Documentation Style Guide for audience, placement, writing, and review standards.
Your PR should have a clear, descriptive title and a detailed description of the changes. Follow the Conventional Commits standard for your commit messages.
- Good PR Title:
feat(cli): Add --json flag to 'config get' command - Bad PR Title:
Made some changes
In the PR description, explain the "why" behind your changes and link to the relevant issue (e.g., Fixes #123).
If you are forking the repository you will be able to run the Build, Test and Integration test workflows. However in order to make the integration tests run you'll need to add a GitHub Repository Secret with a value of GEMINI_API_KEY and set that to a valid API key that you have available. Your key and secret are private to your repo; no one without access can see your key and you cannot see any secrets related to this repo.
Additionally you will need to click on the Actions tab and enable workflows for your repository, you'll find it's the large blue button in the center of the screen.
This section guides contributors on how to build, modify, and understand the development setup of this project.
Prerequisites:
-
Node.js:
- Development: Please use the Node.js version pinned in
.nvmrc(currently Node.js 24.22.1). You can use a tool like nvm to manage Node.js versions (nvm use). - Production: For running the CLI in a production environment, any version of Node.js
>=24.22.1is acceptable.
- Development: Please use the Node.js version pinned in
-
Bun: Install Bun (version pinned in
.bun-version, currently1.4.4). Bun is the runtime for the CLI run path and the package manager for development. The npm/npx/Homebrew install flows still work, but the development workflow uses Bun directly. See dev-docs/bun.md for details on the dual-lockfile policy and Bun-specific configuration.Note: The root
package.jsonstill declarespackageManager: npm@11.19.1, and several root scripts (presubmit,preflight,build:all) chain tonpm run ...internally. During the migration transition,bun run presubmitdelegates to npm internally. This will be reconciled in a later subissue. -
Git
To clone the repository:
git clone https://github.com/vybestack/llxprt-code.git # Or your fork's URL
cd llxprt-codeTo install dependencies defined in package.json as well as root dependencies:
bun installTo build the entire project (all packages):
bun run buildTypeScript source (.ts) is shipped directly. The CLI's installed command uses platform-native launchers (packages/cli/bin/llxprt) that resolve the package-bundled Bun and exec packages/cli/index.ts directly — no Node process is started on the installed path, and no pre-compiled CLI dist/ artifact or bundle/llxprt.js artifact is required for the CLI to run. Type checking uses tsc --noEmit, and package builds still produce dist/ artifacts for workspace/library packaging. Refer to scripts/build.ts, scripts/build_package.ts, and package.json scripts for more details on what happens during the build.
Sandboxing is highly recommended and requires, at a minimum, setting LLXPRT_SANDBOX=true in your ~/.env and ensuring a sandboxing provider (e.g. macOS Seatbelt, docker, or podman) is available. See Sandboxing for details.
To build both the gemini CLI utility and the sandbox container, run build:all from the root directory:
bun run build:allTo skip building the sandbox container, you can use bun run build instead.
To start LLxprt Code from the source code, run the following command from the root directory (no build step is required — the launcher runs Bun on the TypeScript sources directly):
bun run startAlternatively, the dev launcher (scripts/start.ts) spawns Bun directly on the TypeScript source entry (packages/cli/index.ts); in debug mode (DEBUG=1) Bun starts with the inspector:
bun scripts/start.tsThe production launcher (packages/cli/bin/llxprt) is a POSIX sh script with a valid #!/bin/sh shebang. It resolves the bundled Bun by checking, in order: the package-local node_modules/bun/bin/bun.exe, the enclosing node_modules/bun/bin/bun.exe (for hoisted installed packages, stopping at the enclosing node_modules boundary), and — for the source workspace only — a verified repository root's node_modules/bun/bin/bun.exe (the root manifest must reference this package). At each boundary, if bun/bin/bun.exe is absent (e.g. npm v12 default-deny of install scripts, RFC 0054), it falls back to the @oven/bun-<platform>/bin/bun[.exe] variant declared as an optionalDependencies entry; those tarballs have no install scripts and materialize under default-deny. On macOS, a Bun already on PATH that meets the pinned version floor is preferred over all bundled candidates (issue #2962). It never scans .bin symlinks. When the package declares an exact Bun pin, a candidate whose package.json/version is missing or mismatched is rejected. It validates the executable's native binary magic (ELF/Mach-O) before exec to reject corrupt or unusable binaries with an actionable exit 43. On Windows, the CLI workspace postinstall (packages/cli/scripts/install-native-launchers.cjs) replaces npm's cmd-shim with native .cmd / .ps1 launchers that invoke the same package-local Bun. The cmd launcher preserves the child exit code exactly (no errorlevel remapping); the PowerShell launcher wraps the invocation in try/catch to surface launch failures as exit 43. (The root scripts/postinstall.cjs delegates to this script on Windows after linking internal workspace packages.) If Bun is absent, the launcher prints actionable guidance and exits 43. See the Bun Runtime and Install Fallback section in the README.
If you'd like to run the source build outside the llxprt-code folder you can utilize npm link path/to/llxprt-code/packages/cli (see: docs) or alias llxprt="bun path/to/llxprt-code/packages/cli" to run with llxprt
This project contains two types of tests: unit tests and integration tests. Testing uses bun:test as the test runner.
The CLI profile integration suites exercise Gemini, which is supplied by the
optional plugins/google-gemini runtime plugin. Root bun install, npm ci
and builds do not install plugin dependencies. Before running the full test
suite from a checkout, install them separately, as the CI test job does:
# Use a separate Bun >= 1.4.4 executable for plugin installs.
BUN_PLUGIN=/absolute/path/to/bun-1.4.4
for plugin in plugins/*/; do
if [ -f "${plugin}package.json" ]; then
(cd "$plugin" && "$BUN_PLUGIN" install --omit=peer)
fi
done
npm run testThe root runtime remains pinned by .bun-version. Plugin installs need Bun
1.4.4 or newer to handle their unresolved host peers; --omit=peer leaves the
host packages supplied by the checkout. See plugins/README.md.
Checkout discovery loads marked packages from plugins/ only when each has
its own node_modules. Without the Gemini plugin install, selecting gemini
fails with Provider 'gemini' not found before any API request. Changing
NODE_ENV or building the base packages does not register an absent plugin.
For individual CLI integration suites, use an explicit relative path so Bun does not also match retained worktree copies elsewhere in the checkout:
bun test ./packages/cli/src/integration-tests/cli-args.integration.test.ts
bun test ./packages/cli/src/integration-tests/cli-args.profile-flag.integration.test.ts
bun test ./packages/cli/src/integration-tests/cli-plugin-provider.integration.test.tsThese three suites use invalid credentials or a local HTTP fixture. They do not need a live Gemini API key. The endpoint fixture verifies that the real CLI discovers the installed plugin and sends the selected model to the configured endpoint.
npm run test runs every workspace's test script — the full set of workspace
suites — but does not include the scripts/tests harness. For the complete
uncredentialed suite (every workspace plus the script harness, honouring
pretest hooks), use bun run test:bun. For the standard pre-PR check, run
bun run presubmit.
bun run presubmitType checking uses tsc --noEmit:
bun run typecheckComplete repository suite (including credentialed roots):
bun run test:bun:allThis is the canonical command that runs every Bun-native test root in the
repository — the uncredentialed workspace-plus-script suite (test:bun), the
credentialed integration-tests root in sandbox:none mode
(test:integration:sandbox:none), and all credentialed evals with
RUN_EVALS=1 (test:all_evals) — in fail-fast order. Every test file
executes directly under Bun. Credentials and provider quota are required
for this command because the integration and evals roots call a real provider.
Complete uncredentialed suite (workspaces + script harness):
bun run test:bunThis script (scripts/test.ts) orchestrates testing across all workspace
packages using Bun as the runtime, then runs the script harness. Every
workspace executes under Bun's native test runner via
scripts/run_bun_tests.ts (one isolated process per test file). Tests import
bun:test; see dev-docs/bun.md for the mocking patterns Bun requires.
Two roots are excluded from that uncredentialed run because they call a real
provider and consume quota. test:bun:all includes them; alternatively, run
them individually:
npm run test:integration:sandbox:none
npm run test:all_evalsThe script explicitly runs each workspace's pretest lifecycle hook before
its test phase (npm does this automatically; Bun does not), so the agents
API-surface guard and other pretest checks are preserved. It also runs the
script harness tests (scripts/tests/) by default.
Supported flags:
bun run test:bun -- --workspace core # run only one workspace
bun run test:bun -- --skip-scripts # skip script harness tests
bun run test:bun -- --skip-pretest # skip pretest hooks
bun run test:bun -- --continue-on-error # run all even if some failThe --workspace flag accepts a package directory name (core), a relative
path (packages/core), or a package name (@vybestack/llxprt-code-core).
The integration tests are designed to validate the end-to-end functionality of LLxprt Code. They are not run as part of the default bun run test command.
To run the integration tests, use the following command:
bun run test:e2eFor more detailed information on the integration testing framework, please see the Integration Tests documentation.
To ensure code quality and formatting consistency before a PR, run the presubmit check:
bun run presubmitThis command runs formatting, linting, the build, type checking, and tests.
For a larger PR or a difficult-to-reproduce failure, run the heavier preflight check:
bun run preflightThis command runs a clean install and then formatting, CI linting, build, type checking, and CI tests as defined in the project's package.json.
ProTip
After cloning, you can create a git pre-push hook to check your branch before pushing:
echo "
# Run contributor readiness checks before pushing
if ! bun run presubmit; then
echo "bun presubmit failed. Push aborted."
exit 1
fi
" > .git/hooks/pre-push && chmod +x .git/hooks/pre-pushTo separately format the code in this project by running the following command from the root directory:
bun run formatThis command uses Prettier to format the code according to the project's style guidelines.
To separately lint the code in this project, run the following command from the root directory:
bun run lint- Please adhere to the coding style, patterns, and conventions used throughout the existing codebase.
- Consult LLXPRT.md (typically found in the project root) for specific instructions related to AI-assisted development, including conventions for React, comments, and Git usage.
- Imports: Pay special attention to import paths. The project uses ESLint to enforce restrictions on relative imports between packages.
packages/: Contains the individual sub-packages of the project.a2a-server: A2A server implementation for LLxprt Code. (Experimental)cli/: The command-line interface.core/: The core backend logic for LLxprt Code.test-utils: Utilities for creating and cleaning temporary file systems for testing.vscode-ide-companion/: The LLxprt Code Companion extension pairs with LLxprt Code.
docs/: Contains all project documentation.scripts/: Utility scripts for building, testing, and development tasks.
For more detailed architecture, see docs/architecture.md.
-
Run the CLI to interactively debug in VS Code with
F5 -
Start the CLI in debug mode from the root directory:
bun run debug
This launches Bun directly on the TypeScript entry (via
scripts/start.ts) with the inspector, pausing execution until a debugger attaches before Bun runspackages/cli/index.ts. You can then openchrome://inspectin your Chrome browser to connect to the debugger. -
In VS Code, use the "Attach" launch configuration (found in
.vscode/launch.json).
Alternatively, you can use the "Launch Program" configuration in VS Code if you prefer to launch the currently open file directly, but 'F5' is generally recommended.
To hit a breakpoint inside the sandbox container run:
DEBUG=1 llxprtNote: If you have DEBUG=true in a project's .env file, it won't affect llxprt-code due to automatic exclusion. Use .llxprt/.env files for llxprt-code specific debug settings.
Note: Debugging via the dev launcher (scripts/start.ts) spawns Bun directly on the TypeScript source entrypoint (packages/cli/index.ts), equivalent to what the installed llxprt binary does via the native launcher (packages/cli/bin/llxprt).
To debug the CLI's React-based UI, you can use React DevTools. Ink, the library used for the CLI's interface, is compatible with React DevTools version 4.x.
-
Start LLxprt Code in development mode:
DEV=true bun run start
-
Install and run React DevTools version 4.28.5 (or the latest compatible 4.x version):
You can either install it globally:
npm install -g react-devtools@4.28.5 react-devtools
Or run it directly using npx:
npx react-devtools@4.28.5
Your running CLI application should then connect to React DevTools.

On macOS, llxprt-code uses Seatbelt (sandbox-exec) under a permissive-open profile (see packages/cli/src/utils/sandbox-macos-permissive-open.sb) that restricts writes to the project folder but otherwise allows all other operations and outbound network traffic ("open") by default. You can switch to a restrictive-closed profile (see packages/cli/src/utils/sandbox-macos-restrictive-closed.sb) that declines all operations and outbound network traffic ("closed") by default by setting SEATBELT_PROFILE=restrictive-closed in your environment or .env file. Available built-in profiles are {permissive,restrictive}-{open,closed,proxied} (see below for proxied networking). You can also switch to a custom profile SEATBELT_PROFILE=<profile> if you also create a file .llxprt/sandbox-macos-<profile>.sb under your project settings directory .llxprt.
For stronger container-based sandboxing on macOS or other platforms, you can set LLXPRT_SANDBOX=true|docker|podman|<command> in your environment or .env file. The specified command (or if true then either docker or podman) must be installed on the host machine. Once enabled, bun run build:all will build a minimal container ("sandbox") image and bun run start will launch inside a fresh instance of that container. The first build can take 20-30s (mostly due to downloading of the base image) but after that both build and start overhead should be minimal. Default builds (bun run build) will not rebuild the sandbox.
Container-based sandboxing mounts the project directory (and system temp directory) with read-write access and is started/stopped/removed automatically as you start/stop LLxprt Code. Files created within the sandbox should be automatically mapped to your user/group on host machine. You can easily specify additional mounts, ports, or environment variables by setting SANDBOX_{MOUNTS,PORTS,ENV} as needed. You can also fully customize the sandbox for your projects by creating the files .llxprt/sandbox.Dockerfile and/or .llxprt/sandbox.bashrc under your project settings directory (.llxprt) and running llxprt with BUILD_SANDBOX=1 to trigger building of your custom sandbox.
All sandboxing methods, including macOS Seatbelt using *-proxied profiles, support restricting outbound network traffic through a custom proxy server that can be specified as LLXPRT_SANDBOX_PROXY_COMMAND=<command>, where <command> must start a proxy server that listens on :::8877 for relevant requests. See docs/examples/proxy-script.md for a minimal proxy that only allows HTTPS connections to example.com:443 (e.g. curl https://example.com) and declines all other requests. The proxy is started and stopped automatically alongside the sandbox.
We publish an artifact for each commit to our internal registry. But if you need to manually cut a local build, then run the following commands:
bun run clean
bun install
bun run auth
bun run prerelease:dev
for package_dir in packages/*; do
if [ -f "${package_dir}/package.json" ] && \
[ "$(node -p "require('./${package_dir}/package.json').private === true ? 'true' : 'false'")" != "true" ]; then
(cd "${package_dir}" && bun publish)
fi
done