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

> Integrate custom terminal backends with SwarmForge easily. Our guide shows how to add new terminal emulators without core code changes, leveraging SwarmForge's dynamic loading.

- Repository: [Robert C. Martin/swarm-forge](https://github.com/unclebob/swarm-forge)
- Tags: how-to-guide
- Published: 2026-08-09

---

**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`](https://github.com/unclebob/swarm-forge/blob/main/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`](https://github.com/unclebob/swarm-forge/blob/main/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`](https://github.com/unclebob/swarm-forge/blob/main/wezterm.sh):

```zsh

# 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:

```zsh
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:

```sh
export SWARMFORGE_TERMINAL=wezterm
./swarm

```

To support shorter aliases, extend the `normalize_terminal_backend` function in [`swarm-terminal-adapter.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm-terminal-adapter.sh):

```zsh
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`](https://github.com/unclebob/swarm-forge/blob/main/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`](https://github.com/unclebob/swarm-forge/blob/main/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`](https://github.com/unclebob/swarm-forge/blob/main/ghostty.sh) (full-featured) and [`windows-terminal.sh`](https://github.com/unclebob/swarm-forge/blob/main/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`](https://github.com/unclebob/swarm-forge/blob/main/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`](https://github.com/unclebob/swarm-forge/blob/main/windows-terminal.sh)), or support both (like [`ghostty.sh`](https://github.com/unclebob/swarm-forge/blob/main/ghostty.sh)).