Skip to main content

Workspace Terminal

The Workspace Terminal is an Electron desktop feature that opens a dedicated window for implementing tasks with Claude Code. It provides a side-by-side view of task details and an embedded terminal, connected to a real PTY process.

Overview

When you click the terminal icon on a task's action bar, a new workspace window opens:

  • Left panel: Task details with auto-refresh (detects note updates from the ud CLI)
  • Right panel: xterm.js terminal running Claude Code with ud CLI skill context

The terminal uses ud prompt to inject the ud CLI skill instructions into Claude Code via --append-system-prompt, then passes the task initialization instruction as the user prompt. This means users don't need the /init-task skill file installed — it works out of the box.

This feature is only available in the Electron desktop app.

Quick Start

  1. Open the Electron desktop app
  2. Navigate to a board's settings (gear icon) and set the Project Directory
  3. Open any task on that board
  4. Click the terminal icon in the task actions bar
  5. A workspace window opens with Claude Code ready to implement the task

Board Configuration

Each kanban board stores workspace settings in its metadata. Configure these in the board settings drawer (click the gear icon on the board header).

Project Directory

The working directory for workspace terminals. When set, all workspace terminals for tasks on this board will start in this directory.

Example: Set to /Users/me/projects/my-app and Claude Code will run with that as its working directory, giving it access to your project files.

If not set, the terminal defaults to your HOME directory.

Tmux Session

Optional. When configured, the Claude Code command is wrapped in a named tmux session:

tmux new-session -As <session-name> 'claude --dangerously-skip-permissions --append-system-prompt "$(ud prompt)" "Initialize and implement task <task-id>..."'

Behavior:

  • If the session already exists: attaches to it
  • If the session doesn't exist: creates it and runs Claude Code
  • Closing the workspace window does NOT kill the tmux session — it continues in the background
  • Reopening the workspace reattaches to the running session

This is useful for long-running implementations where you want the Claude Code session to persist even if you close the window.

Configuration Resolution Chain

Workspace options are resolved from the board that a task belongs to:

task → findTaskCurrentBoard(task, boards) → board.metadata.projectDir → terminal cwd
task → findTaskCurrentBoard(task, boards) → board.metadata.tmuxSession → tmux session name

Fallback: If no board configuration is found, the terminal uses HOME as the working directory and runs without tmux.

The task-to-board mapping uses findTaskCurrentBoard(), which checks which board's column queries match the task.

Architecture

Components

ComponentLocationRole
workspace-manager.jsElectron main processSpawns BrowserWindow + PTY per task
preload/index.jsElectron preloadExposes workspaceApi via contextBridge
workspace.tsVite appTyped API wrapper for renderer
TaskActions.tsxVite appButton + board config resolution
Workspace pageVite appSplit layout with xterm.js terminal

Data Flow

TaskActions (click)
→ resolve board metadata (projectDir, tmuxSession)
→ openWorkspace(taskId, { cwd, tmuxSession })
→ IPC invoke 'workspace:open'
→ workspace-manager creates BrowserWindow + spawns PTY
→ PTY sends claude command on first shell prompt
→ PTY output → IPC → xterm.js renderer
→ User keyboard input → IPC → PTY stdin

IPC Channels

ChannelDirectionPurpose
workspace:openrenderer → mainOpen workspace window
workspace:closerenderer → mainClose workspace window
workspace:pty-datamain → rendererPTY stdout data
workspace:pty-inputrenderer → mainUser keyboard input
workspace:pty-resizerenderer → mainTerminal resize events
workspace:pty-exitmain → rendererPTY process exited
workspace:closedmain → rendererWindow was closed

Window Management

  • One window per task: Opening the same task again focuses the existing window
  • Position persistence: Window bounds saved to ~/.undercontrol/workspace-positions.json
  • Cascade positioning: New windows are offset by 30px from the last one
  • Cleanup on close: PTY process is killed when window closes

Terminal Details

Shell Startup

The workspace spawns a non-login shell (fast startup) with an augmented environment:

  • TERM=xterm-256color for full color support
  • COLORTERM=truecolor for 24-bit color
  • PATH extended with common tool locations (/usr/local/bin, /opt/homebrew/bin, ~/.local/bin, etc.)

Command Execution

The claude command is sent to the shell after the first prompt appears (detected via first PTY data event), ensuring the shell is ready before executing:

# Without tmux:
claude --dangerously-skip-permissions --append-system-prompt "$(ud prompt)" \
"Initialize and implement task <task-id>. Start by running: ud describe task <task-id>"

# With tmux session configured:
tmux new-session -As <session-name> 'claude --dangerously-skip-permissions \
--append-system-prompt "$(ud prompt)" "Initialize and implement task <task-id>..."'

The ud prompt command outputs the full ud CLI skill instructions, which are appended to Claude's system prompt via --append-system-prompt. This eliminates the need for users to install the /init-task skill file.

After Claude Code exits, the shell remains alive so you can run additional commands.

Troubleshooting

Terminal shows wrong directory

Problem: The terminal starts in HOME instead of the project directory.

Solution: Check that the board has a Project Directory configured in its settings. The task must be on the board (matched by column queries). After changing board settings, you may need to restart the Electron app for main process changes to take effect.

node-pty errors on startup

Problem: Error like "NODE_MODULE_VERSION mismatch" in the console.

Solution: Run npx electron-rebuild in the ud-electron-vite directory to rebuild native modules for the current Electron version. This is needed after Electron or Node.js version changes.

Tmux session not persisting

Problem: Closing the workspace window kills the Claude Code session.

Solution: Make sure you have the Tmux Session field configured in the board settings. Without it, the PTY process is killed directly when the window closes. With tmux, only the tmux client detaches — the session continues in the background.

Window opens but terminal is blank

Problem: The workspace window opens but the terminal panel shows nothing.

Solution: This can happen if node-pty failed to load. Check the Electron main process console for [WorkspaceManager] Failed to load node-pty errors. Run npx electron-rebuild to fix native module issues.