cheapskate platform for running agents on your local projects.
lux lets you browse directories on your machine, mark them as projects, and spin up persistent terminal sessions running CLI agents. You interact with agents through an in-browser terminal, review git diffs as they work, and manage multiple projects and sessions at the same time.
- Browse local directories and register them as projects
- Start named sessions that run an AI agent CLI in a tmux pane
- Connect to sessions through a web terminal (xterm.js or ghostty-web)
- Review git diffs, with syntax highlighting and split/unified views
- Auto-detect changes by polling git status and refreshing diffs on the fly
- Manage multiple projects and sessions from a single tab-based UI
You will need Vite+, tmux, Node.js (Vite+ can do this for you).
This repo ships with a flake.nix development shell:
nix develop
cp .env.example .env
vp i
vp devThe shell includes Node.js, tmux, git, and native build tools used by Node packages.
It also provides a vp command (it uses local node_modules/.bin/vp when available, otherwise falls back to npx vite-plus).
Copy .env.example -> .env, then set what you need.
# required when auth mode = github
LUX_AUTH_MODE=github
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
LUX_GITHUB_ALLOWED_USERS=your-github-username[,another-username]
# common tweaks
LUX_HOME_DIR=~:~/Developer:~/Projects
LUX_DEFAULT_AGENT_CLI=amp
VITE_LUX_TERMINAL_WEBGL=0Env cheat sheet (short + blunt):
LUX_AUTH_MODE:githubornone.GITHUB_CLIENT_ID/GITHUB_CLIENT_SECRET: GitHub OAuth app creds (githubmode only).LUX_GITHUB_ALLOWED_USERS: comma list of GitHub usernames, no@.LUX_HOME_DIR: roots shown in dir picker.:separated.~supported. Default~.LUX_DEFAULT_AGENT_CLI: default CLI for new sessions (amp,codex, etc). Defaultamp.LUX_DB_PATH: sqlite db path. Default~/.lux/lux.db.LUX_SESSION_SECRET: cookie/session signing secret. If not set, auto-generated and stored in DB.LUX_TMUX_BIN: tmux binary path. Defaulttmux.LUX_TMUX_SOCKET: tmux socket name. Defaultlux-sessions.LUX_STATE: base state dir for legacy/fallback runtime status files.- Default fallback:
$XDG_STATE_HOMEthen~/.local/state. - Files written under
<LUX_STATE>/lux/<agent>/<paneId>.json(agents:amp,pi).
- Default fallback:
LUX_AGENT_STATUS_TTL_SECONDS: freshness window for live/fallback agent status snapshots. Default120.VITE_LUX_TERMINAL_WEBGL: xterm WebGL renderer toggle (1/true/yes/on=> on, else off).
Agent bridge env vars (auto-injected by lux into each session shell):
LUX_SESSION_ID: lux session id for the current tmux session.LUX_AGENT_BRIDGE_URL: websocket endpoint base URL (ws(s)://.../api/agent-bridge/ws).LUX_AGENT_BRIDGE_TOKEN: signed token for bridge auth (append as?token=...).
If you are writing your own agent integration, connect to LUX_AGENT_BRIDGE_URL with LUX_AGENT_BRIDGE_TOKEN, then emit status messages with protocol version v: 1.
Expected payload shape:
{
"v": 1,
"t": "status",
"sessionId": "<LUX_SESSION_ID>",
"paneId": "<TMUX_PANE>",
"agent": "amp",
"pid": 12345,
"status": "busy",
"ts": 1712600000
}Notes:
tcan bestatus,heartbeat, orremove.statuscan beidle,busy,awaiting-input(orawaiting_input), orerrored.- Send
removeon shutdown so lux clears stale runtime state quickly. - These vars are set when sessions are created/launched/restarted by lux.
- lux uses the Amp Plugin API via
plugins/amp/lux.tsto report status events (working/busy, idle, awaiting input, errored) back to lux. - When a session command resolves to
amp, lux launches it asPLUGINS=all amp(preserving any extra args) so Amp loads plugins automatically.
Install dependencies with vp i, vp build, and finally vp preview.
