Architecture¶
This section is for people who want to understand letscode — and especially for those who want to extend it. Each page commits to contracts that should drift less than the code.
-
Event-driven async generator, parallel tool execution, cancellation, retries, the event sequence per turn.
-
The five extension types, the ten lifecycle hooks, the stable-surface contract, the versioning rule.
Layers¶
letscode is a three-layer Python application. Each layer is independently usable.
flowchart TD
subgraph "Frontend layer"
Basic[Basic TUI<br/>prompt_toolkit + rich]
Future[Plugin frontends<br/>Textual / web / RPC]
end
subgraph "Agent loop layer"
Loop[agent_loop<br/>AsyncIterator<Event>]
State[AgentState<br/>messages, tools, queues]
Hooks[Lifecycle hooks<br/>before/after_tool_call<br/>transform_context<br/>convert_to_llm<br/>render_custom_message<br/>system_prompt<br/>on_event]
end
subgraph "LLM client layer"
Client[LLMClient<br/>openai.AsyncOpenAI wrapper]
end
Basic --> Loop
Future -.->|same contract| Loop
Loop --> Hooks
Loop --> State
Loop --> Client
Client --> Provider[(OpenAI Chat Completions<br/>endpoint)]
Plugins[(Pluggy + entry points<br/>tools · commands · skills · frontends · hooks)] -.extend.-> Loop
Plugins -.extend.-> Basic
The frontend can be the basic terminal one or any plugin-supplied alternative. The agent loop is provider-agnostic by design — the LLM client is the only layer that knows about OpenAI's wire format.
Functional core / imperative shell¶
Side effects (LLM I/O, file I/O, subprocess) live at the edges. The agent loop is pure-ish: it consumes messages and tools, produces events. State mutation happens in controlled places (the loop's own structure, the steering / follow-up queues).
This shape makes the loop testable end-to-end without ever calling a real LLM — see tests/_helpers.py for the FakeLLMClient that drives the same event stream from canned chunks.
Where the contracts live¶
- Type signatures for messages, events, tools, hooks →
src/letscode/agent/*.py.02-design.mdis the design rationale. - Stable surface for plugins → extension model §4.
- CLI flags and env vars → configuration.
- Session JSONL format → sessions.
If a contract here turns out wrong, update the doc and the code in the same change. That's the rule.