Releases: vlshcc/vlsh
v1.1.7.1
2026-03-06 — version 1.1.7.1
Bug fixes
Plugin compilation uses runtime V lookup instead of compile-time path
plugins/plugins.vused the V compile-time constant@VEXEto locate the
V compiler for compiling plugins. This baked the absolute path of the build
machine's V binary (e.g./home/sarm/repos/v/v) into the vlsh binary, so
plugins reloadfailed on any other system where that path did not exist.- Replaced the
const v_compiler = @VEXEcompile-time constant with a
find_v_compiler()function that searches$PATHat runtime, matching the
approach already used byexec/exec.vfor.vshscripts. The lookup also
skips directories namedvto avoid false matches. - When
vis not found in$PATH, a clear error message is printed instead
of a cryptic "file not found" failure.
Startup no longer blocks on plugin compilation
plugins.load()now accepts acompileboolean parameter. On startup the
shell passesfalse, so only pre-compiled plugin binaries are loaded — the
V compiler is never invoked and the shell cannot hang waiting for it.
Explicit operations (plugins reload,plugins enable,plugins update)
passtrueto compile outdated plugins as before.
Platform support
DragonFlyBSD compatibility
mux/pty_helpers.h: added__DragonFly__to the FreeBSD preprocessor guard
so that<libutil.h>is used foropenpty/forkptyon DragonFlyBSD (same
header as FreeBSD).pkg/build.sh: added--dragonflycross-compilation flag. The script
automatically downloads the DragonFlyBSD 6.4.2 ISO (~260 MB), extracts the
sysroot (headers and libraries), compiles V's bundled Boehm GC for the target,
and cross-compiles with clang + lld. The sysroot is cached at
~/.vmodules/dragonflybsdrootfor subsequent builds.pkg/dragonfly/: added DPorts package files (Makefile,pkg-descr,
distinfo) for building a native package directly on DragonFlyBSD, using the
same format as FreeBSD ports.
Housekeeping
- Maintainer email updated to
sarmonsiill@tilde.guruthroughout all packaging
files (pkg/deb/control.in,pkg/build.sh,pkg/freebsd/Makefile,
pkg/dragonfly/Makefile). - Repository URL updated from
https://github.com/dvwallin/vlshto
https://github.com/vlshcc/vlshinREADME.md,pkg/build.sh,
pkg/deb/control.in, andpkg/rpm/vlsh.spec.in.
Version bumps
- Version bumped to
1.1.7.1invlsh.v,v.mod, andREADME.md.
v1.1.7
2026-03-05 — version 1.1.7
Bug fixes
Fix interactive/TUI programs hanging the shell
- Commands like
vi,atto,nano,ssh, and other interactive programs
would hang vlsh becauseexec.run()unconditionally redirected
stdin/stdout/stderr through pipes viaset_redirect_stdio(). Interactive
programs need direct terminal (TTY) access for keyboard input and screen
control, which pipes cannot provide. exec.run()now only redirects stdio when it is actually needed: pipe
chains, file redirection (>,>>,<), or piped stdin input from a
previous command. Standalone commands inherit the terminal's file
descriptors directly, allowing TUI programs to work correctly.
Version bumps
- Version bumped to
1.1.7invlsh.v,v.mod, andREADME.md.
v1.1.6
2026-03-05 — version 1.1.6
Breaking changes
Unified environment variable handling (POSIX export/unset only)
- The three separate mechanisms for managing environment variables —
path=directives in~/.vlshrc, thevenvbuiltin, andexport—
have been consolidated into the single POSIX-standardexport/unset
interface. - The
path=config directive and thepathbuiltin command (list / add /
remove) have been removed. PATH is now set viaexportin~/.vlshrc,
e.g.export PATH="/usr/local/bin:/usr/bin:/bin:$PATH". - The
venvbuiltin command (list / add / rm) and its__VLSH_VENV
registry have been removed. Useexport KEY=VALUEandunset KEY
instead.
Features
~/.vlshrc now supports export and unset
- At startup,
exportandunsetlines in~/.vlshrcare executed
through the shell'smain_loop, soexport PATH="/opt/bin:$PATH"works
with full$VARexpansion. - The config file now uses
#for comments (POSIX-style) instead of".
find_exe uses $PATH directly
exec/find_exe()now readsos.getenv('PATH')and splits on:to
locate executables, instead of reading a customcfg.Cfg.pathsfield.- When
$PATHis empty, a hardcoded fallback (/usr/local/bin,
/usr/bin,/bin) is used so the shell cannot be softlocked.
Removals
cfg.Cfg.pathsfield removed from the config struct.cfg.extract_paths(),cfg.add_path(),cfg.remove_path(),
cfg.paths(), andcfg.fallback_pathsconstant removed.shellops.venv_registry,shellops.venv_tracked(),
shellops.venv_track(),shellops.venv_untrack()removed.exec.Cmd_object.cfgfield replaced withexec.Cmd_object.aliases
(the only config data the exec module still needs).find_v_exe()no longer takes acfg_pathsparameter.
Housekeeping
cfg/cfg.v: alias and style parsers hardened —starts_with()checks
replace fragileent[0..N]slicing that could panic on short lines.- Default
~/.vlshrcrewritten in POSIX-style format with#comments
andexport PATH=.... - Help system updated:
pathandvenventries removed;export,
unset, andsourceentries added. .gitignoreadded — ignores compiledvlshbinary,builds/, and V
compiler artifacts.builds/directory (34 MB of release binaries) removed from git
tracking.build_deb.shmoved topkg/build.shalongside the packaging
templates.CHANGELOGrenamed toCHANGELOG.md.- README updated: config example, feature bullets, built-in commands
table,.vshsection, and POSIX compatibility section all reflect the
newexport-only approach.
Version bumps
- Version bumped to
1.1.6invlsh.v,v.mod, andREADME.md.
v1.1.5.2
2026-03-05 — version 1.1.5.2
Bug fixes
Always capture stdout for plugin output_hooks
- Previously,
set_redirect_stdio()was only called when the command had
piped input orintercept_stdiowas set. Commands that ran directly on the
terminal never had their stdout captured, so pluginoutput_hooks (e.g. the
hist plugin) missed their output entirely. run()inexec/exec.vnow callschild.set_redirect_stdio()unconditionally
and always slurps stdout. The captured output is stored inlast_outputand
printed, ensuring every command's output is available to hooks.- The stdin pipe close (
C.close(child.stdio_fd[0])) is also now unconditional,
removing the separate branch that left it open for non-piped commands. lscolour flag changed from--color=autoto--color=alwaysso that
colour output is preserved when stdout is captured through the redirect.
Version bumps
- Version bumped to
1.1.5.2invlsh.v,v.mod, andREADME.md.
v1.1.5.1
2026-03-05 — version 1.1.5.1
Bug fixes
PATH failsafe to prevent shell softlock
- Previously, if all configured
path=entries in~/.vlshrcwere empty,
non-existent, or otherwise malformed,extract_pathsreturned a hard error
that prevented the config from loading. This made it impossible to run any
external command — including text editors to fix the config — effectively
softlocking the shell. cfg.extract_pathsnow silently skips empty and non-existent path entries
instead of returning an error.exec.find_exenow falls back to standard system paths
(/usr/local/bin,/usr/bin,/bin) when the configured PATH is empty,
printing aWRN|message so the user knows the failsafe is active.- The external-command dispatcher in
vlsh.v(both the generalelsebranch
and thelsfallthrough) now creates a fallbackCfgwith the standard
system paths whencfg.get()fails, instead of silently returning exit
code 1. - A new
cfg.fallback_pathsconstant centralises the list of fallback
directories.
Version bumps
- Version bumped to
1.1.5.1invlsh.v,v.mod, andREADME.md.
v1.1.4
2026-02-20 — version 1.1.4
Bug fixes
Aliases that expand to builtins now work correctly
- An alias whose expansion begins with a shell builtin (e.g.
~local→cd ~/.local)
previously failed with "cd not found in defined aliases or in $PATH" because alias
expansion insideexec.Taskruns after builtin dispatch. - Fixed by adding one level of alias expansion at the very top of
dispatch_cmd, before
thematchstatement. A self-alias guard (expanded_cmd != cmd) prevents infinite
recursion for trivially self-referential aliases.
Features added
|| (OR) and ; (unconditional sequence) command chaining
- The shell now supports three chaining operators:
&&— run next command only if the previous one exited 0 (pre-existing)||— run next command only if the previous one exited non-zero;— run next command unconditionally
- A new
split_commands(input string) []ChainPartfunction replaces the old
split_and_chain. It parses the input character-by-character, correctly ignoring
operators that appear inside single or double quotes. A lone|(pipe) is left in
the buffer forwalk_pipesto handle. ChainPart { cmd string; pre_op string }struct records each segment and the operator
that precedes it. Both have been moved to the newshellopssub-module.main_loopnow updates$?after every sub-command in a chain so that subsequent
segments see the correct last exit status.
$? — last exit status tracking
$?is initialised to"0"inmain()at shell startup.- After every top-level
main_loopcall (and after every sub-command in a chain),
$?is updated viaos.setenv('?', exit_code.str(), true). - Because the existing
echobuiltin already expands$VARthroughos.getenv,
echo $?now works as expected with no further changes required.
export builtin
exportwith no arguments prints all environment variables inKEY=VALUEformat,
sorted alphabetically.export KEY=VALUEsets the variable for the current session and all child processes.export KEY(without=) is accepted as a no-op (variables are already inherited).
unset builtin
unset KEY [KEY …]removes one or more environment variables from the session.
source / . builtin
source <file>(or. <file>) reads the file line-by-line and executes each
non-blank, non-comment (#) line throughmain_loop.- Returns the exit code of the last executed line.
- Useful for re-reading
~/.vlshrcor loading shell scripts.
exit N — exit with status code
exitnow accepts an optional integer argument:exit 0,exit 1,exit 42, etc.- Without an argument, exits with code
0(unchanged).
cd improvements
cd -switches to the previous directory ($OLDPWD) and prints the new path,
matching POSIX / bash behaviour. Returns an error whenOLDPWDis not set.~and~/…are now expanded insidecditself, regardless of whether the shell's
argument parser has already performed tilde expansion.$PWDis set to the resolved working directory after every successfulcd.$OLDPWDis set to the directory that was current before thecd.
Input redirection <
- External commands now support
< fileto supply stdin from a file. parse_redirectinexec/exec.vwas extended to detect the<token and capture
the following argument asstdin_file; the function now returns a 4-tuple
([]string, string, bool, string).Cmd_objectgained astdin_file stringfield;walk_pipesforwards it from
parse_redirect.- In
run(), whenstdin_fileis set, the file is read and used aseffective_input
(replacing the empty piped-input default) before stdio is redirected.
New shellops sub-module
- Pure functions that previously lived in
vlsh.v(and caused root-level test
compilation failures) have been extracted intoshellops/shellops.v:
split_commands,ChainPart,builtin_redirect,write_redirect,
venv_tracked,venv_track,venv_untrack, and thevenv_registryconstant. vlsh.vimports them viaimport shellops { … }.- The sub-module only depends on
osandstrings, keeping it free of heavy
readline/plugin/mux dependencies so the test runner can compile it standalone.
Tests
New shellops/shellops_test.v
- Replaces the deleted
vlsh_test.v(which could not compile standalone due to V's
module resolution for root-levelmodule maintest files). - 8 test groups:
split_commands(single,&&,||,;, mixed, quoted operators,
lone pipe),builtin_redirect,write_redirect, andvenvhelpers — 34 test
functions total; all pass.
New cmds/cmds_test.v
- 12 tests covering
cd: no-args → home, explicit path, nonexistent → error,
file → error,~alone,~/path,PWDupdated,OLDPWDupdated,OLDPWD
updated on second change,cd -withoutOLDPWD→ error,cd -returns to
previous directory,cd -updatesOLDPWD.
Updated exec/exec_test.v
- All
parse_redirectcall sites updated from 3-tuple to 4-tuple destructuring. - New tests for
<: basic stdin file, tilde in stdin path,<combined with>,
<combined with>>, absentstdin_filereturns'', both<tokens stripped
from args.
Documentation
README.mdupdated with a### POSIX compatibilitysection covering all new
chaining operators, input redirection,$?,export/unset,source/.,
cdimprovements,exit N, and aliases expanding to builtins.- Built-in commands table updated:
cdentry mentionscd -;exit [N]; new rows
forexport,unset,source/.. - "Features at a glance" bullet updated to include
||,;, and<. - Version bumped to
1.1.4invlsh.v,v.mod, andREADME.md.
v1.1.3
2026-02-20 — version 1.1.3
Bug fixes
Tab completion inside subdirectory paths
- Pressing Tab after a path that ends with
/(e.g.cd .config/) had no
effect when the shell was mid-cycle from a previous Tab press at a shallower
path level — the saved cycling prefix was used instead of the current path,
so no new completions were generated. - Fixed in
readline_fix.v(vlsh_completion): ifr.currentends with/
and a saved cycling prefix (last_prefix_completion) is present, it is
cleared before the prefix is chosen for the next call. This causes the next
Tab press to compute fresh completions rooted at the directory the user has
navigated into, matching the behaviour expected from bash and zsh.
Features added
Fish-style inline autosuggestions
- As the user types,
vlsh_get_suggestionsearchesr.previous_lines(command
history, most-recent first) for the first entry that starts with the current
input and returns the untyped suffix as a ghost-text rune slice. - The suggestion is rendered as dimmed gray text (
\x1b[38;5;240m) immediately
afterr.currenton everyvlsh_refresh_linecall. The editing cursor is
repositioned to the end of the typed text, so the ghost text is purely visual
and cannot be edited directly. - Pressing
→(right arrow) at the end of the line orEnd/Ctrl+Ewhen
already at the end appends the full suggestion tor.currentand advances
the cursor. Any other key that changes the input automatically recomputes the
suggestion on the next redraw. - On Enter (
vlsh_commit_line), an\x1b[K(erase to end of line) sequence
is emitted after the refresh and before the newline, so only the typed text
appears in the terminal's scrollback history. cdpath validation — forcdhistory candidates the target directory
is extracted from the history entry, tilde-expanded, and checked with
os.is_dir. Entries whose target path no longer exists are skipped; the
search continues to the next history entry.- Filesystem fallback — when no usable history entry is found,
vlsh_get_suggestioncallsr.completion_callback(the same engine that
drives Tab completion) with the current prefix and returns the suffix of the
first result. Forcdthis yields the first matching directory in the
current working directory; for other commands it yields the first matching
file or plugin completion. - Implemented entirely in
readline_fix.v; no global state is required.
vlsh_get_suggestion(r Readline) []runeis a pure read of theReadline
struct and may be called from any context.
v1.1.2
2026-02-20 — version 1.1.2
Bug fixes
DEL key now works correctly
- The DEL key was silently ignored because the terminal emits
ESC[P(ANSI
DCH — Delete Character) rather than the more commonESC[3~(VT220 Delete).
Added'P' { return .delete_right }tovlsh_analyse_escapein
readline_fix.vso thatESC[Pis correctly mapped to a forward-delete. - Reverted an earlier incorrect fix that mapped byte
127todelete_right;
127is the Backspace key on this system and must remaindelete_left. vlsh_delete_character(Backspace) simplified: the erroneous col-0
forward-delete workaround has been removed; Backspace at column 0 now
correctly does nothing, matching standard shell behaviour.vlsh_suppr_character(forward delete) now clears completion state after
deleting a character, preventing stale completion matches.
Plugin help command
- Plugins can now declare a
helpcapability; vlsh invokes
<binary> help [command]when the user runshelp <plugin-command>. has_help booladded to thePluginstruct; newshow_help()function
inplugins/plugins.vdispatches to the correct plugin binary.dispatch_cmd()invlsh.vtries plugin help before falling back to
the built-in help output.examples/hello_plugin.vupdated with ahelphandler and thehelp
capability listed undercapabilities.
v1.1.1
2026-02-20 — version 1.1.1
Features added
output_hook plugin capability
- Plugins can now capture the stdout of every command by declaring the
output_hookcapability. The shell invokes the plugin binary as:
<binary> output_hook <cmdline> <exit_code> <output>
where<output>is the captured stdout of the command. - Output is captured for pipe-chain commands (the final stage that receives
piped input) and for the built-inechocommand. Direct-terminal commands
(interactive programs such asvimorhtop, and plain non-piped
commands) pass an empty string so that TTY behaviour is never broken. has_output_hook booladded to thePluginstruct inplugins/plugins.v;
parsed inplugins.load()alongside the existing capability tokens.- New
run_output_hooks(loaded []Plugin, cmdline string, exit_code int, output string)function inplugins/plugins.v; invoked from
dispatch_cmd()invlsh.vafter every external command, theecho
built-in, and the system-lsfallthrough path. last_output stringfield added toexec.Task; populated inrun()when
the final pipe-stage output is naturally captured.dispatch_cmd()invlsh.vgains afull_cmdline stringparameter so
hooks see the original unsplit input rather than a reconstructed string.
New example plugin: examples/output_log.v
- Demonstrates
output_hookby recording a timestamped block per command
(header + captured stdout) to~/.vlsh/output.log. - Registers two shell commands:
output_search <pattern>— grep the log for entries matching a patternoutput_log_clear— wipe the log file
- Skips empty commands and internal plugin-management invocations to keep
the log clean.
Documentation
README.mdupdated: feature bullets, plugin-system paragraph, plugin
protocol tables (output_hookargument and capability token rows),
example-plugin snippet, newoutput_logplugin section, module API
summary (run_output_hooksadded).help pluginsupdated incmds/cmds.v: capabilities block added listing
all six capability tokens includingoutput_hook.examples/hello_plugin.vprotocol header updated withoutput_hook
argument documentation.- Version bumped to
1.1.1invlsh.vandv.mod.
v1.1.0
2026-02-20 — version 1.1.0
Features added
Versioned remote plugin structure
- The remote plugin repository now uses a folder-per-plugin layout:
vlshcc/plugins/<name>/contains aDESCmetadata file (TOML: name,
author, email, description) and one subdirectory per semver release
(v1.0.0/,v1.1.0/, …), each holding the plugin's.vsource file. - Locally, plugins are stored in
~/.vlsh/plugins/<name>/<version>/<name>.v
(only the installed version is kept). Compiled binaries remain in the flat
~/.vlsh/plugins/.bin/directory as before. - Old flat
.vfiles in~/.vlsh/plugins/are silently ignored; no
migration is required.
New and updated plugin subcommands
plugins listnow shows the installed semver version next to each name.plugins remotenow marks each installed plugin with its version and flags
when a newer version is available in the remote repository
([installed v1.0.0, update available: v1.1.0]).plugins search <query>(new) — replacesplugins remote search; searches
both the plugin name and thedescriptionfield from the DESC file
(case-insensitive).plugins install <name>now resolves and installs the latest semver version
and prints the installed version string (git v1.1.0 installed).plugins update [name](new) — with a name, updates that one plugin to the
latest remote version; without a name, updates every installed plugin.
Prints one status line per plugin (updated or already at latest). Reloads
plugins after any successful update.plugins delete <name>now removes the entire plugin folder
(os.rmdir_all) instead of just the.vfile.
New structs and helpers in plugins/plugins.v
PluginDesc { name, author, email, description string }— holds metadata
parsed from a remoteDESCfile.InstalledPlugin { name, version, src string }— holds the name, installed
semver version, and source path of a local plugin.Pluginstruct gains aversion stringfield populated at load time.- New public functions:
installed_list(),remote_plugin_names(),
remote_versions(),latest_remote_version(),fetch_desc(),
update_plugin(),search_remote(). install(name)now returns the installed version string (!string).remote_available()removed; replaced byremote_plugin_names().- Semver ordering:
parse_semver/compare_semverhelpers sort version
strings using a[3]intfixed array. - TOML parsing: lightweight line-by-line
parse_desc/extract_toml_val
helpers — no external library required.
Automatic plugin directory creation
~/.vlsh/plugins/(and~/.vlsh/plugins/.bin/) are now created
automatically on startup if they do not exist.
Bugs fixed
GitHub API directory detection broken (plugins remote)
- V's JSON decoder does not correctly handle
@[json: 'type']when the
JSON key is a language keyword, leaving the mapped field always empty.
Replaced with a check onhtml_url.contains('/tree/'), which is a
guaranteed GitHub API property for directory entries (files use/blob/).
This fixesplugins remotereporting "no remote plugins found" even when
the remote repository is reachable and correctly structured.
Documentation
README.mdupdated: plugin feature bullet, installation section
(v1.1.0 URLs), built-in commands table, shell-features plugins paragraph,
"How plugins work" section, managing-plugins command block, example plugin
install instructions, and module API summary.help pluginsupdated: removedplugins remote list/plugins remote search; addedplugins search <query>andplugins update [name];
updated descriptions forplugins listandplugins remote.- Version bumped to
1.1.0invlsh.vandv.mod.