GSH (Gleam Shell)

Package Version Hex Docs

Copyright 2026 v4rm4n

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at here. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

GSH is an interactive REPL for the Gleam Programming Language written in Gleam and Erlang.

Installation

Add gsh to your project as a dependency:

gleam add gsh

⚠ This was previously gleam add gsh --dev

Usage

gsh can either be used as a standalone REPL or a live-app bootloader.

Standalone

gleam run -m gsh

Call functions from GSH

Erlang/OTP 28 [erts-16.1.2] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]

Interactive Gleam (GSH 1.1.1) - press Ctrl+C to exit (type h() ENTER for help)
gsh(1)> import your_app/config
ok
gsh(2)> config.load()
Runtime Error: "error:undef"
gsh(3)> :cc
  Compiling your_app
   Compiled in 0.25s

Ok (Imports hot-reloaded)
gsh(4)> config.load()
Config("0.1.0", "0.0.0.0", 8000) : Config
gsh(5)> 

App loader

gleam run -m gsh -- my_app worker_pool bg_module_1

Built-in Commands

GSH includes several built-in commands to manage your session:

Target limitations

Note: GSH is heavily tied to the Erlang VM (BEAM) for state persistence and dynamic evaluation. It does not support the JavaScript target.

Why a REPL?

After using Elixir’s iex, OCaml’s utop or even Rust’s evcxr. I really wanted to build a tool for Gleam that gets me closer to the BEAM. GSH, expanded as Gleam SHell is a materialization of that dream.

REPL use cases

Demo

Latest Bugfixes

State issues

// Defined here
gsh(7)> let resp = client.check_ip("8.8.8.8", ctx)
Ok(Response(...)) : Result(Response(String), IpqsError)

// check_ip --> renamed to --> do_check_ip

gsh(20)> client.do_check_ip("8.8.8.8", ctx)
error: Unknown module value
   ┌─ REPL:26:23
   │
26 │     let resp = client.check_ip("8.8.8.8", ctx)
   │                       ^^^^^^^^ Did you mean `do_check_ip`?

The module `example/integrations/ipqs/client` does not have a `check_ip`
value.
gsh(8)> client.do_check_ip("8.8.8.8", ctx)
Ok(IpQualityScore(True, "Success", 0, "US", "California", "Mountain View", "Google DNS", False, "America/Los_Angeles", "dns.google", False, False, False, False, False, False, False, "N/A", 37.38999939, -122.06999969)) : Result(IpQualityScore, IpqsError)
[debug] latency: 2119.150ms | bindings: 2 | imports: 3
gsh(9)> :cc
  Compiling t4z3r
   Compiled in 0.22s

// do_check_ip --> renamed to --> check_ip

Ok (Imports hot-reloaded)
gsh(10)> client.check_ip("8.8.8.8", ctx)
Ok(IpQualityScore(True, "Success", 0, "US", "California", "Mountain View", "Google DNS", False, "America/Los_Angeles", "dns.google", False, False, False, False, False, False, False, "N/A", 37.38999939, -122.06999969))
[debug] latency: 1847.289ms | bindings: 2 | imports: 3

Latency & Type Resolution (Hot/Cold Path)

Compiler Directory & Path Scrubbing

Terminal Rendering & Output Alignment

Error Formatting & Decoder Fixes

How it works

In-RAM Fast Compilation Pipeline (Sub-20ms Latency)

Rather than spawning heavy OS subprocesses with gleam build or writing .beam files to disk, GSH compiles and executes code directly in memory:

Single Persistent Node & Side-Effect Memoization

GSH runs inside a single, long-lived Erlang VM node. To prevent historic variable assignments from re-executing side effects (like spawning processes, printing logs, or hitting a database) during session re-evaluations:

Stateful Lexical Scope Tracking

Session scope is tracked in an explicit ShellState record across evaluations. GSH dynamically merges, prunes, and re-injects:

Raw Terminal TUI & I/O Engine

Powered by etch_erlang, GSH toggles terminal raw mode on the fly to support character-by-character key handling, live TAB completion, multiline syntax buffering (...>), and ANSI color formatting without corrupting background process stdout.

Feature set comparison with iex

FeatureGSH (Gleam Shell)IEx (Interactive Elixir)
Live App Bootstrappinggleam run -m gsh -- appiex -S mix
SyntaxGleam (Rust-like, strict types)Elixir (Ruby-like, dynamic)
Syntax HighlightingYes (ANSI-based)Yes (Configurable ANSI)
Type SystemStatic (recompiles on the fly)Dynamic
Evaluation EngineFile-backed generation + Hot code reloadDirect Erlang AST evaluation
Side-Effect SafetyYes (Process Dictionary memoization)Yes (Native to AST loop)
VM State PersistenceYes (Actors, PIDs, ETS stay alive)Yes
Fault ToleranceYes (Catches Badarg / VM crashes)Yes
Multiline InputYes (Buffer completion)Yes (Native AST parsing)
Built-in Helperspid() (easily extensible)h(), i(), v(), pid(), etc.
AutocompleteKeywords, bound vars, module exportsDeeply context-aware + docstrings

Elixir-Style Live Documentation (h command)

By combining intelligent package path resolution with a live glexer token stream, the shell locates raw .gleam source files, lexes them on the fly, and extracts both module-level documentation and function signatures. This brings the legendary, tactile developer experience of Elixir’s iex to Gleam, allowing developers to read rich, ANSI-formatted markdown documentation directly in the REPL without requiring modifications to the Gleam compiler.

Acknowledgments

GSH stands on the shoulders of some excellent Gleam libraries:

Contributing

Contributions are massively appreciated! A REPL would be a nice to have tool in the Gleam ecosystem, and there is plenty of room to grow.

License

This project is licensed under the Apache-2.0.

Search Document