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"`