Worktree-scoped dev stack orchestrator. Run as many dev stacks as you have worktrees.
demo.mp4
What it is
A CLI that reads a lich.yaml file describing your stack (docker containers, host processes, env variables, lifecycle) and brings it up with per-worktree isolation. Run lich up in two worktrees, get two independent stacks running whatever code exists in those worktrees.
Who it's for
Developers who want to run parallel development stacks from multiple worktrees. Typically this is to enable workflows using multiple parallel coding agents.
Why
Traditional local dev tooling is done ad hoc. It uses various tools: bash/npm scripts, procfiles, docker compose, etc. These systems generally can't run multiple copies of the same stack on one machine at the same time. Ports collide, container names conflict, memory leaks from zombie processes never cleaned up, agents get derailed running down bugs in tooling instead fixing actual problems. Lich implements a robust and observable system to manage this complexity.
Install
curl -fsSL https://lich.sh/install.sh | bashOr download a release tarball from GitHub Releases and put lich on your PATH.
Quickstart
The fastest way to see lich working is the t3 starter. A T3 Stack (Next.js + tRPC + Prisma + Postgres + Tailwind) preconfigured with a lich.yaml:
git clone https://github.com/RPate97/lich-starter-t3
cd lich-starter-t3
lich upPostgres boots in a Docker container on a dynamically allocated port, DATABASE_URL is wired automatically, Prisma pushes the schema, Next.js starts. Open the URL lich prints and you have a working full-stack app.
Run two stacks in parallel
Add a worktree and bring up a second stack from the same template:
git worktree add ../lich-starter-t3-feature -b feature
cd ../lich-starter-t3-feature
lich up
lich stacksWORKTREE STATUS UPTIME SERVICES URL
lich-starter-t3 up 00:02:15 2/2 http://web.lich-starter-t3.lich.localhost:3300/
lich-starter-t3-feature up 00:00:08 2/2 http://web.lich-starter-t3-feature.lich.localhost:3300/
Two stacks. Two databases. Two dev servers. No port collisions. Same lich.yaml. Both URLs work independently in your browser.
Tear down both:
Use lich in your own repo
To wire lich into an existing repo, install the lich-instrument skill and run it in your coding agent:
npx skills add https://github.com/RPate97/lich/skills/lich-instrument
The skill walks an agent through writing a lich.yaml for your stack. You may need to iterate a few times to get the env variable mapping and loading right; if you get stuck, the lich.yaml reference is the place to look. Then it's the same workflow:
lich up # brings the stack up lich logs # tail logs lich down # stop it
Minimal lich.yaml
version: 1 services: postgres: image: postgres:16 ports: - { container_port: 5432, published_env: POSTGRES_HOST_PORT } environment: POSTGRES_PASSWORD: dev owned: api: cwd: apps/api cmd: pnpm dev port: { published_env: PORT } env: DATABASE_URL: "postgres://postgres:dev@localhost:${services.postgres.host_port}/app"
Docs
Full docs at lich.sh. Entry points:
Agent skills
Lich ships agent skills that let Claude (or other agents) work with lich effectively:
lich— daily-driver on-ramp; understand the CLI surface and use itlich-instrument— guides an agent through writing your firstlich.yaml
Add a skill:
npx skills add https://github.com/RPate97/lich/skills/lich npx skills add https://github.com/RPate97/lich/skills/lich-instrument
Contributing
Setup:
git clone https://github.com/RPate97/lich
cd lich
bash scripts/install-git-hooks.shBuild:
cd packages/lich && bun install && bun run build
Test:
cd packages/lich && bun test # unit cd packages/e2e && bun run test # end-to-end
The test suites disable telemetry automatically (set in vitest.config.ts and the e2e helper). For ad-hoc local runs of the lich binary while developing, set LICH_TELEMETRY=0 in your shell so your dev usage doesn't show up in real-user analytics:
echo 'export LICH_TELEMETRY=0' >> ~/.zshrc # or ~/.bashrc
Telemetry
Lich collects anonymous CLI usage telemetry (command name, exit code, duration, version, platform). No paths, no lich.yaml contents, no env values, no log content. The distinct_id is a machine-stable SHA-256 hash of your home directory + hostname + platform (one-way, no reversal), cached at ~/.lich/installation-id.
Disable:
export LICH_TELEMETRY=0 # per-shell echo '{"telemetry":false}' > ~/.lich/config.json # per-user, persistent
Or in lich.yaml:
runtime: telemetry: false
Full disclosure of what's collected, where it goes, and how to delete the per-installation ID: lich.sh/telemetry.
If you can, please leave telemetry enabled.
License
MIT.