Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.clideck.dev/llms.txt

Use this file to discover all available pages before exploring further.

CliDeck is a local Node.js application with a browser-based frontend. This page explains the key architectural decisions for anyone who wants to understand what’s happening behind the scenes.

System Overview

┌─────────────────────────────────────────────────┐
│                   Browser                        │
│  ┌─────────────┐  ┌──────────┐  ┌────────────┐  │
│  │  Sidebar     │  │ Terminal  │  │  Settings   │  │
│  │  (sessions,  │  │ (xterm.js)│  │  Panel      │  │
│  │   search)    │  │           │  │             │  │
│  └──────┬───────┘  └─────┬────┘  └──────┬──────┘  │
│         └────────────┬───┘──────────────┘          │
│                      │ WebSocket                    │
└──────────────────────┼─────────────────────────────┘

┌──────────────────────┼─────────────────────────────┐
│              Node.js Server (:4000)                 │
│                      │                              │
│  ┌───────────────────┴────────────────────────┐    │
│  │           WebSocket Handler                 │    │
│  │  (message routing, config, telemetry setup) │    │
│  └──────┬─────────┬──────────┬────────────────┘    │
│         │         │          │                      │
│  ┌──────┴───┐ ┌───┴────┐ ┌──┴──────────┐          │
│  │ Sessions  │ │ Config │ │ Transcript   │          │
│  │ (PTY mgmt)│ │ (CRUD) │ │ (JSONL store)│          │
│  └──────┬───┘ └────────┘ └─────────────┘          │
│         │                                           │
│  ┌──────┴──────────────────────────────────┐       │
│  │         PTY Processes (node-pty)         │       │
│  │  ┌─────────┐ ┌─────────┐ ┌──────────┐  │       │
│  │  │ claude   │ │ codex   │ │ gemini   │  │       │
│  │  └─────────┘ └─────────┘ └──────────┘  │       │
│  └─────────────────────────────────────────┘       │
│                                                     │
│  ┌──────────────┐  ┌────────────────┐              │
│  │  OTLP Receiver│  │ OpenCode Bridge│              │
│  │  POST /v1/logs│  │ POST /opencode │              │
│  └──────────────┘  └────────────────┘              │
│                                                     │
│  ┌──────────────────────────────────────────┐       │
│  │         Plugin System                     │       │
│  │  ~/.clideck/plugins/ (Trim Clip,           │       │
│  │   Voice Input, Autopilot, custom plugins) │       │
│  └──────────────────────────────────────────┘       │
└─────────────────────────────────────────────────────┘

Key Components

PTY Sessions (node-pty)

Each terminal session spawns a real pseudo-terminal using node-pty. The agent process runs inside this PTY and behaves exactly as if it were in a normal terminal. CliDeck doesn’t inject any middleware between you and the agent. Terminal output is buffered (up to 200KB per session) and sent to the browser over WebSocket. The browser renders it using xterm.js.

WebSocket Communication

A single WebSocket connection carries all messages between the browser and server. Messages are JSON-encoded with a type field for routing. The server broadcasts to all connected clients — you can have multiple browser tabs open.

OTLP Telemetry Receiver

The server runs an HTTP endpoint at POST /v1/logs that accepts OpenTelemetry log data in JSON format. When an agent is launched, CliDeck sets environment variables that point the agent’s OTLP exporter to this local endpoint. The receiver:
  1. Parses the OTLP resource logs
  2. Matches the log to the correct session (via clideck.session_id resource attribute)
  3. Extracts the agent’s session ID from log record attributes
  4. Determines working/idle status from event types (per-agent logic)
  5. Detects if telemetry is properly configured (triggers setup toast if not)

OpenCode Plugin Bridge

OpenCode uses a different integration path. A JavaScript plugin inside OpenCode sends HTTP events to POST /opencode-events. The bridge maps these events to the correct CliDeck session by matching the working directory.

Activity Monitor

A 1-second polling loop measures I/O rates for each session:
  • Bytes in/out since last poll
  • Burst duration — how long the current output burst has been going
  • Resets after 2 seconds of silence
For Shell sessions and custom agents, these stats are the source of truth for working/idle status. For Claude Code, Codex, and Gemini CLI, status is driven by telemetry events instead — the activity monitor serves only as a timing reference (e.g., confirming output silence after an API response).

Plugin System

Plugins live in ~/.clideck/plugins/. Each plugin is a folder with an index.js (backend) and optional client.js (frontend), clideck-plugin.json (manifest), and public/ (static assets). On startup, CliDeck seeds bundled plugins (Trim Clip, Voice Input, Autopilot) into the plugins directory if they don’t already exist. Then it loads all plugin folders, calls their init() with a sandboxed API, and wires up their hooks (input/output/status). Plugins communicate with the browser through namespaced WebSocket messages. The plugin API provides:
  • Session hooks — observe output, transform input, react to status changes
  • Frontend messaging — bidirectional communication between backend and browser
  • Toolbar actions — add buttons to the terminal toolbar
  • Settings — user-configurable settings with type validation, persisted in config.json
  • Lifecycle — shutdown handlers for cleanup
Plugin errors are isolated — one plugin can’t crash CliDeck or affect other plugins.

Transcript Store

Every session’s I/O is recorded as plain-text JSONL in ~/.clideck/transcripts/. ANSI codes are stripped, short lines filtered out. The transcript is used for sidebar search — a cache is built on startup and updated incrementally.

Data Flow: Agent Status

Telemetry-driven agents (Claude Code, Codex, Gemini CLI):
Agent emits telemetry → POST /v1/logs → OTLP receiver
                                         ├→ Matches event to session
                                         ├→ Determines working or idle from event type
                                         └→ Broadcasts session.status → browser updates indicator
Shell and custom agents (I/O fallback):
Agent outputs text → PTY → node-pty onData
                           ├→ WebSocket 'output' → browser renders in xterm.js
                           ├→ Activity tracker counts bytes
                           └→ Transcript records stripped text

Activity poll (1s) → broadcasts stats → browser computes working/idle
                                         ├→ Updates status indicator
                                         └→ Triggers notification (if idle transition)

Data Flow: Session Resume

Agent starts → telemetry logs flow to /v1/logs
             → OTLP receiver extracts session ID
             → stored as session.sessionToken

CliDeck shuts down → SIGTERM/SIGINT handler
                   → saves sessions to sessions.json
                   → kills PTY processes

CliDeck starts → loads sessions.json
              → shows in "Previous Sessions" sidebar
              → user clicks resume
              → spawns new PTY with resume command + saved session ID

Startup Detection

When CliDeck starts, it scans for existing agent configurations on disk:
  • Codex: checks ~/.codex/config.toml for an [otel] section pointing to localhost:4000
  • Gemini CLI: checks ~/.gemini/settings.json for a telemetry object with the local endpoint
  • OpenCode: checks if ~/.config/opencode/plugins/clideck-bridge.js exists
  • Claude Code: checks ~/.claude/settings.json for CliDeck lifecycle hooks
If a configuration is found, the integration is marked as enabled in the UI automatically — no manual setup needed. This means if you’ve already configured an agent in a previous session, CliDeck picks it up on restart.

Local Only

CliDeck runs entirely on your machine. There’s no cloud service, no account, no external API calls. By default, the Node.js server binds to 127.0.0.1:4000 — not accessible from other machines. Use --host 0.0.0.0 to bind to all interfaces if you need LAN or VPN access. If the Node.js server stops, all PTY processes stop too. This is intentional — CliDeck manages terminal sessions locally.