# Agent CLI Architecture

## Overview

The Agent CLI is a conversational interface that combines the **Loom** (context tree) with **Bash** (tool execution) to create an interactive agent REPL. Every interaction—user messages, tool executions, and assistant responses—is captured in the context tree, enabling branching, replay, and exploration of alternate execution paths.

## Core Concept

The context tree captures the full execution trace:

```
└── (root)
    └── User: List the files in this directory
        └── [bash:session-1/cmd-1]     ← external handle
            └── Assistant: I can see the following files...
                └── User: Delete the tmp folder
                    └── [bash:session-1/cmd-2]
                        └── Assistant: Done, I've removed tmp
                            └── User: Actually, undo that
                                └── [bash:session-1/cmd-3]  ← mkdir tmp
```

Each node is either:
- **Text node**: User message or assistant response
- **External node**: Handle pointing to tool output (bash, file, API, etc.)

## Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│                        Agent CLI (Haskell)                       │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                      REPL Loop                             │  │
│  │  1. Read user input                                        │  │
│  │  2. Add user message to tree                               │  │
│  │  3. Send context to LLM                                    │  │
│  │  4. Process LLM response:                                  │  │
│  │     - Tool calls → Execute via hub → Add handles to tree   │  │
│  │     - Text → Add assistant message to tree                 │  │
│  │  5. Display response                                       │  │
│  │  6. Repeat                                                 │  │
│  └───────────────────────────────────────────────────────────┘  │
│                              │                                   │
│                              ▼                                   │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                   Session State                            │  │
│  │  - current_tree: TreeId                                    │  │
│  │  - current_node: NodeId (position in tree)                 │  │
│  │  - bash_session: SessionId                                 │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘
                               │
                               │ WebSocket RPC
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                          Hub (Rust)                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐              │
│  │    Loom     │  │    Bash     │  │   Health    │              │
│  │  (context)  │  │  (execute)  │  │             │              │
│  └─────────────┘  └─────────────┘  └─────────────┘              │
└─────────────────────────────────────────────────────────────────┘
```

## Commands

### Conversation Management

| Command | Description |
|---------|-------------|
| `cognition new [name]` | Start new conversation |
| `cognition list` | List all conversations |
| `cognition use <tree>` | Select/resume conversation |
| `cognition delete <tree>` | Archive conversation |
| `cognition rename <tree> <name>` | Rename conversation |

### Interactive Mode

| Command | Description |
|---------|-------------|
| `cognition chat` | Enter agent REPL |
| `cognition chat <tree>` | Resume conversation in REPL |

### REPL Slash Commands

| Command | Description |
|---------|-------------|
| `/tree` | Render current conversation tree |
| `/fork` | Create branch from current position |
| `/back [n]` | Go back n messages (default 1) |
| `/branches` | Show branches at current node |
| `/switch <node>` | Jump to specific node |
| `/context` | Show path from root to current |
| `/leaves` | Show all leaf nodes (conversation endpoints) |
| `/export` | Export current branch as text |
| `/quit` | Exit REPL |

### Direct Commands (non-interactive)

| Command | Description |
|---------|-------------|
| `cognition show <tree>` | Render tree |
| `cognition add <tree> "message"` | Add message to current branch |
| `cognition exec <tree> "command"` | Execute bash and add to tree |
| `cognition context <tree> [node]` | Show context path |

## Data Flow

### Adding a User Message

```
User types: "list files"
     │
     ▼
CLI calls: loom.node_create_text(tree, current_node, "User: list files")
     │
     ▼
Returns: new_node_id
     │
     ▼
CLI updates: current_node = new_node_id
```

### Executing a Tool

```
LLM requests: bash("ls -la")
     │
     ▼
CLI calls: bash.execute("ls -la")
     │
     ▼
Bash streams: stdout, stderr, exit_code
     │
     ▼
CLI stores output (or streams to external storage)
     │
     ▼
CLI calls: loom.node_create_external(tree, current_node, Handle{
    source: "bash",
    source_version: "1.0.0",
    identifier: "session-123/cmd-456",
    metadata: {exit_code: 0, command: "ls -la"}
})
     │
     ▼
Returns: new_node_id
     │
     ▼
CLI updates: current_node = new_node_id
```

### Building Context for LLM

```
CLI calls: loom.context_get_path(tree, current_node)
     │
     ▼
Returns: [Node] from root to current
     │
     ▼
For each node:
  - Text node → Include content directly
  - External node → Resolve handle:
      - bash handle → Fetch stored output
      - file handle → Read file content
      - etc.
     │
     ▼
Assemble into LLM prompt
```

## Handle Resolution

Handles are pointers to external data. Resolution depends on the source:

| Source | Resolution |
|--------|------------|
| `bash` | Fetch from bash session storage |
| `file` | Read file from path |
| `http` | Fetch URL |
| `postgres` | Query database |

### Future: Hub Extensions

Handle resolution could be a hub-level capability. A "render" extension would:
1. Get tree from loom
2. For each external node, call the appropriate plugin to resolve
3. Return fully-rendered context

```
┌─────────────────────────────────────────────────┐
│                      Hub                         │
│  ┌───────────────────────────────────────────┐  │
│  │            Hub Extensions                  │  │
│  │  ┌─────────────────────────────────────┐  │  │
│  │  │  context_render (requires: loom)    │  │  │
│  │  │   1. call loom.tree_get             │  │  │
│  │  │   2. for each external handle:      │  │  │
│  │  │      - call {source}.resolve        │  │  │
│  │  │   3. return rendered context        │  │  │
│  │  └─────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────┘  │
│                                                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │   loom   │  │   bash   │  │  health  │      │
│  └──────────┘  └──────────┘  └──────────┘      │
└─────────────────────────────────────────────────┘
```

## Session State

The CLI maintains session state locally:

```haskell
data Session = Session
  { sessionTree    :: TreeId           -- Current conversation
  , sessionNode    :: NodeId           -- Position in tree
  , sessionBash    :: Maybe BashSession -- Active bash session
  , sessionConfig  :: Config           -- User preferences
  }
```

State persists across CLI invocations via a local file (e.g., `~/.cognition/session.json`).

## Example Session

```
$ cognition chat
Starting new conversation...
Tree: a1b2c3d4-...

> list the python files in src/

[bash] ls src/*.py
src/main.py
src/utils.py
src/config.py

I found 3 Python files in the src directory:
- main.py
- utils.py
- config.py

> show me the contents of main.py

[bash] cat src/main.py
#!/usr/bin/env python3
import utils
...

Here's the contents of main.py:
...

> /tree
└── (root)
    └── User: list the python files in src/
        └── [bash:ls src/*.py]
            └── Assistant: I found 3 Python files...
                └── User: show me the contents of main.py
                    └── [bash:cat src/main.py]
                        └── Assistant: Here's the contents...

> /fork
Created branch at current position.

> actually, show me utils.py instead

[bash] cat src/utils.py
...

> /branches
Current node has 2 branches:
  1. [current] → "actually, show me utils.py instead"
  2. → (previous branch continues from here)

> /quit
Session saved. Resume with: cognition chat a1b2c3d4
```

## Implementation Plan

### Phase 1: Basic CLI
- [ ] Session state management
- [ ] `new`, `list`, `use` commands
- [ ] `show` (tree render)
- [ ] Basic REPL loop (no LLM)

### Phase 2: Tool Integration
- [ ] Bash execution with handle storage
- [ ] Context building from tree path
- [ ] Handle resolution

### Phase 3: LLM Integration
- [ ] Connect to LLM provider
- [ ] Tool calling protocol
- [ ] Streaming responses

### Phase 4: Advanced Navigation
- [ ] `/fork`, `/back`, `/switch`
- [ ] Branch comparison
- [ ] Export/import

## File Structure

```
frontend-hs/
├── app/
│   └── Main.hs              -- CLI entry point
├── src/
│   └── Cognition/
│       ├── Rpc.hs           -- Hub connection
│       ├── Loom.hs          -- Loom API
│       ├── Bash.hs          -- Bash API
│       ├── Cli/
│       │   ├── Session.hs   -- Session state
│       │   ├── Commands.hs  -- Command handlers
│       │   ├── Repl.hs      -- REPL loop
│       │   └── Render.hs    -- Output formatting
│       └── Agent/
│           ├── Context.hs   -- Context building
│           ├── Tools.hs     -- Tool execution
│           └── Llm.hs       -- LLM integration
└── test/
    └── LoomTest.hs          -- Integration tests
```
