|
2 | 2 |
|
3 | 3 | Path: `example/tui_coding_agent` |
4 | 4 |
|
5 | | -This example shows a terminal UI coding agent built with: |
6 | | - |
7 | | -- `llamadart` for local model inference and tool calling |
8 | | -- `nocterm` for the interactive TUI layer |
9 | | - |
10 | | -By default it starts with GLM 4.7 Flash (`unsloth/GLM-4.7-Flash-GGUF:UD-Q4_K_XL`). |
11 | | -You can switch to any local path, URL, or Hugging Face shorthand at startup or |
12 | | -inside the app. |
13 | | - |
14 | | -Tool mode defaults to a **stable text-protocol tool loop** (recommended for |
15 | | -GLM). Native template grammar tool-calling is optional and can be enabled with |
16 | | -`--native-tool-calling`. |
17 | | - |
18 | | -## Features |
19 | | - |
20 | | -- Streaming assistant output in a TUI chat layout |
21 | | -- TurboVision-inspired desktop with overlapping windows |
22 | | -- Turbo C-style menu bar with popup menus (`Alt` + mnemonic) |
23 | | -- Compact centered exit confirmation dialog |
24 | | -- Built-in coding tools: `list_files`, `read_file`, `search_files`, |
25 | | - `write_file`, `run_command` |
26 | | -- Workspace path guard (blocks tool access outside the selected workspace) |
27 | | -- Model switching command at runtime (`/model <source>`) |
28 | | -- Multi-session chat workflow (create/switch/close local sessions) |
29 | | -- One desktop window per session (MDI-style overlapping chat windows) |
30 | | -- Mouse window controls (focus, drag title bar, resize from lower-right handle) |
31 | | -- Slash command autocomplete (`Tab`, `Shift+Tab`, `Up`, `Down`) |
32 | | -- Tool-policy guardrails: |
33 | | - - direct conceptual prompts default to no-tool answers |
34 | | - - workspace/repo prompts enforce inspection before answering |
35 | | - - duplicate and over-limit tool calls are skipped safely |
36 | | -- Default coding-oriented sampling values |
| 5 | +This is a deliberately small, Pi-style local coding agent built with |
| 6 | +`llamadart` and `nocterm`. It keeps one model, one conversation, one screen, |
| 7 | +four general tools, and a sequential model-to-tool loop that is easy to read |
| 8 | +and adapt. |
37 | 9 |
|
38 | | -## Run |
| 10 | +## Agent loop |
39 | 11 |
|
40 | | -```bash |
41 | | -cd example/tui_coding_agent |
42 | | -dart pub get |
43 | | -dart run bin/tui_coding_agent.dart |
44 | | -``` |
| 12 | +For each user request, the example: |
45 | 13 |
|
46 | | -### Run with a specific workspace |
| 14 | +1. asks the model for the next response; |
| 15 | +2. accepts either a normal final answer or exactly one standalone JSON tool |
| 16 | + call; |
| 17 | +3. executes that tool and returns its JSON result to the model; and |
| 18 | +4. repeats until the model answers normally or the round limit is reached. |
47 | 19 |
|
48 | | -```bash |
49 | | -dart run bin/tui_coding_agent.dart --workspace /path/to/project |
| 20 | +The only executable text-protocol form is: |
| 21 | + |
| 22 | +```text |
| 23 | +<tool_call>{"name":"tool_name","arguments":{...}}</tool_call> |
50 | 24 | ``` |
51 | 25 |
|
52 | | -### Run with a specific model |
| 26 | +The block must be the complete assistant response. Calls run one at a time; |
| 27 | +prose around a call, sibling calls, shorthand, XML variants, unknown tools, |
| 28 | +and malformed or incomplete JSON are not executed. |
53 | 29 |
|
54 | | -```bash |
55 | | -dart run bin/tui_coding_agent.dart --model /path/to/model.gguf |
| 30 | +## Tools |
| 31 | + |
| 32 | +The model normally receives exactly four tools: |
| 33 | + |
| 34 | +| Tool | Purpose | |
| 35 | +| --- | --- | |
| 36 | +| `read` | Read bounded UTF-8 text from a workspace file. | |
| 37 | +| `write` | Create or overwrite a UTF-8 workspace file. | |
| 38 | +| `edit` | Replace exactly one literal occurrence in a workspace file. | |
| 39 | +| `bash` | Run a command with Bash on Unix or `cmd.exe` on Windows. | |
| 40 | + |
| 41 | +The file tools resolve paths canonically and reject paths, including symlink |
| 42 | +targets, outside the selected workspace. Applicable `AGENTS.md` files from the |
| 43 | +workspace and its ancestors are loaded once when the session starts and added |
| 44 | +to the system prompt. Start with `--read-only` to expose only `read` and omit |
| 45 | +all mutation and shell tools. |
| 46 | + |
| 47 | +## Default model and cache |
| 48 | + |
| 49 | +The default model is Qwen3.6 35B-A3B: |
| 50 | + |
| 51 | +```text |
| 52 | +hf://unsloth/Qwen3.6-35B-A3B-GGUF/Qwen3.6-35B-A3B-UD-Q4_K_M.gguf |
56 | 53 | ``` |
57 | 54 |
|
58 | | -or |
| 55 | +The native-desktop preset targets systems with at least 32 GB of RAM and uses: |
| 56 | + |
| 57 | +- 16,384-token context |
| 58 | +- up to 4,096 output tokens |
| 59 | +- full GPU offload where supported |
| 60 | +- logical batch size `2,048`, physical micro-batch size `512` |
| 61 | +- temperature `0.7`, top-K `20`, top-P `0.8`, min-P `0` |
| 62 | +- repeat penalty `1.0`, presence penalty `1.5` |
| 63 | +- non-thinking generation |
| 64 | +- at most 24 tool rounds per request |
| 65 | + |
| 66 | +Pass `--thinking` to opt into the higher-quality reasoning profile: |
| 67 | + |
| 68 | +- 32,768-token context |
| 69 | +- up to 8,192 output tokens |
| 70 | +- logical batch size `2,048`, physical micro-batch size `512` |
| 71 | +- temperature `0.6`, top-K `20`, top-P `0.95`, min-P `0` |
| 72 | +- repeat penalty `1.0`, presence penalty `0` |
| 73 | +- thinking enabled |
| 74 | + |
| 75 | +Thinking is opt-in because it is slower, uses substantially more context and |
| 76 | +memory, and can spend much of the output budget reasoning before it reaches a |
| 77 | +tool call or final answer. The default non-thinking profile keeps the local |
| 78 | +demo responsive; use `--thinking` when solution quality matters more than |
| 79 | +latency and your machine can accommodate the larger context. The 32K profile |
| 80 | +keeps practical memory headroom for the quantized model on a 64 GB Apple Silicon |
| 81 | +system; larger 48K and 64K contexts can exhaust Metal allocations. |
| 82 | + |
| 83 | +Qwen3.6 exposes thinking as an on/off capability, not a trained |
| 84 | +low/medium/high reasoning-effort scale. The example therefore keeps the control |
| 85 | +honest: omit `--thinking` for off, or pass it for on. When enabled, prior |
| 86 | +reasoning is preserved across the agent's tool rounds through Qwen's |
| 87 | +`preserve_thinking` template option. Upstream llama.cpp also has a mechanical |
| 88 | +reasoning-token budget, but llamadart's embedded inference API does not expose |
| 89 | +that control yet; total output tokens are not used as a fake reasoning level. |
| 90 | + |
| 91 | +The session passes this source directly to `LlamaEngine.loadModelSource`, so |
| 92 | +downloads use `llamadart`'s shared per-user model cache and resume behavior. |
| 93 | +Use `--cache-dir` only to override that cache root. An earlier flat |
| 94 | +`UD-Q4_K_S` TUI file remains usable by passing its local path explicitly; it is |
| 95 | +not substituted for the new `UD-Q4_K_M` default. |
| 96 | + |
| 97 | +The model is selected only at startup. Pass a local GGUF path, HTTP(S) URL, or |
| 98 | +exact `hf://owner/repo/path/to/model.gguf` reference with `--model` before |
| 99 | +launching the TUI. |
| 100 | + |
| 101 | +## Run |
59 | 102 |
|
60 | 103 | ```bash |
61 | | -dart run bin/tui_coding_agent.dart --model owner/repo:Q4_K_M |
| 104 | +cd example/tui_coding_agent |
| 105 | +dart pub get |
| 106 | +dart run bin/tui_coding_agent.dart |
62 | 107 | ``` |
63 | 108 |
|
64 | | -### Enable native template tool-calling (experimental) |
| 109 | +Choose a workspace and model at startup when needed: |
65 | 110 |
|
66 | 111 | ```bash |
67 | | -dart run bin/tui_coding_agent.dart --native-tool-calling |
| 112 | +dart run bin/tui_coding_agent.dart --workspace /path/to/project |
| 113 | +dart run bin/tui_coding_agent.dart --thinking |
| 114 | +dart run bin/tui_coding_agent.dart --read-only |
| 115 | +dart run bin/tui_coding_agent.dart --model /path/to/model.gguf |
| 116 | +dart run bin/tui_coding_agent.dart \ |
| 117 | + --model hf://owner/repo/path/to/model.gguf \ |
| 118 | + --cache-dir /path/to/model-cache |
68 | 119 | ``` |
69 | 120 |
|
70 | | -## Interactive Commands |
71 | | - |
72 | | -- `/help` - Show available commands |
73 | | -- `/clear` - Clear active session log |
74 | | -- `/model` - Show current model source and loaded path |
75 | | -- `/model <path|url|owner/repo[:hint]>` - Switch model and reset session history |
76 | | -- `/workspace` - Show workspace root |
77 | | -- `/new` - Create a new session |
78 | | -- `/next` - Switch to next session |
79 | | -- `/prev` - Switch to previous session |
80 | | -- `/close` - Close current session |
81 | | -- `/zoom-window` - Zoom/unzoom active desktop window |
82 | | -- `/next-window` - Focus next desktop window |
83 | | -- `/prev-window` - Focus previous desktop window |
84 | | -- `/tile-windows` - Arrange all windows in a tiled grid |
85 | | -- `/stack-windows` - Arrange all windows in stacked cascade |
86 | | -- `/cancel` - Cancel active generation |
87 | | -- `/exit` - Open exit confirmation dialog |
88 | | -- `/quit` - Open exit confirmation dialog |
89 | | - |
90 | | -## Keyboard Shortcuts |
91 | | - |
92 | | -- `F1` - Show help |
93 | | -- `F2` - Seed `/model ` command |
94 | | -- `F3` - Clear conversation |
95 | | -- `F4` - Previous session |
96 | | -- `F5` - Zoom/unzoom active window |
97 | | -- `F6` - Focus next desktop window |
98 | | -- `F7` - Create new session |
99 | | -- `F8` - Next session |
100 | | -- `F9` - Focus previous desktop window |
101 | | -- `F10` - Open/close top menu bar |
102 | | -- `F12` - Close active session |
103 | | -- `Alt+W` - Close active session |
104 | | -- `Alt+X` - Open exit confirmation |
105 | | -- `Alt` + Arrow keys - Move active window |
106 | | -- `Alt` + `Shift` + Arrow keys - Resize active window |
107 | | -- `Esc` - Cancel generation when busy, otherwise open exit confirmation |
108 | | -- `Alt+<menu letter>` - Open top menu (`File/Edit/Search/...`) |
109 | | - |
110 | | -In the exit confirmation dialog: |
111 | | - |
112 | | -- `Left`/`Right`/`Tab` (`h`/`l` also supported) toggles YES/NO |
113 | | -- `Enter` confirms the currently selected option |
114 | | -- `Esc` or `N` selects NO and dismisses the dialog |
115 | | - |
116 | | -## Notes |
117 | | - |
118 | | -- The first run may take time due to model download and initialization. |
119 | | -- Downloaded models use the per-user shared `llamadart` model cache by default; |
120 | | - pass `--cache-dir` to use a workspace-local or app-specific cache instead. |
121 | | -- `run_command` uses a safety filter and executes from the workspace root |
122 | | - (or a workspace-relative subdirectory). |
123 | | -- `run_command` accepts `command` and also tolerates alias keys (`cmd`, |
124 | | - `input`, `shell_command`) for model-compatibility. |
125 | | -- If you hit grammar-related crashes in native mode, run without |
126 | | - `--native-tool-calling` (default stable mode). |
| 121 | +Use `--help` for the complete startup options. |
| 122 | + |
| 123 | +## TUI |
| 124 | + |
| 125 | +The single-screen UI restores a compact TurboVision-inspired blue window, |
| 126 | +gray status chrome, and framed transcript without restoring the old multi-window |
| 127 | +desktop. It maintains one in-memory conversation for the loaded model. |
| 128 | + |
| 129 | +Normal answers stream incrementally through a compact GitHub-flavored Markdown |
| 130 | +renderer with headings, lists, links, inline code, and syntax-highlighted fenced |
| 131 | +code. Code fences use their language label when available, cheaply recognize a |
| 132 | +few common unlabelled formats, and fall back safely to plain code. Their |
| 133 | +background fills every row, including empty lines. Markdown blocks and |
| 134 | +transcript messages no longer add automatic blank spacer rows. With |
| 135 | +`--thinking`, reasoning streams separately under a subdued `[think]` label and |
| 136 | +the final answer remains under `[agent]`. The stream gate withholds text that |
| 137 | +could be a split `<tool_call>` envelope, so raw executable JSON never flashes |
| 138 | +in the transcript before validation. High-frequency deltas are coalesced to |
| 139 | +terminal frame cadence and accumulated in buffers. Off-screen transcript rows |
| 140 | +are built lazily, while the active Markdown row is reparsed only at presentation |
| 141 | +cadence rather than for every burst of backend deltas. Syntax highlighting is |
| 142 | +bounded for unusually large generated blocks so streaming stays responsive. |
| 143 | + |
| 144 | +- `/help` shows the small command list. |
| 145 | +- `/clear` resets the conversation without reloading the model. |
| 146 | +- `/model` reports the startup-selected source and loaded model name. |
| 147 | +- `/workspace` reports the workspace root. |
| 148 | +- `/cancel` cancels current work. |
| 149 | +- `/quit` exits. |
| 150 | +- `Esc` cancels while busy; otherwise press it twice to quit. |
| 151 | +- `Ctrl+C` cancels while busy and exits cleanly while idle. |
| 152 | + |
| 153 | +## Limits and trust boundary |
| 154 | + |
| 155 | +Model resolution and downloads, generation, and the active `bash` process tree |
| 156 | +can be cancelled. File reads and edits, tool results, shell output, command |
| 157 | +duration, context use, and tool rounds are bounded so the demo fails clearly |
| 158 | +instead of growing without limit. |
| 159 | + |
| 160 | +Native model allocation itself cannot be interrupted. If cancellation is |
| 161 | +requested during that phase, the session unloads the model as soon as the |
| 162 | +allocation call returns. |
| 163 | + |
| 164 | +Cancellation does not roll back effects that already happened. The transcript |
| 165 | +shows the command or file path, a bounded result summary, and a warning when an |
| 166 | +interrupted tool may have changed state; the next turn retains that warning. |
| 167 | + |
| 168 | +`bash` is intentionally a general shell tool and is **not sandboxed**. Its |
| 169 | +commands run with the current user's normal permissions and can read or modify |
| 170 | +files outside the selected workspace, access the environment and network, and |
| 171 | +start arbitrary child processes. Workspace confinement applies only to |
| 172 | +`read`, `write`, and `edit`. Use the demo only with trusted prompts and |
| 173 | +repositories, or run the whole process inside an external sandbox or |
| 174 | +container. |
127 | 175 |
|
128 | 176 | ## Test |
129 | 177 |
|
|
0 commit comments