Getting started
What you'll learn: how to install Lich, configure a provider three different ways, and get your first reply through the one-shot CLI, the TUI, and the gateway webhook.
Prerequisites
- Node >= 20 or Bun (Bun recommended for development; both run the same code).
- A model endpoint: a local Ollama server, an OpenAI or Anthropic api key, or any OpenAI-compatible API (OpenRouter, vLLM, LM Studio, ...).
- The
lichCLI, installed from npm. The examples below use the installed binary; from a clone of the repository the same commands run asbun src/cli.ts ...(see Development install (from source)).
npm install -g @moikapy/lich
lich --version # -> 0.8.0 (reads package.json)Choose a configuration path
Lich needs exactly one thing before it runs: a model. You can provide it three ways. Flags override a config file and override LICH_* env vars. Those env vars apply only when no config file is found; they are not merged into a discovered file.
Path A: environment variables only
# ollama — no api key needed
LICH_PROVIDER_KIND=ollama LICH_MODEL=qwen3:8b lich "Reply with ok"
# openai-compatible (api.openai.com/v1 by default; use a model id your provider accepts)
LICH_PROVIDER_KIND=openai_compat LICH_MODEL=gpt-4o-mini lich "Reply with ok"
# anthropic
LICH_PROVIDER_KIND=anthropic LICH_MODEL=claude-sonnet-4-20250514 lich "Reply with ok"Defaults per kind when LICH_BASE_URL/LICH_API_KEY_ENV are unset: openai_compat uses https://api.openai.com/v1 and reads OPENAI_API_KEY; anthropic uses https://api.anthropic.com and reads ANTHROPIC_API_KEY; ollama uses http://localhost:11434 and needs no key. For Ollama's hosted models, set LICH_BASE_URL=https://ollama.com and LICH_API_KEY_ENV=OLLAMA_API_KEY (create the key at ollama.com).
Path B: the config template (recommended)
mkdir -p .lich
lich config > .lich/config.json
# edit .lich/config.json and replace "<model-name>"lich config honors LICH_PROVIDER_KIND and LICH_MODEL when you have them set, and otherwise prints an ollama-oriented template. The file is picked up automatically from .lich/config.json in the working directory, merged over ~/.lich/config.json if you keep defaults there — after this, plain lich "task" needs no env vars.
lich init writes that same starter file for you (it creates .lich/ and never overwrites an existing .lich/config.json). Bare lich on a TTY, with no config in that search chain, runs a setup wizard and writes .lich/config.json once before opening the TUI. Answer yes to its last question to save the answers as your global default in ~/.lich/config.json (or run lich init --global), so new projects skip the wizard. LICH_MODEL / --model prefills the model prompt; it does not skip the wizard. Non-TTY stdin skips the wizard. .lich/ is gitignored.
Path C: an explicit config file
lich --config ./lich.json "Reply with ok"The full schema is documented in the CLI reference. Search order: a --config path (must exist) is used alone; otherwise ./.lich/config.json is merged over ~/.lich/config.json (see Global config).
Your first one-shot
lich "Use the list_dir tool to list the current directory then reply done"Observed output (stderr progress, then the final answer on stdout):
[lich] turn 1
[lich] list_dir: ok
doneExit code 0 means the model produced a final answer; 1 means the turn budget ran out or the run failed (see exit codes).
Your first TUI session
lich # TUI; first run on a TTY opens the setup wizard
lich tui # same TUI, no wizardType a message and press Enter. The transcript shows your line, live tool-call rows while the agent works, and the reply; the status bar at the bottom tracks turns, tokens, and the session file path. Slash commands: /help, /model, /usage, /clear, /sessions, /resume, /exit. Details in the TUI guide.
Your first gateway webhook
lich gateway webhookIn another terminal:
curl -s -X POST http://localhost:8089/message \
-H "content-type: application/json" -d '{"text": "hello"}'Observed response shape (the reply text is the model's answer; usage is null on this endpoint):
{"reply":"Hello! How can I help you today? I can assist with coding, file management, running commands, web searches, and more — just let me know what you'd like to do.","usage":{"prompt_tokens":812,"completion_tokens":41,"total_tokens":853}}Stop the gateway with Ctrl+C (SIGINT) or kill (SIGTERM); both shut down adapters cleanly.
Where sessions live
Every run appends a JSONL transcript to .lich/sessions/ (override with --session-dir or session_dir in config). File names encode time, a counter, and the origin label — ...-tui.jsonl for TUI runs, ...-gw-webhook-<chat>.jsonl for gateway conversations.
Each line is a JSON record with a ts, a kind of message or meta, and the payload:
{"ts":"2026-09-13T05:21:25.778Z","kind":"message","message":{"role":"user","content":"Use the list_dir tool to list the current directory then reply done"}}
{"ts":"2026-09-13T05:21:25.852Z","kind":"message","message":{"role":"assistant","content":"","tool_calls":[{"id":"ollama_mtzd9c8b_1","name":"list_dir","args":{}}]}}Print just the conversation with jq:
jq -r 'select(.kind=="message") | "\(.message.role): \(.message.content)"' .lich/sessions/<file>.jsonlTroubleshooting
| Symptom | Cause and fix |
|---|---|
no model configured: set LICH_MODEL, pass --model, or create .lich/config.json | No provider was resolvable. Set LICH_MODEL, pass --model, or save a config file (lich init or lich config). |
lich: cannot use config file <path>: ... | --config was given a path that does not exist or is not readable JSON. Check the path or drop the flag to use discovery. |
Provider error kind=auth, http 401/403 | The api key is missing or wrong. Verify the env var named by LICH_API_KEY_ENV (default OPENAI_API_KEY/ANTHROPIC_API_KEY) is exported in the same shell. |
fetch failed / connection refused | The endpoint is unreachable. For ollama, check ollama serve is running on http://localhost:11434; for remote APIs, check LICH_BASE_URL. |
[lich] budget exhausted after N turns — the ritual is spent | The task did not finish within max_turns (default 25). The budget exhausted keyword stays; the flavor suffix comes from the theme. Raise the cap with --max-turns 50 or in config. |
unknown flag: --foo | Flag typo, or the flag was placed where a subcommand is expected. Run lich --help. |
Updating
Check the npm registry and install a newer release with:
lich update
lich --version # -> the version you just installedlich update compares the installed version to npm view @moikapy/lich version. When the registry copy is newer, it runs the equivalent command:
npm install -g @moikapy/lich@latestExit any running lich tui or lich gateway first — npm cannot replace the package while those processes are running. A git clone updates with git pull instead; npx cannot persist an update.
Updates never touch your data: the per-project .lich/ directory holds your config and session transcripts, installers neither read nor migrate it, and it is gitignored by design so a checkout never collides with it. For what changed between versions, see the changelog.
Development install (from source)
To hack on Lich itself, run the CLI straight from a clone instead of the npm package:
git clone https://github.com/Moikapy/lich.git && cd lich
bun install
bun src/cli.ts --version # -> 0.8.0 (package.json)bun src/cli.ts accepts the same arguments as the installed lich binary, so every command on this page works unchanged.
Next steps
- All four CLI modes, flags, and provider resolution: CLI reference.
- Slash commands and the status bar: TUI guide.
- Electron desktop shell from a clone: Ossuary guide.
- Telegram, Discord, Twitch, and webhook setup: Gateway guide.
- Embedding the agent in your own TypeScript: Library guide.
- Session JSONL as a combat log: Games guide.
- Editor MCP (
lich mcp): Redot guide. Play is still the Godot guide.