Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]

### Added
- **Persistent live sessions:** zsh now runs in a small per-user session service (`nmshd`, started on demand over a private Unix socket), so closing a window **detaches** its shell instead of ending it. Running commands keep going. `exit`, Ctrl+D and `/zsh` still end the session. `NMSH_SESSION_SERVICE=0` runs the shell in-process as before.
- **Reattach:** get back to a detached session from the startup prompt, from `/resume` (LIVE sessions are listed above ARCHIVED transcripts; Enter attaches, and Ctrl+K kills after confirmation), or with `nmsh --attach <id>`. `nmsh --sessions` lists live sessions, and `nmsh --new` always starts a fresh one. A session attached in another window is never taken over.
- **Output while detached:** what a detached session prints is kept (1 MB in memory, then up to 64 MB spooled to disk; `NMSH_BACKLOG_MEMORY_BYTES`, `NMSH_BACKLOG_SPOOL_BYTES`). It is added to the transcript on reattach, with a note of commands that completed and anything that exceeded the limit. Fullscreen apps are repainted at the new window size.
- **Multiplexer interoperability notes:** [docs/architecture/multiplexer-interop.md](docs/architecture/multiplexer-interop.md) records how NMSh behaves inside and around tmux and GNU screen (rendering, resize, job control, fullscreen, paste, and live-session detach when a pane closes), what remains unverified (Zellij), and follow-ups. NMSh stays independent of any multiplexer.
- **Live session status:** `/resume` LIVE rows and `nmsh --list` show what each live session is doing, from evidence only: the running command and elapsed time, the foreground process or a known CLI's name (Claude Code, Codex, Aider, …), active or quiet output, *needs attention* when the program sent a terminal notification or bell, fullscreen, the title it set, and the last command's result while idle. Nothing is guessed, and paths under your home directory are shown as `~/…`.
- **Live session status:** `/resume` LIVE rows and `nmsh --sessions` show what each live session is doing, from evidence only: the running command and elapsed time, the foreground process or a known CLI's name (Claude Code, Codex, Aider, …), active or quiet output, *needs attention* when the program sent a terminal notification or bell, fullscreen, the title it set, and the last command's result while idle. Nothing is guessed, and paths under your home directory are shown as `~/…`.
- **`/layout` showcase:** preview composer position (Bottom, Top, Flow) × transcript presentation (Normal, Chat) on sample content through the real renderer, then save and apply live. Also under Config → Layout. Nothing in the preview runs or reaches the transcript, journal or `/copy`.
- **Flow composer:** Config → Composer position → Flow (the command palette's Toggle composer position cycles Bottom, Top and Flow). The prompt and input follow the newest output inside NMSh's document, like a conventional terminal, and scroll with it. Typing while scrolled back returns to them; scrolling alone does not. Menus open below the input, panels pin to the bottom, and Chat presentation and fullscreen passthrough work as before.
- **Startup restore is your choice:** Config → Sessions → Startup restore (Ask, the default; Always; or Never) and Multiple detached sessions (Ask which, or Open all). With one detached session, NMSh asks: Resume, Not now, Always or Don't resume at startup. With several, a picker restores the ones you select: this window takes one, and the others open in new Ghostty, Terminal.app or kitty windows. Where a host can't open windows, NMSh names the `nmsh --attach` command for each. Never only skips restoring at launch; it never ends a session.
Expand All @@ -19,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- **Session limit:** at most 16 live sessions per service (`NMSH_MAX_SESSIONS`). When the limit is reached, a new window falls back to an in-process shell with a notice. Detached sessions are never ended to make room.

### Fixed
- Ctrl+Z suspends the foreground job again (raw `^Z` and the Kitty keyboard encoding), and `jobs` and `fg` work as in plain zsh.
- After Ctrl+Z, `jobs` could list a stray `suspended (tty output)` job. NMSh's own prompt hook ran `stty` as a job, and it could be stopped when it ran before zsh had taken the terminal back. The hooks now change terminal modes outside job control.
- The session service no longer crashes when a window resize races a shell's exit. node-pty could throw `EBADF` for a PTY that had just closed, which inside nmshd would have ended every live session. Other resize failures are now reported to that window instead of ending the service.
- A launch notice (for example, sessions archived while no window was attached) is no longer erased when the window reattaches to a live session.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ NMSh provides a richer interactive frontend without throwing away the proven rob
- Welcome providers: Vespyr (default), Fastfetch, Neofetch (legacy, if installed), or None (`/settings` → Welcome)
- Command palette: `/palette`, F1, or Ctrl+Shift+P (Cmd+Shift+P where the terminal reports it) to search NMSh commands, settings, and actions
- Sticky command headers keep the current command visible while scrolling
- **Live sessions:** closing a window detaches its shell instead of ending it, and running commands keep going. Come back through the startup prompt (Config → Sessions: Ask, Always or Never), `/resume` (LIVE sessions with their status, above archived transcripts), or `nmsh --attach <id>` (`nmsh --sessions` lists them). `exit`, Ctrl+D and `/zsh` end a session.
- NMSh checkpoints the local presentation session during use; `/clear` starts a fresh view and `/resume` browses retained sessions without rewinding live zsh state
- `/zsh` hands off to an ordinary interactive zsh
- Rich paste atoms for large multiline pastes
Expand Down
10 changes: 9 additions & 1 deletion docs/design/session-interaction-ux.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ While a parent is running, its activity timeline is rendered at the end of the a

First-run onboarding asks for NMSh Native or Starship (NMSh is preselected), then the independent two-line/one-line composer choice. NMSh Native then shows an appearance step for theme, start, connector, gap, end, icons, and a module manager (Space shows/hides, Shift+↑↓ reorders, ←→ changes the exit-status condition; custom module colors are preserved), with a live preview from the real renderer and a one-row preview of every theme (the gallery is omitted on short terminals). Theme rows use the draft's real geometry over a synthetic preview-only context (project, cwd, git, Node, Go, Python, Docker) so every role is visible; the live prompt still shows only detected modules. The panel shows the saved configuration as `Current`, marks each changed value with its saved value, and labels the preview `unsaved preview` or `matches current`; nothing is applied until Enter saves. Every step ends with a consistent controls row listing its keys. Starship setup reports binary version and config path, supports existing/default configuration and truthful preset guidance, and offers an explicit Homebrew install confirmation on macOS when available. Both provider paths ask for composer layout. Escape can skip; completion and choices persist in the existing prompt config. `/prompt` reopens the same settings flow. Switching providers retains inactive provider settings and never edits Starship config or the user's ordinary `.zshrc`.

## Live sessions

The frontend talks to its shell only through a `SessionClient` using a versioned, newline-delimited JSON protocol (#126). By default the shell and its PTY live in a per-user session service, `nmshd`. The frontend starts it on demand, and it listens on a Unix socket in a private (`0700`) runtime directory (#127). Each new session receives the launching frontend's environment and working directory. If the service can't be used, the frontend runs the shell in-process and says so. `NMSH_SESSION_SERVICE=0` forces in-process mode, and `NMSH_SESSION_MODE` in the shell shows which mode is active.

- **Detach (#128):** closing the window (SIGHUP/SIGTERM, or the frontend disappearing) detaches the session: its shell and any running command keep going. `exit`, Ctrl+D and `/zsh` end it. The service exits once it has no sessions and no windows.
- **Reattach (#128, #130, #197):** through the startup prompt, `/resume` (LIVE rows above ARCHIVED ones: Enter attaches, and Ctrl+K kills after confirmation), or `nmsh --attach <id>`. `nmsh --sessions` lists live sessions, and `nmsh --new` skips restoring. Only one window is attached to a session at a time, and an attached session is never offered or taken over.
- **Detached output (#129):** output produced while detached is kept in memory (1 MB) and then spooled to disk in the runtime directory (up to 64 MB; `NMSH_BACKLOG_MEMORY_BYTES`, `NMSH_BACKLOG_SPOOL_BYTES`). The reattaching window continues the session's journal and appends what it missed, noting commands that completed while detached and anything beyond the limit. A fullscreen app is nudged to repaint at the new window's size.

## Live-session recovery, updates and limits

Live sessions are owned by the per-user session service (`nmshd`), which listens on a Unix socket in a private (`0700`) runtime directory. Each protocol version has its own socket (`nmshd.sock` for v1, `nmshd-v<N>.sock` afterwards).
Expand All @@ -138,7 +146,7 @@ Live sessions are owned by the per-user session service (`nmshd`), which listens
- Terminal.app: AppleScript `do script`. macOS asks once for Automation permission.
- kitty: `kitten @ launch --type=os-window`, which needs kitty remote control.
- Hosts without a way to open windows (VS Code, Zed, others), or a launcher that fails: the remaining sessions keep running, and this window names the `nmsh --attach` command for each one.
- **Live status (#174):** `/resume` LIVE rows and `nmsh --list` show evidence-based status for each live session. The service gathers it from the session's own output and reads it only when sessions are listed. Nothing polls, and nothing reads the screen.
- **Live status (#174):** `/resume` LIVE rows and `nmsh --sessions` show evidence-based status for each live session. The service gathers it from the session's own output and reads it only when sessions are listed. Nothing polls, and nothing reads the screen.
- What runs and for how long. The foreground process is shown when it differs from the command (for example `process node` for `npm test`). A known interactive CLI (Claude Code, Codex, Aider, Gemini CLI, OpenCode, Goose, …) gets its display name. The table only supplies names: every program gets the same states.
- Output recency: *active* if the session wrote in the last 10 s, otherwise *quiet* for how long.
- *needs attention*: the running program asked for it with a terminal notification (OSC 9 excluding progress, OSC 777 notify) or a bell. It clears when someone types into the session or the command ends.
Expand Down
Loading