How to Integrate Custom Terminal Backends with SwarmForge: A Complete Guide

SwarmForge loads terminal backends dynamically from swarmforge/scripts/terminal-adapters/ based on a six-function contract defined in the adapter loader, enabling new terminal emulators to be added without modifying core code.

SwarmForge (unclebob/swarm-forge) creates a separate terminal surface for every agent window through a pluggable adapter system. To integrate custom terminal backends with SwarmForge, you implement a small Zsh script that follows a strict function contract, then drop it into the adapters directory where the loader discovers it automatically at runtime.

Understanding the Terminal Backend Architecture

The terminal integration layer in SwarmForge consists of a loader script and individual adapter implementations. According to the SwarmForge source code, the system separates detection logic from execution logic, allowing backends to be developed independently of the core swarm orchestration.

The Adapter Loader

The loader in swarmforge/scripts/swarm-terminal-adapter.sh handles three responsibilities: detecting which backend to use, normalizing aliases to canonical names, and sourcing the appropriate adapter file. The function detect_terminal_backend (lines 27-48) checks the SWARMFORGE_TERMINAL environment variable first; if unset, it defaults based on the host platform (AppleScript for macOS Terminal, wt.exe for Windows Terminal, or none).

Once detected, normalize_terminal_backend (lines 5-24) maps common aliases like iterm or terminal-app to canonical filenames. Finally, load_terminal_backend (lines 50-60) constructs the path $TERMINAL_ADAPTERS_DIR/<backend>.sh and sources the file, aborting if the file does not exist.

The Adapter Contract

Every backend script must export six specific functions with exact signatures. The reference implementation in terminal-adapters/ghostty.sh demonstrates the full contract:

  • terminal_backend_label – Prints a human-readable name for logging purposes.
  • terminal_backend_can_open_sessions – Returns 0 if the backend can create new sessions, non-zero otherwise.
  • terminal_backend_tracks_windows – Returns 0 if the backend returns stable window IDs for watchdog monitoring, 1 if it only launches surfaces.
  • terminal_open_session <session> <title> [<sibling_id>] – Launches a terminal surface attached to the specified tmux session and prints a stable identifier.
  • terminal_window_exists <window_id> – Checks if the identifier returned by terminal_open_session still exists (required only when tracking is enabled).
  • terminal_close_window <window_id> – Closes the surface identified by the window ID.

Implementing a Custom Terminal Backend

Creating a new backend requires implementing the contract in a Zsh script placed in the adapters directory.

Step 1: Create the Adapter Script

Create a new file in swarmforge/scripts/terminal-adapters/ with a .sh extension. For example, to add WezTerm support, create wezterm.sh:


# File: swarmforge/scripts/terminal-adapters/wezterm.sh

#!/usr/bin/env zsh

terminal_backend_label() {
  echo "WezTerm"
}

terminal_backend_can_open_sessions() {
  return 0
}

terminal_backend_tracks_windows() {
  return 0
}

Step 2: Implement the Required Functions

Complete the implementation by defining the session management functions. The following example uses WezTerm's CLI to spawn tabs and track them by ID:

terminal_open_session() {
  local session="$1"
  local title="$2"
  local sibling_id="${3:-}"

  wezterm cli spawn \
    --cwd "$WORKING_DIR" \
    --title "$title" \
    --tmux-attach "$session" \
    --print-tab-id
}

terminal_window_exists() {
  local tab_id="$1"
  [[ -n "$tab_id" ]] || return 1
  wezterm cli list-tabs | grep -q "^$tab_id$"
}

terminal_close_window() {
  local tab_id="$1"
  [[ -n "$tab_id" ]] || return 0
  wezterm cli close-tab "$tab_id"
}

Step 3: Register and Test

Set the environment variable before launching SwarmForge:

export SWARMFORGE_TERMINAL=wezterm
./swarm

To support shorter aliases, extend the normalize_terminal_backend function in swarm-terminal-adapter.sh:

case "$backend" in
  wez|wezterm) echo "wezterm" ;;
  # …existing cases…

esac

Runtime Integration and Window Management

SwarmForge does not merely launch terminals; it actively manages their lifecycle through the adapter interface.

Backend Detection and Normalization

At startup, SwarmForge invokes detect_terminal_backend to determine which adapter to load. If SWARMFORGE_TERMINAL is set to a recognized value, that backend takes precedence over platform defaults. The normalization layer ensures that variations like iterm and iTerm2 both resolve to the canonical adapter filename.

Window Watchdog Integration

When terminal_backend_can_open_sessions returns 0, SwarmForge creates a tmux session and immediately calls terminal_open_session. The returned identifier is stored by the window watchdog in swarmforge/scripts/swarmforge.bb (around line 423).

If terminal_backend_tracks_windows returns 0, the watchdog periodically invokes terminal_window_exists to verify the surface is still alive. Should the window close unexpectedly, SwarmForge can reopen it using the same session parameters. Backends that return 1 from terminal_backend_tracks_windows (such as the minimal windows-terminal.sh) launch surfaces without watchdog monitoring.

Summary

  • SwarmForge discovers terminal backends dynamically from swarmforge/scripts/terminal-adapters/ without requiring core code changes.
  • The loader in swarm-terminal-adapter.sh handles detection via SWARMFORGE_TERMINAL, normalization of aliases, and sourcing of adapter scripts.
  • Custom backends must implement six functions: terminal_backend_label, terminal_backend_can_open_sessions, terminal_backend_tracks_windows, terminal_open_session, terminal_window_exists, and terminal_close_window.
  • Window tracking enables the watchdog in swarmforge.bb to reopen crashed terminal surfaces automatically.
  • Reference implementations in ghostty.sh (full-featured) and windows-terminal.sh (launch-only) provide templates for new adapters.

Frequently Asked Questions

What file naming convention must custom terminal backends follow?

Custom backends must be placed in swarmforge/scripts/terminal-adapters/ with a .sh extension matching the canonical backend name. For example, a backend named wezterm must reside in terminal-adapters/wezterm.sh. The loader constructs this path dynamically in load_terminal_backend by appending .sh to the normalized backend name.

Can I use a custom backend without modifying the SwarmForge repository?

Yes. While the simplest method places the script inside the repository's terminal-adapters/ directory, you can also fork the repository or mount a custom directory at that path. The loader sources files from $TERMINAL_ADAPTERS_DIR, so as long as your script is present and executable, SwarmForge will load it regardless of whether it ships with the original distribution.

How does the window watchdog interact with terminal backends?

The watchdog in swarmforge/scripts/swarmforge.bb (around line 423) stores the identifier returned by terminal_open_session and periodically calls terminal_window_exists to verify the surface remains open. This only occurs if terminal_backend_tracks_windows returns 0. If the function returns 1, SwarmForge launches the terminal once and assumes no further lifecycle management is possible.

What is the difference between terminal_backend_can_open_sessions and terminal_backend_tracks_windows?

terminal_backend_can_open_sessions indicates whether the backend can create new terminal surfaces at all; if it returns non-zero, SwarmForge skips terminal automation entirely. terminal_backend_tracks_windows indicates whether the backend can return stable identifiers that persist across checks, enabling the watchdog to detect crashes. A backend might support opening sessions but not tracking (like windows-terminal.sh), or support both (like ghostty.sh).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →