AGENTS.md

  1<!--
  2SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  3
  4SPDX-License-Identifier: Unlicense
  5-->
  6
  7# AGENTS.md - Developer Guide for synu
  8
  9This document helps AI agents work effectively with the synu codebase.
 10
 11## Project Overview
 12
 13**synu** is a wrapper for AI agents that tracks quota usage for [Synthetic](https://synthetic.new) API calls. It provides:
 14
 15- Transparent quota tracking before/after agent execution
 16- Agent-specific configuration system (model routing, API endpoints)
 17- Persistent model preferences via cache file
 18- Interactive model selection with `gum`
 19- Passthrough mode for agents without special configuration
 20
 21### Architecture
 22
 23Both shell implementations follow the same structure:
 24
 25```
 26fish/                                 zsh/
 27└─ functions/                         ├─ synu.plugin.zsh (entry point)
 28   ├─ synu.fish (main wrapper)        └─ functions/
 29   ├─ _synu_get_quota.fish               ├─ synu.zsh (main wrapper)
 30   ├─ _synu_cache.fish                   ├─ _synu_get_quota.zsh
 31   └─ _synu_agents/                      ├─ _synu_cache.zsh
 32       ├─ claude.fish                    └─ _synu_agents/
 33       ├─ opencode.fish                      ├─ claude.zsh
 34       ├─ aider.fish                         ├─ opencode.zsh
 35       ├─ llxprt.fish                        ├─ aider.zsh
 36       └─ qwen.fish                          ├─ llxprt.zsh
 37                                             └─ qwen.zsh
 38```
 39
 40**Control Flow:**
 41
 421. Parse arguments (check for interactive mode, agent name)
 432. Load agent definition from `fish/functions/_synu_agents/<agent>.fish` if it exists
 443. Parse agent-specific flags using `argparse` with `--ignore-unknown` for passthrough
 454. Configure agent environment (if agent has `_configure` function)
 465. Fetch initial quota from Synthetic API
 476. Execute agent with remaining arguments (plus any `_args` output)
 487. Clean up environment variables (if agent has `_env_vars` function)
 498. Fetch final quota and calculate/display session usage
 50
 51**Data Flow:**
 52
 53- Quota API → `_synu_get_quota` → space-separated "requests limit" string
 54- Agent flags → `argparse``_flag_*` variables → agent `_configure` function
 55- Cache file → `_synu_cache_get` → default model values
 56- Interactive mode → `gum` + Synthetic API → model IDs → recursive `synu` call with flags
 57
 58## File Structure
 59
 60```
 61├── fish/
 62│   ├── functions/
 63│   │   ├── synu.fish              # Main wrapper function
 64│   │   ├── _synu_get_quota.fish   # Private: Fetch quota from API
 65│   │   ├── _synu_cache.fish       # Private: Model preference cache
 66│   │   └── _synu_agents/
 67│   └── completions/
 68│       └── synu.fish              # Fish completions
 69├── zsh/
 70│   ├── synu.plugin.zsh            # Zsh plugin entry point
 71│   ├── functions/
 72│   │   ├── synu.zsh               # Main wrapper function
 73│   │   ├── _synu_get_quota.zsh    # Private: Fetch quota from API
 74│   │   ├── _synu_cache.zsh        # Private: Model preference cache
 75│   │   └── _synu_agents/
 76│   └── completions/
 77│       └── _synu.zsh              # Zsh completions
 78```
 79
 80## Essential Commands
 81
 82This is a shell library with no build system. Key commands:
 83
 84### Testing In one line
 85
 86```
 87fish -c 'set fish_function_path fish/functions fish/functions/_synu_agents \$fish_function_path; synu'
 88zsh -c 'source zsh/synu.plugin.zsh; synu'
 89```
 90
 91### Installation Testing
 92
 93```fish
 94# Install via fundle (in a clean fish instance)
 95fundle plugin 'synu' --url 'https://git.secluded.site/synu' --path 'fish'
 96fundle install
 97fundle init
 98```
 99
100## Important patterns
101
102- Agents without definitions work as passthrough (no special config)
103- Use `argparse --ignore-unknown` in main wrapper so agent-native flags pass through
104- Interactive functions return flags that trigger recursive `synu` call
105- Configure functions receive flags already parsed from `_flag_*` variables
106- Environment variables listed in `_env_vars` are automatically unset after agent exits
107- Use `_synu_cache_get`/`_synu_cache_set` for persistent model preferences
108
109### Understanding Model Configuration Flow
110
111**Example: Claude with flag override**
112
1131. User runs: `synu claude --large hf:some/model "prompt"`
1142. `synu.fish` loads `fish/functions/_synu_agents/claude.fish`
1153. Calls `_synu_agent_claude_flags` to get flag spec
1164. Parses with `argparse --ignore-unknown` → sets `_flag_large`
1175. Rebuilds flags for configure: `--large=hf:some/model`
1186. Calls `_synu_agent_claude_configure --large=hf:some/model`
1197. Configure sets opus/sonnet/subagent models from flag
1208. Exports `ANTHROPIC_*` environment variables
1219. Executes `command claude "prompt"` with those environment variables
12210. Claude Code sees Synthetic models via env vars and uses them
12311. After exit, env vars are cleaned up via `_env_vars`
124
125**Example: OpenCode with CLI args**
126
1271. User runs: `synu opencode "prompt"`
1282. `synu.fish` loads `fish/functions/_synu_agents/opencode.fish`
1293. `_configure` sets `_synu_opencode_selected_model` from cache/fallback
1304. `_args` returns: `--model synthetic/hf:MiniMaxAI/MiniMax-M2`
1315. Executes: `command opencode -m "synthetic/hf:..." "prompt"`