1# SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2#
3# SPDX-License-Identifier: Unlicense
4
5# Aider agent definition for synu
6# Provides model configuration for routing through Synthetic API
7# Aider accepts model via CLI flags and API config via environment variables
8
9# Fallback defaults (used when no cache entry exists)
10typeset -g _SYNU_AIDER_FALLBACK_MODEL="hf:zai-org/GLM-4.6"
11typeset -g _SYNU_AIDER_FALLBACK_EDITOR_MODEL="hf:deepseek-ai/DeepSeek-V3.1-Terminus"
12
13_synu_aider_default() {
14 local slot=$1
15 local cached
16 cached=$(_synu_cache_get aider "${slot}")
17 if [[ $? -eq 0 ]]; then
18 echo "${cached}"
19 else
20 local var_name="_SYNU_AIDER_FALLBACK_${(U)slot//-/_}"
21 echo "${(P)var_name}"
22 fi
23}
24
25_synu_agent_aider_flags() {
26 # Note: no short flags to avoid collision with aider's -m (--message) and -e (--env-file)
27 echo "model="
28 echo "editor-model="
29}
30
31_synu_agent_aider_env_vars() {
32 echo OPENAI_API_BASE
33 echo OPENAI_API_KEY
34}
35
36_synu_agent_aider_configure() {
37 local -A opts
38
39 # Parse flags passed from main synu
40 while [[ $# -gt 0 ]]; do
41 case "$1" in
42 --model=*) opts[model]="${1#*=}" ;;
43 --editor-model=*) opts[editor_model]="${1#*=}" ;;
44 esac
45 shift
46 done
47
48 # Start with defaults (from cache or fallback)
49 typeset -g _SYNU_AIDER_SELECTED_MODEL=$(_synu_aider_default model)
50 typeset -g _SYNU_AIDER_SELECTED_EDITOR_MODEL=$(_synu_aider_default editor_model)
51
52 # Apply overrides if provided
53 [[ -n "${opts[model]}" ]] && typeset -g _SYNU_AIDER_SELECTED_MODEL="${opts[model]}"
54 [[ -n "${opts[editor_model]}" ]] && typeset -g _SYNU_AIDER_SELECTED_EDITOR_MODEL="${opts[editor_model]}"
55
56 # Export environment variables for Aider
57 export OPENAI_API_BASE="https://api.synthetic.new/openai/v1"
58 export OPENAI_API_KEY="${SYNTHETIC_API_KEY}"
59}
60
61_synu_agent_aider_args() {
62 # Always return --model
63 echo --model
64 echo "openai/${_SYNU_AIDER_SELECTED_MODEL}"
65
66 # Return --editor-model if set
67 if [[ -n "${_SYNU_AIDER_SELECTED_EDITOR_MODEL}" ]]; then
68 echo --editor-model
69 echo "openai/${_SYNU_AIDER_SELECTED_EDITOR_MODEL}"
70 fi
71}
72
73_synu_agent_aider_interactive() {
74 # Check for gum
75 if ! (( $+commands[gum] )); then
76 print -u2 "Error: gum is required for interactive mode. Install: https://github.com/charmbracelet/gum"
77 return 1
78 fi
79
80 # Fetch available models
81 local models_json
82 models_json=$(gum spin --spinner dot --title "Fetching models..." -- \
83 curl -s -H "Authorization: Bearer ${SYNTHETIC_API_KEY}" \
84 "https://api.synthetic.new/openai/v1/models")
85 [[ $? -ne 0 ]] && return 1
86
87 local -a model_names
88 model_names=("${(@f)$(echo "${models_json}" | jq -r '.data[].name')}")
89 [[ $? -ne 0 ]] && return 1
90
91 # Ask if editor model should be set
92 local use_editor_model
93 use_editor_model=$(gum choose --limit 1 \
94 --header "Aider has two modes. Set editor model?" \
95 "No (single model mode)" "Yes (architect + editor mode)")
96 [[ $? -ne 0 ]] && return 1
97
98 # Build flags array
99 local -a flags=()
100
101 # Get current models for display
102 local current_model_id=$(_synu_aider_default model)
103 local current_model_name=$(echo "${models_json}" | \
104 jq -r --arg id "${current_model_id}" '.data[] | select(.id == $id) | .name // "unknown"')
105 local current_editor_id=$(_synu_aider_default editor_model)
106 local current_editor_name=$(echo "${models_json}" | \
107 jq -r --arg id "${current_editor_id}" '.data[] | select(.id == $id) | .name // "unknown"')
108
109 # Select main model
110 local model_name
111 model_name=$(printf "%s\n" "${model_names[@]}" | \
112 gum filter --limit 1 --header "Select main model for Aider (current: ${current_model_name})" \
113 --placeholder "Filter models...")
114 [[ $? -ne 0 ]] && return 1
115
116 local model_id
117 model_id=$(echo "${models_json}" | \
118 jq -r --arg name "${model_name}" '.data[] | select(.name == $name) | .id')
119
120 if [[ -z "${model_id}" ]]; then
121 print -u2 "Error: Could not find model ID"
122 return 1
123 fi
124
125 flags+=(--model="${model_id}")
126
127 # Select editor model if requested
128 if [[ "${use_editor_model}" == "Yes (architect + editor mode)" ]]; then
129 local editor_model_name
130 editor_model_name=$(printf "%s\n" "${model_names[@]}" | \
131 gum filter --limit 1 --header "Select editor model for Aider (current: ${current_editor_name})" \
132 --placeholder "Filter models...")
133 [[ $? -ne 0 ]] && return 1
134
135 local editor_model_id
136 editor_model_id=$(echo "${models_json}" | \
137 jq -r --arg name "${editor_model_name}" '.data[] | select(.name == $name) | .id')
138
139 if [[ -z "${editor_model_id}" ]]; then
140 print -u2 "Error: Could not find editor model ID"
141 return 1
142 fi
143
144 flags+=(--editor-model="${editor_model_id}")
145 fi
146
147 # Offer to save as defaults
148 if gum confirm "Save as default for 'aider'?"; then
149 local flag
150 for flag in "${flags[@]}"; do
151 local key="${flag%%=*}"
152 key="${key#--}"
153 local value="${flag#*=}"
154 # Replace hyphens with underscores for cache keys
155 key="${key//-/_}"
156 _synu_cache_set aider "${key}" "${value}"
157 done
158 fi
159
160 # Output flags for caller to use
161 printf '%s\n' "${flags[@]}"
162}