# mouadse/pi-lite architecture

> Architecture diagram of the GitHub repository mouadse/pi-lite, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-30). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/mouadse/pi-lite
- Repository: https://github.com/mouadse/pi-lite

## Overview

1. **Overall shape**
   `pi-lite` is a single-process C++20 CLI agent. The center of the design is a small agent loop that alternates between model calls and tool execution. There is no server, no UI framework, and no plugin system; everything runs in one binary built with CMake.

2. **Entry point and orchestration**
   `src/main.cpp` is the top-level entry. It wires together configuration, input handling, the agent loop, status output, and optional self-test mode. `src/agent.cpp` / `agent.hpp` appear to hold the main conversation orchestration: prompt assembly, message history, tool-call handling, context compaction, and the “call model → execute tools → feed results back” cycle described in the README.

3. **Configuration boundary**
   `src/config.*` is the configuration layer. It likely merges CLI flags, real environment variables, and workspace `.env` values, then exposes runtime options such as model name, base URL, memory settings, shell permissions, and context-budget controls. This is an important boundary because it determines what external services the agent can talk to and which local capabilities are enabled.

4. **Model client subsystem**
   `src/llm_client.*` is the OpenAI-compatible transport layer. It uses libcurl to call chat completions endpoints and consume SSE streaming responses. Architecturally, this module is the only component that knows about HTTP, streaming deltas, and provider-specific API details; everything else works with normalized assistant/tool messages.

5. **Conversation data model**
   `src/messages.hpp` likely defines the shared message and tool-call structures used across the agent, tools, and memory layers. This is the glue that keeps user messages, assistant responses, tool requests, and tool results in a uniform format. The architecture depends on this shared model to move data cleanly between the LLM client and the tool executor.

6. **Tooling subsystem**
   `src/tool.*` and `src/tools.*` form the model-facing tool registry and dispatcher. The README shows the exposed tools: file reads/writes, directory listing, grep, patch application, optional bash, and memory operations. This subsystem is a major architectural boundary because it translates model tool calls into constrained local actions and returns structured results back into the agent loop.

7. **Workspace safety and secret protection**
   The file tools are intentionally sandboxed to the workspace. Secret-like files such as `.env`, credentials, and SSH keys are blocked from reads, writes, grep, and patching. That safety policy is part of the tool boundary, not the model layer, which keeps the LLM from directly accessing sensitive local state even when it asks for files.

8. **Long-term memory subsystem**
   `src/memory.*` implements optional local memory backed by SQLite. The README indicates a fully local store with hashed embeddings and in-process cosine search, plus save/list/search/update/delete operations. Architecturally, memory is a separate persistence layer that can be queried before model calls and can also be written explicitly by tools or automatically through the opt-in capture flow.

9. **Automatic memory capture path**
   Memory search and memory injection happen on the hot path when memory is enabled: relevant memories are retrieved and inserted into the system prompt as a short “Relevant long-term memory” section. Auto-capture is a second, separate path that extracts durable facts after interactions, filters noisy or unsafe content, deduplicates locally, and persists only the curated facts. That separation matters because memory is not just a log; it is a gated knowledge store.

10. **Interactive terminal I/O**
    `src/line_reader.*` and `src/status_indicator.*` handle the human-facing terminal experience. The line reader likely manages prompt editing, autocomplete, history, and shortcuts like `/help` or `!command`. The status indicator handles “thinking”, elapsed-time, and tool progress badges, with stdout/stderr separation so streamed assistant output stays clean when piped.

11. **Rendering layer**
    `src/markdown_renderer.*` is a lightweight terminal presentation layer for assistant output. It converts common Markdown shapes into ANSI-styled terminal text: headings, lists, emphasis, inline code, fenced code blocks, blockquotes, and simple tables. This sits between streamed model text and terminal output, and it degrades gracefully when output is not an interactive terminal or when `NO_COLOR=1` is set.

12. **Shared utilities and support code**
    `src/util.*` probably hosts cross-cutting helpers such as path handling, string utilities, token/cost estimation helpers, and other small functions needed across modules. `src/linked_list.hpp` suggests a small internal container or queue structure, likely used for message history or compaction bookkeeping where a lightweight custom structure is preferred over heavier abstractions.

13. **Runtime data flow**
    The core data flow is: user input enters through stdin or the line reader, the agent builds a prompt from system instructions plus conversation history, optional memory is retrieved and injected, the LLM streams a response, tool calls are accumulated, tools execute locally, tool results are appended as messages, and the loop repeats until the assistant finishes without more tool calls. This is the main architecture to draw: user terminal ↔ agent ↔ LLM client ↔ tools/memory, with streamed text and tool results flowing back into the same conversation state.

14. **External dependencies and persistence**
    The project depends on libcurl for HTTP streaming, SQLite for memory persistence, `nlohmann-json` for structured payloads, and the `patch` command for unified-diff application. The binary also reads `.env` for configuration but keeps that separate from file-tool access. Those dependencies matter architecturally because they define the only persistent store, the network boundary, and the patch-based code-editing capability.

Main entry points: [`src/agent.cpp`](https://github.com/mouadse/pi-lite/blob/main/src/agent.cpp) (Agent loop), [`src/tools.cpp`](https://github.com/mouadse/pi-lite/blob/main/src/tools.cpp) (Tool registry), [`src/main.cpp`](https://github.com/mouadse/pi-lite/blob/main/src/main.cpp) (main), [`src/config.cpp`](https://github.com/mouadse/pi-lite/blob/main/src/config.cpp) (Config)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_g1["Runtime"]
  node_n1(("main<br/>entrypoint<br/>[main.cpp]"))
  node_n2["Agent loop<br/>orchestrator<br/>[agent.cpp]"]
  node_n3["Config<br/>[config.cpp]"]
end

subgraph group_g2["Model"]
  node_n4["Messages<br/>conversation model<br/>[messages.hpp]"]
  node_n5["LLM client<br/>http stream client<br/>[llm_client.cpp]"]
end

subgraph group_g3["Tools"]
  node_n6["Tool registry<br/>tool dispatch<br/>[tools.cpp]"]
  node_n7["Tool API<br/>tool interface<br/>[tool.cpp]"]
  node_n8["File tools<br/>sandboxed fs"]
  node_n9["Shell tool<br/>command runner"]
end

subgraph group_g4["Memory"]
  node_n10[("Memory core<br/>sqlite memory<br/>[memory.cpp]")]
  node_n11["Memory capture<br/>fact extraction"]
end

subgraph group_g5["Terminal"]
  node_n12["Line reader<br/>terminal input<br/>[line_reader.cpp]"]
  node_n13(("Status<br/>progress ui"))
  node_n14["Markdown render<br/>terminal render"]
end

subgraph group_g6["Support"]
  node_n15["Utilities<br/>shared helpers<br/>[util.cpp]"]
  node_n16{{"List queue<br/>internal container<br/>[linked_list.hpp]"}}
end

node_n1 -->|"loads config"| node_n3
node_n1 -->|"reads input"| node_n12
node_n1 -->|"starts loop"| node_n2
node_n2 -->|"uses messages"| node_n4
node_n2 -->|"calls model"| node_n5
node_n2 -->|"executes tools"| node_n6
node_n2 -->|"queries memory"| node_n10
node_n2 -.->|"captures facts"| node_n11
node_n5 -->|"emits replies"| node_n4
node_n6 -->|"implements"| node_n7
node_n6 -->|"file ops"| node_n8
node_n6 -->|"shell ops"| node_n9
node_n8 -.->|"enforces policy"| node_n3
node_n10 -->|"uses settings"| node_n3
node_n12 -->|"updates status"| node_n13
node_n2 -->|"renders output"| node_n14
node_n14 -.->|"uses helpers"| node_n15
node_n2 -.->|"uses helpers"| node_n15
node_n2 -.->|"tracks history"| node_n16

click node_n1 "https://github.com/mouadse/pi-lite/blob/main/src/main.cpp"
click node_n2 "https://github.com/mouadse/pi-lite/blob/main/src/agent.cpp"
click node_n3 "https://github.com/mouadse/pi-lite/blob/main/src/config.cpp"
click node_n4 "https://github.com/mouadse/pi-lite/blob/main/src/messages.hpp"
click node_n5 "https://github.com/mouadse/pi-lite/blob/main/src/llm_client.cpp"
click node_n6 "https://github.com/mouadse/pi-lite/blob/main/src/tools.cpp"
click node_n7 "https://github.com/mouadse/pi-lite/blob/main/src/tool.cpp"
click node_n10 "https://github.com/mouadse/pi-lite/blob/main/src/memory.cpp"
click node_n12 "https://github.com/mouadse/pi-lite/blob/main/src/line_reader.cpp"
click node_n13 "https://github.com/mouadse/pi-lite/blob/main/src/status_indicator.cpp"
click node_n14 "https://github.com/mouadse/pi-lite/blob/main/src/markdown_renderer.cpp"
click node_n15 "https://github.com/mouadse/pi-lite/blob/main/src/util.cpp"
click node_n16 "https://github.com/mouadse/pi-lite/blob/main/src/linked_list.hpp"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_n1,node_n2,node_n3 toneBlue
class node_n4,node_n5 toneAmber
class node_n6,node_n7,node_n8,node_n9 toneMint
class node_n10,node_n11 toneRose
class node_n12,node_n13,node_n14 toneIndigo
class node_n15,node_n16 toneTeal
```

## Components

### Runtime

Top-level binary and agent orchestration.

- [main](https://github.com/mouadse/pi-lite/blob/main/src/main.cpp): entrypoint · Program entry that wires configuration, agent loop, terminal I/O, and self-test mode. · `src/main.cpp`
- [Agent loop](https://github.com/mouadse/pi-lite/blob/main/src/agent.cpp): orchestrator · Coordinates prompt assembly, model calls, tool execution, and context compaction. · `src/agent.cpp`
- [Config](https://github.com/mouadse/pi-lite/blob/main/src/config.cpp): runtime config · Merges CLI, environment, and workspace settings into runtime options. · `src/config.cpp`

### Model

LLM transport and conversation state.

- [Messages](https://github.com/mouadse/pi-lite/blob/main/src/messages.hpp): conversation model · Shared message, tool-call, and tool-result structures passed across subsystems. · `src/messages.hpp`
- [LLM client](https://github.com/mouadse/pi-lite/blob/main/src/llm_client.cpp): http stream client · OpenAI-compatible chat/completions transport with streaming response handling. · `src/llm_client.cpp`

### Tools

Local action execution and safety boundaries.

- [Tool registry](https://github.com/mouadse/pi-lite/blob/main/src/tools.cpp): tool dispatch · Defines model-facing tools and routes tool calls to local executors. · `src/tools.cpp`
- [Tool API](https://github.com/mouadse/pi-lite/blob/main/src/tool.cpp): tool interface · Shared tool definitions and execution contracts. · `src/tool.cpp`
- **File tools**: sandboxed fs · Workspace-scoped file and directory actions with secret-file protections.
- **Shell tool**: command runner · Optional constrained bash execution path exposed to the agent.

### Memory

Optional persistent long-term memory.

- [Memory core](https://github.com/mouadse/pi-lite/blob/main/src/memory.cpp): sqlite memory · Local persistent memory store with search and CRUD operations. · `src/memory.cpp`
- **Memory capture**: fact extraction · Filters and persists durable facts from interactions, including deduplication and opt-in capture.

### Terminal

Interactive input and output presentation.

- [Line reader](https://github.com/mouadse/pi-lite/blob/main/src/line_reader.cpp): terminal input · Interactive prompt, history, shortcuts, and command entry handling. · `src/line_reader.cpp`
- [Status](https://github.com/mouadse/pi-lite/blob/main/src/status_indicator.cpp): progress ui · Thinking, elapsed-time, and tool-progress display for terminal sessions. · `src/status_indicator.cpp`
- [Markdown render](https://github.com/mouadse/pi-lite/blob/main/src/markdown_renderer.cpp): terminal render · Formats assistant output into ANSI-styled terminal text. · `src/markdown_renderer.cpp`

### Support

Shared helpers and small internal data structures.

- [Utilities](https://github.com/mouadse/pi-lite/blob/main/src/util.cpp): shared helpers · Cross-cutting helpers for strings, paths, budgeting, and other shared logic. · `src/util.cpp`
- [List queue](https://github.com/mouadse/pi-lite/blob/main/src/linked_list.hpp): internal container · Lightweight linked-list structure used for internal history or bookkeeping. · `src/linked_list.hpp`

## Connections

- main → Config: loads config · Startup reads runtime settings before constructing the agent.
- main → Line reader: reads input · Main process routes user interaction through the terminal reader.
- main → Agent loop: starts loop · Entry point hands control to the agent orchestrator.
- Agent loop → Messages: uses messages · Conversation state is represented with shared message structures.
- Agent loop → LLM client: calls model · Agent sends prompts and receives streamed assistant output.
- Agent loop → Tool registry: executes tools · Tool calls from the model are dispatched locally.
- Agent loop → Memory core: queries memory · Relevant memories are retrieved and injected when enabled.
- Agent loop → Memory capture: captures facts · Post-interaction durable facts can be extracted and stored.
- LLM client → Messages: emits replies · Normalized assistant and tool messages are produced from streamed responses.
- Tool registry → Tool API: implements · Registry uses the shared tool interface and execution contracts.
- Tool registry → File tools: file ops · Filesystem tools perform sandboxed workspace reads and writes.
- Tool registry → Shell tool: shell ops · Optional command execution is exposed through the tool layer.
- File tools → Config: enforces policy · Workspace and secret-access rules are derived from runtime configuration.
- Memory core → Config: uses settings · Memory behavior depends on enabled features and budget limits.
- Line reader → Status: updates status · Interactive input coordinates with progress display.
- Agent loop → Markdown render: renders output · Assistant text is formatted for terminal display.
- Markdown render → Utilities: uses helpers · Rendering relies on shared formatting and utility helpers.
- Agent loop → Utilities: uses helpers · Agent orchestration depends on shared utility functions.
- Agent loop → List queue: tracks history · Internal bookkeeping may use a lightweight linked structure.
