How to install it, use it, configure it, and check it is healthy. For building and hacking on it, see DEV_DOC.md; for the full manual, see the docs site.
| service | what it is |
|---|---|
| the shell | an almost-POSIX shell that runs your bash scripts and muscle memory — hellish, hellish script.sh, hellish -c 'cmd' |
| prompt themes | 29 named themes; prompt lists them, prompt <name> switches, prompt save <name> persists |
| plugin framework | ~/.hellish/ with a conf manager — conf list, conf on|off <name>, hxp list; plus oh-my-zsh plugin support through the zsh dialect |
| completion | tab completion for commands/files/variables; complete/compgen/compopt specs behind shopt -s progcomp |
| update channel | a daily background check; update on demand, update --now to self-update |
One command (detects sudo rights, asks about plugins):
curl -fsSL https://raw.githubusercontent.com/Univers42/hellish/main/install.sh | sh- With sudo → installs to
/usr/bin/hellish, adds it to/etc/shells, and offers to make it your login shell (chsh). Same asmake my_shell. - Without sudo (42 school machines, shared boxes) → installs to
~/.local/bin/hellish, seeds~/.hellishrc, and adds a small hook to your login shell's rc thatexecs hellish for interactive sessions — a real login shell, not an alias. Same asmake user-install. - Non-interactive:
sh install.sh --yes --plugins=all(ornone, or a list:--plugins="git jump z"); force a mode with--user/--system.
From a source checkout: make user-install (no sudo) or make my_shell
(sudo). Or just run it without installing: ./build/bin/hellish after
make OPT=1 all.
- Start: open a new terminal (if installed as a login shell), or run
hellish. - Stop:
exitor Ctrl-D. If stopped jobs exist, the first exit warns and the second obeys — like bash. - Skip the hook once (user-install route):
HELLISH_NO_EXEC=1 bash. Disable it entirely:touch ~/.hellish-disable.
| how you installed | how to undo |
|---|---|
install.sh / make my_shell |
make my-shell-uninstall (restores your login shell before removing the binary); make my-shell-purge also removes config + caches |
install.sh / make user-install |
make user-uninstall — removes the rc hook and PATH block, leaves your ~/.hellishrc |
| npm | npm uninstall -g hellish-shell |
| path | what it holds |
|---|---|
~/.hellishrc |
your startup file (aliases, exports, shopt, PS1) — sourced by interactive shells only, never by scripts. Seeded from hellishrc.example, never overwritten |
$XDG_CONFIG_HOME/hellish/rc.d/ |
drop-in config fragments, sourced in order before ~/.hellishrc — *.hsh with bash rules, *.zsh with zsh rules |
$XDG_CONFIG_HOME/hellish/after.d/ |
the same, sourced after ~/.hellishrc and the plugin framework — where the installer puts your imported ~/.zshrc, so your own config has the last word |
$XDG_CONFIG_HOME/hellish/themes/ |
the 29 prompt themes (yours to edit; edited ones are never clobbered by reinstall) |
$XDG_CONFIG_HOME/hellish/plugins/ |
plugins as <name>/plugin.hsh |
~/.hellish/ |
the plugin framework (conf, hxp, its plugins and state) |
| history file | in $HOME, multi-line-safe; shopt -s lithist keeps newlines on recall |
~/.cache/hellish |
update-check state (which release was already announced) |
No passwords or credentials are stored anywhere — hellish has none to manage. The files above are the complete list of what an install touches.
Coming from zsh (every 42 account does): hellish does not run your
~/.zshrc. It is code written for zsh — one began with exec /bin/bash,
one ran compinit and twenty zstyles — and a shell that is not zsh has
no business executing it. The installer only appends its hook block to it
(the # >>> hellish >>> … # <<< hellish <<< lines that hand the
terminal over); everything else in that file stays zsh's. Bring across
the lines you want: aliases and exports go in ~/.hellishrc as they are;
a zsh-flavoured snippet (precmd() { vcs_info }, zstyle,
PROMPT/RPROMPT) goes in ~/.config/hellish/rc.d/50-mine.zsh — the
.zsh extension makes hellish read it with zsh rules — or under an
emulate zsh first line in ~/.hellishrc. vcs_info, zstyle ':vcs_info:*', precmd/preexec, autoload and colors all work there.
If you want the whole file loaded anyway, ask for it by name:
sh install.sh --zshrc writes ~/.config/hellish/after.d/90-zshrc.zsh,
which sources ~/.zshrc in the zsh dialect, best effort, after
~/.hellishrc so your zsh config keeps the last word. A plain re-run of the
installer switches it off again (kept as .off). In that mode
source $ZSH/oh-my-zsh.sh is understood: hellish loads the plugins your
plugins=(…) names and your $ZSH_CUSTOM/*.zsh — gst, gco and the
rest of the git plugin work — skips what needs zsh's line editor
(autosuggestions, syntax highlighting) silently, and keeps its own prompt
(prompt picks one of 29); themes, lib/ and the completion system stay
zsh's.
A syntax error in any file hellish loads is reported once, with the file and line, and the line itself — the same two lines bash prints — and the lines before it still run.
PS1 is the prompt, and it is bilingual: bash's backslash escapes (\u,
\w, \$, \[ \]…) and zsh's percent escapes (%~, %F{cyan},
%(?.a.b)…) work in the same string. PROMPT is the zsh spelling with
zsh's exact rules, and wins when both are set; RPROMPT is the right
prompt. The full escape list is section 1 of hellishrc.example, and
wiki/interactive.md has the rest.
What is live at every render, and what is not:
| in the prompt | rendered? |
|---|---|
$VAR, ${VAR}, ${VAR:-x}, $? and the other specials |
yes, every time the prompt is drawn |
$((…)) |
yes |
$(…) and backquotes |
no — the text stays on screen as written; it would fork on every redraw |
Preview caveat: print -P formats its expanded input again using zsh's
prompt escapes. If a variable contains % sequences, print -rP "[${V}]"
can interpret them as prompt escapes even though the live prompt leaves the
expanded value literal. Do not treat print -P output as an exact preview of
variable-backed prompt segments; this mismatch is tracked in issue #134.
For a computed segment, set a variable from a hook and put the variable in
the prompt. HELLISH_PRECMD_FUNCS runs once before each prompt
(hellishrc.example, section 5b):
_branch() { BR=$(git branch --show-current 2>/dev/null); }
HELLISH_PRECMD_FUNCS=(_branch)
PS1='${BR:+($BR) }\w \$ 'prompt is a shell function, not a builtin. The installer seeds it
into ~/.config/hellish/rc.d/40-prompt-switch.hsh, next to the 29 themes
in ~/.config/hellish/themes/. type prompt says whether it is loaded;
from a checkout, HELLISH_THEMES=share/themes; . share/rc.d/40-prompt-switch.hsh
loads it by hand.
\A is not bash's 24-hour clock here. It is the animated glyph, and it
was there first. A bash PS1 pasted in with \A shows the glyph — or
nothing, since the animation is off by default — where bash shows 14:30.
Write \D{%H:%M} or %T for the clock.
The animation is off by default, on purpose. HELLISH_ANIM=spinner
(or pulse, ember) makes \A repaint the prompt row about ten times a
second while the shell is idle. That repaint has been reported to corrupt
the prompt on some terminals: an escape sequence printed as text
(8;2;90;96;106m), or a cut glyph shown as �. It could not be
reproduced on demand, so it ships off. If you turn it on and see that,
HELLISH_ANIM=off is the fix.
hellish --version # version + which repo the updater points at
echo 'echo ok $((6*7))' | hellish # prints: ok 42
make doctor # from a checkout: which hellish PATH reaches,
# and whether `update` will need sudo
update # ask the release channel by hand
conf list # plugin framework: what is on and offIf a terminal ever misbehaves after an experiment: reset — and the
escape hatches above get you back to your previous shell at any time.
make <TAB> / make cl<TAB> completes target names from the Makefile in the
current directory (also gmake, /usr/bin/make, and behind sudo, env X=1,
&&, (; a wrapper option that takes an argument, as in sudo -u root make,
falls back to filenames). The file is parsed in the shell itself; make is never run.
- Files read: the first regular file of
GNUmakefile,makefile,Makefile(GNU make's order, symlinks followed), at most 4 MiB. No such file, a directory, or an unreadable file means plain filename completion. - Offered: the names on
target:/a b c:/a::lines. Not offered: variable assignments, directives, recipe lines,defineblocks, names with%or$, names starting with.; text after the colon is ignored. include/-include/sincludeof literal file names are followed ONE level deep (what an included file includes is ignored). Changing only an included file does not refresh the cache; touch the Makefile.- Cache: the targets are reused while the Makefile's name, inode, mtime and
size are unchanged (one
statper TAB). - A word starting with
-or containing=or/keeps filename completion, and so does anymakeline with-C,--directory,-f,--fileor--makefilebefore the cursor, or a prefix that matches no target (make out.txt). - A
completespec formake(undershopt -s progcomp) wins over all of this;complete -r makebrings the targets back.
| variable | effect |
|---|---|
HELLISH_BANNER=0|1 |
welcome panel off / on |
HELLISH_HISTORY_PREFIX=0|off |
Up/Down plain history; default is prefix search, read at every prompt |
HELLISH_AUTOSUGGEST=1|on |
inline suggestion, off by default: the newest history line that starts with what you typed, shown dim after the cursor (see below); read at every prompt |
HELLISH_SUGGEST_MAX=N |
how many of the newest history lines the suggestion looks at (default 1000; not a number, or below 1, is the default); read at every prompt |
HELLISH_NO_UPDATE_CHECK=1 |
never check for updates, no badge |
HELLISH_NO_ANIM=1 |
no startup animation |
HELLISH_NO_EXEC=1 |
user-install hook: skip for this one session |
With HELLISH_AUTOSUGGEST=1 (set it at the prompt or in the rc file) the
newest history line that starts with what you have typed appears dim after
the cursor. It is a hint, not part of the line: Right, End or
Ctrl-F at the end of the line insert it; anywhere else, and when there
is no hint, those keys do what they always did. Enter runs only what you
typed, and the hint is wiped from the screen.
| key | with a hint | without one |
|---|---|---|
| Right, End, Ctrl-F | insert all of it | forward-char / end-of-line |
| Alt-F, Ctrl-Right | insert its next word; the rest stays as the hint | forward-word |
| Alt-N | show the next older, different matching line | non-incremental forward search |
| Alt-P | go back to the newer one shown before | non-incremental reverse search |
A word is the blanks at the front of the hint plus the run of characters up
to the next space or tab (so -m, a/b, x=1 are one word each). Alt-N
and Alt-P step through distinct lines (same full text offered once), look
at no more than HELLISH_SUGGEST_MAX entries, ring the bell at the oldest
and the newest end instead of wrapping, and start over from the newest
match as soon as the line is edited. Right after Alt-N inserts the line
shown, not the newest. A hint a zle widget set as POSTDISPLAY is not
cycled or taken by words. In vi insert mode Alt-F / Alt-N / Alt-P work the
same (no Ctrl-Right). unset HELLISH_AUTOSUGGEST
(or =0) turns it off at the next prompt. A key you bound yourself
(bind / bindkey / .inputrc) is never taken over.
Never suggested: a line with a newline, a tab or any other control character
(an old printf '\e[31m' cannot repaint your prompt), and nothing at all
while you search (Ctrl-R), in vi command mode, on a continuation prompt, with
the cursor inside the line, or on a line that would not fit the terminal's
width (the hint is cut at the edge, never wrapped). A right prompt
(RPROMPT) is hidden while the hint reaches it and comes back when it does
not.
For zsh widgets (zle -N), the variable POSTDISPLAY is the text shown
after the cursor: read it in a widget to get the current hint, assign it
(POSTDISPLAY=' <hint>') to show your own until the line next changes.