A structured-data shell in Gleam, inspired by Nushell
5.4 kB
156 lines
1# gleshell
2
3A **structured-data shell** written in [Gleam](https://gleam.run), inspired by [Nushell](https://www.nushell.sh).
4
5Instead of piping opaque text between programs, gleshell pipelines pass typed values: strings, numbers, lists, records, and tables. Built-in commands like `ls`, `where`, and `select` work on that structure.
6
7```text
8~/code/gleshell on main
9❯ ls | where type == file | select name size | first 5
10╭──────┬──────╮
11│ name │ size │
12├──────┼──────┤
13│ … │ … │
14╰──────┴──────╯
15```
16
17The interactive prompt is zero-config and Starship-inspired: full path with `~`,
18optional git branch (read from `.git`, no external tools), and a `❯` character
19that turns red after a non-zero exit.
20
21## Quick start
22
23Requires [Gleam](https://gleam.run/getting-started/) and Erlang (or use Nix):
24
25```bash
26# install a self-contained `gle` (Erlang shipment in the Nix store — no repo checkout)
27nix profile install .
28gle -c 'ls | first 3'
29
30# one-shot without installing
31nix run . -- -c 'ls | first 3'
32
33# dev shell (source tree; gleam, erlang, rebar3)
34# --no-pure-eval lets devenv use the real project dir (not /nix/store)
35nix develop --no-pure-eval
36gleam run # interactive REPL
37gleam run -- -c 'ls | first 3'
38gleam test
39```
40
41### REPL editing
42
43On a TTY the interactive REPL uses a **raw-mode line editor** with
44**Nushell-style syntax highlighting** as you type (commands cyan, strings
45green, numbers purple, pipes purple, flags blue, variables purple, …).
46
47| Key | Action |
48|-----|--------|
49| ↑ / ↓ | History |
50| **Tab** | Command completion (builtins + `PATH`) at the start of a pipeline stage; filename completion for arguments (common prefix; list matches if ambiguous) |
51| **Ctrl+R** | Reverse-i-search through history (Enter accepts onto the line) |
52| Ctrl+A / Ctrl+E | Beginning / end of line |
53| Ctrl+W | Delete previous word |
54| Ctrl+U / Ctrl+K | Kill to start / end of line |
55| Ctrl+L | Clear screen |
56| **Ctrl+C** | Cancel current line at the prompt; while an external command runs, send SIGINT to that process (does not exit the shell) |
57| Ctrl+D | EOF (empty line) or delete under cursor |
58
59History is persisted under the user cache as `gleshell-history/lines`.
60Non-TTY input falls back to Erlang’s `edlin`/`get_until` path.
61
62## Examples
63
64```nu
65# list files as a table, filter, project columns
66ls | where type == file | select name size
67ls | find toml md
68echo [moe larry curly] | find l
69
70# ranges and list ops
71range 10 | reverse | first 3
72range 5 | length
73
74# records and JSON
75echo {name: "gleshell", cool: true}
76echo "{\"a\": 1}" | from json | get a
77open data.json | get users | first
78range 3 | to json
79
80# variables
81let n = range 3 | length
82echo $n
83
84# process environment (Nushell-style)
85$env.HOME
86$env | get PATH
87$env.MY_VAR = hello
88echo $env.MY_VAR
89
90# external programs (stdout captured as a string)
91^uname -a
92which ls
93which -a ls
94```
95
96## Language sketch
97
98| Feature | Syntax |
99|--------|--------|
100| Pipeline | `cmd \| cmd \| cmd` |
101| Strings | `"hello"` or bare words `hello` |
102| Numbers | `42`, `3.14` |
103| Bools | `true` / `false` |
104| Nothing | `null` / `nothing` |
105| Lists | `[1 2 3]` |
106| Records | `{name: alice, age: 30}` |
107| Variables | `let x = …` then `$x` (pipeline input is `$in`) |
108| Env | `$env`, `$env.HOME`, `$env.FOO = value` |
109| Flags | `--flag` / `--flag value` |
110| Force external | `^command args…` |
111| Comments | `# …` |
112
113## Built-ins
114
115Filesystem: `ls`, `cd`, `pwd`, `cat`, `open`, `save`
116
117Table/list: `where`/`filter`, `find`, `select`, `get`, `first`, `last`, `take`, `skip`, `sort-by`, `reverse`, `length`, `columns`, `table`, `flatten`, `uniq`, `wrap`, `unwrap`, `keys`, `values`
118
119Data: `echo`, `range`, `lines`, `to`/`from` (subcommand `json`), `type`, `describe`, `env`, `sys`, `which`, `help`, `exit`
120
121Unknown command names fall through to external executables on `PATH`.
122
123## Layout
124
125```text
126src/
127 gleshell.gleam # entry + REPL
128 gleshell_ffi.erl # line editor (Ctrl+R), cwd, process spawn
129 gleshell/
130 value.gleam # structured Value type
131 lexer.gleam / parser.gleam
132 eval.gleam # pipeline evaluator
133 builtins.gleam # Nu-inspired commands
134 display.gleam # table pretty-printer
135 color.gleam # Nushell-style ANSI colors
136 highlight.gleam # live input syntax highlighting
137 env.gleam / sys.gleam
138```
139
140Input and output are colorized on a TTY (Nu-like shapes on the command line;
141headers bold green, numbers purple, bools cyan, dirs blue, errors red, …).
142External tools (`jj`, `git`, `fastfetch`, …) keep their own colors: final-stage
143commands inherit the real TTY (or get `FORCE_COLOR` when output is captured), and
144pre-colored text is not re-painted by the shell. While the raw line editor is
145active, the TTY is switched to cooked mode for those children so LF-only
146output does not staircase. Paginated output gets `LESS=FRX` when unset so
147`less` passes ANSI through.
148Disable with `NO_COLOR=1`; force with `FORCE_COLOR=1`.
149
150## Status
151
152Early but usable: core pipeline model, tables, filters, JSON, and external commands work. Not a full Nushell clone (no closures/plugins yet). Contributions and experiments welcome.
153
154## License
155
156Apache-2.0 (same as the Gleam project template unless you change it).