Releases: schovi/shelli
v1.2.0
What's Changed
Runtime data moved to /tmp
Socket and session data now live in /tmp/shelli-{uid}/ instead of ~/.shelli/.
Sessions are ephemeral (PTY processes die on reboot, daemon restart loses in-memory state), so persisting in the home directory added clutter with no real benefit. The OS now handles cleanup naturally via /tmp.
- macOS:
/tmp/shelli-501/ - Linux:
/tmp/shelli-1000/
UID scoping prevents collisions between users on shared systems.
Migration
No action needed. The daemon will create the new directory on first start. You can safely remove ~/.shelli/ if it exists:
rm -rf ~/.shelliThe --data-dir flag still works for custom storage locations.
v1.1.1
Bug fix
Fix flaky TestPerCursorReads caused by a race condition in meta updates. Concurrent LoadMeta → modify → SaveMeta cycles (e.g., captureOutput cleanup vs handleRead) could overwrite each other's changes, resetting cursor positions to 0.
Added atomic UpdateMeta to the storage interface so each call site only mutates the fields it owns.
See #11 for details.
v1.1.0
What's changed
Replaced the hand-rolled internal/ansi package with charmbracelet/x/vt, a proper VT terminal emulator. This fixes rendering issues with complex TUI apps that use cursor-addressed painting, alt screen, and grapheme clusters.
Key changes:
- TUI sessions now use a VT emulator directly (no raw byte storage, no frame detection heuristics)
- ANSI stripping uses fast regex for simple output, emulator fallback for cursor-positioned content
- Terminal query responses (DA1, DA2, DSR) handled natively by the emulator
- Deleted ~2900 lines of custom ANSI handling code, replaced with ~700 lines
Validated against 18 TUI apps (btop, htop, lazygit, yazi, vim, etc.) with 170/171 tests passing.
See #9 for full details.
v1.0.0
shelli v1.0.0
The first stable release. After a rapid development sprint, shelli is production-ready.
What is shelli?
Persistent interactive shell sessions via PTY-backed processes, managed by a lightweight daemon. Two interfaces: CLI for humans, MCP for AI agents. Create a session, run commands, read output, search history — sessions survive across tools and reconnects.
What's in 1.0.0
Rock-solid foundations
The daemon core went through a full correctness audit. Four critical bugs were identified and fixed: input validation gaps, mutex contention in kill paths, PTY double-close races, and a listener data race. Child process reaping is now guaranteed on every exit path — no more zombies.
Battle-tested
Full integration test suite covering the complete pipeline: client → socket → server → PTY → storage → read. Concurrent access, per-cursor tracking, error edge cases, and lifecycle transitions are all verified under -race.
Clean internals
Six parallel state maps consolidated into a single sessionHandle. Dead Session struct eliminated. MCP tool registry rebuilt with a registration pattern. Protocol versioning ensures client/daemon compatibility. The codebase is ready for contributors.
Polished interface
14 targeted UX improvements across CLI, MCP, and daemon:
- Partial output on timeout (no more silent data loss)
- Idempotent session creation (
--if-not-exists) - Per-cursor read positions for multi-consumer workflows
- Snapshot reads that don't disturb global read state
- Sorted session listing, cursor visibility in
info, actionable error messages - Correct settle semantics (
settle_ms: 0means "don't wait")
Built for AI agent workflows. Built to last.
v0.10.0
What's Changed
This release resolves 8 issues identified during a deep code review, covering security, bugs, performance, and CLI/MCP consistency.
Security
- Restrictive file permissions: Session output files use
0600and directories use0700(previously world-readable0644/0755) - Secure default data directory: Default storage location moved from
/tmp/shellito~/.shelli/data
Bug Fixes
- Blocking read with
--head/--tail: Pattern matching and settle detection no longer timeout when combined with line-limiting flags. Truncation is now deferred until after the wait loop completes. - Zombie process leaks: All process termination paths (stop, kill, shutdown) now call
cmd.Wait()to properly reap child processes. - Shutdown concurrency: Server shutdown uses
sync.Onceandatomic.Boolto prevent listener race conditions and double-close panics.
CLI/MCP Consistency
- MCP read validation:
wait_patternandsettle_msare now validated as mutually exclusive in MCP read (already enforced in CLI). - MCP read cursor advancement: Blocking MCP reads now advance the read cursor, matching CLI behavior. Subsequent
mode=newreads no longer return duplicate output.
Performance
- Wait polling optimization: New
SizeFuncin the wait loop checks buffer size before performing a full read. Skips expensive content transfers when nothing changed.
New Feature
- Per-consumer read cursors: New
--cursorflag (CLI) andcursorparameter (MCP) allow multiple consumers to independently track read positions on the same session. Each named cursor maintains its own position without interfering with others.
Upgrade Notes
- Default data directory changed from
/tmp/shellito~/.shelli/data. Existing sessions in/tmp/shelliare not migrated automatically. Use--data-dir /tmp/shellito keep the old location. brew upgrade shellito get the new version.
Full Changelog: v0.9.0...v0.10.0
v0.9.0: TUI Application Support
shelli started as a way to give AI agents persistent shell sessions. That works great for CLI tools, but a huge class of developer workflows lives in terminal UI apps: htop, lazygit, vim, ranger, ncdu, and dozens more. Until now, those were a black box. The agent could launch them but couldn't meaningfully read or interact with what's on screen.
This release changes that. shelli now understands TUI applications.
What's new
--tui mode on session create enables adaptive frame detection. The output buffer automatically truncates on screen redraws, so you always read the current frame instead of a pile of historical escape sequences. Five detection strategies cover the full spectrum of TUI frameworks (ncurses, tcell, custom renderers): screen clear, synchronized output, cursor-home heuristics with content look-ahead, cursor-jump-top, and a size-cap fallback.
--snapshot read captures a single clean frame on demand. It clears the buffer, nudges the app into a full redraw via SIGWINCH, waits for output to settle, and returns exactly what you'd see on a real terminal. This is the primary way agents consume TUI state.
ANSI-to-text conversion got a ground-up rewrite. A virtual screen buffer tracks cursor positioning (absolute and relative movement), line erasing, DEC Special Graphics charset, and newline-based grid sizing. The result is readable plain text that preserves the spatial layout of the original TUI.
Terminal query responder automatically answers capability queries (DA1, DA2, Kitty keyboard protocol) that TUI apps send on startup. Without responses, apps like yazi block indefinitely waiting for the terminal to identify itself.
Tested with
htop, btop, glances, lazygit, tig, vim, less, micro, ranger, nnn, yazi, vifm, ncdu, mc, bat, weechat, irssi, newsboat, and more. The included tui-test skill runs a 9-test protocol across all of them.
Commits
- feat: add --tui flag with screen clear detection
- feat: add multi-strategy TUI frame detection
- feat: add size cap strategy and wait integration for TUI mode
- feat: add snapshot read, ANSI strip improvements, tui-test skill
- feat: fix TUI snapshot races, add virtual screen buffer, terminal query responder
- feat: support ESC[nH, ESC[H, ESC[nG, ESC[nd cursor sequences
- feat: add snapshot-mode full suppression and newline-based grid sizing
- fix: add look-ahead to cursor_home to prevent false frame truncation
- fix: resolve CSI terminator, snapshot resize, and escape UTF-8 bugs
- fix: clear stale content in strip-ansi virtual screen buffer
- refactor: simplify FrameDetector internals
v0.8.0
What's New
TUI Application Support 🖥️
shelli now supports full-screen TUI applications like btop, htop, and k9s using the new --follow mode:
shelli create mon --cmd "btop"
shelli read mon --follow # streams output continuously, renders TUI properlyNew Features
--follow/-fflag - Continuous output streaming liketail -f--follow-msflag - Customize poll interval (default: 100ms)- Improved resize - Explicit SIGWINCH signal for better TUI resize handling
Usage
# Stream TUI output
shelli read session --follow
# With custom poll interval
shelli read session --follow --follow-ms 50
# Resize works with TUIs
shelli resize session --cols 150 --rows 50Limitations
- Text editors (vim, nano) display but interaction is impractical
- Some apps with complex mouse/input handling may behave unexpectedly
Changelog
- af3b8cf feat: add --follow flag and improve resize reliability
v0.7.0
New Features
info command - Detailed session information
View comprehensive session details including state, PID, uptime, buffer size, and terminal dimensions.
shelli info myshell
# Session: myshell
# State: running
# PID: 12345
# Command: /bin/zsh
# Created: 2026-02-03T20:00:00+01:00
# Uptime: 5m30s
# Buffer: 1024 bytes
# ReadPos: 512
# Size: 80x24
shelli info myshell --json # structured outputclear command - Reset output buffer
Truncate the output buffer and reset read position. Session continues running.
shelli clear myshell
# Cleared session "myshell"
shelli read myshell --all # now emptyresize command - Dynamic terminal dimensions
Change terminal dimensions of a running session via PTY TIOCSWINSZ.
shelli resize myshell --cols 120 --rows 40 # set both
shelli resize myshell --cols 200 # change only width
shelli exec myshell "stty size" # verify: 40 120Enhanced create command
New flags for environment, working directory, and terminal size:
# Set environment variables (repeatable)
shelli create dev --env "DEBUG=1" --env "LOG_LEVEL=debug"
# Set working directory
shelli create project --cwd /path/to/project
# Set terminal dimensions (default: 80x24)
shelli create wide --cols 200 --rows 50
# Combine all options
shelli create myenv \
--cmd "python3" \
--env "PYTHONPATH=/app" \
--cwd /app \
--cols 120 \
--rows 40--json flag for send/stop/kill
Structured JSON output for scripting and automation:
shelli send myshell "echo hi\n" --json
# { "status": "sent", "count": 1, "bytes": 8 }
shelli stop myshell --json
# { "name": "myshell", "status": "stopped" }
shelli kill myshell --json
# { "name": "myshell", "status": "killed" }MCP Tools
All new commands are available via MCP (11 total tools):
shelli/info- Get detailed session infoshelli/clear- Clear output buffershelli/resize- Change terminal dimensions- Enhanced
shelli/createwithenv,cwd,cols,rowsparameters
Documentation
- Updated README with new commands and flags
- Updated SKILL.md with complete command reference
v0.6.0
Breaking Changes
exec command clarified as high-level command runner
The exec command has been clarified to be a high-level command runner, distinct from send:
- Removed
input_base64: Usesendwithinput_base64for binary/complex data - Input is now required: MCP schema enforces
inputas a required field - No escape interpretation: Input is sent as literal text (the shell may interpret escapes, e.g.,
echo -e)
exec vs send - Clear Roles
| Command | Purpose | Newline | Escapes | Wait |
|---|---|---|---|---|
exec |
Run commands | Auto-added | NOT interpreted (literal text) | Yes |
send |
Send raw bytes | Manual | Always interpreted | No |
Migration
If you were using exec with input_base64:
# Before (v0.5.0)
mcp-cli call shelli/exec '{"name": "py", "input_base64": "cHJpbnQoImhlbGxvIik="}'
# After (v0.6.0) - use send instead
mcp-cli call shelli/send '{"name": "py", "input_base64": "cHJpbnQoImhlbGxvIik="}'
# Then read output separately
mcp-cli call shelli/read '{"name": "py", "settle_ms": 500}'Escape sequences in exec:
# \n is passed literally to the shell - shell's echo -e interprets it
shelli exec myshell "echo -e 'hello\nworld'"
# For shelli to interpret escapes, use send
shelli send myshell "hello\nworld"Documentation
- Updated CLI help text to clarify escape sequence behavior
- Updated README and SKILL.md with clearer exec vs send guidance
- Added examples showing escape sequences passed to shell
v0.5.0
Breaking Changes
send command simplified to low-level raw interface
The send command has been redesigned to be a low-level, precise control interface:
- Removed flags:
--raw,--multi,--submitare all gone - Always interprets escapes:
\n,\r,\x03, etc. are always interpreted - No auto-newline: You must explicitly add
\nor\rwhen needed - Multiple args = separate writes: Each argument is sent as a separate write to PTY
New Mental Model
| Command | Purpose |
|---|---|
exec |
High-level: run command, wait for output (adds newline) |
send |
Low-level: send exact bytes you specify |
Migration Examples
Before:
shelli send session "ls" # sent with newline
shelli send session "hello" --raw # sent without newline
shelli send session "\x03" --raw # Ctrl+CAfter:
shelli send session "ls\n" # explicit newline
shelli send session "hello" # no newline
shelli send session "\x03" # Ctrl+C
shelli send session "hello" "\r" # TUI: message + Enter as separate writesOther Changes
- Added API stability disclaimer: API may change until v1.0.0