Architecture
This document explains how shannon works under the hood.
Shannon IS Nushell
Shannon copies the nushell binary source code (~4,600 lines) and adds mode dispatch for bash (via brush crate). This gives shannon all nushell features for free: terminal ownership, process groups, job control, signal handling, multiline editing, completions, hooks, plugins, and more.
Mode Dispatch
Shannon has two modes, toggled via Shift+Tab:
- nu — nushell’s native evaluation (default)
- bash — bash commands via the brush crate
The mode is stored in $env.SHANNON_MODE. When the mode is “nu”, commands
go through nushell’s parser and evaluator as normal. When the mode is “bash”
or “ai”, a ModeDispatcher trait intercepts the command in
loop_iteration() and routes it to the appropriate engine.
ModeDispatcher Trait
Defined in nu-cli (our nushell fork):
pub trait ModeDispatcher: Send {
fn execute(
&mut self,
mode: &str,
command: &str,
env_vars: HashMap<String, String>,
cwd: PathBuf,
) -> ModeResult;
}
The dispatcher receives string env vars (converted from nushell’s typed
values via env_to_strings()) and returns strings. Nushell’s REPL writes
them back to the Stack. The dispatcher never touches nushell internals
directly.
Forked Dependencies
Shannon depends on three forked repos, maintained as git submodules:
| Submodule | Fork of | Changes |
|---|---|---|
nushell/ | nushell/nushell | ModeDispatcher trait, BashHighlighter, Shift+Tab keybinding, config dir, relaxed libc pin, crate renames |
brush/ | reubeno/brush | Crate renames only |
reedline/ | nushell/reedline | Crate rename only |
All forked crates are renamed to shannon-* and published to crates.io.
Each fork has a shannon branch with our changes. Upstream sync is done via
git rebase upstream/main.
Environment Propagation
When switching modes, all exported environment variables and the cwd are preserved:
Nu to Brush:
env_to_strings()converts nushell’s typed values to stringsENV_CONVERSIONSto_stringclosures handle typed vars (PATH as list)- Strings passed to
BrushEngine::inject_state()
Brush to Nu:
BrushEngine::execute()returns string env vars- Strings written to nushell’s Stack via
add_env_var(Value::string(...)) - Nushell’s REPL automatically applies
from_stringconversions
Configuration
Shannon uses ~/.config/shannon/ with nushell’s native config system:
env.sh— bash environment setup via bash (runs first)env.nu— nushell env setupconfig.nu— nushell config (keybindings, colors, hooks)
Shannon adds no custom configuration — nushell’s config.nu handles everything.
Syntax Highlighting
Each mode has its own highlighter, rebuilt every REPL iteration:
- Nu mode:
NuHighlighter(nushell’s native highlighter) - Bash mode:
BashHighlighter(tree-sitter-bash, Tokyo Night colors)
Source Code Layout
Copied from nushell binary (startup, terminal, signals):
main.rs, run.rs, command.rs, command_context.rs, config_files.rs,
signals.rs, terminal.rs, logger.rs, ide.rs,
experimental_options.rs, test_bins.rs
Shannon-specific (engines and dispatch):
dispatcher.rs, brush_engine.rs, shell_engine.rs, shell.rs,
executor.rs