swarm-mcp profile launchers (zsh)
Source this file from your ~/.zshrc to gain per-profile launcher functions.
A "profile" is just a name you pick (e.g. "personal", "work", "client-x").
Each profile maps the four canonical agent binaries (claude/codex/opencode/
hermes) to launcher aliases of your choice, and points swarm-mcp at the
matching env file under $SWARM_ENV_DIR.
Launch shapes per profile (see env/README.md "Three ways to launch each
agent" for the long form):
worker — profile worker alias (e.g. clowd). Sources the profile env,
exports AGENT_IDENTITY/SWARM_IDENTITY/SWARM_DB_PATH, then execs
the canonical binary. Registers in the swarm as identity:.
gateway — profile lead alias (e.g. clowdg). Same as worker plus
SWARM_ROLE=gateway / SWARM_AGENT_ROLE=planner
so the session routes non-trivial work through dispatch.
vanilla — the unwrapped binary (e.g. plain claude). No alias to define.
SessionStart hooks see no AGENT_IDENTITY and intentionally skip
registration; the session leaves no DB footprint and stays
invisible to swarm peers. Use this for uncoordinated solo work.
Quick start:
1. mkdir -p ~/.config/swarm-mcp
2. Copy env/profile.env.example -> ~/.config/swarm-mcp/.env
and edit identities, MCP names, DB path, herdr socket, etc.
3. Source this file from your shell config and call swarm_define_profile
once per profile to materialize the launcher aliases.
Example invocations are at the bottom of this file — copy whichever rows
match the profiles you want.
export SWARM_ENV_DIR="${SWARM_ENV_DIR:-$HOME/.config/swarm-mcp}"
--- Internal helpers (do not call directly) ----------------------------------
Source $env_file into a subshell, then exec the rest as a command.
_swarm_run() ( local env_file="$1" shift set -a source "$env_file" set +a "$@" )
Source $env_file, force AGENT_IDENTITY/SWARM_DB_PATH to the profile, then exec.
_swarm_run_profile() ( local profile="$1" local env_file="$2" shift 2 set -a source "$env_file" set +a export AGENT_IDENTITY="$profile" export SWARM_IDENTITY="$profile" export SWARM_DB_PATH="${SWARM_DB_PATH:-$HOME/.swarm-mcp-$profile/swarm.db}" "$@" )
Same as _swarm_run_profile but flags the session as a gateway/lead.
_swarm_run_lead() ( local profile="$1" local env_file="$2" local runtime="$3" shift 3 set -a source "$env_file" set +a export AGENT_IDENTITY="$profile" export SWARM_IDENTITY="$profile" export SWARM_DB_PATH="${SWARM_DB_PATH:-$HOME/.swarm-mcp-$profile/swarm.db}" case "$runtime" in claude) export SWARM_CC_ROLE=gateway export SWARM_CC_AGENT_ROLE=planner ;; codex) export SWARM_CODEX_ROLE=gateway export SWARM_CODEX_AGENT_ROLE=planner ;; opencode) export SWARM_OPENCODE_ROLE=gateway export SWARM_OPENCODE_AGENT_ROLE=planner ;; hermes) export SWARM_HERMES_ROLE=gateway export SWARM_HERMES_AGENT_ROLE=planner ;; esac "$@" )
Launch a visible herdr server bound to a profile-scoped socket path.
_swarm_herdr() ( local socket_path="$1" local session="$2" shift 2 mkdir -p "${socket_path:h}" HERDR_SOCKET_PATH="$socket_path" command herdr --session "$session" "$@" )
--- Public generator ---------------------------------------------------------
swarm_define_profile [option=value ...]
Materializes launcher shell functions for one profile. Options (all
optional unless noted):
env_file= Defaults to $SWARM_ENV_DIR/.env
claude= Worker alias for the claude binary
codex= Worker alias for the codex binary
opencode= Worker alias for the opencode binary
hermes= Worker alias for the hermes binary
claude_lead= Lead/gateway alias for claude (adds gateway env)
codex_lead= Lead/gateway alias for codex (adds gateway env)
opencode_lead= Lead/gateway alias for opencode (adds gateway env)
hermes_lead= Lead/gateway alias for hermes (adds gateway env)
herdr= Alias that launches a visible herdr server
herdr_socket= Override herdr socket; defaults to
${HERMES_HOST_HOME:-$HOME}/.config/herdr/sessions//herdr.sock
claude_args= Extra args appended to every claude launch
(split on whitespace; defaults to "--enable-auto-mode")
claude_lead_args= Extra args for the claude_lead alias only; falls
back to claude_args when unset. Use this to enable
gateway-only flags like --remote-control.
Aliases you omit simply aren't defined — you can mix profile-specific
launchers freely. The function exports SWARM_HARNESS_= into
the profile env file the next time it is sourced; swarm-mcp reads those to
resolve dispatch normalization for this profile.
swarm_define_profile() { emulate -L zsh local profile="$1" shift if [[ -z "$profile" ]]; then print -u2 "swarm_define_profile: profile name required" return 64 fi local env_file="$SWARM_ENV_DIR/$profile.env" local claude_alias="" codex_alias="" opencode_alias="" hermes_alias="" local claude_lead_alias="" codex_lead_alias="" local opencode_lead_alias="" hermes_lead_alias="" local herdr_alias="" herdr_socket="" local claude_args="--enable-auto-mode" local claude_lead_args="" local kv key value for kv in "$@"; do key="${kv%%=}" value="${kv#=}" case "$key" in env_file) env_file="$value" ;; claude) claude_alias="$value" ;; codex) codex_alias="$value" ;; opencode) opencode_alias="$value" ;; hermes) hermes_alias="$value" ;; claude_lead) claude_lead_alias="$value" ;; codex_lead) codex_lead_alias="$value" ;; opencode_lead) opencode_lead_alias="$value" ;; hermes_lead) hermes_lead_alias="$value" ;; herdr) herdr_alias="$value" ;; herdr_socket) herdr_socket="$value" ;; claude_args) claude_args="$value" ;; claude_lead_args) claude_lead_args="$value" ;; *) print -u2 "swarm_define_profile($profile): unknown option '$key'" return 64 ;; esac done if [[ -z "$herdr_socket" ]]; then herdr_socket="${HERMES_HOST_HOME:-$HOME}/.config/herdr/sessions/$profile/herdr.sock" fi [[ -z "$claude_lead_args" ]] && claude_lead_args="$claude_args"
Define each requested alias. eval is used to template the function name;
bodies stay quoted so $profile/$env_file values are baked in at define time.
if [[ -n "$claude_alias" ]]; then eval "$claude_alias() { _swarm_run_profile $(printf %q "$profile") $(printf %q "$env_file") command claude ${claude_args} "$@"; }" fi if [[ -n "$codex_alias" ]]; then eval "$codex_alias() { _swarm_run_profile $(printf %q "$profile") $(printf %q "$env_file") command codex "$@"; }" fi if [[ -n "$opencode_alias" ]]; then eval "$opencode_alias() { _swarm_run_profile $(printf %q "$profile") $(printf %q "$env_file") command opencode "$@"; }" fi if [[ -n "$hermes_alias" ]]; then eval "$hermes_alias() { _swarm_run_profile $(printf %q "$profile") $(printf %q "$env_file") command hermes "$@"; }" fi if [[ -n "$claude_lead_alias" ]]; then eval "$claude_lead_alias() { _swarm_run_lead $(printf %q "$profile") $(printf %q "$env_file") claude command claude ${claude_lead_args} "$@"; }" fi if [[ -n "$codex_lead_alias" ]]; then eval "$codex_lead_alias() { _swarm_run_lead $(printf %q "$profile") $(printf %q "$env_file") codex command codex "$@"; }" fi if [[ -n "$opencode_lead_alias" ]]; then eval "$opencode_lead_alias() { _swarm_run_lead $(printf %q "$profile") $(printf %q "$env_file") opencode command opencode "$@"; }" fi if [[ -n "$hermes_lead_alias" ]]; then eval "$hermes_lead_alias() { _swarm_run_lead $(printf %q "$profile") $(printf %q "$env_file") hermes command hermes "$@"; }" fi if [[ -n "$herdr_alias" ]]; then eval "$herdr_alias() { _swarm_herdr $(printf %q "$herdr_socket") $(printf %q "$profile") "$@"; }" fi }
--- Auto-discovery -----------------------------------------------------------
Scan $SWARM_ENV_DIR for *.env files and call swarm_define_profile for each,
using the SWARM_HARNESS_* values declared in the file. This makes profile
setup a single step: drop ~/.config/swarm-mcp/.env and the matching
aliases appear on next shell. Manual swarm_define_profile calls (below or in
your own dotfiles) still work and override the auto-defined ones.
Opt out by setting SWARM_MCP_DISABLE_AUTODEFINE=1 before sourcing this file.
swarm_autodefine_profiles() { emulate -L zsh setopt local_options null_glob extended_glob
[[ -n "${SWARM_MCP_DISABLE_AUTODEFINE:-}" ]] && return 0 [[ -d "$SWARM_ENV_DIR" ]] || return 0
local env_file profile local claude_alias codex_alias opencode_alias hermes_alias local claude_lead_alias codex_lead_alias opencode_lead_alias hermes_lead_alias local herdr_alias local claude_args_val claude_lead_args_val local -a args
for env_file in "$SWARM_ENV_DIR"/*.env(.N); do [[ -r "$env_file" ]] || continue profile="${env_file:t:r}"
# Read SWARM_HARNESS_* from the file in a subshell so per-profile
# AGENT_IDENTITY / SWARM_DB_PATH don't leak into the parent shell.
eval "$(
source "$env_file" 2>/dev/null
printf 'claude_alias=%q
' "${SWARM_HARNESS_CLAUDE:-}" printf 'codex_alias=%q ' "${SWARM_HARNESS_CODEX:-}" printf 'opencode_alias=%q ' "${SWARM_HARNESS_OPENCODE:-}" printf 'hermes_alias=%q ' "${SWARM_HARNESS_HERMES:-}" printf 'claude_lead_alias=%q ' "${SWARM_HARNESS_CLAUDE_LEAD:-}" printf 'codex_lead_alias=%q ' "${SWARM_HARNESS_CODEX_LEAD:-}" printf 'opencode_lead_alias=%q ' "${SWARM_HARNESS_OPENCODE_LEAD:-}" printf 'hermes_lead_alias=%q ' "${SWARM_HARNESS_HERMES_LEAD:-}" printf 'herdr_alias=%q ' "${SWARM_HARNESS_HERDR:-}" printf 'claude_args_val=%q ' "${SWARM_HARNESS_CLAUDE_ARGS:-}" printf 'claude_lead_args_val=%q ' "${SWARM_HARNESS_CLAUDE_LEAD_ARGS:-}" )"
args=()
[[ -n "$claude_alias" ]] && args+=("claude=$claude_alias")
[[ -n "$codex_alias" ]] && args+=("codex=$codex_alias")
[[ -n "$opencode_alias" ]] && args+=("opencode=$opencode_alias")
[[ -n "$hermes_alias" ]] && args+=("hermes=$hermes_alias")
[[ -n "$claude_lead_alias" ]] && args+=("claude_lead=$claude_lead_alias")
[[ -n "$codex_lead_alias" ]] && args+=("codex_lead=$codex_lead_alias")
[[ -n "$opencode_lead_alias" ]] && args+=("opencode_lead=$opencode_lead_alias")
[[ -n "$hermes_lead_alias" ]] && args+=("hermes_lead=$hermes_lead_alias")
[[ -n "$herdr_alias" ]] && args+=("herdr=$herdr_alias")
[[ -n "$claude_args_val" ]] && args+=("claude_args=$claude_args_val")
[[ -n "$claude_lead_args_val" ]] && args+=("claude_lead_args=$claude_lead_args_val")
(( ${#args[@]} > 0 )) && swarm_define_profile "$profile" "${args[@]}"
done }
swarm_autodefine_profiles
