feat(agents): add aider, llxprt, and qwen agents
Amolith
created 2 months ago
- Add aider agent with dual model support (architect + editor)
- Add llxprt agent with openai-compatible config
- Add qwen agent with environment variable config
- Update shell completions to include all new agents
- Follow established agent configuration patterns
Assisted-by: GLM 4.6 via Crush
Change summary
completions/synu.fish | 28 +++++-
functions/_synu_agents/aider.fish | 147 ++++++++++++++++++++++++++++++++
functions/_synu_agents/llxprt.fish | 92 ++++++++++++++++++++
functions/_synu_agents/qwen.fish | 93 ++++++++++++++++++++
4 files changed, 355 insertions(+), 5 deletions(-)
Detailed changes
@@ -4,14 +4,14 @@
# Provide basic usage information for the wrapper itself
# Suggest known agents and interactive mode for the first argument
-complete -c synu -n "not __fish_seen_subcommand_from i claude crush amp octo codex" \
+complete -c synu -n "not __fish_seen_subcommand_from i claude opencode aider llxprt qwen crush amp octo codex" \
-a "i" -d "Interactive model selection"
-complete -c synu -n "not __fish_seen_subcommand_from i claude crush amp octo codex" \
- -a "claude crush amp octo codex" -d "AI agent to wrap"
+complete -c synu -n "not __fish_seen_subcommand_from i claude opencode aider llxprt qwen crush amp octo codex" \
+ -a "claude opencode aider llxprt qwen crush amp octo codex" -d "AI agent to wrap"
# After "i" subcommand, suggest agents
-complete -c synu -n "__fish_seen_subcommand_from i; and not __fish_seen_subcommand_from claude crush amp octo codex" \
- -a "claude crush amp octo codex" -d "AI agent"
+complete -c synu -n "__fish_seen_subcommand_from i; and not __fish_seen_subcommand_from claude opencode aider llxprt qwen crush amp octo codex" \
+ -a "claude opencode aider llxprt qwen crush amp octo codex" -d "AI agent"
# Claude-specific flags (when claude is the agent)
complete -c synu -n "__fish_seen_subcommand_from claude" \
@@ -29,3 +29,21 @@ complete -c synu -n "__fish_seen_subcommand_from claude" \
# Inherit claude completions for claude subcommand
complete -c synu -n "__fish_seen_subcommand_from claude" -w claude
+
+# OpenCode-specific flags (when opencode is the agent)
+complete -c synu -n "__fish_seen_subcommand_from opencode" \
+ -s m -l model -r -d "Override model"
+
+# Aider-specific flags (when aider is the agent)
+complete -c synu -n "__fish_seen_subcommand_from aider" \
+ -s m -l model -r -d "Main model"
+complete -c synu -n "__fish_seen_subcommand_from aider" \
+ -s e -l editor-model -r -d "Editor model (enables architect + editor mode)"
+
+# llxprt-specific flags (when llxprt is the agent)
+complete -c synu -n "__fish_seen_subcommand_from llxprt" \
+ -s m -l model -r -d "Override model"
+
+# Qwen Code-specific flags (when qwen is the agent)
+complete -c synu -n "__fish_seen_subcommand_from qwen" \
+ -s m -l model -r -d "Override model"
@@ -0,0 +1,147 @@
+# SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+#
+# SPDX-License-Identifier: Unlicense
+
+# Aider agent definition for synu
+# Provides model configuration for routing through Synthetic API
+# Aider accepts model via CLI flags and API config via environment variables
+
+# Source cache functions
+source (status dirname)/../_synu_cache.fish
+
+# Fallback defaults (used when no cache entry exists)
+set -g _synu_aider_fallback_model "hf:MiniMaxAI/MiniMax-M2"
+set -g _synu_aider_fallback_editor_model ""
+
+function _synu_aider_default --description "Get default model: _synu_aider_default slot"
+ set -l slot $argv[1]
+ set -l cached (_synu_cache_get aider $slot)
+ if test $status -eq 0
+ echo $cached
+ else
+ set -l var_name _synu_aider_fallback_$slot
+ echo $$var_name
+ end
+end
+
+function _synu_agent_aider_flags --description "Return argparse-compatible flag specification"
+ echo "m/model="
+ echo "e/editor-model="
+end
+
+function _synu_agent_aider_env_vars --description "Return list of environment variables set by configure"
+ echo OPENAI_API_BASE
+ echo OPENAI_API_KEY
+end
+
+function _synu_agent_aider_configure --description "Configure Aider environment variables and model selection"
+ # Parse flags passed from main synu
+ argparse 'm/model=' 'e/editor-model=' -- $argv
+ or return 1
+
+ # Start with defaults (from cache or fallback)
+ set -g _synu_aider_selected_model (_synu_aider_default model)
+ set -g _synu_aider_selected_editor_model (_synu_aider_default editor_model)
+
+ # Apply overrides if provided
+ if set -q _flag_model
+ set -g _synu_aider_selected_model $_flag_model
+ end
+ if set -q _flag_editor_model
+ set -g _synu_aider_selected_editor_model $_flag_editor_model
+ end
+
+ # Export environment variables for Aider
+ set -gx OPENAI_API_BASE "https://api.synthetic.new/openai/v1"
+ set -gx OPENAI_API_KEY $SYNTHETIC_API_KEY
+end
+
+function _synu_agent_aider_args --description "Return CLI arguments to pass to aider"
+ # Always return --model
+ echo --model
+ echo "openai/$_synu_aider_selected_model"
+
+ # Return --editor-model if set
+ if test -n "$_synu_aider_selected_editor_model"
+ echo --editor-model
+ echo "openai/$_synu_aider_selected_editor_model"
+ end
+end
+
+function _synu_agent_aider_interactive --description "Interactive model selection using gum"
+ # Check for gum
+ if not command -q gum
+ echo "Error: gum is required for interactive mode. Install: https://github.com/charmbracelet/gum" >&2
+ return 1
+ end
+
+ # Fetch available models
+ set -l models_json (gum spin --spinner dot --title "Fetching models..." -- \
+ curl -s -H "Authorization: Bearer $SYNTHETIC_API_KEY" \
+ "https://api.synthetic.new/openai/v1/models")
+ or return 1
+
+ set -l model_names (echo $models_json | jq -r '.data[].name')
+ or return 1
+
+ # Ask if editor model should be set
+ set -l use_editor_model (gum choose --limit 1 \
+ --header "Aider has two modes. Set editor model?" \
+ "No (single model mode)" "Yes (architect + editor mode)")
+ or return 1
+
+ # Build flags array
+ set -l flags
+
+ # Select main model
+ set -l model_name (printf "%s\n" $model_names | \
+ gum filter --limit 1 --header "Select main model for Aider" \
+ --placeholder "Filter models...")
+ or return 1
+
+ set -l model_id (echo $models_json | \
+ jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
+
+ if test -z "$model_id"
+ echo "Error: Could not find model ID" >&2
+ return 1
+ end
+
+ set flags $flags --model=$model_id
+
+ # Select editor model if requested
+ if test "$use_editor_model" = "Yes (architect + editor mode)"
+ set -l editor_model_name (printf "%s\n" $model_names | \
+ gum filter --limit 1 --header "Select editor model for Aider" \
+ --placeholder "Filter models...")
+ or return 1
+
+ set -l editor_model_id (echo $models_json | \
+ jq -r --arg name "$editor_model_name" '.data[] | select(.name == $name) | .id')
+
+ if test -z "$editor_model_id"
+ echo "Error: Could not find editor model ID" >&2
+ return 1
+ end
+
+ set flags $flags --editor-model=$editor_model_id
+ end
+
+ # Offer to save as defaults
+ if gum confirm "Save as default for 'aider'?"
+ for flag in $flags
+ # Parse --key=value format
+ set -l parts (string match -r -- '^--([^=]+)=(.+)$' $flag)
+ if test -n "$parts[2]"
+ set -l key $parts[2]
+ set -l value $parts[3]
+ # Replace hyphens with underscores for cache keys
+ set key (string replace -a '-' '_' $key)
+ _synu_cache_set aider $key $value
+ end
+ end
+ end
+
+ # Output flags for caller to use
+ echo $flags
+end
@@ -0,0 +1,92 @@
+# SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+#
+# SPDX-License-Identifier: Unlicense
+
+# llxprt agent definition for synu
+# Provides model configuration for routing through Synthetic API
+# llxprt only accepts configuration via CLI flags
+
+# Source cache functions
+source (status dirname)/../_synu_cache.fish
+
+# Fallback default (used when no cache entry exists)
+set -g _synu_llxprt_fallback_model "hf:MiniMaxAI/MiniMax-M2"
+
+function _synu_llxprt_default --description "Get default model"
+ set -l cached (_synu_cache_get llxprt model)
+ if test $status -eq 0
+ echo $cached
+ else
+ echo $_synu_llxprt_fallback_model
+ end
+end
+
+function _synu_agent_llxprt_flags --description "Return argparse-compatible flag specification"
+ echo "m/model="
+end
+
+function _synu_agent_llxprt_configure --description "Configure llxprt model selection"
+ # Parse flags passed from main synu
+ argparse 'm/model=' -- $argv
+ or return 1
+
+ # Start with default (from cache or fallback)
+ set -g _synu_llxprt_selected_model (_synu_llxprt_default)
+
+ # Apply override if provided
+ if set -q _flag_model
+ set -g _synu_llxprt_selected_model $_flag_model
+ end
+end
+
+function _synu_agent_llxprt_args --description "Return CLI arguments to pass to llxprt"
+ # Return --provider, --baseurl, and --model flags
+ echo --provider
+ echo openai
+ echo --baseurl
+ echo "https://api.synthetic.new/openai/v1"
+ echo --model
+ echo $_synu_llxprt_selected_model
+end
+
+function _synu_agent_llxprt_interactive --description "Interactive model selection using gum"
+ # Check for gum
+ if not command -q gum
+ echo "Error: gum is required for interactive mode. Install: https://github.com/charmbracelet/gum" >&2
+ return 1
+ end
+
+ # Fetch available models
+ set -l models_json (gum spin --spinner dot --title "Fetching models..." -- \
+ curl -s -H "Authorization: Bearer $SYNTHETIC_API_KEY" \
+ "https://api.synthetic.new/openai/v1/models")
+ or return 1
+
+ set -l model_names (echo $models_json | jq -r '.data[].name')
+ or return 1
+
+ # Select model
+ set -l model_name (printf "%s\n" $model_names | \
+ gum filter --limit 1 --header "Select model for llxprt" \
+ --placeholder "Filter models...")
+ or return 1
+
+ set -l model_id (echo $models_json | \
+ jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
+
+ if test -z "$model_id"
+ echo "Error: Could not find model ID" >&2
+ return 1
+ end
+
+ # Build flags
+ set -l flags --model=$model_id
+
+ # Offer to save as default
+ if gum confirm "Save as default for 'llxprt'?"
+ _synu_cache_set llxprt model $model_id
+ end
+
+ # Output flags for caller to use
+ echo $flags
+end
@@ -0,0 +1,93 @@
+# SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+#
+# SPDX-License-Identifier: Unlicense
+
+# Qwen Code agent definition for synu
+# Provides model configuration for routing through Synthetic API
+# Qwen Code only accepts configuration via environment variables
+
+# Source cache functions
+source (status dirname)/../_synu_cache.fish
+
+# Fallback default (used when no cache entry exists)
+set -g _synu_qwen_fallback_model "hf:MiniMaxAI/MiniMax-M2"
+
+function _synu_qwen_default --description "Get default model"
+ set -l cached (_synu_cache_get qwen model)
+ if test $status -eq 0
+ echo $cached
+ else
+ echo $_synu_qwen_fallback_model
+ end
+end
+
+function _synu_agent_qwen_flags --description "Return argparse-compatible flag specification"
+ echo "m/model="
+end
+
+function _synu_agent_qwen_env_vars --description "Return list of environment variables set by configure"
+ echo OPENAI_API_KEY
+ echo OPENAI_BASE_URL
+ echo OPENAI_MODEL
+end
+
+function _synu_agent_qwen_configure --description "Configure Qwen Code environment variables"
+ # Parse flags passed from main synu
+ argparse 'm/model=' -- $argv
+ or return 1
+
+ # Start with default (from cache or fallback)
+ set -l model (_synu_qwen_default)
+
+ # Apply override if provided
+ if set -q _flag_model
+ set model $_flag_model
+ end
+
+ # Export environment variables for Qwen Code
+ set -gx OPENAI_API_KEY $SYNTHETIC_API_KEY
+ set -gx OPENAI_BASE_URL "https://api.synthetic.new/openai/v1"
+ set -gx OPENAI_MODEL $model
+end
+
+function _synu_agent_qwen_interactive --description "Interactive model selection using gum"
+ # Check for gum
+ if not command -q gum
+ echo "Error: gum is required for interactive mode. Install: https://github.com/charmbracelet/gum" >&2
+ return 1
+ end
+
+ # Fetch available models
+ set -l models_json (gum spin --spinner dot --title "Fetching models..." -- \
+ curl -s -H "Authorization: Bearer $SYNTHETIC_API_KEY" \
+ "https://api.synthetic.new/openai/v1/models")
+ or return 1
+
+ set -l model_names (echo $models_json | jq -r '.data[].name')
+ or return 1
+
+ # Select model
+ set -l model_name (printf "%s\n" $model_names | \
+ gum filter --limit 1 --header "Select model for Qwen Code" \
+ --placeholder "Filter models...")
+ or return 1
+
+ set -l model_id (echo $models_json | \
+ jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
+
+ if test -z "$model_id"
+ echo "Error: Could not find model ID" >&2
+ return 1
+ end
+
+ # Build flags
+ set -l flags --model=$model_id
+
+ # Offer to save as default
+ if gum confirm "Save as default for 'qwen'?"
+ _synu_cache_set qwen model $model_id
+ end
+
+ # Output flags for caller to use
+ echo $flags
+end