How It Works
The internal architecture of ARK CLI, from terminal UI to AI agent to graph queries.
This page explains the internal architecture of ARK CLI for users who want to understand the system or contribute to the codebase.
Architecture Overview
ARK CLI is built from three main layers:
- Terminal UI: React 19 components rendered to the terminal via
@ai-tui/coreand@opentui/react. - AI Agent: Anthropic Claude orchestrated through the Vercel AI SDK, configured with graph-specific tools.
- Data Layer: DuckDB executing SQL queries against on-disk Parquet files.
Technology Stack
| Component | Technology | Purpose |
|---|---|---|
| Runtime | Bun | Fast JavaScript/TypeScript runtime |
| Language | TypeScript (ESNext) | Type safety and modern syntax |
| Package Manager | pnpm | Dependency management |
| Terminal UI | @ai-tui/core + React 19 | Rich terminal rendering |
| AI Provider | Anthropic Claude | Language model for reasoning |
| AI SDK | Vercel AI SDK | Tool calling, streaming, agent loop |
| Data Engine | DuckDB | In-process SQL queries on Parquet |
| Data Format | Apache Parquet | Columnar graph storage |
| Validation | Zod | Tool input schema validation |
The Agent Loop
When you type a question, the following cycle runs:
- User input is received from the terminal UI.
- The input is sent to Claude via the Vercel AI SDK with the conversation history.
- Claude analyzes the question and decides to call one or more tools.
- Tool calls are executed, running DuckDB SQL queries against Parquet files.
- Tool results are sent back to Claude.
- Claude either makes more tool calls (continuing the loop) or generates a final text response.
- The response is streamed and rendered in the terminal UI.
This cycle repeats for each tool call. A single user question can trigger many iterations.
The tool loop has a maximum of 50 steps per turn. This prevents runaway queries while allowing deep multi-hop reasoning. Most questions resolve in 3-10 tool calls.
One Agent Per Graph
Each knowledge graph gets its own agent instance. When you select a graph, ARK CLI creates an agent with:
- A system prompt that includes the graph's name, description, and metadata, so the agent understands what data it's working with.
- Tools scoped to that graph, where each tool queries only the selected graph's Parquet files.
- Isolated conversation history, so context from one graph doesn't leak into another.
The agent is created lazily on first use via createTransport() in the configuration. This means selecting a graph doesn't incur DuckDB startup cost until you actually send a message.
Lazy Loading
ARK CLI does not load knowledge graphs into memory at startup. The GraphLoader class reads only the lightweight graph.json metadata files during initialization. Actual Parquet data is queried on-demand through DuckDB:
- Startup is fast regardless of how many or how large your graphs are.
- Memory usage scales with query complexity, not graph size.
- Multiple large graphs can coexist on disk without competing for memory.
- A single shared DuckDB instance is lazily created on the first query and reused across all subsequent queries.
Terminal Rendering
ARK CLI uses @ai-tui/core with React 19 and @opentui/react for terminal rendering. Key features:
- Streaming responses. Claude's text output is rendered as it arrives, not after completion.
- Custom tool renderers. Tool results are rendered as structured blocks. For example,
getNodeDetailsdisplays a formatted card with the node's name, type, graph, and properties. - Rich formatting. Markdown, syntax highlighting, and structured data are rendered natively in the terminal.
Tool renderers are decoupled from tool logic via a ToolComponentsMap, allowing each tool to have its own React component for displaying results.
Project Structure
src/
├── index.tsx # Entry point, env config, agent creation
├── prompts.ts # System prompts for the AI agent
├── parquet-tools/
│ ├── index.ts # GraphLoader + tool factory
│ ├── queries.ts # DuckDB SQL query functions
│ ├── tools.ts # Vercel AI SDK tool definitions
│ └── types.ts # TypeScript types
└── tool-renderers/
├── index.ts # Barrel export
└── get-node-details-tool.tsx # Node details TUI componentScripts Reference
| Script | Command | Description |
|---|---|---|
| Dev mode | pnpm cli | Start with Bun hot reload (--watch) |
| Build | pnpm build | Compile to standalone binary via bun build --compile |
| Type check | pnpm check-types | Run TypeScript type checking (tsc --noEmit) |
| Clean | pnpm clean | Remove build artifacts |