AGENTS.md
Project Overview
synclaude.fish is a Fish shell wrapper for the claude CLI that routes requests through 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.
Key Functionality
- Defaults all Claude model tiers (Opus, Sonnet, Haiku, Sub-agent) to
hf:zai-org/GLM-4.6 - Provides interactive TUI for model selection using
gum - Supports group-based (
--large/--light) and individual model overrides - Displays session API usage and remaining quota after each run
- Wraps
claudeCLI completely - all unknown flags pass through
Project Structure
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)
Architecture
Control Flow:
- 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
synclaudewith generated flags (lines 83-84)
- 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
Key Design Patterns:
- Override precedence: Individual flags override group flags, which override defaults (lines 107-121)
- Error propagation: Uses
or returnthroughout to exit on command failures
Code Conventions
Fish Shell Patterns
- Local variables: Always use
set -lfor function-local scope - Export variables: Use
set -lxfor locally-scoped exports (lines 96-104) - Variable checking: Use
set -q variableto 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 returnfor early exits - Math operations: Use
mathbuiltin with-s0for 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
- Descriptive names:
- 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:
set -l response (gum spin --spinner dot --title "Message..." -- curl -s -H "Authorization: Bearer $KEY" https://api.url)
API responses always:
- Use
jq -rfor raw (unquoted) output - Provide fallback values with
// 0or similar (lines 125, 132-133) - Test for empty values after extraction (line 126)
- Redirect stderr to
/dev/nullif errors are expected (lines 124, 131)
Error Handling
- API failures: Use
or return 1to exit on curl/jq errors - Null safety: Always test extracted values before use (line 126)
- Propagate exit codes: Return
$statusfrom 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 afteri(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
argparse --ignore-unknownis critical: Without it, unknown flags would error instead of passing through toclaude(line 91)- Recursive call must use function name, not alias: Line 83 calls
synclaude, notcommand synclaude- this works because Fish functions can recursively invoke themselves _flag_*variable naming:argparsecreates variables from flag names with_flag_prefix - must use exact names (lines 107-121)- 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)
- jq requires argument quoting: In jq, use
--arg name "$var"then$namein filter - don't interpolate directly into filter string - Quota check timing: Initial quota fetch happens before
clauderuns to calculate session usage. If it fails, script continues anyway mathprecision: Use-s0for integer output to avoid decimal points in quota percentages- Local exports don't persist:
set -lxvariables only exist in function scope - won't affect parent shell