A headless terminal layer: a persistent PTY + OSC 133 structure source, exposed to AI agents over MCP.
Project description
cleat
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 }
…and the session is persistent: cd, export, activated venvs, and ssh
sessions all carry across calls — something a fresh subprocess per command
cannot do.
Install
Requires Python ≥3.10 on a POSIX system (Linux/macOS). With uv installed, register it with Claude Code:
claude mcp add cleat -- uvx --from git+https://github.com/sidyellur/cleat cleat
Or add it to a project's .mcp.json:
{
"mcpServers": {
"cleat": {
"command": "uvx",
"args": ["--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) |
{stdout, exit_code, completed} (+ files_changed if watching) |
normal commands — full, exact stdout |
read_output(timeout) |
{output, exit_code, completed} |
watching a long-running / streaming command |
read_screen() |
{screen, cursor} |
inspecting a full-screen TUI (vim, top, less) |
send_keys(keys, enter) |
{screen, cursor, exit_code, completed} |
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) — drive it with send_keys and poll with read_output.
How it works
agent ─(MCP)─ server.py ─ engine.py (persistent PTY, ptyprocess)
├─ structure.py → OSC 133 marks → {stdout, exit_code}
└─ 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 ≥4 via its native shell integration (older fish is unsupported).
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.- 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. - Full-screen TUIs: use
read_screen(the rendered grid), notrun_command. - Shells: zsh and bash are fully supported. fish requires ≥4 (native OSC 133) and is supported for command execution; interactive REPL/TUI driving is verified on zsh/bash.
License
MIT (see LICENSE). Vendors bash-preexec, also MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cleat-0.1.0.tar.gz.
File metadata
- Download URL: cleat-0.1.0.tar.gz
- Upload date:
- Size: 31.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4dfce3e2e4690f25846324b2b2bb062ccd52861099d18ecedee16b204f5191e5
|
|
| MD5 |
8027f36ac82245389a0401b389247940
|
|
| BLAKE2b-256 |
883b6613ed8c759f84da4925e2fa73d9a782f533c03366309895a6ef8568bf6f
|
Provenance
The following attestation bundles were made for cleat-0.1.0.tar.gz:
Publisher:
release.yml on sidyellur/cleat
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cleat-0.1.0.tar.gz -
Subject digest:
4dfce3e2e4690f25846324b2b2bb062ccd52861099d18ecedee16b204f5191e5 - Sigstore transparency entry: 1687245312
- Sigstore integration time:
-
Permalink:
sidyellur/cleat@0cffde538721b3acd2152b5ae821d536114072b2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/sidyellur
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0cffde538721b3acd2152b5ae821d536114072b2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file cleat-0.1.0-py3-none-any.whl.
File metadata
- Download URL: cleat-0.1.0-py3-none-any.whl
- Upload date:
- Size: 31.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8804a6074a413c7f053530ce92f15c41c6d4cc5633f37c8b1d409b63c7c27c3b
|
|
| MD5 |
b9fc4c95aa13c8fcdd77e554d29859bc
|
|
| BLAKE2b-256 |
0e0a8d582945c9f0836eb352f743d54fa067f3e43d74e76dde1032a983be04f6
|
Provenance
The following attestation bundles were made for cleat-0.1.0-py3-none-any.whl:
Publisher:
release.yml on sidyellur/cleat
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cleat-0.1.0-py3-none-any.whl -
Subject digest:
8804a6074a413c7f053530ce92f15c41c6d4cc5633f37c8b1d409b63c7c27c3b - Sigstore transparency entry: 1687246190
- Sigstore integration time:
-
Permalink:
sidyellur/cleat@0cffde538721b3acd2152b5ae821d536114072b2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/sidyellur
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0cffde538721b3acd2152b5ae821d536114072b2 -
Trigger Event:
push
-
Statement type: