Skip to content
Conduit

mcp server

agentfactory

An agent multiplexer built on tmux

compuficial/agentfactorycommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 21, 2026

AgentFactory

AgentFactory is an agent multiplexer built on tmux — Claude Code, Codex, Grok, OpenCode, or anything else: identity, lifecycle, live status, agent-to-agent coordination, and one dashboard over all of them. Daemonless, local-first.

CI Release Stars License: AGPL-3.0 Go 1.25+ Platform: Linux · macOS

Running several AI agents means juggling terminal windows with no record of what each one is, what it's doing, or whether it's been sitting blocked on a question for the last hour. Sessions die with their terminal, results scroll away, and agents have no way to wait on or steer each other. af exists to fix exactly that:

  • One view of everything. Every agent's status — working, idle, awaiting-input, done, exited(code) — in one list, from the CLI (af status, --json) or a live dashboard.
  • Define once, launch forever. An agent definition is a name + harness + model + workdir. af open planner beats re-typing flags into tmux windows.
  • Know when you're needed. Auto-wired hooks and terminal signals report awaiting-input immediately; quiet-screen detection catches common permission dialogs on the next reconciliation pass. The dashboard makes it loud, and the next output clears it automatically.
  • Perfect attach. af attach execs into tmux — full fidelity, resize, alt-screen. Works from inside your own tmux too (detach the nest with C-b C-b d). Detach and you're back at the dashboard.
  • Script the whole fleet. af send types into any session; af status --json | jq ... reads the fleet; exit codes are a contract.
  • Unkillable by design. No daemon. Sessions live in tmux, state in SQLite; commands that observe or act on sessions reconcile the two first, and the dashboard reconciles on every tick. Kill, upgrade, or rebuild af freely — your agents don't notice.

Install

Requires tmux ≥ 3.2 on PATH. Linux first; macOS works.

curl -fsSL agentfactory.sh/install.sh | sh

Downloads the release binary for your platform, verifies its checksum, and installs af to ~/.local/bin (read it first if you like).

Pre-built binaries — or grab the archive for your platform from Releases and drop af somewhere on PATH.

From source (Go 1.25+):

make install       # → ~/.local/bin/af, with bash completion
af doctor          # verify the environment

make help lists the rest (build, test, fresh, reset, uninstall, ...). For zsh/fish completion: af completion zsh|fish (details).

Quick start

The fastest way in: just prefix your agent with af.

af claude                 # opens (or resumes) a claude session here, and attaches
af claude --model opus    # anything after the agent name passes straight to it
af --new claude           # force a fresh session (af's own flags go before the agent)

af <agent> in a directory launches that agent as a managed session in the current directory and drops you straight in — as low-friction as running claude directly, but now it's tracked, survives detach, and resumes with the same command. Works for any harness binary on your PATH (af codex, af grok, af opencode, af agent). Detach with C-b d; you're back at your shell and the session keeps running.

The explicit form is always there when you want control:

# Any command, managed ("custom" harness)
af open --cmd 'python train.py' --name trainer

# A Claude Code agent in a specific repo
af open --harness claude-code --model opus -C ~/src/api --name planner

af status            # the fleet at a glance
af peek planner      # its current screen, rendered
af send planner "refactor the auth middleware, then run tests"
af logs planner -f   # follow its output without attaching
af attach planner    # step in (detach: C-b d)
af close planner     # graceful: quit keys → SIGTERM → SIGKILL
af kill planner      # immediate: SIGKILL the process group

All sessions live on a dedicated tmux socket (tmux -L af) with a locked config — your personal tmux and .tmux.conf are never touched.

Built-in harnesses

HarnessLaunchesGraceful close
claude-codeclaude --settings <auto-wired hooks> [--model <m>]/exit
codexcodex [--model <m>]/quit
grokgrok [--model <m>]/exit
opencodeopencode [--model <m>]/exit
cursor-agentcursor-agent [--model <m>] (Cursor CLI)SIGTERM
agentagent [--model <m>] — whatever binary named agent is on your PATH/exit
customwhatever --cmd sayssignal-only

The --settings flag loads the generated hooks file that reports awaiting-input — see docs/detection.md.

The agent harness is generic: it launches whatever binary named agent sits on your PATH, so af agent works out of the box when your CLI installs itself as agent. Cursor's CLI, for instance, installs both agent and cursor-agent — use the cursor-agent harness to launch it explicitly when agent is taken by another tool.

af never talks to models — harnesses do. --model is passed through untouched. A harness is pure data (a command template, env, quit keys, detect patterns, files), so adding or overriding one is a config entry, not a code change.

Definitions: define once, launch forever

af define planner  --harness claude-code --model opus   -C ~/src/api
af define reviewer --harness claude-code --model sonnet -C ~/src/api --open
af open planner reviewer     # open several at once
af defs                      # list definitions
af rm-def reviewer           # delete one (running sessions unaffected)

--open defines and launches in one step.

Definitions may contain environment values. af defs --json returns those values by design; treat its output as sensitive.

A multi-agent workflow

Three agents, three vendors, one repo — architect, builder, critic:

af define architect --harness claude-code --model opus   -C ~/src/shop
af define builder   --harness opencode    --model sonnet -C ~/src/shop
af define critic    --harness grok        --model grok-4 -C ~/src/shop

af open architect builder critic
af dashboard

Kick off the pipeline without attaching to anything:

af send architect "design a checkout retry mechanism; write it to RETRY.md"
af send builder   "implement RETRY.md; run the tests until green"
af send critic    "review the implementation against RETRY.md; list gaps"

Watch all three from the dashboard: working while they work, a loud awaiting-input when one blocks on you, a to attach and unblock, detach drops you back. Or script the loop from outside:

# who is blocked on a human?
af status --json | jq -r '.[] | select(.status=="awaiting-input") | .name'

# block until the critic stops working, then collect the review
af wait critic
af logs critic -n 100

Because sessions live in tmux, the whole crew survives af upgrades, terminal crashes, and SSH disconnects.

Agent-to-agent coordination

Agents can manage each other: every session has the full af CLI and its own AF_SESSION_ID, so a coordinator agent spawns workers, waits on them, and reads their results — no daemon, no message bus.

af open reviewer
af send reviewer "review the auth module. run: af signal \$AF_SESSION_ID done  when finished"
af wait reviewer --for done      # blocks until the reviewer reports done
af peek reviewer                 # read the result

af wait polls one reconciliation pass per tick (default --interval 1s) until the session reaches a --for status (default idle,awaiting-input,done — "the agent stopped working"). Exit codes: 0 target reached · 1 the session ended in a terminal status outside --for · 5 --timeout elapsed. af wait critic --for exited waits for termination; --json prints the final session object.

af signal <session> done is the flip side: an agent (or a hook) reports its task complete. done shows green in the dashboard and sticks until the session's next real output — exactly like awaiting-input, which any agent or hook sets with af signal <session> awaiting-input.

MCP server

MCP-native harnesses (Claude Code, Codex, ...) can drive af through typed tools instead of shelling out:

claude mcp add af -- af mcp      # register for Claude Code

af mcp serves MCP on stdio — spawned per client, dies with it, so the daemonless story holds. It exposes af_status, af_open, af_send, af_peek, af_logs, af_wait, af_signal, af_close, and af_defs. Destructive management (kill, rm, prune, define) stays human-only. An agent signals itself done with af_signal(state: "done") — the session defaults to its own AF_SESSION_ID.

The dashboard

af dashboard — dense, fast, keyboard-first.

KeyAction
j/k, arrowsmove selection (preview follows)
aattach; detach (C-b d) returns to the dashboard
Enterdetail view — metadata, env (likely sensitive values masked by key name), recent log; scrollable
llogs view (f toggles follow)
oopen a session from a definition (d deletes one)
x / Xclose / kill, with y/n confirm
:command bar — bounded, noninteractive af commands, run in-process
?help · q quit (sessions keep running)

The preview pane mirrors the selected session's live screen at the preview's own geometry. It's read-only — type via attach or af send. The command bar rejects commands that would block, replace the process, or start a protocol server; attach remains available through a.

Service sessions

Infrastructure (a local inference server, a build watcher) uses the same machinery; --service just means close skips quit-keys and goes straight to SIGTERM:

af define llama-8b --cmd "llama-server -m ~/models/llama3-8b.gguf --port 8080" --service
af open llama-8b
af open local-agent    # a definition whose env points its harness at :8080

Scripting

--json is available on status, defs, peek, doctor, and wait. Stable exit codes apply across the CLI: 0 success · 1 runtime error · 2 usage error · 3 not found · 4 environment problem · 5 wait timeout. Sessions are addressed by ID or name.

af status --json         # array of session objects
af defs --json           # definitions
af peek critic --json    # {"screen": "..."}
af doctor --json         # environment checks
af wait critic --json    # final session object

Line-limited logs and MCP log reads cap the file bytes scanned at 4 MiB. af logs --lines 0 explicitly requests the entire log; peek reads the rendered screen instead of the log file.

Cleanup is scriptable too: af close --all, af kill --all, af rm <session> (drop one finished session + its log), af prune (drop all exited/failed history).

Status model

StatusMeaning
startingcreated; no output observed yet
workingoutput within the last idle_threshold (default 5s)
idlealive, quiet
awaiting-inputblocked on you — hook-signaled, terminal-declared (bell/notification/prompt mark), or screen-detected
doneagent reported its task complete (af signal <id> done)
exitedended; exit code recorded (-1 = killed by signal)
failedtmux session disappeared without a recorded exit

awaiting-input and done stick until the session's next real output clears them (terminal-signal state also clears on a command-start mark; screen-detected state clears as soon as the screen stops matching).

Blocked-detection is automatic and layered: auto-wired harness hooks (claude-code out of the box), terminal signals (bell, notifications, prompt marks), and universal screen patterns — with explicit af signal always outranking them all. Details and per-harness wiring: docs/detection.md.

Liveness and exit codes come from tmux itself; activity from log growth (every session's output streams to <data_dir>/logs/<id>.log) — with animation frames filtered out, so a spinner doesn't read as work. Details: docs/architecture.md.

Configuration

Optional YAML at ~/.config/agentfactory/config.yaml; every key has a default. Precedence: flags > AF_* env > file > defaults (global flags --socket, --data-dir; env AF_SOCKET, AF_DATA_DIR, AF_IDLE_THRESHOLD, AF_CLOSE_TIMEOUT, AF_SEND_DELAY, AF_DETECT, AF_SIGNALS).

socket: af                                  # tmux -L <socket>
data_dir: ~/.local/share/agentfactory       # db + logs
idle_threshold: 5s                          # working→idle cutoff
close_timeout: 10s                          # SIGTERM→SIGKILL escalation
send_delay: 50ms                            # gap between text and Enter in af send
detect: true                                # screen-pattern status detection
signals:                                    # terminal-signal detection (bell, OSC 9/777, OSC 133)
  enabled: true
  # notify_awaiting: ["(?i)permission"]     # optional: narrow which notifications count
tui:
  tick: 1s                                  # dashboard refresh

# Add harnesses, or override a built-in (same name wins):
harnesses:
  aider:
    command: "aider{{if .Model}} --model {{shellquote .Model}}{{end}}"  # Go text/template
    env: {}
    quit_keys: ["/exit"]                    # empty = signal-only close
    detect:                                 # optional screen patterns (regex)
      awaiting_input: ["\\? for shortcuts"] # quiet screen matching => awaiting-input
      working: ["esc to interrupt"]         # ...unless one of these also matches

Rendered commands run via sh -c, so harness configuration is trusted code. Use {{shellquote .Model}}, {{shellquote .WorkDir}}, and {{shellquote .FilesDir}} whenever a value enters shell syntax; the built-in harnesses do this automatically. Session state is stored under a private 0700 data directory with 0600 database and log files.

Shell completion

make install installs bash completion (picked up automatically if the bash-completion package is present). Completion is dynamic: session arguments complete with appropriate live names and IDs, af open/af define --from complete definitions, and --harness completes known harnesses.

af completion zsh  > "${fpath[1]}/_af"                    # zsh
af completion fish > ~/.config/fish/completions/af.fish   # fish

Notes

  • af manages only sessions it started; pre-existing tmux sessions and stray processes are invisible to it.
  • Switching between different af builds sharing one data dir? Run make fresh after switching — a clean slate beats two builds fighting over one database.
  • Every session gets AF_SESSION_ID and AF_SESSION_NAME in its environment.

Development

make precommit           # tidy + text/docs/shell + format + lint + race tests
make cover               # coverage summary
make fuzz                # byte-parser fuzzers (~20s)
make help

Integration tests use a throwaway tmux socket — your own sessions are never touched.

Suite design: docs/testing.md. Current enforced gates and audited limitations: docs/quality.md.

See CONTRIBUTING.md for guidelines and docs/architecture.md for the design. Coding agents: read AGENTS.md.

Star History

Star History Chart

License

AGPL-3.0

↑↓ navigate openesc close