chabeau 0.7.3

A full-screen terminal chat interface that connects to various AI APIs for real-time conversations
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
# Chabeau - Terminal Chat Interface

![Chabeau rendering a complex table](chabeau.png)

Chabeau is a full-screen terminal chat interface that connects to various AI APIs for real-time conversations. Chabeau brings the convenience of modern chat UIs to the terminal with a focus on speed, ergonomics, and sensible defaults. It is not a coding agent, but preliminary support for the Model Context Protocol is in development. This makes it possible to connect Chabeau with various local and remote services as well.

![Our friendly mascot](chabeau-mascot-small.png)

## Table of Contents
- [Features]#features
- [Getting Started]#getting-started
  - [Install]#install
  - [Authenticate]#authenticate
  - [Launch]#launch
- [Working with Providers and Models]#working-with-providers-and-models
- [Configuration]#configuration
- [MCP Servers]#mcp-servers
- [Character Cards]#character-cards
- [Personas]#personas
- [Presets]#presets
- [Appearance and Rendering]#appearance-and-rendering
- [Keyboard and Workflow Tips]#keyboard-and-workflow-tips
- [Architecture Overview]#architecture-overview
- [Development]#development
- [License]#license

## Features

### See it in action

- [Message retry]https://permacommons.org/videos/chabeau-0.7.0/retry.mp4
- [Presets]https://permacommons.org/videos/chabeau-0.7.0/preset-short.mp4
- [Themes]https://permacommons.org/videos/chabeau-0.7.0/themes.mp4
- [User message editing]https://permacommons.org/videos/chabeau-0.7.0/user-edit.mp4
- [Assistant response editing]https://permacommons.org/videos/chabeau-0.7.0/assistant-edit.mp4
- [Character cards]https://permacommons.org/videos/chabeau-0.7.0/character.mp4
- [In-place refinement]https://permacommons.org/videos/chabeau-0.7.0/refine.mp4
- [Syntax highlighting]https://permacommons.org/videos/chabeau-0.7.0/syntax.mp4
- [Code block selection]https://permacommons.org/videos/chabeau-0.7.0/codeblock.mp4
- [Complex tables]https://permacommons.org/videos/chabeau-0.7.0/table.mp4
- [MCP tool calls]https://permacommons.org/videos/chabeau-0.7.2/mcp.mp4
- [Inspecting MCP requests]https://permacommons.org/videos/chabeau-0.7.2/mcp-inspector.mp4

The MCP server shown in the videos above is "[MCP Research Friend](https://github.com/permacommons/mcp-research-friend)", a Permacommons project.

### Full feature list

- Full-screen terminal UI with real-time streaming responses
- Markdown rendering in the chat area (headings, lists, quotes, tables, callouts, horizontal rules, superscript/subscript, inline/fenced code) with clickable OSC 8 hyperlinks
- Modal pickers and inspectors temporarily suspend hyperlink rendering to keep the screen clean
- Built-in support for many common providers (OpenAI, OpenRouter, Poe, Anthropic, Venice AI, Groq, Mistral, Cerebras)
- Support for quick custom configuration of new OpenAI-compatible providers
- Interactive dialogs for selecting models and providers
- Character card support (v2 format) with in-app picker and defaults per provider/model
- Persona system for defining reusable user identities with variable substitution support
- Reusable preset instructions with picker and CLI toggles for quick context switching
- Extensible theming system that degrades gracefully to terminals with limited color support
- Secure API key storage in system keyring with config-based provider management
- Multi-line input (IME-friendly) with compose mode that can expand to half the terminal for longer responses
- Message retry and message editing
- On-demand refinements of the last assistant response with `/refine <prompt>`
- Slash command registry with inline help for faster command discovery
- Conversation logging with pause/resume; quick `/dump` of contents to a file
- Syntax highlighting for fenced code blocks (Python, Bash, JavaScript, and more)
- Inline block selection (Ctrl+B) to copy or save fenced code blocks
- User message selection (Ctrl+P) to revisit and copy prior prompts
- Assistant message editing (Ctrl+X) to revise or truncate assistant responses without resending, with compose-mode shortcuts available while refining replies
- Prettified API error output with Markdown summaries for easier troubleshooting

For features under consideration, see [WISHLIST.md](WISHLIST.md).

## Getting Started

### Install
```bash
cargo install chabeau
```

Versioned release binaries (tagged semver releases) are published on the
[GitHub Releases page](https://github.com/permacommons/chabeau/releases)
after the release tag becomes reachable from `main` (for example, when a
`release/*` branch carrying the tag is merged).

Nightly pre-release binaries are also published under the `Nightly`
pre-release tag.

Each nightly artifact includes per-file SHA-256 checksums plus a combined
`SHA256SUMS` file. Nightly releases also include `SHA256SUMS.sig` and
`SHA256SUMS.pem`, produced by keyless Sigstore signing in GitHub Actions.

On macOS, unsigned nightly binaries may be quarantined by Gatekeeper. If you
trust the downloaded artifact, you can remove the quarantine attribute:

```bash
xattr -d com.apple.quarantine ./chabeau
```

Run unsigned binaries at your own risk.

Stable release artifacts are signed by `.github/workflows/publish.yml`.
Nightly checksum manifests are signed by `.github/workflows/nightly.yml`.
You can verify them with:

```bash
# Stable release checksums
cosign verify-blob \
  --signature SHA256SUMS.sig \
  --certificate SHA256SUMS.pem \
  --certificate-identity-regexp 'https://github.com/permacommons/chabeau/\.github/workflows/publish\.yml@refs/heads/main' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  SHA256SUMS

# Nightly checksums
cosign verify-blob \
  --signature SHA256SUMS.sig \
  --certificate SHA256SUMS.pem \
  --certificate-identity-regexp 'https://github.com/permacommons/chabeau/\.github/workflows/nightly\.yml@refs/heads/main' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  SHA256SUMS

sha256sum -c SHA256SUMS
```

### Configure Providers
```bash
chabeau provider list
chabeau provider add
```

### Launch
```bash
chabeau         # Uses defaults; opens pickers when needed
```

Inside the TUI, use `/provider` and `/model` to switch, and `/help` to see a full breakdown of commands and keyboard shortcuts.

## Working with Providers and Models

```bash
chabeau                              # Start chat with defaults (pickers on demand)
chabeau --provider openai            # Use specific provider
chabeau --model gpt-5                # Use specific model
chabeau --log conversation.log       # Enable logging immediately on startup
```

Discover available options:

```bash
chabeau -p                           # List providers and auth status
chabeau -m                           # List available models
chabeau -p openrouter -m             # List models for specific provider
```

Manage providers from the CLI:

```bash
chabeau provider list
chabeau provider add                 # Built-in token or custom provider
chabeau provider add poe             # Shortcut for built-in provider token
chabeau provider add my-provider     # Shortcut for custom provider id
chabeau provider edit <provider-id>
chabeau provider remove <provider-id>  # Remove custom provider, or built-in token
```

Most users only need `provider add`; it can either attach a token to a
built-in provider or create a custom provider and prompt for a token.
Use `chabeau provider token ...` commands later when rotating or removing
credentials.
Environment variables are used only if no providers are configured, or when you pass `--env`.

```bash
export OPENAI_API_KEY="your-api-key-here"
export OPENAI_BASE_URL="https://api.openai.com/v1"  # Optional
chabeau --env     # Force using env vars even if providers are configured
```

### Quick, Single-Turn Chats

For quick, one-off questions without launching the full TUI, use the `say` command:

```bash
chabeau say "What is the capital of France?"
```

This command sends a single-turn message to the configured model, streams the response directly to your terminal, and exits. It respects your markdown settings, emits OSC8 hyperlinks when your terminal supports them, and uses a monochrome theme for clean, readable output.

MCP is disabled in `chabeau say` mode.

When you omit the prompt argument, `chabeau say` will read from piped or redirected stdin (trimming trailing whitespace) before showing the usage message, so `cat prompt.txt | chabeau say` works as expected.

When stdout is redirected to a file or piped into another program, Chabeau automatically falls back to a plain-text streaming mode. This mode skips OSC8 hyperlinks and cursor control so captured output stays free of escape codes.

If you have multiple providers configured but no default set, Chabeau will prompt you to specify a provider with the `-p` flag. The `-p` and other global flags can be placed before or after the prompt.

Environment variable values can make their way into shell histories or other places they shouldn't, so using the keyring is generally advisable.

## Configuration

Chabeau stores its configuration in `config.toml`.

- Linux: `~/.config/chabeau/config.toml`
- macOS: `~/Library/Application Support/org.permacommons.chabeau/config.toml`

Generally, you can rely on the UI: when you use interactive commands like `/model`, `/provider`, `/theme`, or `/character`, press Alt+Enter (or Ctrl+J) to persist the selection.

### `chabeau set` / `chabeau unset`

`chabeau set` handles **scalar values and selection defaults** — anything that's a single value or a provider/model mapping. Run `chabeau set` with no arguments to print the current configuration and see which values are explicitly set versus inherited defaults.

```bash
chabeau set                                              # Show all settings
chabeau set default-provider openai                      # Provider selection
chabeau set theme dracula                                # Theme selection
chabeau set default-model openai gpt-4o                  # Default model per provider
chabeau set default-character openai gpt-4 hypatia       # Default character per provider/model
chabeau set default-persona anthropic claude-3 developer  # Default persona per provider/model
chabeau set default-preset openai gpt-4o short           # Default preset per provider/model
chabeau set markdown off                                 # Toggle markdown rendering
chabeau set syntax off                                   # Toggle syntax highlighting
chabeau set builtin-presets off                          # Toggle built-in presets
chabeau set refine-prefix "REVISE:"                      # Custom refine trigger
chabeau set refine-instructions "Custom instructions"    # Custom refine system prompt
chabeau set mcp agpedia off                              # Enable/disable an MCP server
chabeau set mcp agpedia yolo on                          # Toggle auto-approve for a server
```

Every `set` key has a matching `unset` to clear the value:

```bash
chabeau unset default-provider
chabeau unset default-model openai                       # Provider-keyed: pass the provider
chabeau unset default-character "openai gpt-4"           # Provider/model-keyed: quote both
chabeau unset markdown                                   # Reverts to default (on)
chabeau unset mcp agpedia                                # Reverts to default (on)
chabeau unset mcp "agpedia yolo"                         # Reverts to default (off)
```

### Editing `config.toml` by hand

The following structured definitions need to be edited in `config.toml`
directly:
- **Custom themes** — multi-field color/style definitions under `[[custom_themes]]`
- **Personas** — id, display name, and bio under `[[personas]]`
- **Presets** — id, pre, and post instructions under `[[presets]]`

You can also edit the following in `config.toml`, but you don't strictly need to:
- **Custom providers** — can be configured via `chabeau provider` subcommands
- **MCP servers** — can be configured via `chabeau mcp` subcommands

Copy [examples/config.toml.sample](examples/config.toml.sample) to your config directory for a starting point.

Both the CLI and TUI run mutations through the same configuration orchestrator. Chabeau caches the parsed file based on its last-modified timestamp, skipping redundant reloads when nothing has changed, and persists updates atomically so a failed write never clobbers your existing `config.toml`.

## MCP Servers

Chabeau lets you connect MCP servers (HTTP or stdio) and use their tools/resources from the TUI.

- Manage servers from the CLI: `chabeau mcp list`, `chabeau mcp add`, `chabeau mcp add -a`, `chabeau mcp edit <server-id>`, `chabeau mcp edit <server-id> -a`, and `chabeau mcp remove <server-id>`.
- `chabeau mcp add` and `chabeau mcp edit` run in basic mode by default and prompt only for required settings; use `-a`/`--advanced` to configure optional fields.
- HTTP servers can use bearer tokens with `chabeau mcp token list [server-id]`, `chabeau mcp token add <server-id>`, and `chabeau mcp token remove <server-id>`.
- Advanced MCP HTTP configs support custom request headers via `headers = { KEY = "VALUE" }` in `[[mcp_servers]]`.
- Streamable HTTP transport reuses pooled HTTP connections across MCP initialize/list/tool calls for lower request overhead.
- `chabeau mcp add` probes OAuth discovery for HTTP/HTTPS servers and starts browser auth when available. You can also run `chabeau mcp oauth list [server-id]`, `chabeau mcp oauth add <server-id>`, and `chabeau mcp oauth remove <server-id>` directly. Use `chabeau mcp oauth add <server-id> -a` to provide an OAuth client id manually.
- For OAuth-backed MCP HTTP servers, Chabeau automatically refreshes expiring access tokens when a refresh token is available; if refresh fails, re-run `chabeau mcp oauth add <server-id>`.
- Stdio servers run a local command with optional `args` and `env`.
- In the TUI, `/mcp` lists servers and `/mcp <server-id>` shows server info. Toggle with `/mcp <server-id> on|off` (or `chabeau set mcp <server-id> on|off`). To also clear session runtime MCP state, use `/mcp <server-id> forget` instead.
- If a tool requires approval, Chabeau prompts you; use `/yolo <server-id> on|off` (or `chabeau set mcp <server-id> yolo on|off`) for per-server auto-approve.
- `--disable-mcp` turns MCP off for a session. `--debug-mcp` writes verbose MCP logs to `mcp.log`.

## Character Cards

Chabeau supports character cards in the v2 format, letting you chat with AI personas that define tone, background, and greeting. Cards can be JSON or PNG files (with embedded metadata).

### Import and Manage Cards
```bash
chabeau import path/to/character.json       # Import JSON card
chabeau import path/to/character.png        # Import PNG with embedded metadata
chabeau import character.json --force       # Overwrite existing card
```

Cards are stored in the Chabeau configuration directory. Use `chabeau -c` to print the directory name and any cards Chabeau discovers.

### Use Characters in Chat
```bash
chabeau -c hypatia                          # Start with character by name
chabeau -c hypatia.json                     # Start with character by filename
```

In the TUI, `/character` opens the character picker (↑↓ to navigate, Ctrl+O to inspect full definitions, Enter to select, Alt+Enter to set as default). You can also run `/character <name>` for quick switches.

### Defaults and Directories

Set defaults for provider/model combinations via Alt+Enter (or Ctrl+J) in the picker, or on the CLI:

```bash
chabeau set default-character openai gpt-4 hypatia
chabeau unset default-character openai gpt-4
```

To use a separate cards directory, set the `CHABEAU_CARDS_DIR` environment variable before launching Chabeau.

Example cards live in [examples/hypatia.json](examples/hypatia.json) and [examples/darwin.json](examples/darwin.json).

### Troubleshooting

- "Character not found": ensure the card is in `~/.config/chabeau/cards/` (or its equivalent on macOS or Windows) or provide the full path.
- "Invalid card format": verify the JSON structure matches the v2 spec with required fields (name, description, personality, scenario, first_mes, mes_example).
- "PNG missing metadata": PNG files must contain a `chara` tEXt chunk with base64-encoded JSON.
- Cards not appearing in picker: check file permissions and ensure files have `.json` or `.png` extensions.

### Format Reference

Character cards follow the [v2 specification](https://github.com/malfoyslastname/character-card-spec-v2).

## Personas

Personas allow you to define different user identities for conversations, each with their own name and optional biographical context. Unlike character cards (which define AI personas), personas define who *you* are in the conversation.

### Configure Personas

Add personas to your `config.toml`:

```toml
[[personas]]
id = "developer"
name = "Alex"
bio = "You are talking to Alex, a senior software developer with expertise in Rust and distributed systems."

[[personas]]
id = "student"
name = "Sam"
bio = "Sam is a computer science student learning about AI and machine learning."

[[personas]]
id = "casual"
name = "Jordan"
# bio is optional - persona will just change the display name
```

Assign defaults per provider/model from the CLI or in `config.toml`:

```bash
chabeau set default-persona openai gpt-4o developer
```

```toml
[default-personas.openai]
"gpt-4o" = "developer"
```

### Use Personas in Chat

```bash
chabeau --persona developer                 # Start with a specific persona
```

In the TUI, `/persona` opens the persona picker (↑↓ to navigate, Ctrl+O to read the persona text, Enter to select, Alt+Enter to set as default). You can also run `/persona <id>` for quick switches, or select "[Turn off persona]" to return to anonymous mode.

When a persona is active:
- Your messages are labeled with the persona's name instead of "You"
- The persona's bio (if provided) is prepended to the system prompt

### Variable Substitutions

Both personas and character cards support `{{user}}` and `{{char}}` variable substitutions:

- `{{user}}` is replaced with the active persona's display name (or "Anon" if no persona is active)
- `{{char}}` is replaced with the character's name (or "Assistant" if no character is active)

### Persona vs Character Integration

Personas and character cards work together seamlessly:
- **Character cards** define the AI's personality, background, and behavior
- **Personas** define your identity and context in the conversation
- Both support `{{user}}` and `{{char}}` variable substitutions
- The persona's bio is added to the system prompt before the character's instructions

## Presets

Presets let you inject reusable system instructions into the first and last system messages that Chabeau sends to the model. They are ideal for lightweight tone or formatting tweaks that you want to toggle quickly.

Chabeau ships with three built-in presets (`short`, `roleplay`, and `casual`) so you can experiment without editing your config. Set `builtin_presets = false` in `config.toml` to hide them from `/preset`, `/preset <id>`, and the `--preset` flag. If you define a preset with the same ID, your version overrides the built-in automatically.

### Configure Presets

Add presets to your `config.toml`:

```toml
[[presets]]
id = "focus"
pre = """
You are collaborating with {{user}}. Keep responses focused and direct.
"""
post = """
Before finishing, list any follow-up actions.
"""

[[presets]]
id = "roleplay"
pre = """
- Engage in roleplay with the user.
- Two paragraphs per turn max.
- Don't be shy to perform actions. Format these in italics, like this: *{{char}} frowns at {{user}}.*
- Be creative! Feel free to take the roleplay into new directions.
"""
```

- `pre` text is wrapped in blank lines and prepended to the very first system message.
- `post` text is wrapped in blank lines and appended to the final system message. If no system message exists at either position, Chabeau creates one automatically.
- Presets support the same `{{user}}` and `{{char}}` substitutions as personas and character cards.

Assign defaults per provider/model from the CLI or in `config.toml`:

```bash
chabeau set default-preset openai gpt-4o focus
```

```toml
[default-presets.openai]
"gpt-4o" = "focus"
```

### Use Presets in Chat

Launch with an ID like `--preset focus`, or pick interactively with `/preset`. Use Ctrl+O in the picker to review the preset instructions. The picker includes a "Turn off preset" option to clear the active preset.

The status bar shows the current preset when one is active so you can confirm the context you're using at a glance.

## Appearance and Rendering

### Themes

Chabeau ships with built-in themes and supports custom ones. Use `/theme` in the TUI to preview and Alt+Enter (or Ctrl+J) to persist the choice. On the CLI, run:

```bash
chabeau set theme dark   # Set a theme
chabeau themes           # List themes (built-in and custom)
chabeau unset theme      # Revert to default detection
```

When no explicit theme is set, Chabeau tries to infer a sensible default from your OS preference (e.g., macOS, Windows, GNOME). If no hint is available, it defaults to the dark theme.

Custom themes belong in `config.toml` under `[[custom_themes]]`. See [src/builtins/themes.toml](src/builtins/themes.toml) for color references and [examples/config.toml.sample](examples/config.toml.sample) for structure.

Themes can also set a `cursor_color` to change the terminal cursor via OSC 12 when the theme is applied.

Input uses a steady bar cursor inside the chat box so the insertion point stays easy to see while typing.

App messages—Chabeau’s own informational banners, warnings, and errors—use dedicated theme knobs so they’re easy to distinguish from assistant replies. Customize them with the `app_info_*`, `app_warning_*`, and `app_error_*` keys in your theme to control the prefix text, prefix styling, and message styling independently.

### Markdown and Syntax Highlighting

Toggle these features at runtime:

- `/markdown on|off|toggle`
- `/syntax on|off|toggle`

 Chabeau persists these preferences to the config file automatically. Syntax colors adapt to the active theme and use the theme’s code block background for consistent contrast.

When markdown is enabled, image ALT text is rendered as an OSC hyperlink pointing to the underlying image URL so you can open assets directly from the transcript.

### Color Support

Chabeau detects terminal color depth and adapts themes accordingly:

- Truecolor: if `COLORTERM` contains `truecolor` or `24bit`, Chabeau uses 24-bit RGB.
- 256 colors: if `TERM` contains `256color`, RGB colors are quantized to the xterm-256 palette.
- ANSI 16: otherwise, colors map to the nearest 16 ANSI colors.

Force a mode when needed with `CHABEAU_COLOR=truecolor|256|16`.

## Keyboard and Workflow Tips

### Interface Controls

See [the built-in help](src/builtins/help.md) for a full list of keyboard controls. A few highlights:

- Alt+Enter (or Ctrl+J) to start a new line; Enter sends. Arrow keys always act on the focused area.
- Compose mode (F4) flips the newline/send defaults; focus behavior stays the same.
- Home/End and Ctrl+A/Ctrl+E jump to the start or end of the visible line in the focused pane, even when text is soft-wrapped.
- PgUp/PgDn scroll the focused area — the transcript or the multi-line input — by a page at a time.
- Ctrl+N repeats your most recent `/refine` prompt on the latest assistant reply.
- Tab switches focus between the transcript and input unless the current input starts with `/`. When it does, Tab autocompletes slash commands. The active region shows a ``; the inactive one shows a `·`.
- Ctrl+O opens the inspect view for picker items—providers include their ID, base URL, and auth mode; themes show their ID and every color override; character cards expand to the full v2 definition.
- Ctrl+D on an empty input prints the transcript and exits; Ctrl+C exits immediately.

### Mousewheel

Chabeau avoids capturing the mouse so selection operations (copy/paste) work as expected. Some terminals treat mousewheel events as cursor key input, so scrolling moves the conversation. Others reveal terminal history; in that case, use the cursor keys or PgUp/PgDn instead.

### External Editor

Set the `EDITOR` environment variable to compose longer responses in your favorite editor:

```bash
export EDITOR=nano          # or vim, code, etc.
export EDITOR="code --wait" # VS Code with wait
```

Once set, press Ctrl+T in the TUI to launch the external editor.

## Architecture Overview

See [ARCHITECTURE.md](ARCHITECTURE.md) for a high-level walkthrough aligned with the current repository state.

Chabeau uses a modular design with focused components:

- `main.rs` – Entry point
- `api/` – API data structures and model-related helpers
  - `mod.rs` – API data structures
  - `models.rs` – Model fetching and sorting functionality
- `auth/` – Authentication and provider management
  - `mod.rs` – Authentication manager implementation
  - `ui.rs` – Interactive prompts and input helpers for auth flows
- `builtins/` – Build-time assets embedded into the binary
  - `help.md` – In-app keyboard shortcut and command reference
  - `models.toml` – Supported provider definitions
  - `presets.toml` – Built-in system instruction presets
  - `themes.toml` – Built-in UI themes
- `character/` – Character card support (v2 format)
  - `cache.rs` – In-memory caching with invalidation
  - `card.rs` – Character card data structures and v2 spec parsing
  - `import.rs` – Import command and validation logic
  - `loader.rs` – Card file loading (JSON and PNG with metadata extraction)
  - `mod.rs` – Module exports and public API
  - `png_text.rs` – PNG tEXt chunk reader/writer
  - `service.rs` – Shared character cache and resolution helpers for the TUI and CLI
- `cli/` – Command-line interface parsing and handling
  - `character_list.rs` – Character card listing functionality
  - `mod.rs` – CLI argument parsing and command dispatching
  - `model_list.rs` – Model listing functionality
  - `provider_list.rs` – Provider listing functionality
  - `settings/` – Trait-based `set`/`unset` handler registry
  - `theme_list.rs` – Theme listing functionality
- `commands/` – Chat command processing and registry-driven dispatch
  - `handlers/` – Domain-specific command handlers (`core`, `config`, `io`, `mcp`)
  - `mcp_prompt_parser.rs``/server-id:prompt-id` parser and helpers
  - `mod.rs` – Command dispatcher and command result plumbing
  - `refine.rs` – Message refinement logic
  - `registry.rs` – Static command metadata registry
- `core/` – Core application components
  - `app/` – Application state and controllers
    - `actions/` – Internal action definitions grouped by domain plus dispatcher routing
      - `input/` – Input subdomains for compose, command, inspect, and status actions
      - `mcp_gate.rs` – MCP initialization gating and deferred-send handling
      - `sampling.rs` – MCP sampling request queueing and permission flow
      - `stream_errors.rs` – Stream error handling and MCP unsupported fallback flow
      - `stream_lifecycle.rs` – Stream creation, chunk/app-message appends, and finalization
      - `streaming.rs` – Streaming action dispatcher
      - `tool_calls.rs` – Tool permission and tool-result completion handling
    - `app.rs` – Main `App` struct and event loop integration
    - `conversation.rs` – Conversation controller for chat flow, retries, and streaming helpers
    - `mod.rs` – App struct and module exports
    - `picker/` – Generic picker that powers all TUI selection dialogs
    - `pickers.rs` – Picker constructors and helpers for each picker type
    - `session.rs` – Session bootstrap and provider/model state
    - `settings.rs` – Theme and provider controllers
    - `streaming.rs` – Stream request construction, tool-flow orchestration, and MCP integration helpers
    - `ui_helpers.rs` – UI state transition helpers
    - `ui_state.rs` – UI state management and text input helpers
    - `builtin_presets.rs` – Built-in preset loader
  - `builtin_mcp.rs` – Built-in MCP prompt/tool context injection helpers
  - `builtin_oauth.rs` – Built-in OAuth callback assets and helpers
  - `builtin_providers.rs` – Built-in provider configuration (loads from `builtins/models.toml`)
  - `chat_stream.rs` – Shared streaming service that feeds responses to the app, UI, and loggers
  - `config/` – Configuration data, defaults, caching, and persistence
    - `data.rs` – Configuration data types and pure helpers
    - `defaults.rs` – Default selection helpers and `Config` implementations
    - `io.rs` – Config path resolution and persistence routines
    - `mod.rs` – Public exports for configuration helpers
    - `orchestrator.rs` – Cached config loader, mutation orchestrator, and test isolation
    - `tests.rs` – Configuration module tests
  - `keyring.rs` – Secure storage for API keys
  - `mcp_auth.rs` – Keyring-backed MCP token storage
  - `mcp_sampling.rs` – MCP sampling request conversion and summarization helpers
  - `message.rs` – Message data structures
  - `oauth.rs` – Shared MCP OAuth discovery, browser flow, callback handling, and token refresh helpers
- `mcp/` – Model Context Protocol client integration
  - `client/` – MCP client orchestration, transport plumbing, protocol parsing, and operations
    - `mod.rs` – Public MCP client manager API, state, and shared context types
    - `operations.rs` – MCP `execute_*` entry points and shared request flow helpers
    - `protocol.rs` – MCP response parsing and protocol-version helpers
    - `transport_http.rs` – Streamable HTTP session lifecycle, request exchange interface, and event listener helpers
    - `transport_stdio.rs` – Stdio transport client lifecycle, request dispatch, and server I/O readers
    - `tests.rs` – MCP client manager integration-style unit tests
  - `events.rs` – MCP server request envelopes
  - `transport/` – MCP transport implementations and shared interfaces
    - `mod.rs` – Shared transport traits, enums, and list-fetch helpers
    - `stdio.rs` – Stdio transport request/list adapters
    - `streamable_http.rs` – Streamable HTTP list adapters plus shared SSE buffering/parsing utilities
  - `mod.rs` – MCP module exports and tool name constants
  - `permissions.rs` – Per-tool permission decision store
  - `registry.rs` – Enabled MCP server registry
  - `persona.rs` – Persona management and variable substitution
  - `preset.rs` – System instruction preset management
  - `providers.rs` – Provider selection and shared provider utilities
  - `shared_selection.rs` – Shared current-selection helpers
  - `text_wrapping.rs` – Text wrapping utilities
- `ui/` – Terminal interface rendering
  - `appearance.rs` – Theme and style definitions
  - `chat_loop/` – Mode-aware chat loop orchestrating UI flows, keybindings, and command routing
    - `event_loop.rs` – Async terminal loop orchestration, event polling, and stream dispatch
    - `executors/` – Background task executors for model loading and MCP operations
    - `keybindings/` – Mode-aware keybinding registry and handlers
    - `lifecycle.rs` – Terminal setup/teardown helpers and resource guards
    - `modes.rs` – Mode-aware key handlers and text interaction utilities
  - `help.rs` – Help text rendering
  - `layout.rs` – Shared width-aware layout engine for Markdown and plain text
  - `markdown/` – Modular markdown pipeline (`parser.rs`, `render.rs`, `lists.rs`, `code.rs`, `metadata.rs`, `table.rs`) plus wrapping helpers and span-metadata tests
  - `mod.rs` – UI module declarations
  - `osc.rs` / `osc_backend.rs` / `osc_state.rs` – OSC hyperlink and cursor-color support
  - `picker.rs` – Picker controls and rendering
  - `renderer.rs` – Terminal interface rendering (chat area, input, pickers)
  - `span.rs` – Span metadata for clickable links
  - `theme.rs` – Theme loading and management
  - `title.rs` – Header bar rendering
- `utils/` – Utility functions and helpers
  - `auth.rs` – Auth and provider utility helpers
  - `clipboard.rs` – Cross-platform clipboard helper
  - `color.rs` – Terminal color detection and palette quantization
  - `editor.rs` – External editor integration
  - `input.rs` – Keyboard/input utility helpers
  - `line_editor.rs` – Shared single-line terminal editor for interactive CLI/auth prompts
  - `logging.rs` – Chat logging functionality
  - `mod.rs` – Utility module declarations
  - `scroll.rs` – Text wrapping and scroll calculations
  - `syntax.rs` – Syntax highlighting support helpers
  - `url.rs` – URL parsing and normalization helpers

## Development

### Running Tests
```bash
cargo test                    # All tests
cargo test scroll::           # Scroll functionality tests
cargo test --release          # Faster execution
```

### Local Quality Checks
```bash
cargo check
cargo fmt
cargo test
cargo clippy --all-targets --all-features
```

### CI and Release Workflows
- `.github/workflows/ci.yml` runs build, test, and reproducibility checks on pushes and pull requests.
- `.github/workflows/publish.yml` selects the newest semver tag reachable from `main`, then publishes the matching crates.io release and GitHub Release binaries with SHA-256 checksums, a keyless Sigstore-signed checksum manifest, and a GitHub Release description extracted from the matching `CHANGELOG.md` version section.
- `.github/workflows/nightly.yml` builds Linux/macOS/Windows release binaries on a schedule and updates the moving `Nightly` pre-release with checksummed artifacts and a keyless Sigstore-signed checksum manifest.

### Performance

Chabeau includes lightweight performance checks in the unit test suite and supports optional Criterion benches.

- Built-in perf checks (unit tests):
  - Short history prewrap (50 iters, ~60 lines): warns at ≥ 90ms; fails at ≥ 200ms.
  - Large history prewrap (20 iters, ~400 lines): warns at ≥ 400ms; fails at ≥ 1000ms.
  - Run with: `cargo test` (warnings print to stderr; tests only fail past the fail thresholds).
- Optional benches (release mode) using Criterion 0.7:
  - A `render_cache` bench validates the cached prewrapped rendering path.
  - Run: `cargo bench`
  - Reports live in `target/criterion/` (HTML under `report/index.html`).
  - To add new benches, create files under `benches/` (e.g., `benches/my_bench.rs`) and use Criterion’s `criterion_group!/criterion_main!`.
  - Benches import internal modules via `src/lib.rs` (e.g., `use chabeau::...`).

## License

CC0 1.0 Universal (Public Domain)