— a glyph for the team list (unicode only).
division_emoji() {
if ! supports_unicode; then printf '*'; return; fi
case "$1" in
academic) printf '📚';; design) printf '🎨';; engineering) printf '💻';;
finance) printf '💵';; game-development) printf '🎮';; gis) printf '🌍';; marketing) printf '📢';;
paid-media) printf '💰';; product) printf '📊';; project-management) printf '🎬';;
sales) printf '💼';; security) printf '🔒';; spatial-computing) printf '🥽';;
specialized) printf '🎯';; support) printf '🛟';; testing) printf '🧪';; *) printf '•';;
esac
}
# Generic multi-select. Inputs (globals): OPT_LABEL[], OPT_SEL[];
# SEL_TITLE, SEL_HINT, SEL_SUMMARY_FN, SEL_NAV, SEL_WARN_FN.
# Mutates OPT_SEL[]; sets SEL_RESULT = next|back|quit.
selector() {
local n=${#OPT_LABEL[@]} cur=0 top=0 query="" searching=false key i idx vn rows W
rows=$(( $(term_rows) - 9 )); (( rows < 3 )) && rows=3
while true; do
local view=() qlc
qlc="$(printf '%s' "$query" | tr '[:upper:]' '[:lower:]')"
for (( i=0; i
=vn )) && cur=$(( vn>0 ? vn-1 : 0 ))
(( cur=top+rows )) && top=$(( cur-rows+1 ))
W=$(( $(term_cols) - 4 )); (( W>74 )) && W=74; (( W<40 )) && W=40
local buf="" hlen=$(( W - ${#SEL_TITLE} - 5 )); (( hlen<1 )) && hlen=1
buf+=" ${C_BOLD}${C_CYAN}${BX_TL}${BX_H}${BX_H} ${SEL_TITLE} $(repeat "$BX_H" "$hlen")${BX_TR}${C_RESET}"$'\n'
buf+=" ${C_DIM}${SEL_HINT}${C_RESET}"$'\n\n'
(( vn==0 )) && buf+=" ${C_DIM}(no matches)${C_RESET}"$'\n'
for (( i=top; i nav -> warnings (-> search line)
buf+=$'\n'" ${C_BOLD}$("$SEL_SUMMARY_FN")${C_RESET}"$'\n'
buf+=" ${C_DIM}${SEL_NAV}${C_RESET}"$'\n'
local _w; _w="$("$SEL_WARN_FN")"; [[ -n "$_w" ]] && buf+=" ${C_YELLOW}${_w}${C_RESET}"$'\n'
if $searching; then buf+=" ${C_CYAN}search:${C_RESET} ${query}_"$'\n'
elif [[ -n "$query" ]]; then buf+=" ${C_CYAN}/${query}${C_RESET} ${C_DIM}(esc clears)${C_RESET}"$'\n'; fi
draw_frame "$buf"
key="$(read_key)"
if $searching; then
case "$key" in
ENTER) searching=false ;;
ESC) query=""; searching=false ;;
BACKSPACE) query="${query%?}" ;;
*) [[ ${#key} -eq 1 ]] && query="$query$key" ;;
esac
continue
fi
case "$key" in
UP|k) (( cur>0 )) && cur=$(( cur-1 )) ;;
DOWN|j) (( cur0 )) && { idx=${view[$cur]}; OPT_SEL[$idx]=$(( 1 - ${OPT_SEL[$idx]} )); } ;;
a|A) for (( i=0; i/dev/null || true)"
if is_detected "$t" 2>/dev/null; then det="${C_GREEN}${GLYPH_DET}${C_RESET}"; else det="${C_DIM}${GLYPH_OFF}${C_RESET}"; fi
label="$(printf '%s %-13s %s' "$det" "$(tool_simple_name "$t")" "${C_DIM}${path:-not found}${C_RESET}")"
OPT_LABEL+=("$label"); OPT_SEL+=("${TOOL_SEL[$i]:-0}")
done
SEL_TITLE="The Agency · Installer — 1/3 · Tools"
SEL_HINT="Pick where to install. ${GLYPH_DET} = detected on this machine."
SEL_SUMMARY_FN=_tools_summary
SEL_NAV="space toggle · a all · n none · / search · enter next · q quit"
SEL_WARN_FN=_no_warn
selector
for (( i=0; i<${#OPT_SEL[@]}; i++ )); do TOOL_SEL[$i]="${OPT_SEL[$i]}"; done
}
tool_simple_name() {
case "$1" in
claude-code) echo "Claude Code";; copilot) echo "Copilot";; antigravity) echo "Antigravity";;
gemini-cli) echo "Gemini CLI";; opencode) echo "OpenCode";; openclaw) echo "OpenClaw";;
cursor) echo "Cursor";; aider) echo "Aider";; windsurf) echo "Windsurf";;
qwen) echo "Qwen Code";; kimi) echo "Kimi Code";; codex) echo "Codex";; *) echo "$1";;
esac
}
# --- Screen: Teams ---
_teams_agents() {
local i c=0 d
for (( i=0; i<${#ALL_DIVISIONS[@]}; i++ )); do
[[ "${OPT_SEL[$i]}" == 1 ]] && { d="${ALL_DIVISIONS[$i]}"; c=$(( c + ${TEAM_COUNTS[$i]} )); }
done
echo "$c"
}
_teams_summary() {
local sel=0 i a; a="$(_teams_agents)"
for (( i=0; i<${#OPT_SEL[@]}; i++ )); do [[ "${OPT_SEL[$i]}" == 1 ]] && sel=$(( sel+1 )); done
printf '%s agents · %s of %s teams' "$a" "$sel" "${#OPT_SEL[@]}"
}
_teams_warn() {
local a cap; a="$(_teams_agents)"; cap="$(tool_cap opencode)"
if _opencode_selected && [[ "$a" -gt "$cap" ]]; then
printf "⚠ OpenCode registers ~%s; ~%s of %s won't load (#27988)" "$cap" "$(( a - cap ))" "$a"
fi
}
_opencode_selected() {
local i; for (( i=0; i<${#TOOL_SEL[@]}; i++ )); do
[[ "${ALL_TOOLS[$i]}" == "opencode" && "${TOOL_SEL[$i]}" == 1 ]] && return 0
done; return 1
}
screen_teams() {
OPT_LABEL=(); OPT_SEL=()
local i
for (( i=0; i<${#ALL_DIVISIONS[@]}; i++ )); do
local d="${ALL_DIVISIONS[$i]}"
OPT_LABEL+=("$(printf '%s %-20s %s' "$(division_emoji "$d")" "$d" "${C_DIM}${TEAM_COUNTS[$i]} agents${C_RESET}")")
OPT_SEL+=("${TEAM_SEL[$i]:-1}")
done
SEL_TITLE="The Agency · Installer — 2/3 · Teams"
SEL_HINT="Pick which teams to install. Fewer teams keeps OpenCode under its limit."
SEL_SUMMARY_FN=_teams_summary
SEL_NAV="space toggle · a all · n none · / search · enter next · ← back · q quit"
SEL_WARN_FN=_teams_warn
selector
for (( i=0; i<${#OPT_SEL[@]}; i++ )); do TEAM_SEL[$i]="${OPT_SEL[$i]}"; done
}
# --- Screen: Review ---
REVIEW_RESULT=""
# grid_2col — lay items out in two column-major columns
# (left column filled top-to-bottom first). Plain text cells (no ANSI) so the
# width padding stays correct.
grid_2col() {
local w="$1"; shift
local n=$# r rows left right out=""
(( n==0 )) && { printf ' %snone%s\n' "$C_DIM" "$C_RESET"; return; }
local items=("$@")
rows=$(( (n + 1) / 2 ))
for (( r=0; r/dev/null && TOOL_SEL+=(1) || TOOL_SEL+=(0); done
TEAM_SEL=(); for (( i=0; i<${#ALL_DIVISIONS[@]}; i++ )); do TEAM_SEL+=(1); done
tui_begin || return 1
local screen=tools
while true; do
case "$screen" in
tools) screen_tools; case "$SEL_RESULT" in next) screen=teams;; quit) tui_end; exit 0;; esac ;;
teams) screen_teams; case "$SEL_RESULT" in next) screen=review;; back) screen=tools;; quit) tui_end; exit 0;; esac ;;
review) screen_review; case "$REVIEW_RESULT" in install) break;; back) screen=teams;; quit) tui_end; exit 0;; esac ;;
esac
done
tui_end
# commit
SELECTED_TOOLS=()
for (( i=0; i<${#TOOL_SEL[@]}; i++ )); do [[ "${TOOL_SEL[$i]}" == 1 ]] && SELECTED_TOOLS+=("${ALL_TOOLS[$i]}"); done
FILTER_DIVISIONS=()
local all=1
for (( i=0; i<${#TEAM_SEL[@]}; i++ )); do [[ "${TEAM_SEL[$i]}" == 1 ]] || all=0; done
if [[ "$all" == 0 ]]; then
for (( i=0; i<${#TEAM_SEL[@]}; i++ )); do [[ "${TEAM_SEL[$i]}" == 1 ]] && FILTER_DIVISIONS+=("${ALL_DIVISIONS[$i]}"); done
fi
build_selection
return 0
}
# ---------------------------------------------------------------------------
# Installers
# ---------------------------------------------------------------------------
install_claude_code() {
local dest; dest="$(resolve_dest claude-code "${HOME}/.claude/agents")"
local count=0 dir f slug
mkdir -p "$dest"
for dir in "${AGENT_DIRS[@]}"; do
[[ -d "$REPO_ROOT/$dir" ]] || continue
while IFS= read -r -d '' f; do
is_agent_file "$f" || continue
slug="$(agent_slug "$f")"; slug_allowed "$slug" || continue
install_file "$f" "$dest/"; incr count
done < <(find "$REPO_ROOT/$dir" -name "*.md" -type f -print0)
done
ok "Claude Code: $count agents -> $dest"
}
install_copilot() {
local dest_github; dest_github="$(resolve_dest copilot "${HOME}/.github/agents")"
local dest_copilot="${HOME}/.copilot/agents"
local count=0 dir f slug
mkdir -p "$dest_github" "$dest_copilot"
for dir in "${AGENT_DIRS[@]}"; do
[[ -d "$REPO_ROOT/$dir" ]] || continue
while IFS= read -r -d '' f; do
is_agent_file "$f" || continue
slug="$(agent_slug "$f")"; slug_allowed "$slug" || continue
install_file "$f" "$dest_github/"
install_file "$f" "$dest_copilot/"
incr count
done < <(find "$REPO_ROOT/$dir" -name "*.md" -type f -print0)
done
ok "Copilot: $count agents -> $dest_github"
ok "Copilot: $count agents -> $dest_copilot"
warn "Copilot: Verify VS Code setting 'chat.agentFilesLocations' includes your install path."
dim " Open Settings (Ctrl/Cmd+,) -> search 'chat.agentFilesLocations'"
}
install_antigravity() {
local src="$INTEGRATIONS/antigravity"
local dest; dest="$(resolve_dest antigravity "${HOME}/.gemini/antigravity/skills")"
local count=0
[[ -d "$src" ]] || { err "integrations/antigravity missing. Run convert.sh first."; return 1; }
mkdir -p "$dest"
local d
while IFS= read -r -d '' d; do
local name; name="$(basename "$d")"
slug_allowed "$name" || continue
mkdir -p "$dest/$name"
install_file "$d/SKILL.md" "$dest/$name/SKILL.md"
incr count
done < <(find "$src" -mindepth 1 -maxdepth 1 -type d -print0)
ok "Antigravity: $count skills -> $dest"
}
install_gemini_cli() {
local src="$INTEGRATIONS/gemini-cli/agents"
local dest; dest="$(resolve_dest gemini-cli "${HOME}/.gemini/agents")"
local count=0
[[ -d "$src" ]] || { err "integrations/gemini-cli/agents missing. Run ./scripts/convert.sh --tool gemini-cli first."; return 1; }
mkdir -p "$dest"
local f
while IFS= read -r -d '' f; do
slug_allowed "$(basename "$f" .md)" || continue
install_file "$f" "$dest/"
incr count
done < <(find "$src" -maxdepth 1 -name "*.md" -print0)
ok "Gemini CLI: $count agents -> $dest"
}
install_opencode() {
local src="$INTEGRATIONS/opencode"
local dest; dest="$(resolve_dest opencode "${PWD}/.opencode/agents")"
local count=0
[[ -d "$src" ]] || { err "integrations/opencode missing. Run convert.sh first."; return 1; }
# Support both flat layout (integrations/opencode/*.md) and nested (integrations/opencode/agents/*.md)
local search_dir="$src"
[[ -d "$src/agents" ]] && search_dir="$src/agents"
mkdir -p "$dest"
local f base
while IFS= read -r -d '' f; do
base="$(basename "$f")"
[[ "$base" == "README.md" ]] && continue
slug_allowed "${base%.md}" || continue
install_file "$f" "$dest/"; incr count
done < <(find "$search_dir" -maxdepth 1 -name "*.md" -print0)
if (( count == 0 )); then
warn "OpenCode: no agent files found in $search_dir. Run convert.sh --tool opencode first."
else
ok "OpenCode: $count agents -> $dest"
fi
capacity_warn opencode "$count"
warn "OpenCode: project-scoped. Run from your project root to install there."
}
install_openclaw() {
local src="$INTEGRATIONS/openclaw"
local dest; dest="$(resolve_dest openclaw "${HOME}/.openclaw/agency-agents")"
local count=0
local existing_agents=""
[[ -d "$src" ]] || { err "integrations/openclaw missing. Run convert.sh first."; return 1; }
mkdir -p "$dest"
if command -v openclaw >/dev/null 2>&1; then
existing_agents=$'\n'"$(openclaw agents list --json 2>/dev/null | sed -n 's/^[[:space:]]*\"id\": \"\\([^\"]*\\)\".*/\\1/p')"$'\n'
fi
local d
while IFS= read -r -d '' d; do
local name; name="$(basename "$d")"
slug_allowed "$name" || continue
[[ -f "$d/SOUL.md" && -f "$d/AGENTS.md" && -f "$d/IDENTITY.md" ]] || continue
mkdir -p "$dest/$name"
install_file "$d/SOUL.md" "$dest/$name/SOUL.md"
install_file "$d/AGENTS.md" "$dest/$name/AGENTS.md"
install_file "$d/IDENTITY.md" "$dest/$name/IDENTITY.md"
if command -v openclaw >/dev/null 2>&1; then
if [[ "$existing_agents" != *$'\n'"$name"$'\n'* ]]; then
openclaw agents add "$name" --workspace "$dest/$name" --non-interactive || true
fi
fi
(( count++ )) || true
done < <(find "$src" -mindepth 1 -maxdepth 1 -type d -print0)
if (( count == 0 )); then
err "integrations/openclaw contains no generated workspaces. Run ./scripts/convert.sh --tool openclaw first."
return 1
fi
ok "OpenClaw: $count workspaces -> $dest"
if command -v openclaw >/dev/null 2>&1; then
warn "OpenClaw: run 'openclaw gateway restart' to activate new agents"
fi
}
install_cursor() {
local src="$INTEGRATIONS/cursor/rules"
local dest; dest="$(resolve_dest cursor "${PWD}/.cursor/rules")"
local count=0
[[ -d "$src" ]] || { err "integrations/cursor missing. Run convert.sh first."; return 1; }
mkdir -p "$dest"
local f
while IFS= read -r -d '' f; do
slug_allowed "$(basename "$f" .mdc)" || continue
install_file "$f" "$dest/"; incr count
done < <(find "$src" -maxdepth 1 -name "*.mdc" -print0)
ok "Cursor: $count rules -> $dest"
warn "Cursor: project-scoped. Run from your project root to install there."
}
install_aider() {
local src="$INTEGRATIONS/aider/CONVENTIONS.md"
local dest="${PWD}/CONVENTIONS.md"
[[ -f "$src" ]] || { err "integrations/aider/CONVENTIONS.md missing. Run convert.sh first."; return 1; }
if [[ -f "$dest" ]]; then
warn "Aider: CONVENTIONS.md already exists at $dest (remove to reinstall)."
return 0
fi
install_file "$src" "$dest"
ok "Aider: installed -> $dest"
$SELECTION_ACTIVE && warn "Aider: single-file format — team/agent filtering N/A (installs the full roster)."
warn "Aider: project-scoped. Run from your project root to install there."
}
install_windsurf() {
local src="$INTEGRATIONS/windsurf/.windsurfrules"
local dest="${PWD}/.windsurfrules"
[[ -f "$src" ]] || { err "integrations/windsurf/.windsurfrules missing. Run convert.sh first."; return 1; }
if [[ -f "$dest" ]]; then
warn "Windsurf: .windsurfrules already exists at $dest (remove to reinstall)."
return 0
fi
install_file "$src" "$dest"
ok "Windsurf: installed -> $dest"
$SELECTION_ACTIVE && warn "Windsurf: single-file format — team/agent filtering N/A (installs the full roster)."
warn "Windsurf: project-scoped. Run from your project root to install there."
}
install_qwen() {
local src="$INTEGRATIONS/qwen/agents"
local dest; dest="$(resolve_dest qwen "${PWD}/.qwen/agents")"
local count=0
[[ -d "$src" ]] || { err "integrations/qwen missing. Run convert.sh first."; return 1; }
mkdir -p "$dest"
local f
while IFS= read -r -d '' f; do
slug_allowed "$(basename "$f" .md)" || continue
install_file "$f" "$dest/"
incr count
done < <(find "$src" -maxdepth 1 -name "*.md" -print0)
ok "Qwen Code: installed $count agents to $dest"
warn "Qwen Code: project-scoped. Run from your project root to install there."
warn "Tip: Run '/agents manage' in Qwen Code to refresh, or restart session"
}
install_kimi() {
local src="$INTEGRATIONS/kimi"
local dest; dest="$(resolve_dest kimi "${HOME}/.config/kimi/agents")"
local count=0
[[ -d "$src" ]] || { err "integrations/kimi missing. Run convert.sh first."; return 1; }
mkdir -p "$dest"
local d
while IFS= read -r -d '' d; do
local name; name="$(basename "$d")"
slug_allowed "$name" || continue
mkdir -p "$dest/$name"
install_file "$d/agent.yaml" "$dest/$name/agent.yaml"
install_file "$d/system.md" "$dest/$name/system.md"
incr count
done < <(find "$src" -mindepth 1 -maxdepth 1 -type d -print0)
ok "Kimi Code: installed $count agents to $dest"
ok "Usage: kimi --agent-file ~/.config/kimi/agents//agent.yaml"
}
install_codex() {
local src="$INTEGRATIONS/codex/agents"
local dest; dest="$(resolve_dest codex "${HOME}/.codex/agents")"
local count=0
[[ -d "$src" ]] || { err "integrations/codex missing. Run convert.sh first."; return 1; }
mkdir -p "$dest"
local f
while IFS= read -r -d '' f; do
slug_allowed "$(basename "$f" .toml)" || continue
install_file "$f" "$dest/"
incr count
done < <(find "$src" -maxdepth 1 -name "*.toml" -print0)
ok "Codex: $count agents -> $dest"
}
install_tool() {
ensure_converted "$1"
case "$1" in
claude-code) install_claude_code ;;
copilot) install_copilot ;;
antigravity) install_antigravity ;;
gemini-cli) install_gemini_cli ;;
opencode) install_opencode ;;
openclaw) install_openclaw ;;
cursor) install_cursor ;;
aider) install_aider ;;
windsurf) install_windsurf ;;
qwen) install_qwen ;;
kimi) install_kimi ;;
codex) install_codex ;;
esac
}
# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------
main() {
local tool="all"
local interactive_mode="auto"
local use_parallel=false
local parallel_jobs
parallel_jobs="$(parallel_jobs_default)"
local list_what=""
while [[ $# -gt 0 ]]; do
case "$1" in
--tool) tool="${2:?'--tool requires a value'}"; shift 2; interactive_mode="no" ;;
--division)
local _d
IFS=',' read -ra _divs <<< "${2:?'--division requires a value'}"
for _d in "${_divs[@]}"; do
_d="$(printf '%s' "$_d" | xargs)"; [[ -z "$_d" ]] && continue
validate_division "$_d"; FILTER_DIVISIONS+=("$_d")
done
interactive_mode="no"; shift 2 ;;
--agent)
local _a
IFS=',' read -ra _ags <<< "${2:?'--agent requires a value'}"
for _a in "${_ags[@]}"; do
_a="$(printf '%s' "$_a" | xargs)"; [[ -n "$_a" ]] && FILTER_AGENTS+=("$_a")
done
interactive_mode="no"; shift 2 ;;
--agents-file) AGENTS_FILE="${2:?'--agents-file requires a value'}"; interactive_mode="no"; shift 2 ;;
--link) USE_LINK=true; shift ;;
--path) OVERRIDE_PATH="${2:?'--path requires a value'}"; shift 2 ;;
--no-convert) AUTO_CONVERT=false; shift ;;
--dry-run) DRY_RUN=true; interactive_mode="no"; shift ;;
--list) if [[ -z "${2:-}" || "${2:-}" == --* ]]; then list_what="all"; shift; else list_what="$2"; shift 2; fi ;;
--interactive) interactive_mode="yes"; shift ;;
--no-interactive) interactive_mode="no"; shift ;;
--parallel) use_parallel=true; shift ;;
--jobs) parallel_jobs="${2:?'--jobs requires a value'}"; shift 2 ;;
--help|-h) usage ;;
*) err "Unknown option: $1"; usage ;;
esac
done
[[ -n "$list_what" ]] && { do_list "$list_what"; exit 0; }
build_selection
check_integrations
# Validate explicit tool
if [[ "$tool" != "all" ]]; then
local valid=false t
for t in "${ALL_TOOLS[@]}"; do [[ "$t" == "$tool" ]] && valid=true && break; done
if ! $valid; then
err "Unknown tool '$tool'. Valid: ${ALL_TOOLS[*]}"
exit 1
fi
fi
# Decide whether to show interactive UI
local use_interactive=false
if [[ "$interactive_mode" == "yes" ]]; then
use_interactive=true
elif [[ "$interactive_mode" == "auto" && -t 0 && -t 1 && "$tool" == "all" ]]; then
use_interactive=true
fi
SELECTED_TOOLS=()
if $use_interactive && interactive_wizard; then
: # wizard committed SELECTED_TOOLS + FILTER_DIVISIONS
elif [[ "$tool" != "all" ]]; then
SELECTED_TOOLS=("$tool")
else
# Non-interactive (or no TTY): auto-detect
header "The Agency -- Scanning for installed tools..."
printf "\n"
local t
for t in "${ALL_TOOLS[@]}"; do
if is_detected "$t" 2>/dev/null; then
SELECTED_TOOLS+=("$t")
printf " ${C_GREEN}[*]${C_RESET} %s ${C_DIM}detected${C_RESET}\n" "$(tool_label "$t")"
else
printf " ${C_DIM}[ ] %s not found${C_RESET}\n" "$(tool_label "$t")"
fi
done
fi
if [[ ${#SELECTED_TOOLS[@]} -eq 0 ]]; then
warn "No tools selected or detected. Nothing to install."
printf "\n"
dim " Tip: use --tool to force-install a specific tool."
dim " Available: ${ALL_TOOLS[*]}"
exit 0
fi
# --dry-run: print the plan and exit without writing anything.
if $DRY_RUN; then
local agents; agents="$(selected_agent_count)"
printf "\n"; header "The Agency -- Dry run (nothing written)"
printf " Tools: %s\n" "${SELECTED_TOOLS[*]}"
if $SELECTION_ACTIVE; then
[[ ${#FILTER_DIVISIONS[@]} -gt 0 ]] && printf " Teams: %s\n" "${FILTER_DIVISIONS[*]}"
[[ ${#FILTER_AGENTS[@]} -gt 0 ]] && printf " Agents: %s\n" "${FILTER_AGENTS[*]}"
[[ -n "$AGENTS_FILE" ]] && printf " File: %s\n" "$AGENTS_FILE"
else
printf " Teams: all (%s)\n" "${#ALL_DIVISIONS[@]}"
fi
printf " Agents: %s Mode: %s\n" "$agents" "$($USE_LINK && echo symlink || echo copy)"
local _t _cap
for _t in "${SELECTED_TOOLS[@]}"; do
_cap="$(tool_cap "$_t")"
[[ "$_cap" -gt 0 && "$agents" -gt "$_cap" ]] && \
warn "$_t caps ~$_cap — ~$(( agents - _cap )) of $agents won't register (anomalyco/opencode#27988)"
done
printf "\n"; exit 0
fi
# When parent runs install.sh --parallel, it spawns workers with AGENCY_INSTALL_WORKER=1
# so each worker only runs install_tool(s) and skips header/done box (avoids duplicate output).
if [[ -n "${AGENCY_INSTALL_WORKER:-}" ]]; then
local t
for t in "${SELECTED_TOOLS[@]}"; do
install_tool "$t"
done
return 0
fi
printf "\n"
header "The Agency -- Installing agents"
printf " Repo: %s\n" "$REPO_ROOT"
local n_selected=${#SELECTED_TOOLS[@]}
printf " Installing: %s\n" "${SELECTED_TOOLS[*]}"
if $SELECTION_ACTIVE; then
[[ ${#FILTER_DIVISIONS[@]} -gt 0 ]] && printf " Teams: %s\n" "${FILTER_DIVISIONS[*]}"
printf " Agents: %s of %s\n" "$(selected_agent_count)" "$(selected_agent_count_all)"
fi
$USE_LINK && printf " Mode: ${C_CYAN}symlink${C_RESET} (--link)\n"
if $use_parallel; then
ok "Installing $n_selected tools in parallel (output buffered per tool)."
fi
printf "\n"
local installed=0 t i=0
if $use_parallel; then
local install_out_dir
install_out_dir="$(mktemp -d)"
export AGENCY_INSTALL_OUT_DIR="$install_out_dir"
export AGENCY_INSTALL_SCRIPT="$SCRIPT_DIR/install.sh"
export AGENCY_INSTALL_EXTRA="$(worker_flags)"
printf '%s\n' "${SELECTED_TOOLS[@]}" | xargs -P "$parallel_jobs" -I {} sh -c 'AGENCY_INSTALL_WORKER=1 "$AGENCY_INSTALL_SCRIPT" --tool "{}" --no-interactive $AGENCY_INSTALL_EXTRA > "$AGENCY_INSTALL_OUT_DIR/{}" 2>&1'
for t in "${SELECTED_TOOLS[@]}"; do
[[ -f "$install_out_dir/$t" ]] && cat "$install_out_dir/$t"
done
rm -rf "$install_out_dir"
installed=$n_selected
else
for t in "${SELECTED_TOOLS[@]}"; do
(( i++ )) || true
progress_bar "$i" "$n_selected"
printf "\n"
printf " ${C_DIM}[%s/%s]${C_RESET} %s\n" "$i" "$n_selected" "$t"
install_tool "$t"
(( installed++ )) || true
done
fi
# Done box
local msg=" Done! Installed $installed tool(s)."
printf "\n"
box_top
box_row "${C_GREEN}${C_BOLD}${msg}${C_RESET}"
box_bot
printf "\n"
dim " Run ./scripts/convert.sh to regenerate after adding or editing agents."
printf "\n"
}
main "$@"