Skip to main content

cleat

CI

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_changed detects writes, not reads (create/modify/delete under the watched root). Read-tracking needs privileged syscall tracing.
  • stdout is 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. Pass exact=True to run_command for stdout_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), not run_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)

Source distribution for cleat 0.2.4
File Size Uploaded Provenance
cleat-0.2.4.tar.gz 89.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for cleat 0.2.4
File Interpreter ABI Platform
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page