Metadata-Version: 2.5
Name: agentknit-tui
Version: 0.1003.0
Summary: Textual TUI front-end for agentknit coding agents.
Project-URL: Homepage, https://github.com/monperrus/agentknit-tui
Project-URL: Repository, https://github.com/monperrus/agentknit-tui
Project-URL: Issues, https://github.com/monperrus/agentknit-tui/issues
Author: agentknit contributors
License: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console :: Curses
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.11
Requires-Dist: agentknit<1,>=0.2.0
Requires-Dist: rich>=13
Requires-Dist: textual>=0.86
Provides-Extra: test
Requires-Dist: pytest-asyncio>=0.23; extra == 'test'
Requires-Dist: pytest-cov>=4; extra == 'test'
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# agentknit-tui

A [Textual](https://textual.textualize.io/) TUI front-end for
[agentknit](https://github.com/monperrus/agentknit) coding agents.

Features unique to this harness (things Claude Code and Codex do not do):

- **Side questions.** Type while a turn is running and the prompt is
  answered as a *side question* — a separate read-only LLM call over a
  snapshot of the conversation, rendered next to the turn without
  touching the turn's context or progress.
- **Token-budget countdown at deciles.** The agent runs under a hard
  token budget; the log prints `remaining/budget` only when usage
  crosses into a new 10% band, instead of nagging after every call.
- **Read-through streaming.** Scrolling up detaches the view from the
  end: the agent keeps streaming into the log, but nothing yanks you
  back down. Scrolling to the bottom re-attaches the follow.
- **Paste-safe rendering.** No `│` gutters, no box-drawing borders, no
  right-hand padding on any rendered line — copying a selection (or a
  whole block) yields the payload text exactly.
- **Per-folder prompt history shared with the REPL.** The TUI and the
  agentknit REPL read and write the same per-directory history file, so
  `↑` recalls prompts typed in either front-end, scoped to the folder
  they were typed in.
- **Quota vs rate-limit labeling.** HTTP 403 is reported as "Quota"
  (exhausted window), 429 as "Rate limited" — providers that use 403
  for quota are not misreported.
- **Clipboard that works in VTE terminals.** Copy tries the platform
  tool (`xclip`/`xsel`, `wl-copy`, `pbcopy`, `clip.exe`) *and* emits
  OSC 52; one of the two reaches the system clipboard even in terminals
  (Terminator, older gnome-terminal) that ignore OSC 52.
- **Word-level diff highlighting.** `str_replace` edits render as
  colorized unified diffs with the changed *words* inside each modified
  line bolded on a dark background — a one-word change in a long line
  is visible at a glance. `write_file` calls render the same way, as an
  all-additions diff of the new content. Copied diff lines paste clean.
- **Clickable file paths.** The path in a `str_replace`/`write_file`
  diff header carries an OSC 8 hyperlink, so terminals that support it
  (kitty, iTerm2, WezTerm, VS Code's integrated terminal, Ghostty, …)
  let you open the edited file straight from the log.

Everything else is standard TUI furniture: one conversation pane, a
multiline prompt, tool calls/results/token accounting inline, a live
status bar. The TUI is a pure subscriber to the event stream from
agentknit.

## Install

```
pip install agentknit-tui
```

It depends on `agentknit`, `textual`, and `rich`.

## Usage

```
agentknit-tui                       # default: glm-5.2 via z.ai
agentknit-tui glm-5.2
agentknit-tui "qwen3-8b" "https://openrouter.ai/api/v1"
agentknit-tui --session <id>        # resume a previous trajectory
agentknit-tui --non-interactive     # drop ask_user* tools from the schema
agentknit-tui --no-strict-cache-proof
```

### Key bindings

| key                  | action                          |
| -------------------- | ------------------------------- |
| `Enter`              | submit the prompt               |
| `Shift/Ctrl/Alt+Enter` | insert a newline              |
| `↑` / `↓`            | recall past prompts (same folder, shared with the REPL) |
| `Esc` / `Ctrl+C`     | cancel the running turn (or quit when idle) |
| `Ctrl+Shift+C`       | copy the mouse selection in the log          |
| `Ctrl+L`             | clear the on-screen conversation log only |

While reading: scrolling up (mouse wheel, or `PageUp` with the prompt
focused) detaches the view from the end — new messages keep arriving in
the log but no longer pull you back down, so you can keep reading while
the agent works. Scrolling back to the bottom re-attaches the
auto-follow. `PageDown` pages down and snaps to the end when a page
away.

### Mouse

- **Drag** over the conversation log selects text in place (highlighted as
  you drag); `Ctrl+Shift+C` then copies the selection (plain `Ctrl+C` also
  works when your terminal delivers it). `Esc` clears the selection.
- Paste into the prompt with the terminal's own paste (`Ctrl+Shift+V`,
  `Shift+Insert`, or middle-click) — `Ctrl+V` inside the TUI only reads
  text copied *within* the TUI.
- Copying tries the platform clipboard tool first (`xclip`/`xsel`, `wl-copy`,
  `pbcopy`, `clip.exe`) and also emits OSC 52; one of the two reaches the
  system clipboard even in VTE terminals that ignore OSC 52 (Terminator,
  older gnome-terminal).
- Every line the TUI renders is paste-safe: turns, replies, diffs and tool
  output carry no `│` gutters or box-drawing borders, and no right-hand
  padding — copying a selection (or a whole block) yields the payload text
  exactly. Copied lines are trimmed of any trailing filler.

`Ctrl+L` (and the TUI's built-in `/clear` alias) wipes the displayed log
and zeroes the token counters in the status bar; the agent's message
history — the context sent to the model — is untouched, and `/usage`
still reports the session's full totals.

### Status bar

The line under the prompt always shows the model, working directory, and
total tokens in the format `<model> - <working directory> - <tokens>`.
While a turn is running, the task the agent is working on appears below it,
wrapped over two terminal-width lines so long prompts stay readable.
To reset the LLM context (session history, keeping the system prompt), run
`/reset-context` in the TUI; it forwards to agentknit's `/clear` handler.

Slash commands (`/help`, `/usage`, `/clear`, `/compact`, `/model`, `/reset-context`, `/exit`)
are forwarded to agentknit's command registry; their printed output is
captured and shown inline.

### `str_replace`/`write_file` diffs

Edits made through the `str_replace` tool are shown as a colorized unified
diff instead of the tool's argument dump: deleted lines in red, added lines
in green, and the specific changed *words* within each modified line
highlighted in bold on a dark background, so a one-word change inside a long
line is visible at a glance. Each row carries the file's real line number
plus the `+`/`-` marker, separated from the content by spaces only — no
vertical-bar gutter, so copied diff lines paste clean. `write_file` calls
render the same way with an empty "before" side, so the new content shows
up as an all-green diff instead of a repr'd content blob.

```diff
--- pkg/mod.py
+++ pkg/mod.py
@@ -1,4 +1,4 @@
-def greet(name):
-    # say hello to the user
-    print("hello " + name)
+def greet(name, greeting="hi"):
+    # greet the user
+    print(greeting + " " + name)
     return None
```

The `--- path`/`+++ path` header and the tool-call title both carry an
OSC 8 hyperlink to the file, so a terminal that supports it renders the
path as clickable.


On exit the TUI prints the command to resume the session on the console,
exactly like the end of the agentknit REPL:

```
Resume: agentknit-tui glm-5.2 --session <session-id>
```

Set `AGENTKNIT_RESUME_COMMAND` to override the program prefix (wrappers
embedding the model already do this); the `--session <id>` suffix is always
appended.

## Design notes

- The agent loop (`agentknit.run_turn`) is synchronous and blocking and
  emits typed events through a session `on_event` callback. The TUI runs
  each turn in a Textual worker thread and forwards every event onto a
  thread-safe queue; a 50 ms UI-thread timer drains the queue and renders
  each event's pre-formatted ANSI string into a Rich renderable.
- agentknit's `fmt` strings are raw `\033[…]` ANSI escapes (not Rich
  markup), so they are decoded with `rich.ansi.AnsiDecoder` for faithful
  colour/style reproduction.
- Multi-line input uses `TextArea`, so users can paste and edit freely.
  A `CancelToken` is wired to the cancel bindings so a turn can be
  interrupted cooperatively, exactly like Ctrl+C in the REPL.
- Prompt history is per-folder and shared with the REPL: both read and
  write agentknit's readline history file
  (`~/.local/share/agent_probe/repl_history/<md5(cwd)>.hist`), so arrow-up
  recalls instructions typed in either front-end, scoped to the folder they
  were typed in. `↑` on the first prompt line walks back, `↓` forward, and
  any edit drops back to normal typing.
- On exit the TUI mirrors the REPL's teardown: the trajectory snapshot is
  saved (if the session has any messages), a `session_end` record is
  appended to the session log, and — after Textual restores the plain
  console — the same dim `Resume: …` line the REPL prints is echoed so the
  session can be continued with `--session <id>`.
- agentknit installs a module-level SIGINT handler (meant for the
  foreground REPL) that kills the active tool subprocess. The TUI
  neutralises it for each turn's duration and drives cancellation through
  the `CancelToken` instead.

## License

MIT.
