Commits
spawnDaemon now kills orphaned daemons on timeout. launchd install
compiles a C wrapper binary for FDA — grant it Full Disk Access
instead of node. Install flow validates FDA via one-shot launchd
job with interactive prompt. Fix events --wait timeout not being
cancelled on match. Fix displayCommand showing duplicated command
for toml sessions. PATH baked into wrapper at compile time.
peek and peek --wait now read saved output from exited sessions
(lastLines increased to 200). --wait accepts multiple patterns
(any match). saveExitMetadata runs twice: immediately in onExit
(for status display) and again in close() (for complete output).
Fix TUI exit race by delaying session list refresh 200ms. Fix
SKILL.md examples to use multiple --wait instead of regex.
Session supervisor watches strategy tags and restarts permanent
sessions with exponential backoff. Runs as a foreground process,
integrates with launchd via esbuild bundling. Mutable tags via
pty tag command. pty kill/down properly handle supervised sessions.
Also: peek --wait blocks until text appears, peek --full shows
scrollback, events --wait blocks for event types, interactive TUI
shows [permanent] markers with color, 10s supervisor scan interval,
supervisor state in its own subdirectory, defensive meta.args
handling, and isolated shell test sessions.
Move PtyServer from ./client to ./server so client-only imports
stay free of native addon dependencies. Add ./keys export for
browser-safe key resolution. The export structure is now cleanly
tiered: browser-safe (./protocol, ./keys), Node-only (./client),
and native addon (./server, ./testing, ./tui).
Sessions can be tagged at creation with key-value pairs via
pty run --tag owner=forge -- command or the tags field in
SpawnDaemonOptions. Tags are persisted, survive process exit,
and appear in pty list --json. Closes #12.
Adds a ./protocol entry to the package.json exports map, pointing at
the existing src/protocol.ts module (built to dist/protocol.js by the
existing tsc build). This module exports PacketReader, MessageType,
Packet, and all encode/decode helpers with zero Node built-in deps
beyond Buffer — making it safe for browser bundlers.
Motivation: consumers like forge maintain a copy of the wire protocol
for browser-side xterm.js packet parsing. With this subpath they can
import from @myobie/pty/protocol instead, keeping the protocol in one
place.
resolveKey("shift+return") previously returned plain "\r" because
shift was silently ignored for non-letter keys. Now:
- CSI sequence keys (arrows, home/end, delete, pageup/down) get the
modifier parameter inserted: e.g. shift+up → \x1b[1;2A
- Control char keys (return, tab, escape, space, backspace) use
Kitty keyboard protocol CSI u encoding: e.g. shift+return → \x1b[13;2u
- shift+tab produces the legacy backtab sequence \x1b[Z
- Combined modifiers (ctrl+shift, alt+shift, ctrl+alt+shift) are
supported using the standard xterm modifier bitmask
Fixes #13
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bump version to 0.5.0, flesh out CHANGELOG with missing entries
(DA1 fix, postinstall fix, plugins, exit name), add Plugins section
to README, and audit all shell completions to cover every command
(stats, rm, gc, wrap, unwrap, test) including proper session-name
completion for pty rm and pty stats.
Fix node-pty spawn-helper chmod for pnpm GVS layouts
The previous postinstall used a relative path that silently no-op'd
under pnpm's global virtual store. Replace with a proper Node.js
script that uses createRequire + require.resolve to find node-pty
regardless of install layout.
Credit: @schickling
Fish 4.x sends a DA1 (Primary Device Attribute) query at startup
and blocks for up to 10s waiting for a response. Add a CSI handler
in PtyServer that intercepts the query and writes back a VT220
response. Add shell integration tests for bash, zsh, and fish.
Closes #5
Based on the approach by @schickling-assistant in #5
New public API covering every CLI feature: SessionConnection for
bidirectional attach without stdin/stdout, sendData/peekScreen as
Promise-based alternatives, events, keys, stats, and protocol types.
Refactor spawnDaemon to options object with optional rows/cols.
Detect when pty run is invoked inside an existing session via
PTY_SESSION env var and exec the command directly (-d bypasses).
Add CPU, memory, and PID reporting to pty stats for both the
child process and daemon.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Track mouse mode (1000/1002/1003) via CSI parser on local and
attached handles, exposed as readonly mouseMode property
- Add cursorRow/cursorCol properties (from xterm buffer cursor)
- Accept scrollback option in createPty/attachPty (default 0)
- Add bufferLength/baseY properties for scrollback navigation
- Support readCells(scrollOffset) to read into scrollback history
- Fix readXtermCells to read from baseY (was reading from line 0,
masked by scrollback:0)
- Re-export spawnDaemon from tui index
pty wrap claude creates ~/.local/pty/bin/claude that runs
pty run -a -- /real/path/to/claude "$@"
pty unwrap claude removes the wrapper
pty wrap --list shows wrapped commands
Uses PTY_BIN_PATH env var (default ~/.local/pty/bin/). Warns
if the directory isn't in PATH.
Fix stale screen content on reattach by tracking last resize time and
delaying serialization when a resize recently occurred. After sending
the serialized screen, nudge the child process with a SIGWINCH to
trigger a fresh redraw, eliminating visual artifacts from the serialize
addon's ECH/CUF approximation.
Create session directory with mode 0o700 and chmod sockets to
0o600 after creation. Prevents other local users from connecting
to sessions on systems where home directories are world-readable
(e.g., Debian/Ubuntu with default 0o755 home dirs).
- app() now calls onEnter/onLeave on screen transitions, overlay
changes, and shutdown. Fixes tick timers, PTY cleanup, and
spinner leaks across screen switches.
- Interactive create wizard spawns via sh -c instead of splitting
on whitespace. Quoted args, pipes, and env vars now work.
- Reject pty peek -f --plain — follow mode streams raw ANSI,
incompatible with plain output.
Interactive list:
- Fuzzy matching for session filter (fzf-style, characters in order)
- Name matches scored 10000+ above cwd/command matches
- ▸ selection indicator on the active item
- Ctrl+G cycles through all themes, name shown in footer
- Theme preference saved to ~/.local/state/pty/theme
Themes:
- Light variants of all 5 themes (coolBlueLight, warmAmberLight, etc.)
- New "terminal" theme: null colors, uses terminal's own fg/bg
- Terminal theme is the default — works on any color scheme
- Theme type widened to allow null (renderer already handled it)
Framework fix:
- Text nodes inside panels now preserve the panel's background color
instead of clearing to null. Previously invisible on dark themes
but caused white bleed-through on light terminals.
Sessions are now auto-named from directory + command when no name is
given: `pty run -- node server.js` creates `myapp-node-server`. Use
`--name` for explicit names. Old positional syntax still works with
a hint to migrate.
The interactive wizard auto-generates the name as you type the
command. Manually editing the name stops auto-generation. Filter
results now rank name matches above path/command matches.
Fix: add postinstall script to chmod +x node-pty's spawn-helper
binary. npm strips the executable bit during install, causing
posix_spawnp to fail on global installs.
Fix: move @preact/signals-core from optional peer dependency to
regular dependency — the CLI now uses it since the interactive
list rewrite.
The EXIT packet handler in attach() didn't set exitHandled, so the
socket close event called onExit a second time, which added a
duplicate stdin listener via resumeTui → setupInput.
Added regression test that types after session exit and verifies
characters aren't doubled.
Node refuses to run .ts files from node_modules/. The CLI and daemon
now run from dist/ (compiled by tsc). Tests and exports updated to
use dist/ for subprocess spawning.
npm run build is now required before using the CLI locally.
Node refuses to type-strip .ts files under node_modules/, so
`pty` failed after `npm install -g`. The CLI entry point is now
bundled to a single bin/cli.js via esbuild during prepublishOnly.
The testing and TUI exports remain .ts — consumers' toolchains
handle those.
TUI framework (src/tui/):
Declarative UI with reactive signals, two-pass layout engine,
cell-buffer diffing, and 5 color themes. Exported as ptym/tui.
Includes text wrapping with word-boundary breaking and a
highlight callback for per-character span styling.
Demo apps (demos/):
Three standalone apps that showcase the framework and the
PTY testing library:
- file-browser: two-pane directory tree + file preview with
soft-wrap and markdown highlighting
- reminders: full CRUD backed by .md files with YAML frontmatter,
three views (grouped list, kanban board, calendar), overlays
- agent-teams: live dashboard of a simulated AI agent hierarchy
with real-time timeline, inter-agent messages, progress bars
Each demo has unit tests and PTY integration tests that verify
actual rendered screen output — not just string presence, but
layout correctness (gutter alignment, panel sizing, selection
indicators, overlay compositing).
Testing improvements:
- JSDoc on all Session methods and type fields
- docs/testing.md: server-mode API (attach, reconnect, resize,
connectToExisting, hasExited)
- Fix timing-sensitive filter test (waitForText instead of sleep)
README:
- Beta notice
- Expanded testing library section with realistic example
- TUI framework alpha notice with demo descriptions
Also: vitest.config.ts, demos/run convenience script.
Resize to the smallest dimensions across all connected writable clients
spawnDaemon now kills orphaned daemons on timeout. launchd install
compiles a C wrapper binary for FDA — grant it Full Disk Access
instead of node. Install flow validates FDA via one-shot launchd
job with interactive prompt. Fix events --wait timeout not being
cancelled on match. Fix displayCommand showing duplicated command
for toml sessions. PATH baked into wrapper at compile time.
peek and peek --wait now read saved output from exited sessions
(lastLines increased to 200). --wait accepts multiple patterns
(any match). saveExitMetadata runs twice: immediately in onExit
(for status display) and again in close() (for complete output).
Fix TUI exit race by delaying session list refresh 200ms. Fix
SKILL.md examples to use multiple --wait instead of regex.
Session supervisor watches strategy tags and restarts permanent
sessions with exponential backoff. Runs as a foreground process,
integrates with launchd via esbuild bundling. Mutable tags via
pty tag command. pty kill/down properly handle supervised sessions.
Also: peek --wait blocks until text appears, peek --full shows
scrollback, events --wait blocks for event types, interactive TUI
shows [permanent] markers with color, 10s supervisor scan interval,
supervisor state in its own subdirectory, defensive meta.args
handling, and isolated shell test sessions.
Adds a ./protocol entry to the package.json exports map, pointing at
the existing src/protocol.ts module (built to dist/protocol.js by the
existing tsc build). This module exports PacketReader, MessageType,
Packet, and all encode/decode helpers with zero Node built-in deps
beyond Buffer — making it safe for browser bundlers.
Motivation: consumers like forge maintain a copy of the wire protocol
for browser-side xterm.js packet parsing. With this subpath they can
import from @myobie/pty/protocol instead, keeping the protocol in one
place.
resolveKey("shift+return") previously returned plain "\r" because
shift was silently ignored for non-letter keys. Now:
- CSI sequence keys (arrows, home/end, delete, pageup/down) get the
modifier parameter inserted: e.g. shift+up → \x1b[1;2A
- Control char keys (return, tab, escape, space, backspace) use
Kitty keyboard protocol CSI u encoding: e.g. shift+return → \x1b[13;2u
- shift+tab produces the legacy backtab sequence \x1b[Z
- Combined modifiers (ctrl+shift, alt+shift, ctrl+alt+shift) are
supported using the standard xterm modifier bitmask
Fixes #13
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fish 4.x sends a DA1 (Primary Device Attribute) query at startup
and blocks for up to 10s waiting for a response. Add a CSI handler
in PtyServer that intercepts the query and writes back a VT220
response. Add shell integration tests for bash, zsh, and fish.
Closes #5
Based on the approach by @schickling-assistant in #5
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Track mouse mode (1000/1002/1003) via CSI parser on local and
attached handles, exposed as readonly mouseMode property
- Add cursorRow/cursorCol properties (from xterm buffer cursor)
- Accept scrollback option in createPty/attachPty (default 0)
- Add bufferLength/baseY properties for scrollback navigation
- Support readCells(scrollOffset) to read into scrollback history
- Fix readXtermCells to read from baseY (was reading from line 0,
masked by scrollback:0)
- Re-export spawnDaemon from tui index
Fix stale screen content on reattach by tracking last resize time and
delaying serialization when a resize recently occurred. After sending
the serialized screen, nudge the child process with a SIGWINCH to
trigger a fresh redraw, eliminating visual artifacts from the serialize
addon's ECH/CUF approximation.
- app() now calls onEnter/onLeave on screen transitions, overlay
changes, and shutdown. Fixes tick timers, PTY cleanup, and
spinner leaks across screen switches.
- Interactive create wizard spawns via sh -c instead of splitting
on whitespace. Quoted args, pipes, and env vars now work.
- Reject pty peek -f --plain — follow mode streams raw ANSI,
incompatible with plain output.
Interactive list:
- Fuzzy matching for session filter (fzf-style, characters in order)
- Name matches scored 10000+ above cwd/command matches
- ▸ selection indicator on the active item
- Ctrl+G cycles through all themes, name shown in footer
- Theme preference saved to ~/.local/state/pty/theme
Themes:
- Light variants of all 5 themes (coolBlueLight, warmAmberLight, etc.)
- New "terminal" theme: null colors, uses terminal's own fg/bg
- Terminal theme is the default — works on any color scheme
- Theme type widened to allow null (renderer already handled it)
Framework fix:
- Text nodes inside panels now preserve the panel's background color
instead of clearing to null. Previously invisible on dark themes
but caused white bleed-through on light terminals.
Sessions are now auto-named from directory + command when no name is
given: `pty run -- node server.js` creates `myapp-node-server`. Use
`--name` for explicit names. Old positional syntax still works with
a hint to migrate.
The interactive wizard auto-generates the name as you type the
command. Manually editing the name stops auto-generation. Filter
results now rank name matches above path/command matches.
Fix: add postinstall script to chmod +x node-pty's spawn-helper
binary. npm strips the executable bit during install, causing
posix_spawnp to fail on global installs.
Fix: move @preact/signals-core from optional peer dependency to
regular dependency — the CLI now uses it since the interactive
list rewrite.
TUI framework (src/tui/):
Declarative UI with reactive signals, two-pass layout engine,
cell-buffer diffing, and 5 color themes. Exported as ptym/tui.
Includes text wrapping with word-boundary breaking and a
highlight callback for per-character span styling.
Demo apps (demos/):
Three standalone apps that showcase the framework and the
PTY testing library:
- file-browser: two-pane directory tree + file preview with
soft-wrap and markdown highlighting
- reminders: full CRUD backed by .md files with YAML frontmatter,
three views (grouped list, kanban board, calendar), overlays
- agent-teams: live dashboard of a simulated AI agent hierarchy
with real-time timeline, inter-agent messages, progress bars
Each demo has unit tests and PTY integration tests that verify
actual rendered screen output — not just string presence, but
layout correctness (gutter alignment, panel sizing, selection
indicators, overlay compositing).
Testing improvements:
- JSDoc on all Session methods and type fields
- docs/testing.md: server-mode API (attach, reconnect, resize,
connectToExisting, hasExited)
- Fix timing-sensitive filter test (waitForText instead of sleep)
README:
- Beta notice
- Expanded testing library section with realistic example
- TUI framework alpha notice with demo descriptions
Also: vitest.config.ts, demos/run convenience script.