{"id":49900575,"url":"https://github.com/utensils/ptywright","last_synced_at":"2026-05-21T08:01:50.147Z","repository":{"id":357717962,"uuid":"1238236409","full_name":"utensils/ptywright","owner":"utensils","description":"Rust CLI and library for driving interactive terminal applications through PTYs","archived":false,"fork":false,"pushed_at":"2026-05-16T05:40:22.000Z","size":676,"stargazers_count":2,"open_issues_count":13,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-16T05:41:06.267Z","etag":null,"topics":["automation","cli","pty","rust","terminal","terminal-automation","tui"],"latest_commit_sha":null,"homepage":"https://utensils.io/ptywright/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/utensils.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-14T00:12:09.000Z","updated_at":"2026-05-16T03:55:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/utensils/ptywright","commit_stats":null,"previous_names":["utensils/ptywright"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/utensils/ptywright","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utensils%2Fptywright","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utensils%2Fptywright/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utensils%2Fptywright/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utensils%2Fptywright/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/utensils","download_url":"https://codeload.github.com/utensils/ptywright/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/utensils%2Fptywright/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33293427,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-21T02:57:32.698Z","status":"ssl_error","status_checked_at":"2026-05-21T02:57:31.990Z","response_time":62,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","cli","pty","rust","terminal","terminal-automation","tui"],"created_at":"2026-05-16T05:26:23.594Z","updated_at":"2026-05-21T08:01:50.139Z","avatar_url":"https://github.com/utensils.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ptywright\n\n[![CI](https://github.com/utensils/ptywright/actions/workflows/ci.yml/badge.svg)](https://github.com/utensils/ptywright/actions/workflows/ci.yml)\n[![Deploy Docs](https://github.com/utensils/ptywright/actions/workflows/pages.yml/badge.svg)](https://github.com/utensils/ptywright/actions/workflows/pages.yml)\n[![codecov](https://codecov.io/gh/utensils/ptywright/graph/badge.svg)](https://codecov.io/gh/utensils/ptywright)\n\n**A cross-platform Rust CLI and library for driving interactive terminal applications through PTYs.**\n\nptywright is an early general-purpose PTY/TUI automation toolkit. It is designed to drive interactive terminal applications from code without coupling the core abstractions to any one program. A generic `Extension` trait sits above the PTY/session/screen/action/matcher primitives, with Claude Code shipped as the first plugin under that trait.\n\nThe library now includes target, session, rich screen snapshot, action, temporal and plugin-defined matchers, bounded transcripts with turn segmentation marks and optional raw file streaming, redaction, JSON-RPC with atomic `adapter.turn` / cooperative `adapter.cancel_wait` / introspection via `plugin.describe`, the generic `Extension` layer with in-process `Session::events` / `ExtensionHandle::subscribe`, a Lua-backed interactive Claude Code adapter with stable-screen turn evidence and structured `metadata.*` channels (permission, plan, error, usage, status, dialog correlation), plugin manifest/runtime primitives with per-method permission gating, a `Session` SIGTERM→SIGKILL signal ladder, and shell completion primitives backed by real PTYs. The CLI includes `run` for live stdin/stdout PTY debugging, `serve --stdio` for NDJSON or LSP-style JSON-RPC automation, multi-client local IPC via Unix sockets on macOS/Linux and named pipes on Windows, `serve --plugin \u003cmanifest.toml\u003e` for trusted-local third-party plugins, `logs --tail` for following the rotated log file, `repl` for an interactive embedded-Lua client over a running server (auto-spawning one when none is listening), and `completions` for shell setup.\n\nDocs: \u003chttps://utensils.io/ptywright/\u003e\n\n## Quickstart\n\n```bash\nptywright --help\nptywright --version\nptywright run -- /bin/sh -lc 'printf ready'\nprintf '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"server.capabilities\"}\\n' | ptywright serve --stdio\nprintf '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"adapter.list\"}\\n' | ptywright serve --stdio | jq '.result.plugins[].name'\nprintf '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"adapter.start\",\"params\":{\"plugin\":\"claude-code\",\"program\":\"/bin/sh\",\"args\":[\"-lc\",\"cat\"]}}\\n' | ptywright serve --stdio | jq '.result | {adapter, plugin}'\nptywright serve --stdio --framing lsp\nptywright serve --socket /tmp/ptywright.sock\nsource \u003c(ptywright completions zsh)\n```\n\n## Install from source\n\nThe default build embeds Lua 5.4 for trusted adapter plugins, so source builds need a working C compiler in addition to Rust. The Nix dev shell provides the expected toolchain on macOS/Linux.\n\n```bash\ngit clone https://github.com/utensils/ptywright\ncd ptywright\nnix develop\ncargo build --release\n./target/release/ptywright --help\n```\n\nOr run through Nix on macOS/Linux:\n\n```bash\nnix run github:utensils/ptywright -- --help\n```\n\n## Project goals\n\n- Provide Rust abstractions for spawning, attaching to, and driving PTY-backed terminal applications.\n- Keep application-specific adapters separate from the core architecture.\n- Support deterministic turn execution, transcript capture, prompt detection, and output parsing.\n- Target macOS, Linux, and Windows.\n- Keep a clean CLI surface while exposing reusable library primitives.\n- Preserve a small, auditable, local-first implementation.\n\n## Current layers\n\n1. Target configuration.\n2. PTY session lifecycle.\n3. Terminal screen observation.\n4. Input actions and key sequences.\n5. Matchers and waits.\n6. Bounded transcript capture with explicit raw transcript file streaming opt-in.\n7. JSON-RPC over stdio or multi-client local IPC for external automation clients, with NDJSON and LSP-style framing.\n8. Generic `Extension` trait with `ExtensionHandle` host loop; Claude Code ships as the first plugin under that trait, driven through the `adapter.*` JSON-RPC surface.\n9. Plugin manifests, permission declarations, and trusted embedded Lua runtime for adapter orchestration. Trusted-local third-party plugins load via the `ptywright serve --plugin \u003cmanifest.toml\u003e` CLI flag or the `plugin.load` JSON-RPC method (gated by `--allow-plugin-load`).\n10. Per-method permission gating at the JSON-RPC dispatcher — every `adapter.*` method consults the bound plugin manifest's declared permissions and rejects calls with `-32004 PermissionDenied` carrying structured `data`.\n11. Per-user runtime directory under `~/.ptywright/` with structured logging (daily rotation, redaction-aware writers).\n12. Interactive REPL client (`ptywright repl`, default-on `repl` Cargo feature): embedded Lua 5.4 evaluator over the generic `adapter.*` surface, reedline line-editing with Lua-introspection completion / highlighting / multi-line continuation, auto-spawn of a background server when none is listening, tmux-style attach, and inline notification rendering.\n13. Shell completion generation for bash, zsh, fish, elvish, and PowerShell.\n14. Rich screen snapshots with cell/style/mode metadata.\n15. Redaction helpers with built-in and caller-supplied patterns plus default RPC redaction for sensitive-looking output.\n16. In-process event subscription on `Session` and `ExtensionHandle`; structured matcher outcomes on `adapter.wait`; cooperative cancellation via `CancellationToken` / `adapter.cancel_wait`; plugin-defined `Matcher::Lua` predicates evaluated against a bound `PluginRegistry`.\n17. Atomic `adapter.turn` (send + wait under the per-adapter mutex), `adapter.resume` chaining across PTY restarts, and `plugin.describe` runtime introspection of intents, wait matchers, and classifier states.\n18. Cross-platform PID + signal ladder (`Session::pid`, `Session::signal`, `Session::terminate` with SIGTERM→SIGKILL escalation) and plugin-mandated env (`default_target.required_env`) plus `Target::clear_env` for sandboxed spawns.\n19. Bounded transcript turn segmentation (`Transcript::mark` / `marker` / `slice_between`), classifier-driven `host_marks`, and the `Action::MarkTranscript` plan primitive.\n\n## Planned layers\n\n1. More Claude Code real-world fixtures and transition tests as upstream Claude Code UI changes.\n2. A second built-in adapter (Codex / shell) to validate the plugin model against a non-Claude TUI.\n3. Optional WASM only if untrusted marketplace-style plugins become a concrete priority.\n\n## Development\n\n```bash\nnix develop        # auto via direnv + use_flake\nci-local           # fmt-check → check → clippy → test → build\ndocs-dev           # run the VitePress docs site\n```\n\nUseful direct commands:\n\n```bash\ncargo fmt --all -- --check\ncargo check --locked\ncargo clippy --locked -- -D warnings\ncargo test --locked --features _test-fixtures\ncargo run -- --help\n```\n\n## Runtime directory and logging\n\nptywright keeps configuration and rotated log files under `~/.ptywright/` (override with `PTYWRIGHT_HOME=/some/path`). Logs are written through `tracing` with daily rotation, 14-day retention by default, and built-in redaction of secret-shaped values. Override the filter at runtime with `PTYWRIGHT_LOG`:\n\n```bash\nPTYWRIGHT_LOG=\"info,ptywright::rpc=debug\" ptywright serve --stdio\n```\n\nSee [`config.example.toml`](config.example.toml) for the full set of tunables and the [Runtime directory](https://utensils.io/ptywright/guide/runtime-directory.html) docs for layout details. `ptywright run` writes only to file (it owns your terminal); `serve --stdio` and `serve --socket` write to file and stderr while keeping stdout reserved for JSON-RPC framing.\n\nSee [AGENTS.md](AGENTS.md) for repository guidance.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Futensils%2Fptywright","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Futensils%2Fptywright","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Futensils%2Fptywright/lists"}