A minimalist terminal AI coding agent. No IDE, no electron, no noise — just your terminal, an OpenAI-compatible model, and a sharp set of tools.
_ _
| | ___ ___ ___ __| | ___
| |/ _ \/ __/ _ \ / _` |/ _ \
| | __/ (_| (_) | (_| | __/
|_|\___|\___\___/ \__,_|\___|
by wowi42
╭────────────────────────── lecode — fix-auth ───────────────────────────╮
│ ✓ session fix-auth — new session │
│ ✓ config ~/.config/lecode/config.toml + .lecode/config.toml │
│ ✓ provider openrouter · https://openrouter.ai/api/v1 │
│ model deepseek/deepseek-v4-flash · key from env │
│ ✓ models 327 fetched live from the provider │
│ ✓ prompt minimal │
│ ✓ context AGENTS.md · docs/AGENTS.md │
│ – skills none │
│ ✓ agents primaries: build, plan · custom: researcher │
│ ✓ memory long-term 2.4 KB injected │
│ ✓ tools 19 tools │
│ ✓ permissions mode yolo · custom rules │
│ – hooks none configured │
│ – pierre off │
│ ✓ lsp enabled │
│ ! mcp exa (no EXA_API_KEY) │
╰─────────────────── ~/github.com/you/your-project ─────────────────────╯
Every interactive start shows you exactly what was loaded — config files, provider, prompt, project context, skills, agents, memory, permissions, hooks, LSP, MCP — before the chat opens.
- Tiny harness, big ecosystem. The default system prompt is under 300
tokens; project context comes from your
AGENTS.mdfiles, skills, and memory — not from a bloated prompt. - OpenRouter, or anything OpenRouter-compatible. OpenRouter is the
first-class preset (live catalog, pricing, caching); other gateways —
Ollama, LM Studio, llama.cpp, corporate proxies — plug in with one
--base-urlflag or a[custom_providers]entry. Keyless local endpoints supported. The model catalog is fetched live from the provider at startup (when the fetch fails the catalog is simply empty — models lose their pricing/modality annotations; nothing cached on disk). - Real permissions, not vibes. Two modes (
yoloby default,readonlyvia--safewhen you want a look-but-don't-touch agent), glob + regex rules, last-match-wins, unbypassable denies, doom-loop detection, and lifecycle hooks that can narrow — never widen — any decision. - Money is a metric. Live token + cost totals in the statusline, and a
tokens in/out · costsummary every time a chat ends. - Inspectable by design. Sessions are append-only JSONL you can grep; sessions are always named; every session is resumable, undoable, rewindable, exportable.
-
Python 3.12+
-
Three external binaries on
PATH(verified at startup; no auto-download, no fallback):binary powers install fdfind_filesbrew install fd·apt install fd-find·cargo install fd-findrggrepbrew install ripgrep·apt install ripgrep·cargo install ripgreprtkbash output compaction see https://github.com/rtk-ai/rtk
Targets macOS and Linux; Windows is best-effort.
uv tool install --force git+https://github.com/KalvadTech/lecodelecode is distributed from this repository only — it is not on PyPI.
lecode --setup # import from pi/opencode or answer 4 questions
export OPENROUTER_API_KEY=sk-or-... # or keep the key in config.toml
cd your-project
lecode # name the session, then ask for somethingdir: lecode · commit: 974015a · branch: main · diff: ±1 +4 -4
model: deepseek/deepseek-v4-flash · cost: $0.0062 · ctx: ▓▓░░░ 36.0k/200.0k 18%
session: fix-auth · agent: build · in: 4.1k · out: 0.9k · ⠼
One fixed statusline, every element labelled: directory · commit · branch ·
diff / model · cost · context meter / session · agent · tokens · state
(a reasoning-level override shows on the model line when /thinking is set).
No configuration needed.
Useful things to type:
/help all slash commands, grouped
/welcome key bindings cheat-sheet
/tutor permissions explain one feature
/tasks background tasks, live
/doctor health-check the install
!make test run a shell command, see the output
!!pytest -x run it AND feed the output to the model
@src/auth.py attach a file (images/PDF/audio too)
.plan refactor this run with a persona
Tab cycle agents: build ⇄ plan
Alt-Enter steer the agent mid-turn
lecode -p "write a haiku about this repo" # one prompt, then exit
git diff | lecode -p "review this diff" # a bare -p reads stdin
lecode --loop plan.md --loop-cmd "make test" # iterate until the plan is done
lecode --chain "redesign the parser" # brainstorm→plan→code→reviewThe final answer goes to stdout; a tokens: <in> in / <out> out · cost: $X.XXXX summary goes to stderr, so scripts can pipe the answer cleanly.
Exit codes: 0 done · 1 error · 2 startup (missing deps, bad flags,
non-tty --setup) · 3 max turns / max loop iterations / context overflow.
- Permissions — two modes (
yoloallows everything,readonlyallows read-class tools only; defaultyolo), per-tool glob/regex rules, session allow-always grants, per-agent overlays./permissions,/mode,/toggle. - Sessions — always named, append-only JSONL, scoped to the folder they
were created in (resume never crosses directories),
/new/resume/undo/redo/rewind/retry/compact/handoff/rename, searchable picker with delete, HTML export (/export), secret-gist sharing (/share), re-import (/import). Approaching the context window, the runner compacts automatically (summary + recent tail;[compaction]tunes the trigger, buffer and overflow policy). - Custom agents — markdown files in
~/.config/lecode/agents/or.lecode/agents/with their own model, temperature, prompt and permission overlay. Tab cycles primaries;@agentor thetasktool runs subagents. - Skills —
SKILL.mdpacks in.agents/skills/(project) or~/.agents/skills/(global), discoverable by the model, optionally registered as slash commands. - Memory — persistent per-project markdown: long-term
MEMORY.md(auto-injected, 32 KB cap), daily logs, scratchpad, named notes./memoryto inspect; the agent hasmemory_*tools. - Hooks — shell commands on 15 lifecycle events (
PreToolUse,PostToolUse,UserPromptSubmit,Stop, …) that return verdicts; they can only narrow permissions.--hooks-testdry-runs the pipeline. - Pierre mode — when enabled, a second model reviews every finished
task: it compares your request with the agent's result and tells you
plainly whether it delivered.
/pierre on|off|model. - Structured questions — the
ask_usertool lets the agent ask 1-4 multiple-choice questions mid-turn instead of guessing; you answer inline with the keyboard (1-4to pick,enterto confirm a multi-select,escto dismiss). Headless, loop, chain, and subagent runs get a "use your best judgment" result instead of a prompt. - Notifications — sound (afplay/paplay/aplay, terminal bell fallback) and
desktop notifications (osascript / notify-send) on turn finish, error, and
approval-needed.
/notifications on|off; channels and per-event toggles in[notifications]. - MCP — stdio, streamable-HTTP, and SSE servers, with optional OAuth 2.1
(
auth = "oauth", browser flow, tokens under<config_dir>/mcp-auth/;/mcp authto authorize,/mcp login|logoutto manage). Exa web search is preconfigured (needsEXA_API_KEY); context7 is one flag away. - LSP — diagnostics from real language servers appended to
write/editresults; fail-open, never blocks. - Worktrees —
--worktree <name>or/worktreefor isolated branches,/wt-merge//wt-exitwith conflict detection. - Multimodal —
/add image.pngor@file.pdf; capability-checked against the model. - Background tasks —
bashandtaskacceptrun_in_backgroundand return a task id immediately; the agent inspects them with thetasks_list/tasks_output/tasks_wait/tasks_stoptools, you watch them with/tasks, and completions land in the feed and in the next turn. Remaining tasks are stopped (SIGTERM, then SIGKILL) when the session exits. - Prompts and personas — the default system prompt is minimal (<300
tokens);
style = "rich"opts into a detailed prompt. 16 named personas (.review …,.plan …) overlay one turn,/promptswitches style,/editsysopens the system prompt in$EDITOR. - Reasoning levels —
/thinkingsets the reasoning effort; collapsible thinking blocks in the feed; the statusline shows the active override. - Pickers and polish —
/opens a slash-command dropdown,@and.open fuzzy file/agent/persona pickers — all in themed panels; Tab path completion; queued prompts while the agent runs, Alt-Enter to steer mid-turn; OSC 8 hyperlinks;/copy(OSC 52 / pbcopy / xclip). - Status signals — start/stop/git-conflict events over a Unix socket, for external status bars and scripts.
- Doctor —
/doctorhealth-checks the install: external binaries, config, provider connectivity, MCP servers, memory, hooks, telemetry. - Telemetry (opt-in) — Sentry/GlitchTip error reports and OpenTelemetry
metrics (turns, tokens, cost, tool calls) via
[telemetry]; needs thetelemetryextra, fail-open by design.
Global config: ~/.config/lecode/config.toml (override the directory with
LECODE_CONFIG_DIR); a project-local .lecode/config.toml deep-merges over
it. TOML is the only accepted format. The setup wizard writes the
file with 0600 permissions because it can hold an API key — prefer the
OPENROUTER_API_KEY / OPENAI_API_KEY env vars to keep secrets out of it.
Full reference: docs/configuration.md.
- docs/configuration.md — every config section and default
- docs/hooks.md — hook events, envelope, verdict protocol
- docs/memory.md — the memory store
- docs/agents-and-skills.md — custom agents and skills
- docs/mcp.md — MCP servers, Exa, context7
- docs/build-plan.md — the full product definition
uv sync
uv run python -m pytest # 1200+ tests
uv run ruff check && uv run ruff format --check
prek install # git hooks: ruff on commit, pytest on pushReleases are automated with release-please
and follow semver, driven by conventional commits: fix: → patch, feat: →
minor, feat!: (or any !) → major. Every push to main updates a release
PR that bumps pyproject.toml and CHANGELOG.md; merging that PR creates the
vX.Y.Z tag and the GitHub Release, with the built dists (wheel + sdist)
attached to the release. Nothing is published to PyPI.
MIT