Detailed changes
@@ -8,124 +8,60 @@ SPDX-License-Identifier: Unlicense
## Project Overview
-**synclaude.fish** is a Fish shell wrapper for the `claude` CLI that routes requests through [Synthetic.new](https://synthetic.new)'s API. It enables using alternative AI models (like GLM-4.6, Llama, Qwen) through the Claude CLI by proxying requests through Synthetic's Anthropic-compatible endpoint.
+**synclaude.fish** is a Fish shell wrapper for the `claude` CLI that routes requests through [Synthetic.new](https://synthetic.new)'s API. It enables using alternative AI models through the Claude CLI by proxying requests through Synthetic's Anthropic-compatible endpoint.
### Key Functionality
-- Defaults all Claude model tiers (Opus, Sonnet, Haiku, Sub-agent) to `hf:zai-org/GLM-4.6`
+- Configurable model defaults for each Claude tier with persistent caching
- Provides interactive TUI for model selection using `gum`
-- Supports group-based (`--large`/`--light`) and individual model overrides
+- Supports group-based (`--heavy`/`--medium`/`--light`) and individual model overrides
- Displays session API usage and remaining quota after each run
+- `synclaude u` shows current quota usage
- Wraps `claude` CLI completely - all unknown flags pass through
-## Project Structure
+### Default Models (Fallbacks)
-This is a standard Fish plugin with the following structure:
-
-```
-synclaude.fish/
-├── README.md # User documentation
-├── functions/
-│ └── synclaude.fish # Main wrapper function (~156 lines)
-└── completions/
- └── synclaude.fish # Tab completions (~15 lines)
-```
+- **Opus**: `hf:moonshotai/Kimi-K2-Thinking`
+- **Sonnet**: `hf:zai-org/GLM-4.6`
+- **Haiku**: `hf:deepseek-ai/DeepSeek-V3.1-Terminus`
+- **Sub-agent**: `hf:zai-org/GLM-4.6`
### Architecture
**Control Flow:**
-1. **Interactive mode** (`synclaude i`):
-- Fetches available models from Synthetic API
-- Prompts user to choose Groups vs Individual models
-- Prompts for which groups/models to override
-- For each selection, filters available models with `gum filter`
-- Recursively calls `synclaude` with generated flags (lines 83-84)
-2. **Flag-based mode** (default):
-- Parses custom flags with `argparse --ignore-unknown` (lines 91-94)
-- Sets environment variables for Anthropic endpoint and model overrides
-- Fetches initial quota from Synthetic API
-- Executes `command claude $argv` (line 128)
-- Fetches final quota and displays usage stats with color-coding
+1. **Usage mode** (`synclaude u`):
+ - Fetches and displays current quota usage with colored badge
+2. **Interactive mode** (`synclaude i`):
+ - Fetches available models from Synthetic API
+ - Prompts user to choose Groups vs Individual models
+ - Prompts for which groups/models to override
+ - Shows current defaults in filter headers
+ - Offers to save selections as new defaults
+ - Recursively calls `synclaude` with generated flags
+3. **Flag-based mode** (default):
+ - Parses custom flags with `argparse --ignore-unknown`
+ - Loads defaults from cache (or fallbacks)
+ - Applies group then individual overrides
+ - Sets environment variables for Anthropic endpoint
+ - Fetches initial quota from Synthetic API
+ - Executes `command claude $argv`
+ - Fetches final quota and displays usage stats with badge-style color-coding
**Key Design Patterns:**
-- **Override precedence**: Individual flags override group flags, which override defaults (lines 107-121)
+- **Caching**: Persistent model preferences stored in `~/.config/synclaude/models.conf`
+- **Override precedence**: Individual flags override group flags, which override cached/fallback defaults
- **Error propagation**: Uses `or return` throughout to exit on command failures
-## Code Conventions
-
-### Fish Shell Patterns
-
-- **Local variables**: Always use `set -l` for function-local scope
-- **Export variables**: Use `set -lx` for locally-scoped exports (lines 96-104)
-- **Variable checking**: Use `set -q variable` to test existence (lines 107-121)
-- **Array removal**: Use `set -e argv[1]` to remove elements (line 6)
-- **Command substitution**: Use `(command)` not backticks
-- **Error handling**: Chain commands with `or return` for early exits
-- **Math operations**: Use `math` builtin with `-s0` for integer results (line 139)
-
-### Code Style
-
-- **Indentation**: 4 spaces (not tabs)
-- **Line length**: No hard limit, but keep logical blocks readable
-- **Variable naming**:
- - Descriptive names: `models_json`, `initial_requests`, `quota_color`
- - No abbreviations except common ones: `args` → `argv`, `arg` → `argv`
-- **Comments**:
- - Group logic blocks with descriptive comments (lines 88-90)
- - Inline comments for non-obvious conditions (lines 125, 132)
- - No redundant comments explaining obvious code
-
-### API Interaction Patterns
-
-All API calls wrapped in `gum spin` for UX:
-
-```fish
-set -l response (gum spin --spinner dot --title "Message..." -- curl -s -H "Authorization: Bearer $KEY" https://api.url)
-```
-
-API responses always:
-1. Use `jq -r` for raw (unquoted) output
-2. Provide fallback values with `// 0` or similar (lines 125, 132-133)
-3. Test for empty values after extraction (line 126)
-4. Redirect stderr to `/dev/null` if errors are expected (lines 124, 131)
-
-### Error Handling
-
-- **API failures**: Use `or return 1` to exit on curl/jq errors
-- **Null safety**: Always test extracted values before use (line 126)
-- **Propagate exit codes**: Return `$status` from wrapped command (line 84)
-- **Silent failures**: Only suppress stderr when errors are expected and handled
-
-## Completions
-
-File: `completions/synclaude.fish`
-
-- **Inherits from wrapped command**: `complete -c synclaude -w claude` (line 2)
-- **Subcommand prevention**: Use `-n "not __fish_seen_subcommand_from i"` to prevent completions after `i` (line 5)
-- **Flag completions**: `-s` (short), `-l` (long), `-r` (requires argument), `-d` (description)
-
-## Quota Display Logic
-
-Post-execution, displays:
-- **Session usage**: `final_requests - initial_requests`
-- **Remaining quota**: `limit - final_requests` (color-coded)
-
-Color coding (lines 141-146):
-- **Green**: <33% used
-- **Yellow**: 33-66% used
-- **Red**: >66% used
-
-Color logic uses `set_color` and `set_color normal` to wrap output (lines 151-153).
-
## Common Gotchas
-1. **`argparse --ignore-unknown` is critical**: Without it, unknown flags would error instead of passing through to `claude` (line 91)
-2. **Recursive call must use function name, not alias**: Line 83 calls `synclaude`, not `command synclaude` - this works because Fish functions can recursively invoke themselves
-3. **`_flag_*` variable naming**: `argparse` creates variables from flag names with `_flag_` prefix - must use exact names (lines 107-121)
-4. **Model ID vs Model Name**: Interactive mode fetches model names for display but must extract model IDs for API use (lines 30, 37, 53, 60, 67, 74)
+1. **`argparse --ignore-unknown` is critical**: Without it, unknown flags would error instead of passing through to `claude`
+2. **Recursive call must use function name, not alias**: `synclaude` not `command synclaude` - this works because Fish functions can recursively invoke themselves
+3. **`_flag_*` variable naming**: `argparse` creates variables from flag names with `_flag_` prefix - must use exact names
+4. **Model ID vs Model Name**: Interactive mode fetches model names for display but must extract model IDs for API use
5. **jq requires argument quoting**: In jq, use `--arg name "$var"` then `$name` in filter - don't interpolate directly into filter string
6. **Quota check timing**: Initial quota fetch happens before `claude` runs to calculate session usage. If it fails, script continues anyway
7. **`math` precision**: Use `-s0` for integer output to avoid decimal points in quota percentages
8. **Local exports don't persist**: `set -lx` variables only exist in function scope - won't affect parent shell
+9. **Cache file location**: Respects `XDG_CONFIG_HOME` if set, otherwise defaults to `~/.config/synclaude/`
@@ -10,15 +10,15 @@ A Fish wrapper for the `claude` CLI that routes requests through
[Synthetic.new](https://synthetic.new), enabling use of models like GLM-4.6
through `claude`.
-
+
## tl;dr
-- Defaults to GLM 4.6
+- Configurable defaults per model tier with persistent caching
- Displays session usage and remaining API quota after each run
-- Invoke as `synclaude i` for an interactive model picker
-- Configure models by group or individually through the interactive picker or
- flags
+- `synclaude u` shows current quota
+- Invoke as `synclaude i` for an interactive model picker with save-as-default option
+- Configure models by group or individually through the interactive picker or flags
## Requirements
@@ -52,6 +52,15 @@ set -gx SYNTHETIC_API_KEY your_api_key_here
## Usage
+### Check quota
+
+Run with `u` subcommand to see current API usage:
+
+```fish
+synclaude u
+# Usage: 42% (580/1000 remaining)
+```
+
### Basic
Use `synclaude` exactly like you would use `claude`:
@@ -64,8 +73,16 @@ synclaude
synclaude -p "What does functions/synclaude.fish do?"
```
-By default, all models (Opus, Sonnet, Haiku, and Sub-agent) use
-`hf:zai-org/GLM-4.6`.
+### Default Models
+
+| Tier | Default Model |
+|------|--------------|
+| Opus | `hf:moonshotai/Kimi-K2-Thinking` |
+| Sonnet | `hf:zai-org/GLM-4.6` |
+| Haiku | `hf:deepseek-ai/DeepSeek-V3.1-Terminus` |
+| Sub-agent | `hf:zai-org/GLM-4.6` |
+
+Defaults can be overridden with flags or saved persistently through interactive mode.
### Interactive overrides
@@ -77,18 +94,27 @@ synclaude i
1. Select whether to override models by group or by specific model
2. Select which ones to override
-3. Select what to override them with
+3. Select what to override them with (shows current defaults in header)
+4. Optionally save selections as new defaults
### Flags
-| Flag | Short | Description |
-| ---------- | ----- | ------------------------------------------- |
-| `--large` | `-L` | Override Opus, Sonnet, and Sub-agent models |
-| `--light` | `-l` | Override Haiku |
-| `--haiku` | `-H` | Override Haiku |
-| `--opus` | `-o` | Override Opus |
-| `--sonnet` | `-s` | Override Sonnet |
-| `--agent` | `-a` | Override sub-agent |
+#### Group flags
+
+| Flag | Short | Description |
+|------|-------|-------------|
+| `--heavy` | `-H` | Override Opus model |
+| `--medium` | `-M` | Override Sonnet and Sub-agent models |
+| `--light` | `-l` | Override Haiku model |
+
+#### Individual flags
+
+| Flag | Short | Description |
+|------|-------|-------------|
+| `--opus` | `-o` | Override Opus model |
+| `--sonnet` | `-s` | Override Sonnet model |
+| `--haiku` | `-k` | Override Haiku model |
+| `--agent` | `-a` | Override Sub-agent model |
All other flags are passed through to the `claude` command.
@@ -97,27 +123,30 @@ All other flags are passed through to the `claude` command.
#### By group
```fish
-# Set all "large" models (Opus, Sonnet, Sub-agent) to GLM-4.6
-synclaude --large hf:zai-org/GLM-4.6 "Your prompt"
+# Set heavy model (Opus) to a specific model
+synclaude --heavy hf:moonshotai/Kimi-K2-Thinking "Your prompt"
-# Set the "light" model (Haiku)
+# Set medium models (Sonnet, Sub-agent) to GLM-4.6
+synclaude --medium hf:zai-org/GLM-4.6 "Your prompt"
+
+# Set light model (Haiku)
synclaude --light hf:qwen/Qwen2.5-Coder-32B-Instruct "Your prompt"
-# Combine both
-synclaude --large hf:zai-org/GLM-4.6 --light hf:qwen/Qwen2.5-Coder-32B-Instruct "Your prompt"
+# Combine groups
+synclaude --heavy hf:moonshotai/Kimi-K2-Thinking --medium hf:zai-org/GLM-4.6 "Your prompt"
```
#### By individual model
```fish
# Override specific models
-synclaude --opus hf:zai-org/GLM-4.6 "Your prompt"
-synclaude --sonnet hf:meta-llama/Llama-3.3-70B-Instruct "Your prompt"
+synclaude --opus hf:moonshotai/Kimi-K2-Thinking "Your prompt"
+synclaude --sonnet hf:zai-org/GLM-4.6 "Your prompt"
synclaude --haiku hf:qwen/Qwen2.5-Coder-32B-Instruct "Your prompt"
synclaude --agent hf:zai-org/GLM-4.6 "Your prompt"
```
-Individual overrides take precedence over group overrides.
+Individual overrides take precedence over group overrides, which take precedence over cached/fallback defaults.
## How it works
@@ -132,10 +161,14 @@ Individual overrides take precedence over group overrides.
- `CLAUDE_CODE_SUBAGENT_MODEL`: Model for sub-agent operations
- `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`: Disabled because telemetry bad
+### Caching
+
+Model preferences are stored in `~/.config/synclaude/models.conf` (respects `XDG_CONFIG_HOME`). Use interactive mode (`synclaude i`) to save new defaults.
+
After each run, it queries the Synthetic API to display:
- Requests used during the session
-- Remaining quota (color-coded: green <33%, yellow 33-66%, red >66%)
+- Remaining quota (color-coded badge: green <33%, yellow 33-66%, red >66%)
## Contributions
@@ -5,15 +5,17 @@
# Inherit all claude completions
complete -c synclaude -w claude
-# Interactive mode
-complete -c synclaude -n "not __fish_seen_subcommand_from i" -a i -d "Interactive model selection"
+# Subcommands
+complete -c synclaude -n "not __fish_seen_subcommand_from i u" -a i -d "Interactive model selection"
+complete -c synclaude -n "not __fish_seen_subcommand_from i u" -a u -d "Show current quota usage"
# Group model overrides
-complete -c synclaude -s L -l large -r -d "Override Opus, Sonnet, and Sub-agent models"
+complete -c synclaude -s H -l heavy -r -d "Override Opus model"
+complete -c synclaude -s M -l medium -r -d "Override Sonnet and Sub-agent models"
complete -c synclaude -s l -l light -r -d "Override Haiku model"
# Specific model overrides
complete -c synclaude -s o -l opus -r -d "Override Opus model"
complete -c synclaude -s s -l sonnet -r -d "Override Sonnet model"
-complete -c synclaude -s H -l haiku -r -d "Override Haiku model"
+complete -c synclaude -s k -l haiku -r -d "Override Haiku model"
complete -c synclaude -s a -l agent -r -d "Override Sub-agent model"
@@ -26,14 +26,20 @@ Enter Sleep 2s
Sleep 750ms
Enter Sleep 2s
-# Select heavy group
+# Select light group
+Down Sleep 250ms
+Down Sleep 500ms
Space Sleep 750ms
Enter Sleep 2s
# Select the heavy model
-Type "mmm2" Sleep 1.25s
+Type "dsv31t" Sleep 1.25s
Enter Sleep 2.25s
+# Decline saving as default
+Right Sleep 500ms
+Enter Sleep 2.2s
+
# Send a message
Type "Hi!" Sleep 1.25s
Enter Sleep 4s
@@ -2,43 +2,177 @@
#
# SPDX-License-Identifier: Unlicense
-function synclaude --wraps=claude --description "Run claude with Synthetic GLM-4.6 backend"
- set -l default_model hf:zai-org/GLM-4.6
+# Cache file for model preferences
+set -g _synclaude_cache_file (set -q XDG_CONFIG_HOME; and echo "$XDG_CONFIG_HOME"; or echo "$HOME/.config")"/synclaude/models.conf"
+
+# Fallback defaults (used when no cache entry exists)
+set -g _synclaude_fallback_opus "hf:moonshotai/Kimi-K2-Thinking"
+set -g _synclaude_fallback_sonnet "hf:zai-org/GLM-4.6"
+set -g _synclaude_fallback_haiku "hf:deepseek-ai/DeepSeek-V3.1-Terminus"
+set -g _synclaude_fallback_agent "hf:zai-org/GLM-4.6"
+
+function _synclaude_cache_get --description "Get a cached value: _synclaude_cache_get slot"
+ set -l slot $argv[1]
+ set -l key "claude.$slot"
+
+ if not test -f "$_synclaude_cache_file"
+ return 1
+ end
+
+ set -l match (string match -r "^$key\\s*=\\s*(.+)" < "$_synclaude_cache_file")
+ if test -n "$match[2]"
+ string trim "$match[2]"
+ return 0
+ end
+ return 1
+end
+
+function _synclaude_cache_set --description "Set a cached value: _synclaude_cache_set slot value"
+ set -l slot $argv[1]
+ set -l value $argv[2]
+ set -l key "claude.$slot"
+
+ mkdir -p (dirname "$_synclaude_cache_file")
+
+ if not test -f "$_synclaude_cache_file"
+ echo "# synclaude model preferences" > "$_synclaude_cache_file"
+ echo "# Format: claude.slot = model_id" >> "$_synclaude_cache_file"
+ echo "" >> "$_synclaude_cache_file"
+ end
+
+ if string match -rq "^$key\\s*=" < "$_synclaude_cache_file"
+ set -l tmp (mktemp)
+ string replace -r "^$key\\s*=.*" "$key = $value" < "$_synclaude_cache_file" > "$tmp"
+ mv "$tmp" "$_synclaude_cache_file"
+ else
+ echo "$key = $value" >> "$_synclaude_cache_file"
+ end
+end
+
+function _synclaude_default --description "Get default model: _synclaude_default slot"
+ set -l slot $argv[1]
+ set -l cached (_synclaude_cache_get $slot)
+ if test $status -eq 0
+ echo $cached
+ else
+ set -l var_name _synclaude_fallback_$slot
+ echo $$var_name
+ end
+end
+
+function _synclaude_get_quota --description "Fetch quota from Synthetic API"
+ if not set -q SYNTHETIC_API_KEY
+ echo "Error: SYNTHETIC_API_KEY environment variable not set" >&2
+ return 1
+ end
+
+ set -l response (curl -s -f -H "Authorization: Bearer $SYNTHETIC_API_KEY" \
+ "https://api.synthetic.new/v2/quotas" 2>/dev/null)
+
+ if test $status -ne 0 -o -z "$response"
+ return 1
+ end
+
+ set -l requests (echo "$response" | jq -r '.subscription.requests // 0')
+ set -l limit (echo "$response" | jq -r '.subscription.limit // 0')
+
+ echo "$requests $limit"
+end
+
+function synclaude --wraps=claude --description "Run claude with Synthetic API backend"
+ # Handle usage subcommand
+ if test (count $argv) -gt 0 && test "$argv[1]" = "u"
+ set -l quota (_synclaude_get_quota)
+ if test $status -ne 0
+ echo "Error: Could not fetch quota" >&2
+ return 1
+ end
+ set -l requests (echo "$quota" | cut -d' ' -f1)
+ set -l limit (echo "$quota" | cut -d' ' -f2)
+ set -l remaining (math "$limit - $requests")
+ set -l percent_used (math -s0 "$requests * 100 / $limit")
+ printf "Usage: "
+ if test $percent_used -lt 33
+ set_color -b green black
+ else if test $percent_used -lt 67
+ set_color -b yellow black
+ else
+ set_color -b red black
+ end
+ printf " %s%% " $percent_used
+ set_color normal
+ printf " (%s/%s remaining)\n" $remaining $limit
+ return 0
+ end
# Handle interactive mode
if test (count $argv) -gt 0 && test "$argv[1]" = "i"
set -e argv[1] # Remove 'i' from args
-
+
+ # 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)
+ 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
-
+
+ # Get current defaults for display
+ set -l current_opus_id (_synclaude_default opus)
+ set -l current_sonnet_id (_synclaude_default sonnet)
+ set -l current_haiku_id (_synclaude_default haiku)
+ set -l current_agent_id (_synclaude_default agent)
+
# Prompt for groups vs individual
- set -l mode (gum choose --limit 1 --header "How do you want to select models?" "Groups" "Individual models")
+ set -l mode (gum choose --limit 1 --header "How do you want to select models?" \
+ "Groups" "Individual models")
or return 1
-
+
# Build flags array
set -l flags
-
+
if test "$mode" = "Groups"
# Select which groups to override
- set -l groups (gum choose --no-limit --header "Which group(s) do you want to override?" "Large (Opus, Sonnet, Sub-agent)" "Light (Haiku)")
+ set -l groups (gum choose --no-limit \
+ --header "Which group(s) do you want to override?" \
+ "Heavy (Opus)" "Medium (Sonnet, Sub-agent)" "Light (Haiku)")
or return 1
-
+
for group in $groups
- if test "$group" = "Large (Opus, Sonnet, Sub-agent)"
- set -l model_name (printf "%s\n" $model_names | gum filter --limit 1 --header "Select model for Large group" --placeholder "Filter models...")
+ if test "$group" = "Heavy (Opus)"
+ set -l model_name (printf "%s\n" $model_names | \
+ gum filter --limit 1 --header "Select model for Heavy group (opus: $current_opus_id)" \
+ --placeholder "Filter models...")
or return 1
- set -l model_id (echo $models_json | jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
+ set -l model_id (echo $models_json | \
+ jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
if test -n "$model_id"
- set flags $flags --large=$model_id
+ set flags $flags --heavy=$model_id
+ end
+ else if test "$group" = "Medium (Sonnet, Sub-agent)"
+ set -l model_name (printf "%s\n" $model_names | \
+ gum filter --limit 1 --header "Select model for Medium group (sonnet: $current_sonnet_id, agent: $current_agent_id)" \
+ --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 -n "$model_id"
+ set flags $flags --medium=$model_id
end
else if test "$group" = "Light (Haiku)"
- set -l model_name (printf "%s\n" $model_names | gum filter --limit 1 --header "Select model for Light group" --placeholder "Filter models...")
+ set -l model_name (printf "%s\n" $model_names | \
+ gum filter --limit 1 --header "Select model for Light group (haiku: $current_haiku_id)" \
+ --placeholder "Filter models...")
or return 1
- set -l model_id (echo $models_json | jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
+ set -l model_id (echo $models_json | \
+ jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
if test -n "$model_id"
set flags $flags --light=$model_id
end
@@ -46,115 +180,182 @@ function synclaude --wraps=claude --description "Run claude with Synthetic GLM-4
end
else
# Select which individual models to override
- set -l models (gum choose --no-limit --header "Which model(s) do you want to override?" "Opus" "Sonnet" "Haiku" "Sub-agent")
+ set -l models (gum choose --no-limit \
+ --header "Which model(s) do you want to override?" \
+ "Opus" "Sonnet" "Haiku" "Sub-agent")
or return 1
-
+
for model_type in $models
switch $model_type
case "Opus"
- set -l model_name (printf "%s\n" $model_names | gum filter --limit 1 --header "Select Opus model" --placeholder "Filter models...")
+ set -l model_name (printf "%s\n" $model_names | \
+ gum filter --limit 1 --header "Select Opus model (current: $current_opus_id)" \
+ --placeholder "Filter models...")
or return 1
- set -l model_id (echo $models_json | jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
+ set -l model_id (echo $models_json | \
+ jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
if test -n "$model_id"
set flags $flags --opus=$model_id
end
case "Sonnet"
- set -l model_name (printf "%s\n" $model_names | gum filter --limit 1 --header "Select Sonnet model" --placeholder "Filter models...")
+ set -l model_name (printf "%s\n" $model_names | \
+ gum filter --limit 1 --header "Select Sonnet model (current: $current_sonnet_id)" \
+ --placeholder "Filter models...")
or return 1
- set -l model_id (echo $models_json | jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
+ set -l model_id (echo $models_json | \
+ jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
if test -n "$model_id"
set flags $flags --sonnet=$model_id
end
case "Haiku"
- set -l model_name (printf "%s\n" $model_names | gum filter --limit 1 --header "Select Haiku model" --placeholder "Filter models...")
+ set -l model_name (printf "%s\n" $model_names | \
+ gum filter --limit 1 --header "Select Haiku model (current: $current_haiku_id)" \
+ --placeholder "Filter models...")
or return 1
- set -l model_id (echo $models_json | jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
+ set -l model_id (echo $models_json | \
+ jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
if test -n "$model_id"
set flags $flags --haiku=$model_id
end
case "Sub-agent"
- set -l model_name (printf "%s\n" $model_names | gum filter --limit 1 --header "Select Sub-agent model" --placeholder "Filter models...")
+ set -l model_name (printf "%s\n" $model_names | \
+ gum filter --limit 1 --header "Select Sub-agent model (current: $current_agent_id)" \
+ --placeholder "Filter models...")
or return 1
- set -l model_id (echo $models_json | jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
+ set -l model_id (echo $models_json | \
+ jq -r --arg name "$model_name" '.data[] | select(.name == $name) | .id')
if test -n "$model_id"
set flags $flags --agent=$model_id
end
end
end
end
-
+
+ # Offer to save as defaults
+ if test (count $flags) -gt 0
+ if gum confirm "Save as defaults?"
+ for flag in $flags
+ set -l parts (string match -r -- '^--([^=]+)=(.+)$' $flag)
+ if test -n "$parts[2]"
+ set -l key $parts[2]
+ set -l value $parts[3]
+ switch $key
+ case heavy
+ _synclaude_cache_set opus $value
+ case medium
+ _synclaude_cache_set sonnet $value
+ _synclaude_cache_set agent $value
+ case light
+ _synclaude_cache_set haiku $value
+ case '*'
+ _synclaude_cache_set $key $value
+ end
+ end
+ end
+ end
+ end
+
# Recursively call synclaude with selected flags and remaining args
synclaude $flags $argv
return $status
end
# Parse arguments
- # -L/--large: Sets Opus, Sonnet, and Sub-agent models
+ # -H/--heavy: Sets Opus model
+ # -M/--medium: Sets Sonnet and Sub-agent models
# -l/--light: Sets Haiku model
- # -o/--opus, -s/--sonnet, -H/--haiku, -a/--agent: Set specific models
+ # -o/--opus, -s/--sonnet, -k/--haiku, -a/--agent: Set specific models
argparse --ignore-unknown \
- 'L/large=' 'l/light=' \
- 'o/opus=' 's/sonnet=' 'H/haiku=' 'a/agent=' -- $argv
- or return
+ 'H/heavy=' 'M/medium=' 'l/light=' \
+ 'o/opus=' 's/sonnet=' 'k/haiku=' 'a/agent=' -- $argv
+ or return 1
- set -lx ANTHROPIC_BASE_URL https://api.synthetic.new/anthropic
- set -lx ANTHROPIC_AUTH_TOKEN $SYNTHETIC_API_KEY
-
- # Set defaults
- set -lx ANTHROPIC_DEFAULT_OPUS_MODEL $default_model
- set -lx ANTHROPIC_DEFAULT_SONNET_MODEL $default_model
- set -lx ANTHROPIC_DEFAULT_HAIKU_MODEL $default_model
- set -lx CLAUDE_CODE_SUBAGENT_MODEL $default_model
- set -lx CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC 1
+ # Start with defaults (from cache or fallback)
+ set -l opus_model (_synclaude_default opus)
+ set -l sonnet_model (_synclaude_default sonnet)
+ set -l haiku_model (_synclaude_default haiku)
+ set -l subagent_model (_synclaude_default agent)
# Apply group overrides
- if set -q _flag_large
- set ANTHROPIC_DEFAULT_OPUS_MODEL $_flag_large
- set ANTHROPIC_DEFAULT_SONNET_MODEL $_flag_large
- set CLAUDE_CODE_SUBAGENT_MODEL $_flag_large
+ if set -q _flag_heavy
+ set opus_model $_flag_heavy
+ end
+
+ if set -q _flag_medium
+ set sonnet_model $_flag_medium
+ set subagent_model $_flag_medium
end
if set -q _flag_light
- set ANTHROPIC_DEFAULT_HAIKU_MODEL $_flag_light
+ set haiku_model $_flag_light
end
- # Apply specific overrides (taking precedence over groups)
- if set -q _flag_opus; set ANTHROPIC_DEFAULT_OPUS_MODEL $_flag_opus; end
- if set -q _flag_sonnet; set ANTHROPIC_DEFAULT_SONNET_MODEL $_flag_sonnet; end
- if set -q _flag_haiku; set ANTHROPIC_DEFAULT_HAIKU_MODEL $_flag_haiku; end
- if set -q _flag_agent; set CLAUDE_CODE_SUBAGENT_MODEL $_flag_agent; end
+ # Apply specific overrides (take precedence over groups)
+ if set -q _flag_opus
+ set opus_model $_flag_opus
+ end
+ if set -q _flag_sonnet
+ set sonnet_model $_flag_sonnet
+ end
+ if set -q _flag_haiku
+ set haiku_model $_flag_haiku
+ end
+ if set -q _flag_agent
+ set subagent_model $_flag_agent
+ end
- # Fetch initial quota
- set -l initial_response (gum spin --spinner dot --title "Checking quota..." -- curl -s -H "Authorization: Bearer $SYNTHETIC_API_KEY" https://api.synthetic.new/v2/quotas 2>/dev/null)
- set -l initial_requests (echo $initial_response | jq -r '.subscription.requests // 0' 2>/dev/null)
- test -n "$initial_requests" || set initial_requests 0
+ # Export environment variables for Claude Code
+ set -lx ANTHROPIC_BASE_URL "https://api.synthetic.new/anthropic"
+ set -lx ANTHROPIC_AUTH_TOKEN $SYNTHETIC_API_KEY
+ set -lx ANTHROPIC_DEFAULT_OPUS_MODEL $opus_model
+ set -lx ANTHROPIC_DEFAULT_SONNET_MODEL $sonnet_model
+ set -lx ANTHROPIC_DEFAULT_HAIKU_MODEL $haiku_model
+ set -lx CLAUDE_CODE_SUBAGENT_MODEL $subagent_model
+ set -lx CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC 1
+
+ # Fetch quota before execution
+ set -l quota_before (_synclaude_get_quota)
+ if test $status -ne 0
+ echo "Warning: Could not fetch quota before execution" >&2
+ set quota_before "0 0"
+ end
+ set -l requests_before (echo "$quota_before" | cut -d' ' -f1)
+ set -l limit (echo "$quota_before" | cut -d' ' -f2)
+
+ # Execute claude
command claude $argv
+ set -l exit_status $status
- # Fetch final quota
- set -l final_response (gum spin --spinner dot --title "Fetching usage..." -- curl -s -H "Authorization: Bearer $SYNTHETIC_API_KEY" https://api.synthetic.new/v2/quotas 2>/dev/null)
- set -l final_requests (echo $final_response | jq -r '.subscription.requests // 0' 2>/dev/null)
- set -l limit (echo $final_response | jq -r '.subscription.limit // 0' 2>/dev/null)
-
- # Calculate and display
- if test -n "$limit" && test "$limit" -gt 0
- set -l session_usage (math "$final_requests - $initial_requests")
- set -l remaining (math "$limit - $final_requests")
- set -l percent_used (math -s0 "($final_requests * 100) / $limit")
-
- set -l quota_color green
- if test "$percent_used" -gt 66
- set quota_color red
- else if test "$percent_used" -gt 33
- set quota_color yellow
- end
+ # Fetch quota after execution
+ set -l quota_after (_synclaude_get_quota)
+ if test $status -ne 0
+ echo "Warning: Could not fetch quota after execution" >&2
+ return $exit_status
+ end
- echo
- echo "Session usage: $session_usage requests"
- echo -n "Remaining: "
- set_color $quota_color
- echo -n "$remaining/$limit"
- set_color normal
- echo " requests"
+ set -l requests_after (echo "$quota_after" | cut -d' ' -f1)
+
+ # Calculate session usage and remaining quota
+ set -l session_usage (math "$requests_after - $requests_before")
+ set -l remaining (math "$limit - $requests_after")
+ set -l percent_used (math -s0 "$requests_after * 100 / $limit")
+
+ # Display session usage and remaining quota with color
+ printf "\n"
+ printf "Session: %s requests\n" $session_usage
+ printf "Overall: "
+
+ if test $percent_used -lt 33
+ set_color -b green black
+ else if test $percent_used -lt 67
+ set_color -b yellow black
+ else
+ set_color -b red black
end
+ printf " %s%% " $percent_used
+ set_color normal
+ printf " (%s/%s remaining)\n" $remaining $limit
+
+ return $exit_status
end