| 1 | # Wynba |
| 2 | |
| 3 | A Rust coding agent with tool use and SQLite-backed sessions. It runs on your |
| 4 | machine, edits real files, searches your codebase, runs commands, spawns |
| 5 | sub-agents, and searches the web — all from a fast TUI (or plain REPL). |
| 6 | |
| 7 | ## Features |
| 8 | |
| 9 | - **Terminal UI** — streaming markdown, tool-call blocks with diffs, a |
| 10 | Grok-Build-style prompt editor with paste chips, image paste, a status bar |
| 11 | and scrollable history. Falls back to a plain REPL when stdin isn't a |
| 12 | terminal, and to a one-shot mode when you pass a message on the command line. |
| 13 | - **15 built-in tools** — |
| 14 | `delete_file`, `list_files`, ` |
| 15 | `web_search`, `web_fetch`, ` |
| 16 | `create_goal`, `update_goal`, ` |
| 17 | - **Sub-agents** — the |
| 18 | `explore` (read-only research) sub-agents with their own prompts, capped at a |
| 19 | max nesting depth. |
| 20 | - **Todos** — opencode-style task tracking: |
| 21 | structured per-session task list (`content`/`status`/` |
| 22 | `pending`/`in_progress`/` |
| 23 | Persisted in SQLite so the list survives compaction and relaunches. |
| 24 | - **Goals** — opencode-style long-running goals: |
| 25 | goal and the agent keeps working across turns (tracking time used and |
| 26 | iteration count) until it's `complete`, `blocked`, ` |
| 27 | Manage it with `/goal status`, `/goal pause`, ` |
| 28 | the agent itself uses `create_goal`/`update_goal`/` |
| 29 | stored per session in SQLite. |
| 30 | - **Sessions** — every conversation and tool result is persisted to SQLite. |
| 31 | Create, list, show, resume, revert, and fork sessions at any point in the |
| 32 | history. |
| 33 | - **Many providers** — OpenAI-compatible APIs, Anthropic, Gemini, and Bedrock |
| 34 | wire formats, plus a large registry of providers (see |
| 35 | [`Providers` |
| 36 | - **Context management** — the message history sent to the model is bounded by |
| 37 | round and byte budget so long, tool-heavy runs don't overflow the context |
| 38 | window. |
| 39 | - **Steering** — queue follow-up messages (Alt+Enter) while the agent is |
| 40 | running, or type to steer the running agent mid-turn; interruptions (Esc) |
| 41 | restore the prompt editor. |
| 42 | - **File safety** — edits and writes are backed up per session so you can |
| 43 | revert a turn (and its file changes) with one command. |
| 44 | - **Themes** — popular palettes built in (Dracula, One Dark, Gruvbox, Nord, |
| 45 | Solarized, Tokyo Night, Catppuccin, Monokai, GitHub Dark, Rose Pine — each |
| 46 | repaints the full UI including the background) plus custom themes in |
| 47 | `~/.wynba/themes/*.json`, switchable live with `/theme`. |
| 48 | - **Images** — paste images from the clipboard (Ctrl+V) into your prompt on |
| 49 | vision-capable models; stored per message and sent in each provider's native |
| 50 | format. |
| 51 | |
| 52 | ## Installation |
| 53 | |
| 54 | Requires a recent stable Rust (the crate uses the 2024 edition). |
| 55 | |
| 56 | ```sh |
| 57 | git clone <this-repo> |
| 58 | cd wynba |
| 59 | cargo build --release |
| 60 | # binary at target/release/wynba |
| 61 | ``` |
| 62 | |
| 63 | Run the tests with: |
| 64 | |
| 65 | ```sh |
| 66 | cargo test |
| 67 | ``` |
| 68 | |
| 69 | ## Quick start |
| 70 | |
| 71 | ```sh |
| 72 | wynba |
| 73 | ``` |
| 74 | |
| 75 | On first launch you'll get a new session. Connect a provider (interactively) |
| 76 | or set an API key as an environment variable, then chat: |
| 77 | |
| 78 | - `/connect` — searchable provider picker, then enter credentials. Credentials |
| 79 | are stored in the local database. |
| 80 | - or set the matching env var, e.g. `export OPENAI_API_KEY=...`, and the |
| 81 | provider is ready to use immediately. |
| 82 | |
| 83 | Example one-shot use: |
| 84 | |
| 85 | ```sh |
| 86 | wynba "explain how the revert command works" |
| 87 | wynba chat -s 1 |
| 88 | wynba --session <uuid> |
| 89 | ``` |
| 90 | |
| 91 | ## CLI |
| 92 | |
| 93 | ``` |
| 94 | wynba [OPTIONS] [COMMAND] [MESSAGE...] |
| 95 | ``` |
| 96 | |
| 97 | Global options: |
| 98 | |
| 99 | | Option | Description | |
| 100 | | ----------------- | ---------------------------------------------------------- | |
| 101 | | `--db <PATH>` | Path to the SQLite database (default `~/.wynba/agent.db |
| 102 | | `--provider <N>` | Provider to use (default from `$DEFAULT_PROVIDER |
| 103 | | `--model <N>` | Model name, overrides the provider default | |
| 104 | | `--session <UUID>`| Resume an existing session by UUID (REPL or one-shot) | |
| 105 | | `--theme <NAME>` | Theme name: a built-in palette (`default |
| 106 | | `-h`, `-V` |
| 107 | |
| 108 | Subcommands: |
| 109 | |
| 110 | | Command | Description | |
| 111 | | ------------------------ | -------------------------------------------------- | |
| 112 | | `new [--name N] [MSG]` | Create a new session (interactive or with a message) | |
| 113 | | `list` | List all sessions | |
| 114 | | `show <SESSION>` | Show the full message history of a session | |
| 115 | | `chat [-s ID] [MSG]` | Chat in an existing session (by numeric id) or create one | |
| 116 | | `delete <SESSION>` | Delete a session and all its messages | |
| 117 | | `connect` | Interactively connect a provider | |
| 118 | | `login [PROVIDER]` | Sign in like fx.sh: `vercel |
| 119 | | `logout [PROVIDER]` | Sign out of a provider | |
| 120 | | `provider [NAME]` | Switch provider (`gateway |
| 121 | | `models` | List the active provider's model catalog | |
| 122 | | `status` | Show provider, model and credential status | |
| 123 | | `providers` | List providers and their credential env vars | |
| 124 | | `resume` | List sessions and pick one to continue | |
| 125 | |
| 126 | `<SESSION>` accepts either a UUID or a numeric id. |
| 127 | |
| 128 | ## TUI usage |
| 129 | |
| 130 | Inside the interactive TUI: |
| 131 | |
| 132 | - **Enter** — send. |
| 133 | - **Alt+Enter** — queue a follow-up message (delivered when the agent finishes). |
| 134 | - **Enter while the agent is busy** — steer it: the text is folded into the |
| 135 | conversation before the next LLM call. |
| 136 | - **Esc Esc** — stop the current reply; Escape never quits the session. |
| 137 | - **↑/↓, mouse wheel** — scroll the history. |
| 138 | - **Ctrl+V** — paste text or an image from the clipboard (images on |
| 139 | vision-capable models). |
| 140 | - Select a past user message to **revert** (restores its file changes too) or |
| 141 | **fork** a new session from that point. |
| 142 | |
| 143 | ### Slash commands |
| 144 | |
| 145 | | Command | Description | |
| 146 | | ------------------ | ---------------------------------------------------- | |
| 147 | | `/connect` | Interactively connect a provider | |
| 148 | | `/login` | Provider picker, fx.sh style (`vercel |
| 149 | | `/login <provider>`| Sign in to one provider directly | |
| 150 | | `/provider` | Switch provider (`gateway |
| 151 | | `/status` | Show provider, model and credential status | |
| 152 | | `/logout` | Log out of a provider (`vercel |
| 153 | | `/model <name>` | Switch model directly (or `/model |
| 154 | | `/theme <name>` | Switch theme directly (or `/theme |
| 155 | | `/new` | Start a new session | |
| 156 | | `/resume` | Pick a saved session and continue it | |
| 157 | | `/import` | Import a session from another agent harness (Claude Code, Codex, OpenCode, Pi, GrokBuild) | |
| 158 | | `/import <name>` | Import from a specific harness (`claude-code |
| 159 | | `/redo` | Re-apply the last `/undo |
| 160 | | `/undo` | Restore files the last agent response changed (via git) | |
| 161 | | `/goal <objective>`| Set a long-running goal the agent works toward across turns | |
| 162 | | `/goal status` | Show the current goal | |
| 163 | | `/goal pause` | Pause the goal (keeps it, stops auto-continue) | |
| 164 | | `/goal resume` | Resume a paused goal | |
| 165 | | `/goal clear` | Clear the goal | |
| 166 | | `/review` | Review uncommitted changes (runs as a sub-agent; only the report lands in the chat) | |
| 167 | | `/review <arg>` | Review a commit, branch, or PR (`/review <sha> |
| 168 | | `/compact [focus]` | Compact the session context (summarize older turns) | |
| 169 | | `/help` | Show commands and key bindings | |
| 170 | | `/exit`, `/quit` |
| 171 | |
| 172 | **Undo / redo.** Because the agent can change files through tools * |
| 173 | shell commands, `/undo` reverses the last response's file changes with git: |
| 174 | before each run the whole working tree (tracked files, deletions and |
| 175 | untracked non-ignored files, across the whole repository) is snapshotted into |
| 176 | a throwaway `git commit-tree` object, and `/undo` restores it with |
| 177 | `git read-tree` + `checkout-index` + `clean |
| 178 | never touched, and your session transcript is kept. `/redo` re-applies what |
| 179 | `/undo` took back. Undo works anywhere inside a git repository (even from a |
| 180 | subdirectory); outside one it has nothing to do. Notes: ignored files |
| 181 | (e.g. `node_modules`, build output) are never touched, and any *untracked |
| 182 | files created after the run — not just the agent's — are removed by `/undo`, |
| 183 | so commit or stash anything you care about before running it. |
| 184 | |
| 185 | When the provider rejects a request because the history no longer fits its |
| 186 | context window, the agent **auto-compacts** — the older turns are folded into |
| 187 | a summary, exactly like `/compact` — and retries the turn once (mirroring |
| 188 | pi's overflow recovery). If the provider instead reports an *output* token |
| 189 | limit (`finish_reason=length` with no content, usually thinking eating the |
| 190 | whole budget), raise `MAX_TOKENS` or lower thinking effort with `/effort`. |
| 191 | |
| 192 | ## Providers |
| 193 | |
| 194 | Credential env vars (run `wynba providers` for the full list with |
| 195 | default models). The most common are: |
| 196 | |
| 197 | | Provider | Env var | Notes | |
| 198 | | --------------- | ------------------------ | |
| 199 | | OpenAI | `OPENAI_API_KEY` | also |
| 200 | | Anthropic | `ANTHROPIC_API_KEY` | also |
| 201 | | Google Gemini | `GOOGLE_API_KEY` / `GEMINI_API_KEY |
| 202 | | Groq | `GROQ_API_KEY` | |
| 203 | | Mistral | `MISTRAL_API_KEY` | |
| 204 | | xAI (Grok) | `XAI_API_KEY` | |
| 205 | | DeepSeek | `DEEPSEEK_API_KEY` | |
| 206 | | Ollama | *(none)* |
| 207 | | LM Studio | *(none)* |
| 208 | | Amazon Bedrock | `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY |
| 209 | | Google Vertex | `GOOGLE_VERTEX_TOKEN` | native Gemini format |
| 210 | | Azure OpenAI | `AZURE_OPENAI_API_KEY` | full chat-completions URL |
| 211 | | Vercel Gateway (fx) | `AI_GATEWAY_API_KEY` or `fx |
| 212 | | Codex subscription | ChatGPT subscription (`wynba login codex`) | shares |
| 213 | | Claude subscription | Claude subscription (`wynba login claude`) | reuses the Claude Code CLI session in |
| 214 | | Grok subscription | Grok subscription (`wynba login grok`) | shares |
| 215 | | Compatible | `COMPATIBLE_API_KEY` (optional) | any OpenAI-compatible endpoint |
| 216 | |
| 217 | Subscription providers work just like in [fx.sh](https://fx.sh/docs/getting-started/authentication |
| 218 | `/login` opens the provider picker (Vercel AI Gateway, Codex, Claude, Grok), |
| 219 | `wynba login <provider>` signs in directly, `wynba provider <gateway|codex|claude|grok>` |
| 220 | (or `/provider` in the TUI) switches between them, and `wynba status` (or ` |
| 221 | shows what's connected. Model catalogs come from each provider's authenticated endpoint, |
| 222 | and per-provider model choices are shared with fx via `~/.fx/settings.json`. |
| 223 | |
| 224 | General settings: |
| 225 | |
| 226 | - `DEFAULT_PROVIDER` — default provider name (default `openai`). |
| 227 | - `MAX_TOKENS` — max output tokens for the chat call (default `16384`). |
| 228 | - `WYNBA_DB` — override the SQLite database path. |
| 229 | |
| 230 | Each provider also honors `<PROVIDER>_BASE_URL` and `<PROVIDER>_MODEL` env vars |
| 231 | (see `resolve_provider` in `src/llm.rs`). The broad registry of additional |
| 232 | providers comes from [Mastra's provider registry](https://github.com/mastra-ai/mastra |
| 233 | and is generated by `scripts/gen_providers.py` into |
| 234 | `src/llm/providers_data.rs` — don't edit that file by hand. |
| 235 | |
| 236 | Model selection uses the provider's live `/models` endpoint when available and |
| 237 | falls back to the embedded registry. |
| 238 | |
| 239 | ## Theming |
| 240 | |
| 241 | The agent ships with a set of **built-in popular palettes** — every one |
| 242 | overrides the full color map, so switching themes repaints the whole UI |
| 243 | including the full-screen background. Available out of the box: |
| 244 | |
| 245 | `default`, `dracula`, `one-dark |
| 246 | `tokyo-night`, `catppuccin-mocha`, `monokai |
| 247 | `cursor` (a port of the Cursor editor's dark theme — near-black surfaces, |
| 248 | electric-blue `#4d9eff` accent for AI/highlights, and Cursor's exact diff |
| 249 | greens/reds). |
| 250 | |
| 251 | On top of those, custom themes are JSON files in |
| 252 | `~/.wynba/themes/*.json`. Every field |
| 253 | is optional and falls back to the built-in `default` palette. Values are |
| 254 | 6-digit hex strings (`#rrggbb`). Example: |
| 255 | |
| 256 | ```json |
| 257 | { |
| 258 | "name": |
| 259 | "accent": |
| 260 | "border": |
| 261 | "user_bg": |
| 262 | "md_green": |
| 263 | } |
| 264 | ``` |
| 265 | |
| 266 | Available fields include `bg` (the full-screen interface background — like |
| 267 | Grok Build, the TUI paints its own background instead of inheriting the |
| 268 | terminal's), plus `border`, `accent`, ` |
| 269 | `user_bg`, `user_fg`, `tool_pending_bg |
| 270 | `tool_title_fg`, `tool_call_fg`, `tool_args_fg |
| 271 | `diff_add_fg`, `diff_del_fg`, `diff_hunk_fg |
| 272 | `subagent_pending_*`, `subagent_ok_*`, `sel_bg |
| 273 | `revert_fg`, and `md_*` colors. Pick one with `/theme |
| 274 | at launch with `--theme <name>`. The active theme is persisted in the settings |
| 275 | table. |
| 276 | |
| 277 | ## Storage |
| 278 | |
| 279 | - **Database** — SQLite at |
| 280 | `--db`/`WYNBA_DB`). Holds sessions, messages, provider configs, file |
| 281 | backups, settings, per-session todos, and per-session goals. |
| 282 | - **Themes** — |
| 283 | `src/theme.rs`. |
| 284 | - **File backups** — |
| 285 | turn's file changes when you revert. |
| 286 | |
| 287 | ## Project layout |
| 288 | |
| 289 | ``` |
| 290 | src/ |
| 291 | main.rs CLI, REPL/TUI loop, slash commands, session management |
| 292 | agent.rs agent loop: system prompt, tool rounds, steering, sub-agents |
| 293 | tools.rs tool definitions, execution, diff engine |
| 294 | llm.rs provider adapters, retries, model listing |
| 295 | llm/providers_data.rs generated provider registry (see scripts/) |
| 296 | db.rs SQLite schema and queries |
| 297 | goal.rs long-running goals: template, continuation prompt, statuses |
| 298 | tui.rs terminal UI: editor, streaming render, tool blocks |
| 299 | md.rs streaming markdown renderer |
| 300 | theme.rs color themes |
| 301 | scripts/ |
| 302 | gen_providers.py regenerates src/llm/providers_data.rs from Mastra's registry |
| 303 | ``` |
| 304 | |
| 305 | ## License |
| 306 | |
| 307 | No license file is currently specified in this repository. |