> ## Documentation Index
> Fetch the complete documentation index at: https://docs.declaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Cursor-style agent with PTY handoff

> Anthropic Claude agent with 5 tools and interactive PTY handoff — the agent asks permission before dropping you into a live terminal.

A Cursor-style coding agent backed by Claude and a persistent declaw
sandbox. The agent has five tools for everyday work (`shell`,
`read_file`, `write_file`, `ls`) plus one special tool:
`pty_interactive`, which hands your local TTY to the sandbox when
the agent decides it needs human input.

## Use case

Sometimes an agent hits a wall that requires a human: an OAuth
login, a sudo password, a TUI license agreement, or a `vim` session.
Instead of failing or asking you to switch to another terminal, the
agent calls `pty_interactive` with a reason, you approve with `y/n`,
and your terminal drops into a live shell inside the sandbox. When
the interactive command finishes, control returns to the agent loop.

## What you'll learn

* Defining tool schemas for the Anthropic Messages API
* Running a multi-turn agent loop with tool-use stop reason
* PTY handoff pattern: raw-mode local TTY forwarding with `SIGWINCH`
* Capturing PTY output in a thread-safe `bytearray` and feeding it back to the agent
* Trimming captured output to keep the agent's context bounded

## Prerequisites

<Snippet file="snippets/env-setup.mdx" />

```bash theme={null}
pip install declaw anthropic
export ANTHROPIC_API_KEY="your-anthropic-key"
```

<Warning>
  This example requires a **real TTY** (`sys.stdin.isatty()`). It will
  not work inside a Jupyter notebook or piped stdin.
</Warning>

## Tools

| Tool                           | Description                                                                |
| ------------------------------ | -------------------------------------------------------------------------- |
| `shell(cmd)`                   | Non-interactive command. Returns stdout/stderr/exit\_code.                 |
| `pty_interactive(cmd, reason)` | Interactive command. Prompts user y/n, then forwards local TTY to sandbox. |
| `read_file(path)`              | Read a file from the sandbox.                                              |
| `write_file(path, content)`    | Write a file to the sandbox.                                               |
| `ls(path)`                     | List a sandbox directory.                                                  |

## How PTY handoff works

When the agent calls `pty_interactive`:

1. The script prints the agent's **reason** and the **command** in a
   box and asks `Proceed? [y/N]`.
2. On `y`, `sbx.pty.create()` opens a PTY session. The local TTY
   switches to raw mode (`tty.setraw`), and a `SIGWINCH` handler
   propagates window resizes.
3. Every local keystroke is forwarded via `handle.send_stdin()`.
   Every byte from the sandbox streams to stdout via `on_data` and
   is also captured in a `bytearray`.
4. `Ctrl-D` sends `exit\n` to close the remote shell cleanly.
5. The captured output (trimmed to \~4 KB) is returned as the tool
   result so the agent knows what happened during the session.

```python theme={null}
handle = sbx.pty.create(size=size, on_data=tee, timeout=1800)
signal.signal(signal.SIGWINCH,
              lambda *_: handle.resize(_local_term_size()))
tty.setraw(fd)
while True:
    data = os.read(fd, 1024)
    if data == b"\x04":
        handle.send_stdin(b"exit\n")
        break
    handle.send_stdin(data)
```

## Running it

```bash theme={null}
export DECLAW_API_KEY="your-api-key"
export DECLAW_DOMAIN="api.declaw.ai"
export ANTHROPIC_API_KEY="your-anthropic-key"
python cookbook/examples/agent-with-pty/main.py
```

You are prompted for a goal. Try something like:

```
> install the gh CLI and log me in to my GitHub account
```

The agent will use `shell` to install `gh`, then call
`pty_interactive(cmd="gh auth login", reason="GitHub OAuth flow
requires browser interaction")`. You approve, complete the login in
the live terminal, press `Ctrl-D`, and the agent continues.

## Full source

See `cookbook/examples/agent-with-pty/main.py` in the repo.
