Skip to content

Run claude and other terminal programs ​

By the end of this guide, you run programs that need a terminal in zeropty sessions: a python REPL, Claude Code (claude) and an interactive shell whose jobs you can stop with C-c.

Before you begin ​

  • zeropty installed on a Mac or on Linux. You need zeropty-tty, claude-zeropty and the shim (shim.dylib, or shim.so on Linux) in one folder: the release archive and ./build.sh (in dist/) both put them there. cargo install does not. For details, see Install and platform support.
  • zeropty 0.1.5 or later for C-c to reach a job in a shell (step 4). Check with zeropty -V.
  • For step 3, claude on your PATH.

The commands below write dist/zeropty-tty. If you use the release folder, write the path of that folder instead.

1. See why a program needs a terminal ​

A session's terminal is a socket, not a PTY (pseudo-terminal). Programs that check isatty() decide they are not on a terminal: vim, less, the python REPL and claude all behave differently. For details, see How a session gets a terminal.

zeropty-tty fixes this. It starts the program with a shim (shim.dylib, injected with DYLD_INSERT_LIBRARIES on macOS; shim.so, with LD_PRELOAD on Linux) that answers these calls as a terminal would:

CallWhat the program gets
isatty, tcgetattrA terminal, on the session's own socket
TIOCGWINSZThe session's size
tcgetpgrp, tcsetpgrpThe foreground process group
/dev/ttyThe session's socket, for reading and writing
readpassphrase, getpassA prompt that does not echo (since 0.1.4)

Only the session's socket counts. A program whose stdout is piped (zeropty-tty prog | cat) or whose stdin is /dev/null sees no terminal there, as under tmux.

2. Start a program through zeropty-tty ​

Put zeropty-tty in front of the command:

sh
zeropty new-session -d -s py -x 80 -y 10 "dist/zeropty-tty python3"
zeropty send-keys -t '=py:' -l 'print(6 * 7)'
zeropty send-keys -t '=py:' Enter
zeropty wait-pane -t '=py:' -m '42' -T 10000

vim works the same way: zeropty new-session -d -s v "dist/zeropty-tty vim notes.txt". When the program switches to raw mode, the shim reports it, and keys pass through unchanged.

macOS does not let the shim into SIP-protected binaries (/usr/bin, /bin, /sbin, /System) or binaries signed with the hardened runtime. zeropty-tty runs those from a copy with only the signature changed, kept under ~/.zeropty/tty/ (ZEROPTY_CACHE). It rebuilds the copy when the original changes, and runs it under the name you gave, so ps and pgrep find it.

3. Start claude with claude-zeropty ​

claude-zeropty is a small wrapper that runs zeropty-tty claude:

sh
zeropty new-session -d -s fix-1 -x 220 -y 80 -c ~/proj "$PWD/dist/claude-zeropty"

Replace ~/proj with the repository the agent works in. To run a different claude, give its path in ZEROPTY_CLAUDE_REAL. Set it before the server starts, since sessions get the server's environment, or pass it with -e:

sh
zeropty new-session -d -s fix-2 -e ZEROPTY_CLAUDE_REAL=/opt/claude/bin/claude "$PWD/dist/claude-zeropty"

Replace /opt/claude/bin/claude with the path of your claude. Give the agent work and wait as in Drive agents from the shell.

4. Run an interactive shell and stop its jobs ​

To type commands at a shell in a session, start the shell through zeropty-tty:

sh
zeropty new-session -d -s sh1 "dist/zeropty-tty zsh"
zeropty send-keys -t '=sh1:' -l 'sleep 30'
zeropty send-keys -t '=sh1:' Enter
zeropty send-keys -t '=sh1:' C-c

C-c, C-\ and C-z go to the foreground job as signals, as a terminal sends them. The shell's tcsetpgrp reaches zeropty through the shim, so the shell itself must run with it. The job may be any program, /usr/bin included. C-z and fg work too, pipelines included. Only a process group with a member under the session's program is taken.

Programs typed at the shell

The shim does not get into /usr/bin programs typed at a shell in the session. They run without it. Signals still reach them, but ssh, ssh-keygen and ssh-add typed there still echo a passphrase onto the screen and the scrollback. Start them as their own session with zeropty-tty ssh-keygen instead.

Verify ​

Read the python session:

sh
zeropty capture-pane -p -t '=py:'

You should see the REPL's banner, then the prompt, your line and the answer:

text
>>> print(6 * 7)
42
>>>

The >>> prompt and line editing are the REPL's terminal mode, which it turns on only when it sees a terminal.

For the shell from step 4, zeropty capture-pane -p -t '=sh1:' shows a new prompt after sleep 30 instead of a session still sleeping.

Troubleshooting ​

SymptomFix
A program behaves differently because it is "not a terminal"Start it through zeropty-tty. For claude, use claude-zeropty.
A script started through /usr/bin/env loses the shimSIP strips it there. Start the interpreter directly: zeropty-tty python3 script.py.
top or ps does not runThe copy has none of the original's privileges (setuid, entitlements). Programs that need them do not work.
C-c ends the shell's prompt but not sleep 30The shell was started before the upgrade to 0.1.5 and keeps the old shim. Start the shell again.
A passphrase shows on screenThe program was typed at a shell and runs without the shim. Start it with zeropty-tty.
zeropty-tty cannot find the shimKeep the folder together, or point ZEROPTY_SHIM at the shim (shim.dylib or shim.so).

Next steps ​

Released under the MIT or Apache-2.0 license.