aider.zsh

  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}