Architecture
Agent Manager is a full terminal UI app built with Ink 5 (React 18), node-pty, and xterm-headless. This page describes the high-level architecture and how the pieces fit together.
Directory Structure
src/
├── cli.tsx # Entry point (Commander + Ink)
├── app.tsx # Root component
├── agents/
│ ├── agent-session.ts # Session lifecycle & events
│ ├── agent-registry.ts # Driver registry
│ ├── agent-detector.ts # Standalone process scanner
│ └── drivers/
│ ├── base-driver.ts # Abstract driver interface
│ ├── claude-driver.ts # Claude Code driver
│ └── gemini-driver.ts # Gemini CLI driver
├── connectors/
│ ├── connector-interface.ts # Connector contract
│ ├── connector-manager.ts # Routes events to connectors
│ └── adapters/
│ ├── slack-adapter.ts # Slack (Web API + Socket Mode)
│ ├── discord-adapter.ts # Discord (discord.js)
│ └── telegram-adapter.ts # Telegram (Telegraf)
├── terminal/
│ ├── pty-manager.ts # node-pty wrapper
│ ├── xterm-bridge.ts # xterm-headless rendering
│ └── screen-buffer.ts # FPS-limited display buffer
├── hooks/
│ ├── hook-server.ts # Unix socket IPC server
│ ├── hook.ts # PermissionRequest handler
│ ├── post-tool-hook.ts # PostToolUse handler
│ ├── notification-hook.ts # Notification handler
│ ├── stop-hook.ts # Stop event handler
│ └── install.ts # Hook installer/uninstaller
├── portal/
│ ├── shared.ts # Protocol types (client ↔ server)
│ ├── portal-server.ts # Unix socket server (main app)
│ ├── portal-client.ts # IPC client functions
│ ├── portal-picker.tsx # Interactive session picker
│ └── ansi-renderer.ts # ScreenContent → ANSI converter
├── store/
│ ├── app-store.ts # Session state (Zustand)
│ ├── settings-store.ts # Persisted config (Zustand)
│ ├── connector-store.ts # Connector lifecycle & bridges
│ └── portal-store.ts # Portal connection tracking
├── config/
│ ├── config.ts # Zod schema & load/save
│ └── defaults.ts # Default configuration
└── ui/
├── layout/
│ ├── shell.tsx # Main layout container
│ ├── sidebar.tsx # Session list panel
│ ├── main-pane.tsx # Terminal + header
│ └── status-bar.tsx # Bottom status indicators
├── components/
│ ├── terminal-view.tsx # xterm grid renderer
│ ├── agent-tab.tsx # Session tab
│ ├── new-session-dialog.tsx # Create session dialog
│ ├── adopt-agent-dialog.tsx # Adopt agent dialog
│ ├── standalone-agents.tsx # Detected agents list
│ ├── settings-panel.tsx # Settings UI
│ ├── agents-settings-content.tsx # Agent install/update UI
│ ├── help-modal.tsx # Keybindings reference
│ └── path-input.tsx # Directory autocomplete
└── hooks/
├── use-keybindings.ts # Keyboard shortcut handler
├── use-raw-input.ts # Raw stdin forwarding
├── use-terminal-size.ts # Terminal dimensions
├── use-spinner.ts # Cycling animation hook
├── use-git-info.ts # Git branch/status polling
└── use-portal-connected.ts # Portal connection state bash How It Works
- CLI entry (
cli.tsx) sets up the alternate screen buffer, mouse tracking, and renders the Ink app. Theportalsubcommand launches the interactive picker or connects to a session directly. - App (
app.tsx) initializes stores, starts connectors and the portal server, and renders the shell layout. - Sessions are managed by
AppStore— each session owns aPtyManager(node-pty),XtermBridge(headless terminal emulation), andScreenBuffer(FPS-limited rendering). - Keyboard input is captured by
useRawInputand forwarded to the active session's PTY, except when intercepted byuseKeybindingsfor shortcuts. - Portal server (
portal-server.ts) listens on a Unix socket (/tmp/agent-manager/portal.sock). Portal clients subscribe to sessions, receive throttled screen frames, and forward keystrokes — providing full remote access without disrupting the main UI. - Hooks are installed as Claude Code lifecycle hooks that send events to the manager's IPC server, which routes them through
ConnectorManagerto the configured messaging adapters. - State flows through Zustand stores with selector-based subscriptions for efficient re-renders.
Key Components
Agents
The agent layer uses a driver abstraction. BaseDriver defines the interface that all agent drivers
implement: spawn(), resume(), continue(), and detectStatus().
AgentSession wraps a driver instance with lifecycle management and event emission.
AgentDetector scans for standalone processes that can be adopted.
Terminal
Each session gets a PtyManager (node-pty pseudoterminal), XtermBridge
(xterm-headless for ANSI parsing and terminal state), and ScreenBuffer (FPS-limited
rendering at 30fps default). The screen buffer converts xterm cell data to styled characters
for Ink rendering.
Connectors
ConnectorManager routes events to all enabled adapters. Each adapter implements
ConnectorInterface with methods for permission requests, tool results, and notifications.
Dependencies are optional — only load what's installed.
Hooks
HookServer runs a Unix socket IPC server. Claude Code hooks send events as JSON messages.
Permission requests block until a connector reply arrives or timeout.
Portal
PortalServer listens on another Unix socket. Clients can list sessions, subscribe for
screen frames, forward input, and create new sessions. Frames are throttled to avoid flooding.
Store
State is managed with Zustand. AppStore holds session state, SettingsStore
persists config, ConnectorStore manages connector lifecycles, and PortalStore
tracks portal connections. Selector-based subscriptions minimize re-renders.
UI
Built with Ink 5 (React for the terminal). The shell layout is responsive: wide terminals get a sidebar with session list, narrow terminals get a compact tab bar. Components use React hooks for keybindings, raw input, terminal size tracking, and animations.