Terminal manager — PTY sessions via portable-pty #10

Open
opened 2026-04-16 11:30:00 +00:00 by claude-desktop · 0 comments
Collaborator

User story

As a user, I want forge-agent to manage PTY terminal sessions (open, write, resize, close) per thread, streaming output to the frontend and keeping a replay buffer for reconnection, so that I have a fully functional embedded terminal.

Acceptance criteria

TerminalManager (terminal/manager.rs)

  • Struct holds DashMap<Uuid, TerminalSession> and broadcast::Sender<AppEvent>
  • open(thread_id, working_dir, cols, rows) -> Result<Uuid> — spawns a PTY via portable_pty::native_pty_system(), runs $SHELL (fallback /bin/bash) with TERM=xterm-256color and COLORTERM=truecolor
  • Spawns a tokio::task::spawn_blocking reader that:
    • Reads PTY output in 4096-byte chunks
    • Accumulates in a per-session history buffer
    • Broadcasts AppEvent::TerminalOutput { terminal_id, data }
  • write(terminal_id, data) -> Result<()> — writes bytes to the PTY master
  • resize(terminal_id, cols, rows) -> Result<()> — calls master.resize()
  • snapshot(terminal_id) -> Result<Vec<u8>> — returns the history buffer for replay
  • close(terminal_id) -> Result<()> — kills the child process and removes the session

TerminalSession struct

  • Fields: id, thread_id, pair (PtyPair), writer, status: TerminalStatus, history: Vec<u8>

Kitty/Foot external mode

  • open_kitty_tab(working_dir, title) -> Result<()> — launches a Kitty tab via kitty @ launch --type=tab
  • open_foot_window(working_dir) -> Result<()> — launches foot with --working-directory
  • Auto-detection: check $KITTY_PID env var for Kitty, which foot for Foot
  • Terminal mode selection reads from AppConfig.terminal.mode

Command routing

  • Handles TerminalCommand variants from IPC (Open, Write, Resize, Close, Restart)

Tests

  • Test: open a PTY, write "echo hello\n", verify output contains "hello"
  • Test: resize does not error
  • Test: close removes the session from the map
  • Test: snapshot returns accumulated output

Out of scope

  • VTE4 frontend widget (separate frontend story)
  • Terminal scrollback configuration (reads from config but no UI)

References

  • Spec §9 (Terminal Manager)

Dependencies

  • Blocked by: #3 (TerminalConfig for mode/font/scrollback). TerminalCommand variants come from #6 but can be stubbed and wired later.
  • Blocks: #17
  • Branch off: issue-3-config
  • Full graph: #21
## User story As a **user**, I want forge-agent to manage PTY terminal sessions (open, write, resize, close) per thread, streaming output to the frontend and keeping a replay buffer for reconnection, so that I have a fully functional embedded terminal. ## Acceptance criteria ### TerminalManager (`terminal/manager.rs`) - [ ] Struct holds `DashMap<Uuid, TerminalSession>` and `broadcast::Sender<AppEvent>` - [ ] `open(thread_id, working_dir, cols, rows) -> Result<Uuid>` — spawns a PTY via `portable_pty::native_pty_system()`, runs `$SHELL` (fallback `/bin/bash`) with `TERM=xterm-256color` and `COLORTERM=truecolor` - [ ] Spawns a `tokio::task::spawn_blocking` reader that: - Reads PTY output in 4096-byte chunks - Accumulates in a per-session history buffer - Broadcasts `AppEvent::TerminalOutput { terminal_id, data }` - [ ] `write(terminal_id, data) -> Result<()>` — writes bytes to the PTY master - [ ] `resize(terminal_id, cols, rows) -> Result<()>` — calls `master.resize()` - [ ] `snapshot(terminal_id) -> Result<Vec<u8>>` — returns the history buffer for replay - [ ] `close(terminal_id) -> Result<()>` — kills the child process and removes the session ### TerminalSession struct - [ ] Fields: `id`, `thread_id`, `pair` (PtyPair), `writer`, `status: TerminalStatus`, `history: Vec<u8>` ### Kitty/Foot external mode - [ ] `open_kitty_tab(working_dir, title) -> Result<()>` — launches a Kitty tab via `kitty @ launch --type=tab` - [ ] `open_foot_window(working_dir) -> Result<()>` — launches `foot` with `--working-directory` - [ ] Auto-detection: check `$KITTY_PID` env var for Kitty, `which foot` for Foot - [ ] Terminal mode selection reads from `AppConfig.terminal.mode` ### Command routing - [ ] Handles `TerminalCommand` variants from IPC (Open, Write, Resize, Close, Restart) ### Tests - [ ] Test: open a PTY, write "echo hello\n", verify output contains "hello" - [ ] Test: resize does not error - [ ] Test: close removes the session from the map - [ ] Test: snapshot returns accumulated output ## Out of scope - VTE4 frontend widget (separate frontend story) - Terminal scrollback configuration (reads from config but no UI) ## References - Spec §9 (Terminal Manager) ## Dependencies - **Blocked by:** #3 (TerminalConfig for mode/font/scrollback). `TerminalCommand` variants come from #6 but can be stubbed and wired later. - **Blocks:** #17 - **Branch off:** `issue-3-config` - **Full graph:** #21
claude-desktop added this to the v0.1.0 milestone 2026-04-16 11:30:00 +00:00
Sign in to join this conversation.
No description provided.