Skip to content

Latest commit

 

History

History
1087 lines (857 loc) · 27.3 KB

File metadata and controls

1087 lines (857 loc) · 27.3 KB

Remote Claude Code - Codebase Guide

Table of Contents

  1. Project Overview
  2. Project Structure
  3. Key Components
  4. Custom Hooks
  5. Library Modules
  6. API Routes
  7. Data Flow
  8. Data Models
  9. Testing
  10. Development Guide

Project Overview

Remote Claude Code is a mobile-optimized web interface for interacting with the Claude Agent SDK. It enables users to run Claude Code on their laptop and access it from their phone via a local network.

Tech Stack

  • Framework: Next.js (App Router)
  • UI: React 19 + TypeScript + Tailwind CSS
  • State Management: Zustand
  • Database: SQLite with Drizzle ORM
  • Streaming: Server-Sent Events (SSE)
  • PWA: Service worker for offline capability

Core Features

  • Real-time streaming responses with live tool execution
  • Nested sub-agent tool calls with hierarchical display
  • Session persistence and continuation from Claude Code CLI
  • Message queueing for rapid input during streaming
  • iOS-optimized keyboard handling
  • Task progress tracking with TodoWrite integration
  • Stream reconnection after page reload or network interruption
  • Dark/light theme support

Project Structure

remote-cc/
|-- src/
|   |-- app/                          # Next.js App Router
|   |   |-- api/
|   |   |   |-- filesystem/
|   |   |   |   +-- route.ts          # GET /api/filesystem - Directory listing
|   |   |   |-- sessions/
|   |   |   |   |-- route.ts          # GET /api/sessions - List sessions
|   |   |   |   +-- [id]/
|   |   |   |       |-- route.ts      # GET /api/sessions/:id - Session details
|   |   |   |       |-- message/
|   |   |   |       |   +-- route.ts  # POST /api/sessions/:id/message - Send message
|   |   |   |       +-- stream/
|   |   |   |           |-- status/
|   |   |   |           |   +-- route.ts    # GET - Check stream status
|   |   |   |           +-- reconnect/
|   |   |   |               +-- route.ts    # GET - Reconnect to active stream
|   |   |   +-- debug/
|   |   |       +-- streams/
|   |   |           +-- route.ts      # Debug endpoint for stream inspection
|   |   |-- sessions/
|   |   |   |-- [id]/
|   |   |   |   +-- page.tsx          # Session chat view
|   |   |   +-- new/
|   |   |       +-- page.tsx          # New session page
|   |   |-- settings/
|   |   |   +-- page.tsx              # Settings page
|   |   |-- globals.css               # Global styles with Tailwind
|   |   |-- layout.tsx                # Root layout with providers
|   |   +-- page.tsx                  # Home page (redirects to new chat)
|   |
|   |-- components/                   # React components
|   |   |-- AppShell.tsx              # Layout wrapper with sidebar
|   |   |-- ChatInterface.tsx         # Main chat orchestrator
|   |   |-- ChatMessage.tsx           # Message rendering with markdown
|   |   |-- CodeBlock.tsx             # Syntax-highlighted code blocks
|   |   |-- DirectoryPicker.tsx       # File system navigation
|   |   |-- MobileNav.tsx             # Mobile navigation component
|   |   |-- ServiceWorkerRegistration.tsx  # PWA service worker setup
|   |   |-- Sidebar.tsx               # Session navigation
|   |   |-- ThemeMetaTag.tsx          # Dynamic theme-color meta tag
|   |   |-- ThinkingIndicator.tsx     # Streaming status with stats
|   |   |-- TodoList.tsx              # Task progress display
|   |   +-- ToolCallDisplay.tsx       # Tool call visualization
|   |
|   |-- hooks/                        # Custom React hooks
|   |   |-- useChat.ts                # SSE streaming and state management
|   |   +-- useNotifications.ts       # Browser notifications and haptics
|   |
|   |-- lib/                          # Core business logic
|   |   |-- __mocks__/
|   |   |   +-- claude.ts             # Mock for testing
|   |   |-- claude.ts                 # Claude SDK wrapper
|   |   |-- db.ts                     # Database connection
|   |   |-- sessions.ts               # Session file parsing
|   |   |-- store.ts                  # Zustand state management
|   |   |-- streamRegistry.ts         # SSE stream management
|   |   |-- theme.tsx                 # Theme context provider
|   |   +-- utils.ts                  # Utility functions
|   |
|   +-- __tests__/                    # Test files
|       |-- api/                      # API route tests
|       |-- components/               # Component tests
|       |-- hooks/                    # Hook tests
|       +-- lib/                      # Library tests
|
|-- db/
|   |-- schema.ts                     # Drizzle ORM schema
|   |-- migrations/                   # Database migrations
|   +-- sessions.db                   # SQLite database file
|
|-- public/
|   |-- icons/                        # PWA icons (various sizes)
|   |-- manifest.json                 # PWA manifest
|   |-- sw.js                         # Service worker
|   +-- theme-init.js                 # Theme initialization script
|
|-- scripts/
|   +-- generate-icons.js             # Icon generation utility
|
+-- drizzle.config.ts                 # Drizzle ORM configuration

Key Components

ChatInterface.tsx

Location: /src/components/ChatInterface.tsx

The main chat orchestrator and UI container. Manages the entire chat experience including message display, input handling, and streaming state.

Key Features:

  • Message display with intelligent auto-scroll (respects user scroll position)
  • Message queueing during streaming to allow rapid input
  • iOS keyboard height detection using visualViewport API
  • Safe area insets for notched devices
  • Reconnection banner for interrupted streams

Important State:

const [messageQueue, setMessageQueue] = useState<QueuedMessage[]>([]);
const [workingDir, setWorkingDir] = useState(initialWorkingDir || "");
const [keyboardHeight, setKeyboardHeight] = useState(0);

Key Behaviors:

  • Queues messages when isStreaming is true; processes queue when streaming completes
  • Detects user scroll-up to prevent forced scrolling during streaming
  • Provides vibration and audio feedback on stream completion
  • Filters out TodoWrite tool calls from display (shown separately in TodoList)

Sidebar.tsx

Location: /src/components/Sidebar.tsx

Session navigation with starring and project grouping capabilities.

Key Features:

  • Sessions grouped by project name
  • Star/unstar sessions (persisted to localStorage)
  • Collapsible project groups (state persisted to localStorage)
  • Starred sessions displayed at top in dedicated section
  • New chat button
  • Theme toggle
  • Settings link

Custom Hooks Used:

  • useStarredSessions() - Manages starred session IDs in localStorage
  • useCollapsedGroups() - Manages collapsed project group state

Key Components:

  • SessionItem - Individual session entry with star toggle
  • ProjectGroup - Collapsible group of sessions by project

ChatMessage.tsx

Location: /src/components/ChatMessage.tsx

Message rendering with full markdown support.

Key Features:

  • User messages: Right-aligned blue bubbles
  • Assistant messages: Full-width with markdown parsing
  • Special handling for "Insight" blocks (styled callouts)
  • Session continuation messages (context compaction summaries)
  • Memoized components to prevent unnecessary re-renders

Exported Components:

  • ChatMessage - Historical message display
  • StreamingMessage - Live streaming message with cursor
  • SessionContinuationMessage - Expandable compacted context display
  • MarkdownContent - Markdown parser and renderer
  • isSessionContinuationMessage() - Detection helper function

Markdown Support:

  • Headers (h1, h2, h3)
  • Bold, italic, inline code
  • Links (open in new tab)
  • Bulleted and numbered lists
  • Horizontal rules
  • Code blocks with syntax highlighting

ToolCallDisplay.tsx

Location: /src/components/ToolCallDisplay.tsx

Visualizes tool execution with categorized display patterns.

Tool Categories:

  1. Inline Tools (Read, Glob, Grep, WebSearch)

    • Non-expandable, single-line display
    • Shows: ToolName: parameter
  2. Inline with Output (Bash)

    • Shows command inline
    • Expandable to show output (auto-scrolls)
  3. Expandable Tools (Edit, Write, etc.)

    • Shows input/output when expanded
    • Edit tool displays visual diff
  4. Task Tools (Sub-Agents)

    • Purple color scheme
    • Shows nested child tool calls
    • Expandable "Agent Details" section
    • Displays sub-agent prompt and final response

Key Components:

  • ToolCallDisplay - Routes to appropriate display based on tool name
  • TaskToolDisplay - Specialized display for Task (sub-agent) tools
  • ToolCallList - Renders list of tool calls
  • DiffView - Visual diff for Edit tool

Status Colors:

  • Running: Yellow/amber
  • Complete: Green
  • Error: Red
  • Task running: Purple

TodoList.tsx

Location: /src/components/TodoList.tsx

Displays task progress from TodoWrite tool calls.

Features:

  • Collapsible task list with progress counter
  • Shows current in-progress task when collapsed
  • Status indicators (spinner for in-progress, checkmark for completed)
  • Auto-hides when all tasks completed

Todo Interface:

interface Todo {
  content: string;      // Task description (imperative)
  status: "pending" | "in_progress" | "completed";
  activeForm: string;   // Present tense form shown during execution
}

ThinkingIndicator.tsx

Location: /src/components/ThinkingIndicator.tsx

Shows streaming status with statistics.

Displayed Information:

  • Animated "Thinking..." indicator during streaming
  • "Done" checkmark when complete
  • Elapsed time (minutes:seconds)
  • Token count (input + output breakdown)

State Management:

  • Captures final stats when streaming completes
  • Resets on new stream start
  • Updates elapsed time every second during streaming

DirectoryPicker.tsx

Location: /src/components/DirectoryPicker.tsx

File system navigation for selecting working directories.

Features:

  • Breadcrumb navigation
  • Quick nav buttons (Home, Up, Type Path)
  • Manual path input with keyboard support
  • Directory listing with click-to-navigate
  • Select button to confirm choice

AppShell.tsx

Location: /src/components/AppShell.tsx

Layout wrapper that provides the sidebar and main content area.

Features:

  • Responsive sidebar (persistent on large screens, sliding on mobile)
  • Escape key closes sidebar
  • Handles safe area insets

Custom Hooks

useChat.ts

Location: /src/hooks/useChat.ts

SSE stream parser and state manager. The core hook for chat functionality.

Responsibilities:

  • Parse Server-Sent Events from the API
  • Aggregate text blocks during streaming
  • Track tool call status transitions
  • Manage nested tool calls (Task/sub-agents via parent_tool_use_id)
  • Extract and update TodoWrite data
  • Support stream reconnection after page reload

Key State:

const [streamBlocks, setStreamBlocks] = useState<StreamBlock[]>([]);
const [todos, setTodos] = useState<Todo[]>([]);
const [streamingStats, setStreamingStats] = useState<StreamingStats>({
  startTime: null,
  inputTokens: 0,
  outputTokens: 0,
});
const [isReconnecting, setIsReconnecting] = useState(false);

Returned Values:

{
  sendMessage,        // (prompt, workingDirectory?) => void
  stopGeneration,     // () => void - Abort current stream
  streamBlocks,       // Current streaming content
  todos,              // Current task list
  currentSessionId,   // Session ID (may update mid-stream for new sessions)
  streamingStats,     // Time and token counts
  isReconnecting,     // True during reconnection
  reconnectProgress,  // { total, replayed } during replay
  checkAndReconnect,  // Manual reconnect trigger
}

Stream Message Handling:

  • system with subtype: "init": Captures session ID
  • assistant: Processes text and tool_use blocks
  • user with tool_result: Marks tools complete (especially sub-agents)
  • tool_result: SDK format for tool completion
  • done: Final cleanup and session refresh

Nested Tool Call Logic:

  • Uses parent_tool_use_id from messages to identify sub-agent tools
  • Finds parent Task by toolUseId and appends to childToolCalls
  • Falls back to top-level if parent not found

useNotifications.ts

Location: /src/hooks/useNotifications.ts

Browser notifications and haptic feedback.

Features:

  • Browser notification support (via service worker)
  • Vibration API support (Android)
  • Web Audio API beep (iOS fallback)
  • Combined notifyComplete() for stream completion

Returned Values:

{
  permission,         // NotificationPermission state
  isSupported,        // Browser support check
  requestPermission,  // Request notification permission
  showNotification,   // Send notification via service worker
  vibrate,            // Trigger vibration pattern
  playSound,          // Play audio beep
  notifyComplete,     // Combined notification on completion
}

Library Modules

store.ts

Location: /src/lib/store.ts

Zustand state management for global application state.

State Shape:

interface SessionsState {
  sessions: Session[];
  currentSession: Session | null;
  messages: Message[];
  isLoading: boolean;
  isStreaming: boolean;
  error: string | null;
}

Actions:

  • setSessions, setCurrentSession, setMessages, addMessage
  • setLoading, setStreaming, setError
  • clearForNewChat() - Reset for new chat
  • fetchSessions() - Load session list from API
  • fetchSession(id) - Load single session with messages

Design Pattern:

  • Optimistic UI: Add temp messages immediately
  • Replaced by real data after fetchSession completes

sessions.ts

Location: /src/lib/sessions.ts

Session file parsing from Claude Code CLI data.

Key Functions:

  • listSessions() - List all sessions from ~/.claude/projects/
  • getSessionMessages(sessionId, filePath?) - Parse JSONL session file
  • getSessionProjectPath(sessionId) - Get working directory for session
  • findSessionFilePath(sessionId) - Direct file lookup without listing all
  • invalidateSessionCache() - Clear cached session list

Caching:

  • Session list cached with 5-second TTL
  • Metadata cache (cwd, firstMessage) persisted by file mtime
  • Uses globalThis for cache persistence across Next.js module re-evaluation

Session File Format:

  • JSONL files in ~/.claude/projects/{encoded-path}/{session-id}.jsonl
  • Each line is a JSON message with type, content, timestamp, etc.

streamRegistry.ts

Location: /src/lib/streamRegistry.ts

Server-side module for tracking active streams and enabling reconnection.

Key Functions:

  • createStream(sessionId) - Initialize new stream buffer
  • addMessage(sessionId, message) - Buffer message and notify subscribers
  • completeStream(sessionId, error?) - Mark stream as done
  • getStream(sessionId) - Get stream state
  • getMessagesAfter(sessionId, sequence) - Get missed messages for replay
  • subscribe(sessionId, callback) - Subscribe to live updates
  • migrateStream(tempId, realId) - Update stream key when session ID assigned
  • deleteStream(sessionId) - Remove stream after client catches up

Configuration:

  • MAX_BUFFER_SIZE: 1000 messages
  • TTL_RUNNING: 1 hour
  • TTL_COMPLETED: 30 minutes
  • CLEANUP_INTERVAL: 5 minutes

Stream State:

interface ActiveStream {
  sessionId: string;
  status: "running" | "completed" | "error";
  startedAt: number;
  completedAt?: number;
  buffer: BufferedMessage[];
  lastSequence: number;
  subscribers: Set<callback>;
  error?: string;
}

theme.tsx

Location: /src/lib/theme.tsx

Dark/light theme provider using React Context.

Features:

  • Persists theme choice to localStorage
  • Manages dark class on document element
  • Provides toggleTheme() function
  • Safe defaults for SSR

claude.ts

Location: /src/lib/claude.ts

Claude Agent SDK wrapper.

Key Function:

async function* runClaudeQuery(prompt, options): AsyncGenerator<SDKMessage>

Options:

interface ClaudeQueryOptions {
  sessionId?: string;      // Resume existing session
  workingDir?: string;     // Working directory (default: cwd)
  model?: string;          // Model (default: claude-sonnet-4-5)
  maxBudgetUsd?: number;   // Budget limit (default: 10)
}

Allowed Tools:

  • Read, Edit, Write, Bash
  • Glob, Grep
  • WebSearch, WebFetch
  • Task (sub-agents)
  • TodoWrite (task tracking)

Permission Mode: acceptEdits


db.ts

Location: /src/lib/db.ts

Database connection using better-sqlite3 and Drizzle ORM.

Features:

  • Lazy initialization via Proxy
  • WAL mode for better concurrent access
  • Path: db/sessions.db

Schema (in /db/schema.ts):

  • sessions - Session metadata (id, title, workingDirectory, timestamps, cost, status)
  • messages - Message history (sessionId, role, content, tool data, timestamp)
  • sessionMetadata - Extended session info (model, token counts, errors)

API Routes

GET /api/sessions

Location: /src/app/api/sessions/route.ts

Lists all available sessions from ~/.claude/projects/.

Query Parameters:

  • limit (default: 50) - Number of sessions to return
  • offset (default: 0) - Pagination offset

Response:

{
  "sessions": [
    {
      "id": "session_uuid",
      "title": "First user message...",
      "projectPath": "/path/to/project",
      "projectName": "project-name",
      "createdAt": "2024-01-01T00:00:00Z",
      "updatedAt": "2024-01-01T00:00:00Z"
    }
  ],
  "total": 10
}

GET /api/sessions/[id]

Location: /src/app/api/sessions/[id]/route.ts

Get session details and message history.

Response:

{
  "session": {
    "id": "session_uuid",
    "title": "Session title",
    "projectPath": "/path/to/project",
    "projectName": "project-name",
    "workingDirectory": "/path/to/project",
    "createdAt": "2024-01-01T00:00:00Z",
    "updatedAt": "2024-01-01T00:00:00Z"
  },
  "messages": [
    {
      "id": "msg_uuid",
      "sessionId": "session_uuid",
      "role": "user",
      "content": "Hello",
      "timestamp": "2024-01-01T00:00:00Z"
    },
    {
      "id": "msg_uuid",
      "sessionId": "session_uuid",
      "role": "assistant",
      "content": "Hi there!",
      "toolCalls": [...],
      "timestamp": "2024-01-01T00:00:01Z"
    }
  ]
}

POST /api/sessions/[id]/message

Location: /src/app/api/sessions/[id]/message/route.ts

Send a message and stream the response.

Request Body:

{
  "prompt": "User message text",
  "workingDirectory": "/path/to/project"
}

Response: Server-Sent Events stream

SSE Message Types:

data: {"type":"system","subtype":"init","session_id":"session_123","sequence":1}

data: {"type":"assistant","message":{"content":[{"type":"text","text":"Hello"}]},"sequence":2}

data: {"type":"assistant","message":{"content":[{"type":"tool_use","name":"Read","input":{...},"id":"tool_123"}]},"sequence":3}

data: {"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"tool_123","content":"..."}]},"sequence":4}

data: {"type":"done","sessionId":"session_123","sequence":5}

GET /api/sessions/[id]/stream/status

Location: /src/app/api/sessions/[id]/stream/status/route.ts

Check if a session has an active stream.

Response:

{
  "active": true,
  "status": "running",
  "lastSequence": 42,
  "startedAt": 1704067200000,
  "bufferedMessages": 42
}

GET /api/sessions/[id]/stream/reconnect

Location: /src/app/api/sessions/[id]/stream/reconnect/route.ts

Reconnect to an active or recently completed stream.

Query Parameters:

  • lastSequence - Last received sequence number

Response: Server-Sent Events stream with:

  1. replay_start - Indicates start of missed messages
  2. Missed messages (with sequence numbers)
  3. replay_end - Indicates end of replay
  4. Live messages (if stream still running)
  5. reconnect_done - Final status

GET /api/filesystem

Location: /src/app/api/filesystem/route.ts

List directories for the directory picker.

Query Parameters:

  • path - Directory path (default: home directory, supports ~)

Response:

{
  "currentPath": "/Users/user/projects",
  "parentPath": "/Users/user",
  "directories": [
    { "name": "project-a", "path": "/Users/user/projects/project-a" }
  ],
  "homePath": "/Users/user"
}

Data Flow

Message Flow (User Input to Response)

1. User types message in ChatInterface
   |
2. handleSubmit() called
   |
   +-- If streaming: Queue message for later
   |
   +-- If not streaming:
       |
       v
3. sendMessage() in useChat hook
   |
   +-- Add optimistic user message to store
   |
   +-- POST to /api/sessions/[id]/message
   |
   v
4. API route:
   |
   +-- Create stream in streamRegistry
   |
   +-- Call runClaudeQuery() from claude.ts
   |
   +-- For each SDK message:
       |
       +-- Add to buffer with sequence number
       |
       +-- Stream to client via SSE
   |
   v
5. useChat hook parses SSE:
   |
   +-- "system" init: Capture session ID
   |
   +-- "assistant" text: Append to currentTextBlock
   |
   +-- "assistant" tool_use: Create tool call entry
   |
   +-- "user" tool_result: Mark tool complete
   |
   +-- "done": Fetch fresh session data
   |
   v
6. ChatInterface renders:
   |
   +-- Historical messages from store
   |
   +-- Streaming content from streamBlocks
   |
   +-- Tool calls from ToolCallDisplay
   |
   +-- Todos from TodoList

Reconnection Flow

1. Page reload or network interruption
   |
   v
2. useChat mounts, calls checkAndReconnect()
   |
   +-- GET /api/sessions/[id]/stream/status
   |
   +-- If active or has missed messages:
       |
       v
3. reconnectToStream()
   |
   +-- GET /api/sessions/[id]/stream/reconnect?lastSequence=N
   |
   v
4. Server streams:
   |
   +-- replay_start
   +-- Missed messages from buffer
   +-- replay_end
   +-- Live messages (if still running)
   +-- reconnect_done
   |
   v
5. useChat processes messages normally
   |
   +-- Updates reconnectProgress during replay
   +-- Clears isReconnecting after replay_end

Data Models

Session

interface Session {
  id: string;
  title?: string;
  firstMessage?: string;
  projectPath?: string;
  projectName?: string;
  workingDirectory?: string;
  createdAt: string;
  updatedAt: string;
}

Message

interface Message {
  id: string;
  sessionId: string;
  role: "user" | "assistant" | "system";
  content: string;
  toolCalls?: ToolCall[];
  timestamp: string;
}

ToolCall

interface ToolCall {
  name: string;
  input: unknown;
  output?: unknown;
  status: "running" | "complete" | "error";
  toolUseId?: string;
  childToolCalls?: ToolCall[];  // For Task (sub-agent) tools
}

StreamBlock

interface StreamBlock {
  type: "text" | "tool";
  content?: string;        // For text blocks
  toolCall?: ToolCallData; // For tool blocks
}

Todo

interface Todo {
  content: string;      // Imperative form: "Run tests"
  status: "pending" | "in_progress" | "completed";
  activeForm: string;   // Present tense: "Running tests"
}

Database Schema

sessions table:

  • id (text, PK) - Claude SDK session_id
  • title (text)
  • working_directory (text)
  • created_at (integer, timestamp)
  • updated_at (integer, timestamp)
  • total_cost_usd (real)
  • status (text) - "active" | "archived"

messages table:

  • id (integer, auto-increment, PK)
  • session_id (text, FK to sessions)
  • role (text) - user, assistant, system, tool
  • content (text) - JSON for complex content
  • tool_name (text)
  • tool_input (text) - JSON
  • tool_output (text) - JSON
  • timestamp (integer, timestamp)

session_metadata table:

  • session_id (text, PK, FK to sessions)
  • model (text)
  • total_input_tokens (integer)
  • total_output_tokens (integer)
  • last_error (text)

Testing

Test Structure

Tests are organized in /src/__tests__/ mirroring the source structure:

src/__tests__/
|-- api/
|   |-- filesystem.test.ts
|   |-- message.test.ts
|   |-- sessions.test.ts
|   |-- sessions-id.test.ts
|   |-- stream-reconnect.test.ts
|   +-- stream-status.test.ts
|
|-- components/
|   |-- AppShell.test.tsx
|   |-- ChatInterface.test.tsx
|   |-- ChatMessage.test.tsx
|   |-- CodeBlock.test.tsx
|   |-- DirectoryPicker.test.tsx
|   |-- MobileNav.test.tsx
|   |-- Sidebar.test.tsx
|   |-- ThinkingIndicator.test.tsx
|   |-- TodoList.test.tsx
|   +-- ToolCallDisplay.test.tsx
|
|-- hooks/
|   |-- useChat.test.ts
|   +-- useNotifications.test.ts
|
+-- lib/
    |-- sessions.test.ts
    |-- store.test.ts
    |-- streamRegistry.test.ts
    |-- theme.test.tsx
    +-- utils.test.ts

Running Tests

# Run all tests
npm test

# Run tests in watch mode
npm test -- --watch

# Run specific test file
npm test -- src/__tests__/hooks/useChat.test.ts

# Run with coverage
npm test -- --coverage

Mock Files

  • /src/lib/__mocks__/claude.ts - Mock for Claude SDK in tests

Development Guide

Setup

npm install
npm run dev

Build

npm run build
npm start

Access from Phone

# Find your local IP
ifconfig | grep "inet "

# Start on all interfaces
npm run dev -- -H 0.0.0.0

# Access from phone: http://YOUR_IP:3000

Database Commands

# View database
sqlite3 db/sessions.db

# Run migrations
npm run db:push

# Generate migrations
npm run db:generate

Key Files to Modify

Add new tool display type:

  1. Update INLINE_TOOLS or INLINE_WITH_OUTPUT_TOOLS in ToolCallDisplay.tsx
  2. Add case to getInlineDisplay() function
  3. Optionally create specialized display component

Change streaming behavior:

  1. Modify handleStreamMessageRef in useChat.ts
  2. Update message type handling in switch statement

Add new API endpoint:

  1. Create route file in /src/app/api/
  2. Export GET/POST handler functions
  3. Update types in /src/lib/store.ts if needed

Modify UI layout:

  1. Edit ChatInterface.tsx for chat layout
  2. Update AppShell.tsx for shell structure
  3. Modify globals.css for theme colors

Security Notes

  • No authentication: Designed for trusted local networks only
  • File system access: Backend can read/write files in working directory
  • Tool permissions: "acceptEdits" mode allows automatic file modifications
  • Budget limits: Configured $10 max spend per query

PWA Installation

  1. Open app in mobile browser
  2. Add to Home Screen
  3. App installs as standalone PWA
  4. Service worker enables offline capability

Troubleshooting

Streaming stops working:

  • Check SSE connection in Network tab
  • Verify server logs for errors
  • Check if stream timed out (1 hour TTL)

Messages not persisting:

  • Verify session files exist in ~/.claude/projects/
  • Check database connection
  • Ensure write permissions on session directory

Tools not displaying:

  • Check tool name matches allowed tools list
  • Verify tool call format in stream messages
  • Check console for parsing errors

iOS keyboard issues:

  • Ensure viewport meta tag is set
  • Check visualViewport API support
  • Verify safe area insets in CSS

Reconnection not working:

  • Check if stream is still in registry (30-min TTL after completion)
  • Verify lastSequence is being stored in sessionStorage
  • Check server logs for reconnect endpoint errors

Last Updated: January 2025