cleat
cleat lets an AI agent use a real, persistent terminal session and actually
know what happened — whether a command succeeded, what it printed, and
whether it's still running — instead of guessing from a stream of raw
terminal noise.
A headless terminal layer for AI agents. cleat runs a persistent shell
session behind a PTY, parses its byte stream for OSC 133
shell-integration marks, and exposes it to an agent over MCP
as structured results — stdout, real exit_code, files touched — instead
of raw escape-code soup.
It is not a terminal emulator and not a modification to your terminal. It's a separate process that runs inside whatever terminal you already use, and it stays terminal-agnostic.
Why
When an agent drives a terminal, it normally sees a continuous byte river:
prompt redraws, echoed keystrokes, color codes, and output, with no marker for
where one command's output ends or whether it succeeded. Worse, when you scrape
a PTY, the exit code isn't in the stream at all — the shell knows $? but
never prints it.
cleat injects OSC 133 marks into the shells it spawns and parses them back
out, so the agent gets:
{ "stdout": "...", "exit_code": 0, "completed": true, "state": "idle" }
…and the session is persistent: cd, export, activated venvs, and ssh
sessions all carry across calls — something a fresh subprocess per command
cannot do.
Session state
Every tool response carries a state field, derived from termios flags and
the foreground process group — facts only the process holding the PTY can
read — instead of guessed from output timing:
state |
Meaning | What to do |
|---|---|---|
idle |
nothing is running | call run_command for the next thing |
running |
a command is executing | poll read_output |
awaiting-input |
a REPL/prompt is blocked on stdin | drive it with send_keys |
password |
a secret prompt (sudo, read -s) is waiting, echo off |
stop — send_keys raises unless you pass confirm_password_prompt=True, which you should only do with the human's explicit consent |
tui |
a full-screen program (vim, top, less) owns the terminal | use read_screen/send_keys, not run_command |
possibly-awaiting-input |
best-effort (Linux only): looks like a plain read/cat-style canonical-mode prompt, not a busy program |
try send_keys with a line of input |
Install
Requires Python ≥3.10 on a POSIX system (Linux/macOS). cleat is on
PyPI.
Register it with Claude Code — with uv (no install step needed):
claude mcp add cleat -- uvx cleat
…or if you'd rather install it into your environment first:
pip install cleat
claude mcp add cleat -- cleat
Either way, you can add it to a project's .mcp.json:
{
"mcpServers": {
"cleat": {
"command": "uvx",
"args": ["cleat"]
}
}
}
To run the latest unreleased code straight from git instead of PyPI, use
uvx --from git+https://github.com/sidyellur/cleat cleat.
For local development:
git clone https://github.com/sidyellur/cleat && cd cleat
python -m venv .venv && . .venv/bin/activate
pip install -e .
cleat # runs the MCP server over stdio
Tools
| Tool | Returns | Use for |
|---|---|---|
run_command(command, timeout, exact) |
{stdout, exit_code, completed, state} (+ files_changed if watching, spoofed_marks if tampered, stdout_exact if exact=True) |
normal commands — full stdout, cleaned for readability |
read_output(timeout) |
{output, exit_code, completed, state} |
watching a long-running / streaming command |
wait_for(timeout) |
{output, exit_code, completed, state} |
blocking until the session needs attention — replaces a read_output polling loop |
read_screen() |
{screen, cursor, state} |
inspecting a full-screen TUI (vim, top, less) |
send_keys(keys, enter) |
{screen, cursor, exit_code, completed, state} |
driving a REPL / TUI / prompt (control chars pass through: =Ctrl-C) |
resize(cols, rows) |
{cols, rows} |
laying out a TUI for a given size |
watch_files(path) |
{watch_root} |
enable per-command files_changed under path |
completed: false means the program is still running or waiting for input (e.g.
a REPL) — check state (above) for what to do next.
How it works
agent ─(MCP)─ server.py ─ engine.py (persistent PTY, ptyprocess)
├─ structure.py → OSC 133 marks → {stdout, exit_code, state}
└─ pyte screen → rendered view for REPLs/TUIs
The engine injects OSC 133 per shell without touching your real config: zsh via
a temp $ZDOTDIR, bash via --rcfile + vendored bash-preexec,
and fish via fish -C 'source ...' (fish ≥4 also emits its own native marks
alongside ours; see below for why that’s harmless). Despite spawning a
non-login shell, both also source the login/profile files a real terminal's
login shell would (.zprofile/.zlogin for zsh, .bash_profile/
.bash_login/.profile for bash) — so PATH/env set only there (e.g.
Homebrew's brew shellenv in .zprofile) isn't silently missing.
Nonce-authenticated marks
A command you run can print arbitrary bytes to its own stdout — including a
fake ESC ]133;D;0 BEL, forging a clean exit code for output that actually
failed. cleat closes this: each session gets a fresh secrets.token_hex(8)
nonce at startup, embedded in every mark cleat injects
(\033]133;D;<exit>;k=<nonce>\007). A mark whose k= param is missing or
wrong is ignored outright — it cannot alter exit_code, stdout, or
completed either way. A wrong k= is always surfaced as an attempted
forgery; a missing k= is too, unless the shell is known to run its own
legitimate native OSC 133 alongside ours (fish ≥4) — that's expected
telemetry, not tampering, so it's ignored quietly instead of triggering a
false alarm on every fish command. run_command surfaces the real count as
spoofed_marks in its result (only when it's nonzero), so an agent can tell
when a program it ran tried to lie about how it finished.
The nonce itself is embedded in a temp rcfile at session startup (bash's
--rcfile, fish's -C 'source ...', zsh's $ZDOTDIR) — its path stays
visible in the shell's own argv for the life of the process. cleat removes
that file from disk as soon as the shell has read it (before it ever shows a
prompt), so a same-uid child process spawned later in the session can't open
the path and read the nonce out, even if it discovers it via
/proc/<pid>/cmdline.
Caveats
- POSIX only. Uses
pty/termios; no Windows. - It gives the agent a real shell. Commands run with your user's privileges in a persistent session. Run it only where you'd let an agent run shell commands.
files_changeddetects writes, not reads (create/modify/delete under the watched root). Read-tracking needs privileged syscall tracing.stdoutis cleaned, not byte-exact: it's ANSI-stripped and trimmed (a leaked leading newline, trailing whitespace) for readability, so leading blank lines or trailing whitespace the command itself printed are dropped. Passexact=Truetorun_commandforstdout_exact— ANSI-stripped only, preserving the command's real output exactly.- bash: a command whose first token is a subshell
(...)or brace group{ ...; }emits no start mark; its exit code is recovered but stdout for that one command is not captured. cleat still refuses to answer a terminal query sitting in that command's own output — that check is against the real foreground process, not the (here, missing) mark — so this doesn't reopen the forged-input gap the marks close for every other command; but the missing mark is real, so avoid this shape where the captured stdout matters. - Full-screen TUIs: use
read_screen(the rendered grid), notrun_command. - Shells: zsh and bash are fully supported, including interactive REPL/TUI driving. fish is supported for command execution on ≥4 (its own native marks require that version; cleat's injection works alongside them either way — see Nonce-authenticated marks).
License
MIT (see LICENSE). Vendors bash-preexec, also MIT.
Metadata
Release files for cleat 0.2.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| cleat-0.2.4.tar.gz | 89.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cleat-0.2.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 143.4 kB
Release files / cleat-0.2.4.tar.gz
| Download URL | cleat-0.2.4.tar.gz |
|---|---|
| Size | 89.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
494f2269854d6f9d35056e581934e59ec644b0ee4346ceb2ba71b730431d4d0c
|
|
BLAKE2b-256 checksum How to use checksums |
f60e5891a2b369ac86393ca1130ae5f951edf3675d954159ef9c933f6ddb7300
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency logRelease files / cleat-0.2.4-py3-none-any.whl
| Download URL | cleat-0.2.4-py3-none-any.whl |
|---|---|
| Size | 54.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
955e336f02d33f8e7f078b3def97c5f4145ead92de5b707af49aea166bfc4302
|
|
BLAKE2b-256 checksum How to use checksums |
ac2faf0bcad80eeacc19591d3acbe66f772838c47f7aad1ffe03d0eebc0849c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency logSecurity for cleat 0.2.4
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for cleat-0.2.4.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.