# Local Claude Code Chat Dashboard — Architecture

> A local web dashboard with a chat box. You type a request in the browser;
> it runs **real Claude Code** on your machine (your tools, your files, your
> login); the answer streams back into the dashboard. No terminal needed for
> day‑to‑day work, and **no API key / no extra billing** — it reuses the same
> Claude subscription this terminal already uses.

Status: **design draft — for discussion.** Nothing is built yet.

---

## 1. What we're building (in plain words)

A small website that runs **only on your own computer** (`http://localhost`). It looks
like a normal chat app (ChatGPT-style): a list of past conversations on the left, a
chat window in the middle, an input box at the bottom, and a side panel showing any
**files Claude created** ("artifacts" — reports, code, notes).

When you send a message, your computer quietly runs the `claude` command in the
background, lets it do the work (search, think, read/write files, run tools), and
streams the reply back into the page as it's generated.

So: **browser in, Claude Code does the magic, browser out** — without you touching the
terminal.

---

## 2. The one important reality (please read)

There are two things that *sound* the same but aren't:

| What you might picture | What's actually possible |
| --- | --- |
| The words you type in the browser appear **inside this exact terminal window**, and my reply here gets mirrored to the browser. | A browser page **cannot inject keystrokes into a running terminal session.** This window only moves when its own harness feeds it input — there's no open port it's listening on. |

So we **cannot** literally pipe into *this* live REPL. **But** we don't need to. The
dashboard will start its **own** Claude Code runs using the `claude` command — the
**same program, same login, same tools, same "magic"** — just driven by the web server
instead of by you typing.

Functionally this is your exact vision, and it's **better** than this one window:

- It **persists** — close the terminal, the dashboard keeps working.
- It remembers each conversation **across turns** (session memory).
- It can hold **many separate conversations** at once.
- It uses your **existing Claude login** — no API key, no metered token billing.

**This is the core thing to agree on before we build.** If you're good with
"the dashboard runs its own Claude Code, on your login" (rather than mirroring *this*
specific window), everything below follows.

---

## 3. How it works — the big picture

```
┌──────────────────────────────┐
│  Browser  (localhost:5173)   │   ← the dashboard you look at
│  • conversation list         │
│  • chat window (streaming)   │
│  • artifacts / files panel   │
└───────────────┬──────────────┘
                │  HTTP (send message) / stream (receive reply)
                ▼
┌──────────────────────────────┐
│  Local web server (Node.js)  │   ← runs on your PC only
│  • serves the dashboard      │
│  • /api/chat endpoint        │
│  • remembers session IDs     │
│  • watches the workspace     │
└───────────────┬──────────────┘
                │  spawns:  claude -p "<your message>"
                │           --resume <session-id>
                │           --output-format stream-json
                ▼
┌──────────────────────────────┐
│  Claude Code  (the `claude`  │   ← the real agent, YOUR login
│  CLI, headless)              │     full tools: Read/Edit/Bash/Web/…
└───────────────┬──────────────┘
                │  reads & writes files in:
                ▼
┌──────────────────────────────┐
│  Workspace folder            │   ← reports, code, notes = "artifacts"
│  e.g. ./workspace            │     the dashboard lists & shows these
└──────────────────────────────┘
```

---

## 4. Components

### A. Frontend — the dashboard (what you see)
A single web page. Plain and lightweight (no heavyweight framework needed for v1).
- **Conversation sidebar** — start new chat, switch between chats, rename/delete.
- **Chat window** — your messages + Claude's replies, rendered as Markdown, **streaming
  in live** as Claude generates them.
- **Activity indicators** — small "🔧 Reading file…", "🔍 Searching web…", "✏️ Editing
  report.md" chips so you can see what Claude is doing (these come from the tool-use
  events in the stream).
- **Artifacts panel** — lists files in the workspace; click to preview (Markdown, code,
  text) or download.

### B. Backend — the local server (the bridge)
A small **Node.js** program that:
- Serves the dashboard files.
- Exposes **`POST /api/chat`** — receives `{ message, conversationId }`, runs Claude
  Code, and streams the events back.
- **Spawns** `claude -p "<message>" --resume <session-id> --output-format stream-json`
  in the workspace directory, then **parses the newline-delimited JSON events** and
  forwards them to the browser.
- **Tracks sessions** — captures the `session_id` Claude returns and stores it per
  conversation, so the next message **continues with full memory**.
- **Watches the workspace** so the artifacts panel updates when Claude writes files.

### C. Claude Code — the engine
The `claude` CLI you already have, run **headlessly** (`-p` / print mode). It supplies
all the intelligence and tools. Because we **don't** set an API key, it uses your
existing Claude login automatically.

### D. Workspace folder — where artifacts live
A dedicated folder (e.g. `./workspace`) that Claude reads from and writes to. Every
report, code file, or note Claude produces lands here, and the dashboard shows it. This
also **sandboxes** Claude's file activity to one place instead of your whole disk.

### E. Conversation store — memory of the chats
A small local file/db (start with a simple JSON file or SQLite) holding, per
conversation: an id, a title, the Claude `session_id`, and the message history. This is
just so the dashboard can redraw past chats; the *actual* AI memory is handled by
Claude's session resume.

---

## 5. The message flow, step by step

1. You type "research X and write me a summary" and hit send.
2. Browser → `POST /api/chat` with your message + which conversation it belongs to.
3. Server looks up that conversation's `session_id` (if it's an ongoing chat).
4. Server runs:
   ```
   claude -p "research X and write me a summary"
          --resume <session-id>           # omitted for the first message
          --output-format stream-json
          --verbose
          --permission-mode acceptEdits   # so it can write files without a prompt
   ```
   …inside the `./workspace` folder.
5. Claude Code starts emitting JSON events, one per line. The server reads them and
   pushes them to the browser as they arrive:
   - `system / init` → carries the **session_id** (server saves it for next turn).
   - `assistant` / `stream_event` (text deltas) → the reply text, **streamed live**.
   - `tool_use` → "Claude is reading a file / searching the web" → activity chip.
   - `result` → the final answer; turn complete.
6. Browser renders the streaming reply; if Claude wrote `summary.md`, it appears in the
   artifacts panel.
7. Your next message in the same chat reuses the saved `session_id`, so Claude
   **remembers the whole conversation.**

**Event shapes** (from the official headless docs) we'll parse:
```jsonc
{ "type": "system", "subtype": "init", "session_id": "abc123", "data": { "model": "...", "tools": [...] } }
{ "type": "assistant", "message": { "content": [ { "type": "text", "text": "…" } ] } }
{ "type": "tool_use", "tool_name": "Read", "tool_input": { "file_path": "…" } }
{ "type": "stream_event", "event": { "delta": { "type": "text_delta", "text": "…" } } }
{ "type": "result", "result": "the final text" }
```

---

## 6. Tech stack & why

| Layer | Choice | Why |
| --- | --- | --- |
| Engine | **Claude Code CLI (`claude -p`)** | Reuses your **subscription login** (no API key); identical behavior to the terminal; full tools. The Agent SDK is the alternative but in headless/"bare" use it **wants an `ANTHROPIC_API_KEY`** — so the CLI is the better fit for "use my existing login." |
| Backend | **Node.js** (built-in `http` + `child_process`) | `claude` is a Node tool, so Node is already on your machine; spawning a subprocess and streaming is straightforward; zero heavy dependencies for v1. |
| Streaming | **`--output-format stream-json`** → forwarded to the browser as a stream | Gives token-by-token output + tool activity, for a responsive feel. |
| Frontend | **Plain HTML/CSS/JS** for v1 (can upgrade to React later) | Keep it simple and dependency-light; easy to read and change. |
| Storage | **JSON file** (or SQLite) for conversation list | Tiny, local, no setup. |

> This is my recommendation. If you'd prefer **Python** for the backend, or a polished
> **React** UI from the start, say so — both are fine, just different effort.

---

## 7. Permissions & safety (a real decision)

Headless Claude Code can't stop to ask "is it OK to edit this file?" — there's no one at
the terminal to answer. So we choose a **permission posture** up front. Options, least to
most powerful:

| Mode | What Claude can do | Good for |
| --- | --- | --- |
| `plan` | Read & think only; proposes changes, applies nothing | Safest; pure research/Q&A |
| `default` + allowlist | Reads freely; only the tools you list (e.g. `Read,Glob,Grep,WebSearch`) | Research + web, no file writes |
| **`acceptEdits`** *(recommended)* | Read, **write files in the workspace**, plus common file commands; other shell commands still gated | "Make me reports/code/notes" — the artifacts use case |
| `bypassPermissions` | Everything, no checks | Only inside a throwaway container — **not** recommended on your real machine |

Two extra guardrails regardless of mode:
- **Confine Claude to `./workspace`** (run it there, optionally `--add-dir` for anything
  else you explicitly want it to see).
- **Block destructive tools** we don't want (e.g. disallow raw `rm`-style commands).

My default recommendation: **`acceptEdits`, scoped to `./workspace`.** That lets it
research and produce artifacts freely while keeping the rest of your machine off-limits.
We can tighten or loosen this.

---

## 8. Features — MVP vs later

**v1 (MVP) — the core loop:**
- One web page, chat box, **streaming** replies.
- Multi-turn memory within a conversation (session resume).
- New / switch / rename / delete conversations.
- Artifacts panel: list + preview + download files from the workspace.
- Tool-activity chips ("reading…", "searching…").

**Later (nice-to-have):**
- Stop / interrupt a running reply.
- Pick the model per chat (Opus 4.8 / Sonnet 4.6 / Haiku 4.5).
- Upload a file into the workspace from the browser.
- Render Claude-made charts/HTML inline.
- Cost/usage display per message (the JSON includes `total_cost_usd`, `usage`).
- Polished React UI, dark mode, search across chats.

---

## 9. Folder layout (proposed)

```
Claude Chat/
├── ARCHITECTURE.md          ← this file
├── server/                  ← Node.js backend
│   ├── index.js             ← web server + /api/chat
│   ├── claude-runner.js     ← spawns `claude -p`, parses stream-json
│   └── store.js             ← conversation list (JSON/SQLite)
├── public/                  ← the dashboard (frontend)
│   ├── index.html
│   ├── app.js
│   └── styles.css
├── workspace/               ← Claude's sandbox: artifacts land here
└── data/
    └── conversations.json   ← saved chats + session IDs
```

---

## 10. Prerequisites (to verify before building)

- `claude` CLI on your PATH and **logged in** (it is — you're using it now).
- **Node.js** installed (needed to run the server). We'll confirm the version first.
- A free local port (e.g. 5173).

---

## 11. Decisions — confirmed (2026-06-30)

1. **Core approach (§2):** ✅ Dashboard runs its **own** Claude Code processes on the
   user's existing login (not a mirror of this terminal window).
2. **Backend language:** ✅ **Node.js.**
3. **Permission posture (§7):** ✅ **`acceptEdits`, scoped to `./workspace`** — read,
   web search, and file writes inside the workspace; nothing outside it.
4. **UI:** ✅ **Plain HTML/CSS/JS** for v1 (React is a later option).
5. **MVP scope (§8):** v1 list as written (streaming chat, conversation list, per-chat
   memory, artifacts panel, activity chips) — confirm or adjust.

**Next step:** quick prerequisite check (Node version, `claude` availability), then build
the v1 MVP.
