What’s new¶
Version 0.7.1 (19 October 2026)¶
The 0.7.0 release packages failed to build, so 0.7.0 was never published as a GitHub release; 0.7.1 is 0.7.0 with that fixed.
Version 0.7.0 (19 October 2026)¶
This release makes scripts start fewer processes, adds command substitution
without a subshell, bash’s shopt and zsh’s options for globs that match
nothing, and a recommended configuration with more of zsh’s conveniences.
Fewer processes. basename, dirname, expr (with integers), seq
(with integers), id -u, id -g and uname run inside the shell, without
starting a process, when they are the system’s and given plain arguments.
The result is the same as the utility’s: anything unusual, such as an option,
runs the utility itself. $(echo ...), $(printf ...),
$(pwd), $(command -v ...), $(type ...) and these utilities in $(...)
don’t fork a subshell either, also when nested
($(basename "$(dirname "$f")")). A build script that computes paths with
them now takes a third as long as under dash (see Performance).
Command substitution in the shell itself. ${ list; } is replaced by
the output of list, as $(list) is, but list runs in the shell rather
than in a subshell, as in bash 5.3 and mksh; ${| list; } is replaced by
the value of REPLY after the list (see
Shell language extensions).
bash’s and zsh’s options.
shopt, as in bash, sets the matching luish option (globstar,autocd,histverify,nullglob,dotglob,failglob), so that lines from a.bashrcwork (seehelp shopt).setopt glob.null,glob.dots,glob.failandglob.fail_all(zsh’sNULL_GLOB,GLOB_DOTS,NOMATCHandCSH_NULL_GLOB) decide what a pattern that matches nothing becomes, and whether*matches hidden files (see Patterns that match nothing, and hidden files).testand[accept==, the same as=, as zsh and bash do.
Configuration.
The recommended configuration, which the first run and the install script offer, now has the luish-extra completions,
**/, brace expansion,cd.auto,pushd.auto, highlighted paths and more history options (see The recommended configuration).A plugin source with
release = "luish"takes the newest release made for your version of luish (a tagvX.Y.N), so that it never needs a newer luish than yours.Changed: startup files and
config.tomlare no longer cached as a whole unlesssetopt startup.auto_cacheis on (inconfig.toml’s[options.startup]);__luish_cacheblocks are still cached (see Cached startup files).setopt vars.trace_after_startuptraces variables from the first prompt on, without the cost of tracing the startup files.
Completion.
Tab after
gitoffers the repository’s own aliases with every version of git, and completes git commands that are programs of their own (git annex,git lfs) with the completer ofgit-annex,git-lfsand so on.Tab on options and file names is faster.
vared’s options and__luish_internal’s subcommands are completed, and__luish_internal list-completionslists the commands that plugins complete.
For plugin authors. sh::glob(pattern, dir) lists files with the
shell’s patterns, braces and glob qualifiers; sh::cache_put and
sh::cache_get keep a computed value until it expires or a file it depends
on changes; a completer can hand the line over to another command’s
completer (#{delegate: WORDS, index: I}); and lists and maps have no size
limits, with a warning when a call uses a lot of memory (see
Extensions).
Fixes.
a=1 b=$a cmdgivesbthe value 1, as dash does.A script read from standard input no longer loses a first word that spans lines (
p='a<newline>b'), or the lines of an alias after the first.
Version 0.6.0 (10 October 2026)¶
This release brings remote shells whose line editor runs on your machine,
zsh’s vared, and sources that plugins can make available.
Remote shells that feel local. luish --ssh HOST logs in to HOST with
ssh and runs your commands there, but edits command lines on your machine:
typing, moving in the line, the history, autosuggestions and the completion
menu don’t wait for the network, while Tab completion, the prompt and your
startup files still come from HOST (see Remote shells over SSH).
luish needn’t be installed on HOST:
luish --sshcopies itself there the first time, in the same connection, and keeps one copy per version (-o ssh.no_auto_copynever copies,--copy-luishalways does, and--luish-path=PROGRAMruns a luish already there).Commands run on a terminal of their own on HOST, so full-screen programs, job control,
^C,^Zand window size changes work as over ssh; keys typed while a command runs start the next line.ssh’s escapes
~.,~^Z,~?and~~work as in ssh (-o ssh.escape_charchanges~).-e COMMAND(or--rsh=COMMAND,--ssh-command=COMMAND) uses COMMAND instead ofssh -T, as in rsync;luish --remote COMMAND...works over any COMMAND that startsluish --serveat the other end.Your terminal’s variables (
TERM,COLORTERM,TERM_PROGRAM) and your locale (where HOST has none) reach HOST.std.completioncompletesluish --ssh(ssh’s options, luish’s own and hosts) andluish --remote COMMAND.
vared, as in zsh, edits the value of a variable with the line editor:
vared PATH, or vared -p 'Message: ' -c msg to ask for one (see
help vared).
Plugins. A plugin’s plugin.toml can make sources available in its
[available] table, as config.toml’s plugins.available does, so that a
personal plugin can bring the sources of the plugins you load now and then
to every machine (see
Making sources available: [available]).
Other improvements. A bad glob qualifier names the word it is in, as it
may come from an expansion (echo $PS1 with a prompt that ends in
%(...)).
Version 0.5.0 (5 October 2026)¶
This release brings more of zsh’s and bash’s language, a way to find where variables were set, a menu of the jobs, and options for plugins.
Where variables were set. With setopt vars.trace (or
luish -o vars.trace, to trace the startup files too), the new where
built-in shows the file and line that last set a variable, and the function
that ran. setopt vars.trace_history keeps the last changes of each
variable, which where -a lists, numbered and in colour (see
Tracing variables).
Jobs.
jobs -ishows a menu of the jobs, to bring one to the foreground, continue, stop or kill it (K, then a key for the signal:esendsTERM, thenKILLif the job is still there 5 s later).disown, as in zsh, with bash’s-a,-rand-h, and jobs named by a process id (disown $!).cmd &|andcmd &!runcmdin the background and disown it, as in zsh.
The language.
a |& bpipes standard error too, as in zsh and bash.casearms can end with;&(fall through to the next arm), or with;|or;;&(go on trying the next patterns), as in zsh and bash.Arithmetic has
**(and**=),++and--(prefix and postfix), and the comma operator, as in zsh and bash.
Plugins.
Plugin options: a plugin declares them in its
plugin.toml’s[plugin-options], and they are given inplugins.enabled, in dependencies, or asplugin load NAME OPTION=VALUE; extensions see them assh::plugin_options()and shell code as$LUISH_PLUGIN_OPTIONS(see Plugin options: [plugin-options]).luish-versioninplugin.tomlnames the oldest luish a plugin needs; an older luish doesn’t load the plugin, and says why (see The oldest luish a plugin needs: luish-version).sh::capture_cachedissh::capturewith its output kept in memory for a while; the standard completers that read a program’s-huse it, so pressing Tab again doesn’t rerun the program.The
__luish_cacheblocks of plugins thatconfig.tomlenables (or that anrc.dfile loads) are cached with keys of their own.
Other improvements. help is shown in colour on a terminal, with the
colour scheme in use, and large scripts parse faster, with less memory.
Fixes.
local -restores the options when the function returns, as in dash.test FILE -nt MISSING(andMISSING -ot FILE) is true whenFILEexists, as in POSIX and dash;[[ ... ]]too, as in bash.An empty table in
plugins.enabled(std.completion = { }) enables the plugin, as"*"does, instead of nothing.
Version 0.4.0 (4 October 2026)¶
This release is about the terminal: colour schemes and styles for the line editor, richer highlighting, integration with the terminal emulator, and error messages that show where they came from.
Colours and highlighting.
Styles and colour schemes: highlighting, the completion menu and suggestions take their colours from named styles, set with the
stylebuilt-in or in[style]and[colorscheme.NAME]tables inconfig.tomlor a plugin’splugin.toml. A scheme can be a dark/light pair, chosen by the terminal’s background colour (detected, or from$LUISH_BACKGROUNDor$COLORFGBG), and can set the terminal’s own colours (see Styles and Making a colour scheme).Highlighting tells kinds of command apart (built-in, function, alias, external, precommand), marks options, escapes, special parameters, tildes, braces and globs, shows exported, array and read-only variables, and marks syntax errors.
setopt highlight.pathsmarks words that name files, andsetopt editor.no_highlightturns highlighting off.Prompts can use the same styles, with
%[style:NAME]and%[style_off], and more colour names in%F{...}(bright-redand the like). Styles can be curly, double, dotted or dashed underlines, in a colour of their own.
The terminal. Prompt marks (OSC 133) and the current directory (OSC 7),
so that terminals can jump between prompts and open new tabs in the same
directory (setopt terminal.no_integration turns them off; see
Terminal integration). clipcopy copies to the clipboard through
the terminal, the new std/notify plugin sends a desktop notification when a
long command ends, file names in interactive error messages are links, and in
vi mode the cursor’s shape shows the input mode.
Error messages name the file of the code that failed (not $0), and show
the failing line and the call stack (see Error messages).
The language.
Here-strings,
cmd <<< word, as in bash and zsh.Brace expansion,
a{b,c}and{1..10}, withsetopt expand.braces.zsh’s modifiers in parameter expansion:
${x:h},${x:t},${x:r},${x:e},${x:a},${x:A},${x:u},${x:l}, and:aand:Ain glob qualifiers and history expansion.Array slices,
${a[i..j]}, as in Python.bash’s
BASH_SOURCE(so${BASH_SOURCE:A:h}is the script’s directory),FUNCNAME,BASH_LINENOandcaller.
Configuration. config.toml can set variables ([env],
[env.interactive] and [vars]) and add directories to PATH ([path]; see
Settings in config.toml). install.sh offers to write the
recommended configuration, with luish-extra’s
completion for about 270 more commands.
Plugins.
Plugins of a collection have full names, such as
std/completion, so that short names don’t clash, and collections can have sub-collections (SOURCE/SUB/NAME, orSOURCE.SUB.NAMEin TOML).pluginuses colours and says more:load,unloadandaddsay what they did, andsync,updateandchecklist each source, with its plugins and a link to compare versions.plugin runruns Rhai code once, from a file or with-c, andplugin load -c CODE NAMEloads a plugin without a file.Extensions can have
precmd,preexecandexithooks (see Hooks).std/completioncompletes luish’s own built-ins (options, styles and colour schemes).
Incompatible changes. $LUISH_HIGHLIGHT is gone: use styles ([style] in
config.toml, or the style built-in) instead. Plugins of a collection are
loaded under their full name (std/completion, not completion), which
plugin list-loaded shows and plugin unload takes.
Fixes. typeset -f and the saved state now print back ${x/},
${`cmd`}, a $ before backquotes and a few other constructs correctly.
Some inputs that made luish slow or crash are now errors: an array index of
64 Mi or more, !{} and history expansions of more than 1 MiB, %NG with a
huge N, and deeply nested ${a[${a[...}} or $(( that isn’t arithmetic.
Version 0.3.0 (1 October 2026)¶
This release is about daily use: more of zsh’s interactive features, completion for many more commands, a faster startup cache that you can tune, and plugins that can add commands.
Interactive use.
History expansion:
!!,!$,!-2,!str,^old^newand the rest, withsetopt history.expand(off by default, since!isn’t special in POSIX sh), andhistory.verifyto edit the line before running it (see History expansion).The right prompt, as in zsh:
RPROMPT(orRPS1) andRPROMPT2, withZLE_RPROMPT_INDENTandsetopt prompt.transient_rprompt(see The right prompt).zsh’s
print, with-Pfor prompt expansion.On the first interactive run, a menu offers to write a
config.tomlwith the recommended settings (see The first run).
Completion. std.completion now completes about 230 commands, up from 70:
shells, find, sed, jq, rg, compressors, systemctl, journalctl,
tmux, curl, ip, gpg, cargo, rustup, go, gcc, cmake, python,
pip, uv, conda, pixi, npm, apt, dnf, pacman, brew, flatpak
and more. Programs built with Cobra (gh, docker, kubectl …) or Click
(black, flask …), and nix, are asked for their own completions, and
complete-cobra and complete-click register more of them. git completion is
now part of std.completion (the separate std.git-completion is gone).
The language. Process substitution, <(cmd) and >(cmd), as in bash and
zsh (see Shell language extensions). LUISH_VERSION,
LUISH_PATCHLEVEL, MACHTYPE, HOSTTYPE and OSTYPE tell a script which
shell, build and system run it.
Startup cache. Each file of rc.d and login.d has an entry of its own,
so editing one file reruns only it and the files after it, and
PATH=$HOME/bin:$PATH in a startup file adds to the PATH of each shell.
__luish_cache blocks cache part of a file, keyed on the variables and files
they list, also in $ENV and luishrc (see
Caching part of a file):
__luish_cache env=(NVM_DIR) files=("$NVM_DIR/alias/default") {
. "$NVM_DIR/nvm.sh"
}
check-cache reports differences by file and block.
Plugins.
plugin addadds a plugin (a GitHub repository, a git or file URL, or a path) toconfig.tomland fetches it.Extensions can add commands:
sh::builtin(name, fn)registers a built-in written in Rhai, whichsh::read_line()lets read its input, andtypenames the plugin that added it (see Extensions).New functions for extensions:
sh::which,sh::commands,sh::matches,sh::expand_promptandfs::realpath.sh::capturenow takes the program and its arguments as an array (sh::capture_shruns shell code, assh::capturedid).importis relative to the importing file, andimport "@SOURCE/PLUGIN/MODULE"imports another plugin’s module, such as std’s completion engine.Library plugins (
library = trueinplugin.toml), hidden byplugin list-availableunless given-a.Extensions can no longer use Rhai’s
eval.
Incompatible changes. sh::capture takes an array instead of shell code
(use sh::capture_sh for the old behaviour); std.git-completion is gone
(enable std.completion); install.sh no longer reads LUISH_VERSION (use
--version).
Fixes. git completion of ranges (HEAD^..) and of files after a
revision; umount completion of mount points with a newline or backslash in
them; * in an ssh Include no longer matches files that start with ..
Version 0.2.0 (29 September 2026)¶
This release brings bash’s and zsh’s scripting extensions to luish. Arrays,
associative arrays, [[ ... ]], typeset and the rest work in scripts and in
the interactive shell, and scripts that don’t use them run as fast as before
(see Performance).
Arrays. Indexed and associative arrays, with the syntax and behaviour of
zsh’s sh emulation, and bash’s where that is silent (see
Arrays):
files=(*.txt "my notes") # indexed, counting from 0
files+=(extra)
echo "${files[-1]}" "${#files[@]}"
typeset -A size=([small]=1 [big]=10) # associative
for k in "${!size[@]}"; do
echo "$k=${size[$k]}"
done
read -A(zsh) andread -a(bash) read a line into an array;local,exportandreadonlytake arrays.bash’s
${!a[@]}(the indices or keys) and${!x}(indirection, also${!prefix@}).zsh’s parameter flags:
${(j:,:)a[@]}(join),${(s:/:)PWD}(split),${(o)a[@]}(sort),${(k)h[@]}(keys),u,O,i,n,L,U,Cand more.typesetanddeclare, with-a -A -i -l -u -U -f -g -p -r -x.typeset -U pathkeepsPATHfree of repeated directories, andtypeset -fprints functions.Tab after
${a[completes the indices or keys.Plugins can read and set arrays:
sh::getarray,sh::getmapandsh::setvarwith an array or a map.
More of the language.
[[ ... ]]conditions, with patterns,=~regular expressions (settingmatch,mbegin,mendandBASH_REMATCH),&&,||and-v.function NAME { ...; },NAME+=value,${x:offset:length}and${x/pattern/replacement}.let, andbuiltin(as in zsh and bash).set -o pipefail, from POSIX 2024.zsh’s special variables:
RANDOM,SECONDS,EPOCHSECONDS,EPOCHREALTIME,UID,EUID,GID,EGID,HISTCMD, and the arrayspipestatus(PIPESTATUSin bash),path(tied toPATH) anddirstack(tied to the directory stack).SHLVLis maintained.
Interactive use.
Tab expands globs, variables and command substitutions in the word, as zsh does:
ls *.mdTab becomes the list of files.The syntax highlighter colours unset variables differently (
unsetin$LUISH_HIGHLIGHT).shoptsuggests thesetoptthat does the same.
Startup cache. __luish_internal check-cache reruns the startup files and
compares the result with what the cache restores, and rebuilds it if they
differ. It can run from cron (see Checking the caches).
Fixes. Crashes on Tab after a stray ) or after a word like --opt=value,
and on a plugin source with an empty path. luish no longer prints git’s error
for a locked plugin commit that isn’t fetched yet.
Known problem. The startup cache records what your files changed relative
to the environment of the shell that built it. If conda or nvm fail in
new terminals, remove the caches (rm ~/.cache/luish/rc-* ~/.cache/luish/login-*) and start luish once from a terminal that isn’t
running another shell with the same setup. See Cached startup
files. A fix is planned.
Upgrading. The startup caches rebuild themselves the first time. Scripts that follow POSIX behave as before, with few exceptions.
Version 0.1.0 (27 September 2026)¶
The first release.
Scripts. The POSIX shell language and its built-ins, plus local, behave
as in dash. GNU configure scripts give the same results as under dash.
Scripts run as fast as under dash (see Performance).
Startups are cached: a new terminal is started in < 20 ms even when
using frameworks like conda and nvm that set many options.
Interactive use.
Line editing with zsh’s emacs keys (or vi keys), syntax highlighting, and autosuggestions from the history (
editor.autosuggest).History in zsh’s file format, which luish and zsh can share, optionally between running shells (
history.share), withfcand search as you type.Tab completion with an interactive menu and a standard library of completions for > 70 common commands and support for falling back on bash-completion scripts.
zsh’s prompt sequences (
%~,%?,%F{...}, conditions, …), improved with better variable names.Job control (
jobs,fg,bg, Ctrl-Z), and zsh’s directory stack (pushd,popd,dirs).zsh’s recursive globbing (
**/) and glob qualifiers (*(.om[0])).helpfor every builtin
Configuration. Settings go in ~/.config/luish/config.toml and
~/.config/luish/rc.d.
Plugins. Plugins are directories of shell files, with optional code in Rhai for hooks, prompt variables and Tab completion. The can be automatically installed from GitHub and can be pinned to specific commits. This is built-in to luish rather than built with a separate plugin manager.