Skip to content

Releases: schovi/shelli

v1.2.0

25 Feb 20:16
3b720d1

Choose a tag to compare

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 ~/.shelli

The --data-dir flag still works for custom storage locations.

v1.1.1

25 Feb 20:22
1e7f955

Choose a tag to compare

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

25 Feb 20:07
29eacc5

Choose a tag to compare

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

18 Feb 07:57
0ac25a7

Choose a tag to compare

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: 0 means "don't wait")

Built for AI agent workflows. Built to last.

v0.10.0

16 Feb 19:16
210496f

Choose a tag to compare

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 0600 and directories use 0700 (previously world-readable 0644/0755)
  • Secure default data directory: Default storage location moved from /tmp/shelli to ~/.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.Once and atomic.Bool to prevent listener race conditions and double-close panics.

CLI/MCP Consistency

  • MCP read validation: wait_pattern and settle_ms are 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=new reads no longer return duplicate output.

Performance

  • Wait polling optimization: New SizeFunc in 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 --cursor flag (CLI) and cursor parameter (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/shelli to ~/.shelli/data. Existing sessions in /tmp/shelli are not migrated automatically. Use --data-dir /tmp/shelli to keep the old location.
  • brew upgrade shelli to get the new version.

Full Changelog: v0.9.0...v0.10.0

v0.9.0: TUI Application Support

16 Feb 16:21
d990100

Choose a tag to compare

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

03 Feb 23:29

Choose a tag to compare

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 properly

New Features

  • --follow / -f flag - Continuous output streaming like tail -f
  • --follow-ms flag - 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 50

Limitations

  • 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

03 Feb 20:07

Choose a tag to compare

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 output

clear 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 empty

resize 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 120

Enhanced 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 info
  • shelli/clear - Clear output buffer
  • shelli/resize - Change terminal dimensions
  • Enhanced shelli/create with env, cwd, cols, rows parameters

Documentation

  • Updated README with new commands and flags
  • Updated SKILL.md with complete command reference

v0.6.0

03 Feb 17:22

Choose a tag to compare

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: Use send with input_base64 for binary/complex data
  • Input is now required: MCP schema enforces input as 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

03 Feb 16:52

Choose a tag to compare

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, --submit are all gone
  • Always interprets escapes: \n, \r, \x03, etc. are always interpreted
  • No auto-newline: You must explicitly add \n or \r when 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+C

After:

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 writes

Other Changes

  • Added API stability disclaimer: API may change until v1.0.0