# How SwarmForge Adapts to Different Terminal Backends: The Adapter Pattern Explained

> Discover how SwarmForge uses the Adapter pattern to adapt to various terminal backends. Learn about its modular shell script approach for flexible session management and window tracking.

- Repository: [Robert C. Martin/swarm-forge](https://github.com/unclebob/swarm-forge)
- Tags: deep-dive
- Published: 2026-08-31

---

**SwarmForge decouples its core logic from terminal emulators by sourcing modular shell scripts at runtime that implement a standardized API for session management and window tracking.**

SwarmForge, the open-source automation framework maintained by Uncle Bob (Robert C. Martin), enables developers to adapt to different terminal backends through a lightweight plugin architecture. Rather than hard-coding support for specific emulators, the system isolates terminal-specific operations—such as spawning windows and tracking lifecycle events—behind a thin abstraction layer. This design allows the Babashka-based core to support diverse environments from iTerm2 to Windows Terminal without modification.

## The Terminal Adapter Architecture

SwarmForge implements a **terminal-adapter plugin system** that treats each backend as a standalone shell script. These adapters reside in `swarmforge/scripts/terminal-adapters/` and expose a minimal, well-defined API that the core orchestration logic consumes uniformly.

### Script Location and Naming Convention

Each backend is represented by a single executable shell script following the naming pattern `<backend>.sh`. The repository includes reference implementations such as [`windows-terminal.sh`](https://github.com/unclebob/swarm-forge/blob/main/windows-terminal.sh), [`iterm2.sh`](https://github.com/unclebob/swarm-forge/blob/main/iterm2.sh), [`ghostty.sh`](https://github.com/unclebob/swarm-forge/blob/main/ghostty.sh), and [`none.sh`](https://github.com/unclebob/swarm-forge/blob/main/none.sh). According to the unclebob/swarm-forge source code, these scripts contain platform-specific implementations of common terminal operations while presenting a consistent interface to the Clojure runtime.

### The Standardized API Contract

Every adapter must implement a core set of functions that SwarmForge queries to determine capabilities and trigger actions:

```bash
terminal_backend_label()                # Returns human-readable name

terminal_backend_can_open_sessions()    # Exit 0 if can launch, 1 if not

terminal_backend_tracks_windows()       # Exit 0 if tracks windows, 1 if launch-only

terminal_window_exists()                # Optional: verify window state

terminal_open_session <id> <title>      # Launch new session/window

terminal_close_window <id>              # Close session (if supported)

```

For example, the Windows Terminal adapter implements these functions in [`swarmforge/scripts/terminal-adapters/windows-terminal.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/terminal-adapters/windows-terminal.sh) (lines 3-13), returning exit code 0 for session creation capabilities but 1 for window tracking, indicating it operates in launch-only mode.

## Adapter Selection and Loading Mechanism

SwarmForge determines which terminal backend to use through a hierarchical decision process that prioritizes explicit configuration over auto-detection.

### Environment Variable Override

The system checks for the `SWARMFORGE_TERMINAL` environment variable first. When set, SwarmForge normalizes the value to match a script name in the adapters directory:

```bash
export SWARMFORGE_TERMINAL=iterm2
./swarmforge.sh

```

This setting bypasses auto-detection and forces the system to load [`iterm2.sh`](https://github.com/unclebob/swarm-forge/blob/main/iterm2.sh) regardless of the host environment.

### Auto-Detection Logic

If the environment variable is unset, SwarmForge executes the `detect_terminal_backend` function defined in [`swarmforge/scripts/swarm-terminal-adapter.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm-terminal-adapter.sh) (around lines 27-50). This logic examines the running environment—checking for `$TERM_PROGRAM` or platform-specific indicators—to identify the current terminal emulator and map it to the appropriate adapter script.

### Runtime Loading in Babashka

Once identified, the selected adapter is dynamically sourced at runtime. In `swarmforge/scripts/swarmforge.bb` at line 290, the core executes:

```clojure
(let [path (fs/path (:script-dir ctx) "terminal-adapters" helper)]
  (load-file path))

```

This `(load-file)` call injects the adapter's function definitions into the Babashka runtime, making them available for subsequent capability queries and session management operations.

## Unified Execution Through Wrapper Functions

Rather than calling adapter functions directly, SwarmForge routes all backend interactions through two thin wrappers defined in `swarmforge.bb` (lines 651-681):

- **`terminal-call-ok?`** – Checks whether a function exists and returns a successful exit status (exit code 0)
- **`terminal-call-out`** – Captures and returns the stdout of an adapter function

These wrappers enable safe, conditional execution. For instance, the core queries window tracking support before attempting to monitor lifecycle events:

```clojure
(when (terminal-call-ok? ctx "terminal_backend_tracks_windows")
  (println "Active backend:" (terminal-call-out ctx "terminal_backend_label")))

```

## Capability-Based Behavior Adaptation

SwarmForge adapts its behavior based on the adapter's reported capabilities, specifically interpreting the exit codes of `terminal_backend_can_open_sessions` and `terminal_backend_tracks_windows`.

### Launch-Only Backends

When `terminal_backend_can_open_sessions` returns 0 but `terminal_backend_tracks_windows` returns 1—as seen in the Windows Terminal adapter—SwarmForge treats the backend as **launch-only**. It opens separate surfaces for each session but disables the watchdog process that would otherwise monitor and manage window lifecycles. This mode is suitable for terminals that expose limited scripting APIs.

### Full-Tracking Backends

Backends like iTerm2 ([`iterm2.sh`](https://github.com/unclebob/swarm-forge/blob/main/iterm2.sh)) return exit code 0 for both capability checks. This signals that the adapter supports full window lifecycle management, enabling SwarmForge to maintain a persistent mapping of window IDs, execute `terminal_close_window` commands, and run cleanup watchdogs when sessions terminate.

## Creating Custom Terminal Adapters

Extending SwarmForge to support new terminal emulators requires only a new shell script in the adapters directory. The minimal implementation must define the three core capability functions and at least a stub for `terminal_open_session`:

```bash
#!/usr/bin/env bash

terminal_backend_label() { echo "MyTerm"; }
terminal_backend_can_open_sessions() { return 0; }
terminal_backend_tracks_windows() { return 0; }

terminal_open_session() {
  local session=$1 title=$2
  myterm --new-session "$session" --title "$title"
}

```

Save this as [`swarmforge/scripts/terminal-adapters/myterm.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/terminal-adapters/myterm.sh), then invoke SwarmForge with the custom backend:

```bash
SWARMFORGE_TERMINAL=myterm ./swarmforge.sh

```

Contributors can find detailed guidelines in the "Terminal Back-ends" section of [`README.md`](https://github.com/unclebob/swarm-forge/blob/main/README.md) (lines 401-447), which documents the expected function signatures and testing procedures for new adapters.

## Summary

- SwarmForge isolates terminal-specific code in modular shell scripts located at `swarmforge/scripts/terminal-adapters/`
- The system selects backends via the `SWARMFORGE_TERMINAL` environment variable or auto-detection logic in [`swarmforge/scripts/swarm-terminal-adapter.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm-terminal-adapter.sh)
- Adapters implement a minimal API including `terminal_backend_label()`, `terminal_backend_can_open_sessions()`, and `terminal_open_session()`
- Babashka loads the selected adapter dynamically at runtime using `load-file` at line 290 of `swarmforge.bb`
- Capability flags determine whether SwarmForge runs a watchdog process for window lifecycle management or operates in launch-only mode
- Adding support for new emulators requires only a new shell script following the `<backend>.sh` naming convention

## Frequently Asked Questions

### What environment variable controls the terminal backend in SwarmForge?

Set `SWARMFORGE_TERMINAL` to the base name of your desired adapter script (e.g., `iterm2`, `ghostty`, or `windows-terminal`). SwarmForge normalizes this value and sources the corresponding file from `swarmforge/scripts/terminal-adapters/` before executing any session management commands.

### How do I create a custom terminal adapter for SwarmForge?

Create a new file named `<your-backend>.sh` in the `swarmforge/scripts/terminal-adapters/` directory. Implement the required functions: `terminal_backend_label()`, `terminal_backend_can_open_sessions()`, and `terminal_backend_tracks_windows()`. Optionally implement `terminal_open_session()` and `terminal_close_window()` if your terminal supports programmatic control. Launch SwarmForge with `SWARMFORGE_TERMINAL=<your-backend>` to test your implementation.

### What is the difference between launch-only and window-tracking terminal backends?

Launch-only backends (where `terminal_backend_tracks_windows` returns 1) can spawn new windows via `terminal_open_session()` but cannot detect when those windows close. SwarmForge disables its watchdog and window cleanup logic for these backends. Window-tracking backends (returning 0) support full lifecycle management, allowing SwarmForge to monitor session status and execute `terminal_close_window()` commands.

### Which file contains the auto-detection logic for terminal backends?

The auto-detection logic resides in [`swarmforge/scripts/swarm-terminal-adapter.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm-terminal-adapter.sh), specifically within the `detect_terminal_backend` and `load_terminal_backend` functions around lines 27-50. This script examines environment variables like `$TERM_PROGRAM` to determine which adapter to load when `SWARMFORGE_TERMINAL` is not explicitly set.