You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Research ShellAdapter architecture for future multi-shell support #17
Research and design a ShellAdapter abstraction that would allow NMSh to support shells beyond zsh in the long term.
Why
zsh is NMSh's only supported shell today and will remain the first-class backend for the foreseeable near term. However, planning the abstraction boundary now prevents architectural decisions that would make future shells impossible to add cleanly.
Current coupling. ShellSession combines generic PTY lifecycle (node-pty, read/write/resize/exit) with /bin/zsh -i, temporary ZDOTDIR, zsh startup-file proxying, unsetopt zle, prompt suppression, add-zsh-hook, and OSC 777 marker emission. ShellProtocolDecoder knows NMSh marker syntax but its marker payload currently models zsh precmd status/cwd and preexec command. Completion uses capture.zsh/zsh completion state; HistoryService reads Atuin or parses zsh history including metafied bytes; SemanticService runs isolated zsh and uses whence -w. TerminalApp depends on session exec/prompt events. Job-control behavior depends on a Unix PTY and shell foreground jobs. The zsh docs confirm precmd/preexec, hook arrays and add-zsh-hook are zsh mechanisms: https://zsh.sourceforge.io/Doc/Release/Functions.html
Minimal future seam (proposal, not code): ShellAdapter should own shell identity/capabilities, executable/argv selection, bootstrap generation and config-preservation policy, protocol event decoding, command-line submission/editor integration mode, completion, history import, and command classification. Its session-facing events should be normalized (prompt {cwd, exitStatus?}, commandStarting {displayText?}, data, exit) while PTY creation, byte transport, resize, transcript/rendering, and terminal-host capabilities remain generic. Treat completion/history/classification as optional capability interfaces; do not promise semantic equivalence. Keep a zsh adapter as the sole implementation until another backend is justified.
Feasibility: zsh — proven current fit. Bash — plausible POSIX PTY fit; PROMPT_COMMAND/DEBUG hooks and Readline programmable completion differ, and injecting reliable markers without clobbering user hooks is nontrivial (https://www.gnu.org/software/bash/manual/bash.html). Fish — possible but larger behavioral gap: event functions/config, native autosuggestions/completion, and its own editor/prompt need explicit suppression/ownership policy (https://fishshell.com/docs/current/interactive.html). Nushell — highest semantic mismatch: structured values, pipeline model, and different environment/hooks make zsh assumptions especially leaky; feasibility should be prototyped only against a concrete user need (https://www.nushell.sh/book/hooks.html).
Recommendation. Abstract no broad multi-shell layer now. First preserve the generic PTY/protocol boundary and avoid leaking zsh data types beyond shell-facing services; later extract the existing zsh setup behind an adapter only when a second shell is funded. Do not pursue shell symmetry as a goal.
Refs #17. zsh is the only supported shell. No Bash, Fish, Nushell or PowerShell
backend, generic runtime adapter or implementation child is introduced here.
Existing boundaries
ShellSession owns the persistent PTY, trusted zsh startup proxy, authenticated
OSC command/prompt markers and real shell signals/job control. SessionClient
already separates frontend consumers from in-process/socket transport; that is
a transport contract, not a multi-shell contract. SessionService and backlog
replay preserve lifecycle/output events. The frontend owns its editable buffer,
menus, pickers and presentation. The configured completion bridge consumes zsh
completion knowledge in an isolated hidden zpty and inserts through that editor.
Assumption inventory
Area / current code
zsh assumption
Classification / future action
ShellSession spawn/startup
/bin/zsh -i, HOME startup proxies, ZDOTDIR, prompt suppression
Leave zsh-specific now; another shell needs startup/trust research before extracting a backend factory.
PTY and job control
Persistent PTY, foreground process group, Ctrl+C/Ctrl+Z, resize
Expensive to generalize across OS/terminal models; preserve PTY ownership and test actual shell signal behavior.
Shell cwd from prompt marker; launch environment in helpers
Leave zsh-specific capture now. Future context snapshots need explicit names, privacy and generation semantics; do not pretend helpers mirror live PATH/environment.
Keep provider boundaries; shell history import needs per-shell parsing/privacy rules.
Completion
compinit/compdef, compadd, zpty/ZLE capture
Expensive shell-specific engine; abstract structured requests/candidates only when a second backend produces them. No foreign widgets may own the NMSh editor.
Future metadata capability, with completeness/generation. Another shell needs name/type/visibility research; do not generalize function bodies.
Semantic classification
lexical zsh roles and isolated whence
Safe to leave zsh-specific; syntax families differ materially. Unknown forms must degrade safely.
Prompt suppression
unset ZLE and blank prompts, p10k/fastfetch suppression
Shell/plugin-specific startup policy, never a generic prompt string replacement.
Restore / detach
Service owns the same real shell; frontend replays events
Transport can stay shared. Backend identity/version belongs in future session metadata before supporting multiple backends. No shell-state serialization is promised.
Commands and interactive apps
Buffer submitted to real zsh, passthrough forwards terminal bytes
trusted HOME config, bounded helper, conservative invalidation
Keep helper implementation zsh-specific. A future completion capability must state context provenance and config generation rather than imply live state parity.
No area needs an additional generic interface now: current candidate,
lifecycle, provider and transport contracts already isolate useful consumers.
A tiny extraction without a second concrete behavior would add names without
reducing risk. No child issue or runtime extraction is justified for v0.11.
Future surface to validate
A backend factory could expose persistent-session start/stop/resize/submit and
interrupt/suspend operations, factual output/exec/prompt events, startup trust
policy and capabilities. Capabilities may provide bounded command classification,
name metadata, history import and structured completion for a context containing
buffer/cursor/cwd/session/config generations. Completion remains optional and
failure must preserve native editing. Signal handling and raw PTY forwarding
belong beside the backend, never in command-by-command subprocess execution.
Do not require all shells to implement all capabilities. First investigate one
additional shell in a separate future milestone with real fixtures: bootstrap
without competing prompt UI, exact lifecycle/status/cwd, incomplete/multiline
input, alias/function visibility, history privacy, command-not-found behavior,
completion replacement semantics, job control and detach/reattach. Extract only
the behavior demonstrated by that work. Linux/Windows and broader frontend/TUI
research remain outside v0.11.
Completed post-v0.11 research is documented in #271 and docs/architecture/shell-adapter-v011-research.md. The inventory covers zsh startup/PTY/hooks/cwd/environment/status/signals/history/completion/name metadata/semantics/prompt suppression/restore and the configured bridge. Existing provider/lifecycle/transport boundaries suffice today; no generic extraction or child is justified without another shell’s demonstrated behavior. No additional shell support was implemented or claimed. Issue remains open.
Implemented on the v0.16 branch (PR #303, feature/v016-platform-portability-agents). ShellAdapter is a real contract with zsh, Fish and Bash backends, in-place /shell switching and handoffs: docs/architecture/shell-adapter.md. Nushell and PowerShell remain future and are not tracked here.
Activity
raiseCatError commentedon Sep 28, 2026
Research findings (architecture only)
Current coupling. ShellSession combines generic PTY lifecycle (node-pty, read/write/resize/exit) with /bin/zsh -i, temporary ZDOTDIR, zsh startup-file proxying, unsetopt zle, prompt suppression, add-zsh-hook, and OSC 777 marker emission. ShellProtocolDecoder knows NMSh marker syntax but its marker payload currently models zsh precmd status/cwd and preexec command. Completion uses capture.zsh/zsh completion state; HistoryService reads Atuin or parses zsh history including metafied bytes; SemanticService runs isolated zsh and uses whence -w. TerminalApp depends on session exec/prompt events. Job-control behavior depends on a Unix PTY and shell foreground jobs. The zsh docs confirm precmd/preexec, hook arrays and add-zsh-hook are zsh mechanisms: https://zsh.sourceforge.io/Doc/Release/Functions.html
Minimal future seam (proposal, not code): ShellAdapter should own shell identity/capabilities, executable/argv selection, bootstrap generation and config-preservation policy, protocol event decoding, command-line submission/editor integration mode, completion, history import, and command classification. Its session-facing events should be normalized (prompt {cwd, exitStatus?}, commandStarting {displayText?}, data, exit) while PTY creation, byte transport, resize, transcript/rendering, and terminal-host capabilities remain generic. Treat completion/history/classification as optional capability interfaces; do not promise semantic equivalence. Keep a zsh adapter as the sole implementation until another backend is justified.
Feasibility: zsh — proven current fit. Bash — plausible POSIX PTY fit; PROMPT_COMMAND/DEBUG hooks and Readline programmable completion differ, and injecting reliable markers without clobbering user hooks is nontrivial (https://www.gnu.org/software/bash/manual/bash.html). Fish — possible but larger behavioral gap: event functions/config, native autosuggestions/completion, and its own editor/prompt need explicit suppression/ownership policy (https://fishshell.com/docs/current/interactive.html). Nushell — highest semantic mismatch: structured values, pipeline model, and different environment/hooks make zsh assumptions especially leaky; feasibility should be prototyped only against a concrete user need (https://www.nushell.sh/book/hooks.html).
Recommendation. Abstract no broad multi-shell layer now. First preserve the generic PTY/protocol boundary and avoid leaking zsh data types beyond shell-facing services; later extract the existing zsh setup behind an adapter only when a second shell is funded. Do not pursue shell symmetry as a goal.
raiseCatError commentedon Oct 2, 2026
ShellAdapter boundary: research after v0.11
Refs #17. zsh is the only supported shell. No Bash, Fish, Nushell or PowerShell
backend, generic runtime adapter or implementation child is introduced here.
Existing boundaries
ShellSessionowns the persistent PTY, trusted zsh startup proxy, authenticatedOSC command/prompt markers and real shell signals/job control.
SessionClientalready separates frontend consumers from in-process/socket transport; that is
a transport contract, not a multi-shell contract.
SessionServiceand backlogreplay preserve lifecycle/output events. The frontend owns its editable buffer,
menus, pickers and presentation. The configured completion bridge consumes zsh
completion knowledge in an isolated hidden zpty and inserts through that editor.
Assumption inventory
ShellSessionspawn/startup/bin/zsh -i, HOME startup proxies, ZDOTDIR, prompt suppressionpreexec,precmd,add-zsh-hook, OSC status/cwd markers$?captured before precmd metadata workwhenceNo area needs an additional generic interface now: current candidate,
lifecycle, provider and transport contracts already isolate useful consumers.
A tiny extraction without a second concrete behavior would add names without
reducing risk. No child issue or runtime extraction is justified for v0.11.
Future surface to validate
A backend factory could expose persistent-session start/stop/resize/submit and
interrupt/suspend operations, factual output/exec/prompt events, startup trust
policy and capabilities. Capabilities may provide bounded command classification,
name metadata, history import and structured completion for a context containing
buffer/cursor/cwd/session/config generations. Completion remains optional and
failure must preserve native editing. Signal handling and raw PTY forwarding
belong beside the backend, never in command-by-command subprocess execution.
Do not require all shells to implement all capabilities. First investigate one
additional shell in a separate future milestone with real fixtures: bootstrap
without competing prompt UI, exact lifecycle/status/cwd, incomplete/multiline
input, alias/function visibility, history privacy, command-not-found behavior,
completion replacement semantics, job control and detach/reattach. Extract only
the behavior demonstrated by that work. Linux/Windows and broader frontend/TUI
research remain outside v0.11.
raiseCatError commentedon Oct 2, 2026
Completed post-v0.11 research is documented in #271 and docs/architecture/shell-adapter-v011-research.md. The inventory covers zsh startup/PTY/hooks/cwd/environment/status/signals/history/completion/name metadata/semantics/prompt suppression/restore and the configured bridge. Existing provider/lifecycle/transport boundaries suffice today; no generic extraction or child is justified without another shell’s demonstrated behavior. No additional shell support was implemented or claimed. Issue remains open.
raiseCatError commentedon Oct 4, 2026
Implemented on the v0.16 branch (PR #303,
feature/v016-platform-portability-agents). ShellAdapter is a real contract with zsh, Fish and Bash backends, in-place/shellswitching and handoffs:docs/architecture/shell-adapter.md. Nushell and PowerShell remain future and are not tracked here.