Your agent reads less.
You decide what.
Lirah is a desktop workspace for CLI coding agents.
Claude Code or opencode runs unchanged in a real terminal. You send it each file as full text, signatures or a skeleton.
curl -fsSL https:/ / raw.githubusercontent.com/ emdmed/ lirah/ main/ scripts/ install.sh | bashLinux · v0.4.15 · picks .deb, .rpm or AppImage for your distro · builds from source on Arch · other formats
~/projects/acme-api main $ claude
> add refresh-token rotation to the auth middleware
● I'll start by exploring the codebase.
exploring
No context attached. The agent will go looking.
A replica you can use. Click files and chips to change the context. Demo data; token counts are illustrative.
// context
Stop paying for files your agent didn't need.
Left alone, an agent reads whole files to find one function. You pay for every line, and the session compacts away what you told it earlier. In Lirah you pick what it sees, and send big files as signatures instead of every line: schema.ts goes from 12.6k tokens to 2.1k.
The prompt footer shows the estimate before you send.
import { pgTable, uuid, text, timestamp, boolean } from "drizzle-orm/pg-core";export const sessions = pgTable("sessions", {id: uuid("id").primaryKey().defaultRandom(),userId: uuid("user_id").references(() => users.id),refresh: text("refresh").notNull(),expiresAt: timestamp("expires_at").notNull(),revoked: boolean("revoked").default(false),});export async function findSessionByRefresh(token: string) {const [row] = await db.select().from(sessions).where(and(eq(sessions.refresh, token), eq(sessions.revoked, false)));return row ?? null;}… 368 more lines
This file is in the demo prompt, so the meter at the top of the page moves with it.
- EDITREADCOPY
- Intent per file. Change it, read it without touching it, or copy its pattern, like an existing route to model a new one on.
- hookcontext
- Box or terminal. Send from the prompt box, or type straight into Claude Code and Lirah's hook appends what you picked.
- setauth-flow
- Context sets. Save a selection by name and load it next time you work on that part of the code.
- @mention a file
- altalttemplates and .patterns
- ^⇧Zkeep or restore context
// index
Your agent greps an index instead of reading the repo.
Lirah keeps a symbol index of your project and tells the agent to grep it before opening files, then read only the lines a hit points to. It pays off most on questions that span several files, and we measured it: the results are below.
It runs as a hook on every prompt in each Claude Code session Lirah starts, and can be switched off in settings.
> add refresh-token rotation to the auth middleware
● Search(pattern: "refresh", path: "src")
Found 9 files
● Read(src/routes/index.ts)
● Read(src/routes/session.ts)
● Read(src/middleware/auth.ts)
● Read(src/lib/token.ts)
● Read(src/db/schema.ts)
opened before the first edit: 5 whole files → 2 line ranges
Switch between the two to compare. A demo transcript; the measured results are below.
- grepused-by
- Finds the right lines first. Each hit names a file and line range, plus every caller and the function it sits in. The agent opens those lines and nothing else.
- .orchestration/
- Stays current on its own. The index rebuilds when sources change, in the background on big repos, and never lands in git.
- ^⇧P
- Never in your way. Switch it per tab with the key or :compact on; it applies on the next prompt. A failing hook never blocks a prompt, and your own hooks still run.
24%
cheaper on cross-file questions in Lirah's own codebase, and 16% faster.
- 13% lower API cost per question overall
- 18% fewer tool calls
- cheaper on 7 of 8 tasks
Same answers: every run correct, with or without the index.
8 questions that each need 3+ files, 3 runs each way, on a small repo and on Lirah's own app. Every answer graded against the files it should name. Sonnet · Claude Code 2.1.280.
// tokens
Budgets that catch a runaway session.
Give a project a daily or weekly budget. Lirah warns you at 80% and again at 95%, so a runaway session gets caught before it burns through the rest of the week.
Usage is read from Claude Code's session files: totals per day and per project over a day, a week or a month, with CSV and JSON export.
- tokens this week
- 2.41M+18% vs last week
- sessions
- 63
- projects
- 4
| acme-api | 412k/500k | 82% |
|---|---|---|
| acme-web | 118k/400k | 30% |
| acme-db | 291k/300k | 97% |
| infra | 22k/150k | 15% |
- acme-db97% of daily budget14:02
- acme-api82% of daily budget11:47
// workspace
One window, an agent per project.
Each project tab has its own terminal, agent and sandbox. Hook plugins for Claude Code and opencode report when an agent is working, needs permission, is waiting on you or is done, and the tab row lights to match.
Nvim and lazygit open full window in the tab's second terminal, with your own config, while the agent keeps working. Lirah's shortcuts step aside until you quit.
| Context | tab | agent | branch | last turn | plan |
|---|---|---|---|---|---|
| api | working | feat/token-rotation | auth.ts token.ts | auth-hardening 4/7 | |
| ui | needs permission | main | LoginForm.tsx | · | |
| db | waiting on you | migrations | 0008_api_keys.sql | · | |
| done | ops/tf-1.9 | main.tf variables.tf | · | ||
same repo as acme-api | working | fix/rate-limit | limiter.ts | · | |
| idle | main | · | · |
Pick a tab and the meter at the top switches to it · attach a tab's last-turn files to another tab's prompt · desktop notifications when an agent stops for you · demo data
- ^Klaunch the tab's agent
- ^`second terminal
- altNaltLnvim, lazygit
- :command line
- ^⇧Fsearch scrollback
- ^Hevery shortcut
// jobs and plans
Work you don't have to watch. You choose what lands.
Start headless runs of any supported agent from a template and your context files, each in its own git worktree if you want. Race several agents on one task, compare what they wrote, and apply the one you want.
For longer work, import a *_plan.md: Lirah implements it chunk by chunk in the tab's agent and commits each one.
context: your prompt above · 5 files · 8.5k each
- claudeediting limiter.ts
- codex+84 -12
- geminiconflict in routes/login.ts
- applied · fix flaky session test · claude+12 -4
- failed · lint sweep · opencode · type check
- archived · 6 jobs
Read the diff and send notes back first. Applying waits for the TypeScript check to pass and asks before touching a dirty tree.
1–3 implemented · 3 commits
4 · rotate refresh tokens on use
implementing in acme-api · commit trailer Plan-Chunk: 4
- 5 · revoke sessions on password change
- 6 · add api key scopes
- 7 · audit log for auth events
:autopilotchains the remaining chunks
Check, review, roll back, skip or reorder any chunk. Autopilot stops when the agent asks a question, needs permission or forgets to commit.
// git
Commits drafted. Merges shown first.
When a change is ready, an agent reads the staged diff and drafts a conventional commit message for you to edit. Merging the branch back lists every git command before any of them runs.
All of it is local git, run on your machine.
- Msrc/auth/token.ts+48 -12
- Msrc/auth/middleware.ts+21 -9
- Asrc/auth/token.test.ts+64 -0
feat(auth): rotate refresh tokens on use
Issue a new refresh token on every refresh and revoke the old one, so a stolen token works at most once.
feat/token-rotation → main · --no-ff
- git switch main
- git merge --no-ff -m "Merge branch 'feat/token-rotation' into main" feat/token-rotation
on conflict: git merge --abort, nothing changed
Fast-forward or --no-ff. It knows when either branch is checked out in another worktree.
Diff viewer. Side by side, word level, unchanged regions folded. Step file to file and leave review notes as you read.
// sandbox
Let the agent run, inside a sandbox.
On Linux, each tab can run in a bubblewrap sandbox. The root filesystem is read-only. Your home and the project stay writable, while keys, shell rc files and your git config stay read-only. /tmp is private to the tab.
macOS and Windows builds run without the sandbox.
| / | ro |
| ~ | rw |
| ~/projects/acme-api | rw |
| ~/.ssh | ro |
| ~/.gnupg | ro |
| ~/.bashrc ~/.zshrc ~/.profile | ro |
| ~/.gitconfig | ro |
| ~/.config/autostart | ro |
| /tmp | private |
| pid ipc uts | isolated |
Switch it off to see what an unsandboxed tab can reach.
- network
Network isolation. One more switch cuts the tab off from the network.
- apparmor
Set up for you. On Ubuntu 24.04 and later Lirah installs the AppArmor profile bubblewrap needs.
- retry
Never a dead end. If the sandbox can't start, Lirah offers to retry the tab without it.
Does Lirah replace or wrap my agent?
No. Lirah starts the agent's own CLI in a real login-shell PTY, the same program you would run in any terminal, with your config, your login and your plan. Lirah doesn't call a model API itself. Its git and job features also work by starting those CLIs.
Which agents and platforms does it support?
Claude Code and opencode run interactively in a tab, with hook plugins that report their state. Codex, Gemini, Cursor and Amp run as headless background jobs, alongside Claude Code and opencode.
Linux is the primary platform. macOS and Windows builds exist and work without the sandbox, which is Linux-only.
What do Lirah's hooks add to Claude Code?
Lirah loads a plugin into each Claude Code it starts with --plugin-dir, so your own hooks and settings stay as they are. Its hooks report the agent's state to the tab, append the context you picked to prompts typed in the terminal, and, in tabs where you switch it on, point the agent at a fresh project index. The index lives in .orchestration/ and is kept out of git. Each hook can be turned off in settings.
What happens if I stop using Lirah?
Nothing to migrate. The agents are the same CLIs you would run in any terminal, with their own config, and the commits and merges Lirah makes are ordinary local git. Your repos look the same with or without it.
Is it free?
Yes. Lirah is open source under the MIT license, with no account. The source is at github.com/emdmed/lirah. You still pay for whatever agent plan you use.
Does anything leave my machine?
Lirah itself makes one request: at launch and every four hours it asks GitHub's API for the latest release, to tell you about updates. It sends no data of yours with it. There is no telemetry. Lirah runs only local git commands and never pushes, pulls or fetches.
Token figures are read from Claude Code's session files on disk, and budgets are stored locally. The agents you run talk to their own providers, and features that use an agent send through it what they need: auto commit hands it your staged diff. If Claude Code isn't installed, launching it falls back to npx, which downloads it from npm.
Why not tmux and my editor?
Keep them. The agent runs in a real login-shell PTY, and the second terminal opens nvim and lazygit beside it. What Lirah adds is what a multiplexer can't see: which files the agent is given and at what detail, how full each tab's context is and whether its agent is waiting on you, background jobs in their own worktrees, and plans that commit chunk by chunk.
// download
Give your agents less to read and more to do.
Lirah is a free, open-source workspace for the coding agents you already use. MIT licensed, no account, no telemetry.
curl -fsSL https:/ / raw.githubusercontent.com/ emdmed/ lirah/ main/ scripts/ install.sh | bashLinux · v0.4.15 · picks .deb, .rpm or AppImage for your distro · builds from source on Arch
| Your system | system | format | arch | size | Download |
|---|---|---|---|---|---|
| Linux | AppImage · x64 | x64 | 80 MB | [get] | |
| Linux | deb · x64 | x64 | 5.9 MB | [get] | |
| Linux | rpm · x64 | x64 | 5.9 MB | [get] | |
| macOS | dmg · Intel | Intel | 5.2 MB | [get] | |
| macOS | dmg · Apple silicon | Apple silicon | 5.0 MB | [get] | |
| Windows | exe · x64 | x64 | 4.2 MB | [get] | |
| Windows | msi · x64 | x64 | 6.0 MB | [get] |