Goal: A Tauri app that opens, shows a single terminal pane, and you can type in it.
- 1.1 — Scaffold Tauri v2 project with React + TypeScript + Vite frontend
- 1.2 — Add
portable-ptyto Rust backend, implementpty_spawn,pty_write,pty_resizeTauri commands - 1.3 — Frontend: mount single xterm.js instance, connect to backend via Tauri Channel for output streaming
- 1.4 — Wire bidirectional data: keystrokes → invoke('pty_write'), PTY output → Channel.onmessage → xterm.js
- 1.5 — Implement terminal resize (FitAddon + ResizeObserver → invoke('pty_resize'))
- 1.6 — Load Canvas addon (default), try WebGL with catch-and-fallback (WebKitGTK has known WebGL bugs)
- 1.7 — Basic window chrome: titlebar, min/max/close
- Launch app, get a working bash shell in the terminal
- Resize window, terminal reflows correctly
htop,vim,lessall render correctly
Goal: Split terminals horizontally and vertically, navigate between them.
- 2.1 — Install react-resizable-panels, implement recursive
PaneTreecomponent - 2.2 — Tauri commands:
pty_spawnreturns asurface_id, manage multiple PTYs - 2.3 — Split right (Ctrl+D): add leaf to current pane's parent as horizontal split
- 2.4 — Split down (Ctrl+Shift+D): add leaf as vertical split
- 2.5 — Pane focus management: Alt+Arrow to navigate, visual focus indicator (border highlight)
- 2.6 — Close pane (Ctrl+W): kill PTY, remove from tree, rebalance layout
- 2.7 — Canvas renderer on all panes (WebGL optional/experimental, try-catch per pane)
- Split into 4 panes (2x2), each running independent shell
- Navigate between panes with Alt+Arrow
- Close a pane, remaining panes fill the space
- No WebGL context errors with 6+ panes
Goal: Left sidebar listing workspaces, each workspace is a named group of panes.
- 3.1 — Sidebar component: list of workspaces, click to switch
- 3.2 — Workspace state management (Zustand): create, switch, close, rename
- 3.3 — New workspace (Ctrl+N): spawns fresh pane, user names it
- 3.4 — Workspace metadata display: name, current git branch (read via
git rev-parse), working directory - 3.5 — Workspace status indicator: colored dot (idle=gray, running=green, waiting=yellow, error=red)
- 3.6 — Close workspace (Ctrl+Shift+W): kill all PTYs in workspace, confirm if multiple
- 3.7 — Ctrl+1..9 to jump to workspace by position
- 3.8 — Sidebar resizable (drag handle)
- Create 3 workspaces, switch between them, each preserves its own pane layout
- Sidebar shows branch name and working directory per workspace
- Close a workspace, its PTYs are killed
Goal: Each workspace optionally backed by an isolated git worktree.
- 4.1 — Add
git2crate to backend, implement worktree create/list/remove - 4.2 —
forktty new <name>: creates worktree at.worktrees/<name>, creates branch, spawns workspace with cwd in worktree - 4.3 —
forktty merge [name]: merge worktree branch into current branch of main checkout - 4.4 —
forktty rm [name]: remove worktree + delete branch + close workspace - 4.5 — Setup hook support: if
.forktty/setupexists in repo, run it after worktree creation - 4.6 — Teardown hook: if
.forktty/teardownexists, run before worktree removal - 4.7 — Worktree layout config: nested (
.worktrees/), sibling, outer-nested - 4.8 — Sidebar shows worktree status (clean/dirty/conflicts)
- Creating a worktree workspace via socket API opens workspace inside it
- Work in worktree, commit, then merge via socket API merges into main
- Removing worktree via socket API cleans up everything
- Setup hook runs
npm install(or equivalent) on worktree creation
Goal: Know when an agent needs your attention without watching every pane.
- 5.1 — Output scanner in Rust: intercept PTY output, parse OSC 133 sequences (A/B/C/D)
- 5.2 — Pattern matcher: regex scan last terminal line for Claude Code prompt patterns
- 5.3 — Idle detector: timer resets on each PTY output, fires after threshold (config field reserved, logic not yet implemented)
- 5.4 — Notification engine: when trigger fires and workspace is unfocused, create notification
- 5.5 — In-app: blue dot on sidebar workspace entry, unread count badge
- 5.6 — Desktop notification via notify-rust (XDG/D-Bus)
- 5.7 — Notification panel (Ctrl+Shift+I): list of all notifications, click to jump to workspace
- 5.8 — Jump to latest unread (Ctrl+Shift+U)
- 5.9 — Mark as read when workspace is focused
- 5.10 — Custom notification command support (config.toml
notification_command)
- Start Claude Code in workspace 1, switch to workspace 2
- When Claude Code shows prompt waiting for input, blue dot appears on workspace 1
- Desktop notification pops up
- Click notification → workspace 1 is focused
Goal: Scriptable control from outside the app.
- 6.1 — Unix domain socket server in Rust (tokio), JSON-RPC protocol
- 6.2 — Implement MVP methods: system.ping, workspace., surface., notification.*
- 6.3 — Set env vars in spawned shells:
FORKTTY_WORKSPACE_ID,FORKTTY_SURFACE_ID,FORKTTY_SOCKET_PATH
- Scripts can orchestrate multiple agents via Unix socket JSON-RPC
- Environment variables are set in spawned shells for socket discovery
Goal: Look good, respect user's existing Ghostty config.
- 7.1 — Ghostty config parser: read
~/.config/ghostty/config, extract colors/font/theme - 7.2 — Ghostty theme file parser: read
~/.config/ghostty/themes/<name> - 7.3 — Map Ghostty palette to xterm.js ITheme
- 7.4 — Config file:
~/.config/forktty/config.tomlwith TOML parser (toml crate) - 7.5 — Settings UI (Ctrl+,): appearance, notifications, shell, worktree layout
- 7.6 — Dark/light mode support, follow system preference (CSS has minimal prefers-color-scheme fallback, no user toggle)
- 7.7 — Sidebar theming (respect background/foreground from theme)
- User with Ghostty Catppuccin theme installed: ForkTTY automatically picks up same colors
- Change font size in config → terminals update
- Settings panel allows changing notification preferences without editing TOML
Goal: Stable enough for daily use.
- 8.1 — Session persistence: save workspace layout + restore on restart (no scrollback, just structure)
- 8.2 — Command palette (Ctrl+Shift+P): fuzzy search all commands
- 8.3 — Find in terminal (Ctrl+F) using xterm.js SearchAddon
- 8.4 — Copy mode (Shift+click select, Ctrl+Shift+C to copy)
- 8.5 — Proper error handling: PTY spawn failure, git errors, socket errors
- 8.6 — Logging: structured logs to
~/.local/share/forktty/logs/ - 8.7 — Package as .deb and AppImage via Tauri bundler
- 8.8 — README with install instructions, screenshots, usage guide
- 8.9 — License: switch project to AGPL-3.0
- Install from .deb on Debian 13
- Use daily for 1 week with Claude Code agents
- No crashes, no memory leaks, notifications work reliably
Goal: Match cmux functionality with additional notification and UI features.
- 9.1 — OSC 9/99/777 notification parsing in output scanner
- 9.2 — Notification ring: blue glowing border on panes with unread notifications
- 9.3 — Sidebar notification preview: latest notification text inline
- 9.4 — Window title badge: unread count in document.title
- 9.5 — Auto-reorder: move workspace to top of sidebar on notification
- 9.6 —
forktty read-screen: dump terminal buffer via socket API + CLI - 9.7 — Focus flash: brief blue flash on pane focus change
- 9.8 — Workspace drag-and-drop reorder in sidebar
- 9.9 — System tray icon with unread count tooltip
echo -e '\033]9;Hello\007'triggers notification with blue pane ring- Drag workspace entries in sidebar to reorder
- Tray icon shows unread count, click opens window
surface.read_screensocket method returns terminal content
These are explicitly out of scope for the initial build:
- Built-in browser pane (WebKitGTK embed)
- SSH remote workspaces
- MCP server integration
- Multi-window support
- Tab strip within panes (multiple surfaces per pane)
- Agent orchestration API (primary agent spawning sub-agents)
- Scrollback persistence across restarts
- Plugin system
- Auto-update mechanism