// ZSHRS — THE FIRST JIT-COMPILED UNIX SHELL

zshrs v0.12.58 · Anti-fork architecture · 23 coreutils builtins · VM-executed parallel · Bytecode caching · JIT · Full reference · Daemon impl map · Coverage report · Port report

// Color scheme

>_ZSHRS REFERENCE

The most powerful shell ever created. No-fork architecture, AOP intercept, worker thread pool, bytecode caching, fusevm compiled execution.

SHELL SCRIPTS AT MACHINE CODE SPEED.

NO FORK. NO INTERPRETER. JUST BYTECODES.

0
forks in hot path
100%
bytecode compiled
18
worker threads
243
builtins
23
coreutils builtins
100x
warm start speedup
2000x
fork avoidance speedup
1st
JIT-compiled Unix shell

For the first time in the history of computing — since the dawn of Unix at Bell Labs in 1970 — a shell compiles to executable bytecodes and JITs them to native machine code. Nushell got to bytecode first (its IR evaluator shipped in 0.96.0 and became the default in 0.98.0), but interprets it; zshrs runs its bytecode on a VM with fused superinstructions and hands hot blocks to Cranelift. Every interactive command line, every shell script, every function invocation, every source’d file compiles to fusevm bytecodes and executes at near-machine-code speed. No tree-walking interpreter. No fork. No parsing at runtime if bytecodes are cached.

Compiled bytecodes are cached as rkyv zero-copy archives mmap'd directly into memory. Function invocations deserialize cached bytecodes in microseconds — skipping the lexer, parser, and compiler entirely. source’d scripts and interpreted scripts follow the same path: compile once, cache, execute from bytecode forever. No other shell persists its bytecode: zsh’s .zwc is per-file wordcode for zsh’s own interpreter, and Nushell’s IR is rebuilt on every parse and dropped at process exit.

[0x00a] INVENTIONS — THE LIST

Sixty-four candidates, filtered by three tests that all have to pass: it exists (something in the tree does this, with a name you can type), nobody else has it (not “ours is compiled” — no other shell does the thing at all, and the near misses are named), and it is an idea, not a decision (a file layout, a default or a command name is a choice; an idea is something another project could inherit and be changed by). Twenty-nine survive. The cuts — filing conventions, flags, mechanisms of other entries, and one claim that was simply false — are listed with reasons in docs/INVENTIONS.md, which is the canonical register.

This is a legacy, not a battle. Every item below originated in zshrs and is offered as prior art for the shell-design commons under the MIT grant. Future shells (bash, fish, nushell, elvish, oil, xonsh, murex, projects that don’t exist yet) should inherit any of it. Ports must credit zshrs as the invention source in their docs — see CREATORS.md for the suggested wording. The protected invariants in MAINTAINERS.md guard upstream identity, not the ideas.

Execution

Bytecode, JIT, and persistence across processes

Every command, script, function and sourced file lowers to fusevm bytecode; hot blocks go through Cranelift to x86-64/aarch64. Nushell reached bytecode first (IR in 0.96.0, default in 0.98.0) but interprets it, rebuilds per parse, and drops it at exit; zsh’s .zwc is wordcode for zsh’s own interpreter. What is left after subtracting both: first shell to emit native machine code, first to persist general bytecode across processes.

Native code persisted across launches

The .fjit disk cache keeps JIT output warm between invocations, so hot chunks are not recompiled per process. Android ART’s .oat, .NET ReadyToRun and JVM AppCDS have done this for years — its absence from every shell is the notable part.

AOT completion corpus keyed on compiler identity

--prewarm-autoloads compiles every $fpath directory so a fresh install’s first <TAB> is a shard probe, not a parse. Entries are stamped with the fpath directory, a SHA-256 of the definition text, and the identity of the binary that compiled it — standard in Nix, ccache and Bazel, absent from every shell cache. Deliberately bypassed for ksh_autoload and autoload without -U, where the compiled program is not a function of the file’s bytes alone.

JIT tier introspection as a diagnosis

--tiers runs a script, asks fusevm’s own predicates which tier took each chunk, and for chunks that reached neither, lists the op kinds responsible. Compilers have had optimization remarks for years; a shell telling you why your loop stayed interpreted is new.

The anti-fork architecture

A persistent pool of 2–18 threads takes command substitution, process substitution, globbing, completion and autoloading; 23 coreutils commands run in-process so a cat in a pipeline never leaves the shell. Baumann, Appavoo, Krieger and Roscoe’s “A fork() in the road” (HotOS 2019), applied to a shell. The 2000–5000x figure is fork overhead divided by a function call — a fixed cost disappearing, not a benchmark.

Parallelism as grammar rather than library

pmap, pgrep, peach, barrier, async and await are VM-dispatched builtins (fusevm IDs 210–215), not library calls: the body compiles once and runs on pooled VMs with zero forks. make -j, xargs -P and GNU parallel all sit outside the shell language. This is inside it.

State

Recorder-owns-rebuild

A separate binary sources your real login chain while AOP interception sits on every state-mutating dispatcher — alias, function definition, export, fpath edit, hash -d, zstyle, bindkey, compdef, zmodload, setopt, trap, sched, source, assignment — recording kind, name, value, file, line and call chain. Every other shell discovers your config by static walking and hoping its parse matches the shell’s. The DTrace insight applied to configuration: watch what runs instead of reasoning about the source. It is why zwhere can name the file and line that defined any alias you have.

Cacheable configuration vs. configuration that must be replayed

replay/ holds the non-deterministic .zshrc fragments. Everything the recorder can prove is a pure function of your dotfiles folds into the canonical shard and is skipped at startup; the rest is replayed. Purity analysis on shell configuration is the load-bearing idea under the cold-start number.

Singleton daemon owning every mutation

Canonical state, fsnotify, scheduling, jobs, locks, cache, history — one writer, stateless forkable clients, no auto-spawn. The pattern is emacsclient, Nailgun and LSP; the application to a shell’s own state is new.

Session-persistent jobs with bidirectional ptmx attach

zjob submit --pty opens a pseudo-terminal and runs the child under the daemon. Attaching later puts your terminal in raw mode, pumps stdin in, drains output back, forwards SIGWINCH, detaches on Ctrl-]. Collapses nohup + screen + pueue + disown, and solves what none of them solve cleanly: a background job blocking on a prompt you cannot see.

Cross-shell pub/sub and token-issued named locks

zsubscribe / zpublish / zlock as builtins routed through the daemon, replacing hand-glued flock + socat + FIFOs. Release requires the original token, so a dead shell cannot free someone else’s lock.

Observability

Value lineage at the bytecode level (provenance)

An origin (command substitution, glob, heredoc, process substitution) plus every op that touched the value — expand, concat, assign, exec, call — each stamped with file, line, enclosing function and wall clock. Functions carry the same chain: definition, every redefinition, every call at the caller’s line, the unfunction that ended it. One relaxed atomic load when disarmed. typeset -p answers what a value is; set -x answers what ran; nothing answers where the bytes came from. Weiser’s slicing (1981) is static, Perl’s taint mode is one bit, PASS (2006) instruments the storage layer, W3C PROV standardizes the model — all outside the shell, which is the layer that glues the tools together. See PROVENANCE.md.

Aspect-oriented advice on any command or function

intercept before/after/around with glob patterns, nanosecond timing and intercept_proceed — one primitive subsuming defer, profiling, memoization, retry and timeout. The vocabulary is forty-five years old (Flavors’ before/after methods, CLOS :around in 1988, AspectJ in 1997) and no shell had offered it as anything but hand-rolled function wrapping.

The shell describing itself to a program

--dump-tokens, --dump-wordcode, --dump-ast, --disasm, plus --dump-reflection for a JSON self-description of builtins, keywords and options. This is javap, Python’s dis and perl -MO=Deparse for a shell; no Bourne-family shell can do it.

Language

Sigil dispatch to a second language sharing the VM

@{} routes to embedded stryke, compiling to the same fusevm ops the shell does — including inside AOP advice. Multi-language runtimes are the JVM and CLR story, and Racket’s #lang is the closest analogue for inline dispatch, but no shell has hosted a second language in its own grammar.

A grammar extension with a switch that makes it vanish

intercept … { } is a real lexer extension, since zsh cannot parse a bare } as an argument. The lexer captures the span as raw source, so the body keeps its own redirections, pipelines and heredocs, and nothing expands until the advice fires. Under --zsh the extension is off and the form is rejected exactly as /bin/zsh rejects it. This is how you add syntax to a language you have also promised to be compatible with.

Extensibility

A stable, versioned, independently-published plugin ABI

cargo add znative, ship a cdylib, load it with zmodload -R, version-gated with mismatches refused, only #[repr(C)] crossing the boundary; plugins register builtins and native completion generators wired into compsys. bash’s enable -f and zsh’s zmodload load native code too, but only against the shell’s private headers with no stable ABI and no version gate — which is why neither has third-party native plugins. Apache, nginx, PostgreSQL, Redis and Emacs 25 all published versioned boundaries and got ecosystems.

Absorbing foreign binaries into the shell process

git served as a builtin with no fork, exec or PATH lookup, and an fzf-compatible finder honoring FZF_DEFAULT_OPTS in-process. BusyBox absorbed the coreutils in 1995 and dispatched on argv[0]; this extends that logic past coreutils into full applications. Near misses, named: BusyBox has no git applet, Nushell’s gstat is status-only in a child process, git-shell execs real git, and Elvish’s in-process fuzzy modes are shell UI rather than a pipeline filter.

Compatibility

Two axes of emulation fidelity, both available

--sh reproduces /bin/sh; --sh --zsh reproduces zsh’s model of /bin/sh. The bare POSIX modes use XSI echo where zsh’s emulate sh sets BSD_ECHO, and --bash accepts bash’s own set -o names including the six zsh has no option for. Eight Bourne-family dialects on one bytecode core, each verified against its actual reference shell rather than against zsh’s approximation. No precedent found for offering both notions of fidelity as distinct modes.

A hybrid port: native spine, interpreted leaves, one tree

src/compsys/ported/ mirrors zsh’s Completion/ layout exactly, but engine functions are ported to Rust with citations while end-user completers are copied verbatim alongside them — same filenames, dispatched through a _call_function bridge, 1,240 files under that tree. The shape is a JIT with an interpreter fallback, applied to a script framework.

Inheriting the configuration vocabulary of what you replace

The native ZLE engines are fish’s Rust ports, but the palette and word-level classification are fast-syntax-highlighting’s, and ZSH_HIGHLIGHT_STYLES, ZSH_AUTOSUGGEST_*, HISTORY_SUBSTRING_SEARCH_* and AUTOPAIR_* apply unchanged. The p10k engine does the same with .p10k.zsh. The rule — when replacing a widely-configured userspace layer with a native one, adopt its config surface so nobody migrates — appears twice in the codebase.

Absorbing the prompt theme into the binary

powerlevel10k as 14 files and 15,479 lines of in-process Rust, segment builders cited line-by-line against the theme spec, gitstatusd replaced by a native .git reader, your .p10k.zsh sourced unchanged. The framing generalizes: instant prompt exists to hide the cost of interpreting the theme, so when interpretation stops the workaround is deleted rather than ported.

Wall-clock budgets on per-keystroke rendering

Highlight and autosuggest passes are capped at 8 ms by default, so a huge directory or a pathological $PATH cannot lag typing. Soft-real-time scheduling applied to a line editor. Shell plugins have historically had no budget at all, which is why a slow git status in a prompt hangs the terminal.

Verification

Architectural invariants enforced mechanically in CI

tests/port_purity.rs freezes the port directory: mirror a real Src/<x>.c, cite the C function, no new files, no invented helper names. tests/tree_walker_absent.rs asserts at source level that the old interpreter is still gone, backed by 174 behavioral pins. Contributors and code-generation tools both invent helpers and both create files to hold them; both vectors are closed by a test rather than by review. This is the mechanism that makes a large rewrite finishable.

Differential fuzzing a shell against a reference implementation

Grammar-driven, seed-replayable snippets per mode, run through both zshrs and real zsh, divergences re-confirmed three times so load flakes cannot register: 22,200 cases across 74 modes, 27 divergences at 0.12%, 71 of 74 modes at zero, each remaining mode traced to one named root cause. McKeeman named differential testing in 1998, Csmith made it standard for compilers in 2011, jsfunfuzz for JS engines. Nobody had aimed it at a shell.

Tooling

A source formatter in the shell binary

--fmt: block reindent, idiomatic spacing per the zsh and OMZ style guides, heredoc-safe, idempotent, stdin-to-stdout or in-place. The same engine backs the LSP’s formatting capability, so editor and CLI cannot drift. gofmt made this a language-community expectation in 2009; shells got shfmt, a third-party Go program.

Live completion inside the editor

The LSP does not stop at builtins and in-file functions: it answers from the real compsys completers, reading the recorded environment from the canonical shard, so git ch completes checkout and git checkout lists actual branches — in your editor. Shell-out completers are killed at the request deadline and never touch the editor’s stdio. Because the server is the shell, it answers from the same lexer, parser, option table and completers the interactive session uses, instead of a reimplemented parser in a separate Node project.

First Bourne-lineage shell with LSP and DAP in its own binary

Scoped to what survives a prior-art check. Elvish shipped elvish -lsp in 0.18.0 on 2022-03-20 and Nushell shipped nu --lsp in 0.87.0 on 2023-11-14, so “world’s first shell with a language server” is false; Endo v0.1.0 shipped endo --lsp and endo --dap together on 2026-04-08, before zshrs’s two modules landed on 2026-05-16, so “first with both” is false too. All three are outside the Bourne lineage. What holds: first shell in the Bourne lineage with either, and with both. bash, zsh, ksh, dash and fish ship neither — bash-language-server and fish-lsp are separate Node/TypeScript packages and vscode-bash-debug drives the external bashdb script. Alongside it, --dump-plugins surfaces every sourced plugin (zinit / oh-my-zsh / prezto / antidote / antigen / zplug) under the IDE’s External Libraries node — cmd-clickable, find-usages-able, renameable across plugin boundaries. No shell of any lineage exposes its plugin-manager state to an IDE.

A unit-test framework and runner in the binary

Fourteen assertion verbs plus a worker-pool runner: one persistent subprocess per CPU, fork-on-receive per test, JSON over pipes, per-test fd 2 capture so concurrent workers cannot tear each other’s output. Bounded to POSIX-compatible shells — Pester is named and excluded as non-POSIX, Nushell’s std assert named and excluded for lacking a runner. bats-core, shunit2, Bach, ShellSpec and zunit are all separate installs.

Full enumeration + suggested attribution wording in CREATORS.md. Op-by-op detail in daemon-report.html. Port-coverage report in report.html.

[0x00] THE COMPILATION PIPELINE

Every path through zshrs ends at the same place: fusevm bytecode execution.

┌─────────────────────────────────────────────────────────────────┐ │ THREE EXECUTION PATHS │ │ │ │ ┌─────────────┐ Interactive command line │ │ │ REPL Input │──► Parser ──► ShellCompiler ──► fusevm::Op │ │ └─────────────┘ │ │ │ ▼ │ │ ┌─────────────┐ Shell script / source file VM::run() │ │ │ Script File │──► Parser ──► ShellCompiler ──► │ │ │ └─────────────┘ │ │ │ │ └──► rkyv image ──────────┘ │ │ (cached on first compile) │ │ │ │ ┌─────────────┐ Autoload function (compinit) │ │ │ rkyv image │──► mmap zero-copy archive ──► fusevm::Chunk ──►│ │ │ shard │ (no lexer, no parser, VM::run() │ │ └─────────────┘ no compiler — microseconds) │ └─────────────────────────────────────────────────────────────────┘
PathLexParseCompileExecuteCache
Interactive commandYesYesYesfusevmNo (ephemeral)
Script file (first run)YesYesYesfusevmYes → rkyv
Script file (cached)NoNoNofusevmHit → deserialize
Autoload function (cached)NoNoNofusevmHit → deserialize
Plugin source (cached)NoNoNofusevmDelta replay → µs

[0x01] ARCHITECTURE

zshrs Architecture ┌─────────────────────────────────────────────────────┐ │ REPL / ZLE │ │ reedline + syntax highlighting + autosuggestions │ └──────────────────────┬──────────────────────────────┘ │ ┌──────────────────────▼──────────────────────────────┐ │ ShellExecutor (vm_helper.rs, 7.9K lines) │ │ parser ─► compiler ─► fusevm bytecode dispatch │ │ 243 builtins │ AOP intercept │ trap/signal │ └──────┬─────────┬─────────┬─────────┬────────────────┘ │ │ │ │ ┌──────▼───┐ ┌───▼────┐ ┌─▼──────┐ ┌▼──────────────┐ │ Worker │ │ rkyv │ │ fusevm │ │ compsys │ │ Pool │ │ images │ │ VM │ │ completion │ │ [2-18] │ │ + │ │ │ │ engine │ │ threads │ │catalog │ │ Op enum│ │ │ │ │ │ .db │ │ fused │ │ MenuState │ │ glob │ │(query) │ │ super- │ │ MenuKeymap │ │ rehash │ │bytecode│ │ instr │ │ rkyv shards │ │ compinit │ │mmap'd │ │ JIT ► │ │ │ │ history │ │ shards │ │Cranelft│ │ │ └──────────┘ └────────┘ └────────┘ └───────────────┘

Source-tree layout: ported / extensions / recorder split

The runtime crate is physically split so port code and original code can never be confused. Bots, contributors, and humans all read docs/PORT.md before writing a single line of code; tests/port_purity.rs mechanically enforces the rules in CI.

  • src/ported/ — 106 files, FROZEN. Strict 1:1 port. Every .rs mirrors a real src/zsh/Src/<x>.c file (same stem, same relative subpath). Every top-level fn carries a /// Port of <cname>() from Src/<file>.c:NNNN doc-comment. New file creation is banned; new fn names that don't exist in upstream zsh C source are banned. The freeze closes both drift vectors: bots invent helper names ("shell_quote", "find_in_path") and bots create fresh files to drop helpers in. Both blocked.
  • src/extensions/ — 102 files. The non-port directory. Features zsh C demonstrably does not have: AOT (aot.rs, compile_zsh.rs), autoload/plugin/script caches, fish-style autosuggest/abbrev/highlight, the native powerlevel10k engine (p10k/, 21 files), persistent worker pool, arith JIT, AST s-exp dump, ZWC byte-code helpers, recorder hooks, daemon presence, structured logging, ZLE keymaps/widgets, ext builtins, regex module, config, overlay snapshot + canonical apply, subscript/fds/hist extensions. port_purity exempts this directory from the 1:1 file-existence rule on the basis that no C ancestor exists.
  • src/recorder/ — 1 file, feature-gated. Every symbol #[cfg(feature = "recorder")]; deleted by rustc when the feature is off. Compiled into the separate zshrs-recorder binary, never the default zshrs build.
  • src/zsh/ — vendored upstream. Read-only reference. The C spec; never modified.

Lexer + parser ports live in src/ported/lex.rs and src/ported/parse.rs (no separate parse/ crate). Workspace siblings: daemon/ (41 files, zshrs-daemon), znative/ (1 file, the published plugin-ABI SDK), and runtime/ (1 file, zshrs-runtime — emits libzsh.a for AOT linking without making cargo install zshrs build it) — [workspace] members = [".", "daemon", "znative", "runtime"] in root Cargo.toml; compsys was folded into the main zshrs crate (no longer a standalone sibling). Vendored: vendor/fish/ (157 files, fish-style reader/highlighter/abbreviations), bins/ (5 entry points: zshrs, zshrs-recorder with required-features = ["recorder"], zd with required-features = ["zd"], bench-autoload, parity-fuzz with required-features = ["parity-fuzz"]). 861 .rs files total · 915,020 Rust LOC (git ls-files '*.rs' | xargs wc -l, 2026-08-29). Test count: 19,916 #[test] markers across the tree (rg '^\s*#\[test\]', the same anchored count the engineering report prints).

[0x01] ANTI-FORK ARCHITECTURE

Every fork is a full process copy. On macOS, fork() costs 2-5ms including exec + ld.so + libc init. zsh forks for every $(...), <(...), cat, grep, subshell, and completion. zshrs forks for none of them.

OperationzshzshrsSpeedup
cat filefork + exec /bin/catBuiltin — zero fork2000-5000x
head/tail/wcfork + execBuiltin — zero fork2000-5000x
sort/find/uniqfork + execBuiltin — zero fork2000-5000x
date/hostname/unamefork + execDirect syscall3000-8000x
sleep/mktemp/touchfork + execBuiltin — zero fork2000-5000x
xattr operationsfork + exec xattrDirect syscall2000-5000x
pmap/pgrep/peachfork N times to sh -cVM execution — zero forkNx
$(cmd)fork + pipe + execIn-process stdout capture via dup2
<(cmd) / >(cmd)fork + FIFOWorker pool thread + FIFO
Glob **/*.rsSingle-threaded opendirParallel walkdir per-subdir on pool
rehashSerial readdir per PATH dirParallel scan across pool
Autoload functionRead file + parse every timeZero-copy mmap of rkyv-archived bytecode (µs)100x

Coreutils Builtins (23 commands, zero fork)

cat head tail wc sort find uniq cut tr seq rev tee basename dirname touch realpath sleep whoami id hostname uname date mktemp

Every invocation of these commands is 2000-5000x faster than forking to the external binary.

[0x02] WORKER THREAD POOL

Persistent pool of warm threads. Bounded crossbeam channel with backpressure. Panic recovery keeps workers alive. Task cancellation on Ctrl-C. Instant shutdown on exit.

# ~/.zshrs/zshrs.toml (seeded on first run; idempotent) [log] level = "info" # env $ZSHRS_LOG overrides; "trace" prints startup diagnostics [shell] skip_configs = "auto" # when daemon up + canonical shard recorded, skip /etc/zsh{env,rc} + ~/.{zshenv,zprofile,zshrc,zlogin} [worker_pool] size = 8 # 0 = auto (num_cpus clamped [2, 18]) [completion] bytecode_cache = true # compile autoload functions to fusevm bytecodes [history] async_writes = true # daemon-side writes via IPC; prompt never blocks [glob] parallel_threshold = 32 # min files before parallel metadata prefetch recursive_parallel = true # fan out **/ across worker pool [zle] autosuggest = true # fish-ported editor engines — ALL default off, so `zshrs -f` == `zsh -f` syntax_highlight = true history_search = true autopair = true [provenance] enabled = true # value-lineage engine; default true, inert until `provenance -m NAME`

Tasks shipped to the pool:

compinit

Background fpath scan + bytecode compilation of 16K+ functions

Process Sub

<(cmd) and >(cmd) on pool threads instead of fork

Parallel Glob

**/ recursive walk split per-subdir across pool

Metadata Prefetch

Glob qualifiers: one parallel stat batch, zero syscalls after

PATH Rehash

Parallel readdir across every PATH directory

History Writes

Daemon-side writes via IPC — prompt never waits, zero client SQLite handle

[0x03] AOP INTERCEPT

The first shell ever with aspect-oriented programming. Hook before, after, or around any command or function — at machine code speed, no fork. One primitive that replaces defer, profile, memo, retry, and timeout.

# Before — log every git command intercept before git { echo "[$(date)] git $INTERCEPT_ARGS" >> ~/git.log } # After — show timing for completion functions intercept after '_*' { echo "$INTERCEPT_NAME took ${INTERCEPT_MS}ms" } # Around — memoize expensive function intercept around expensive_func { local cache=/tmp/cache_${INTERCEPT_ARGS// /_} if [[ -f $cache ]]; then cat $cache else intercept_proceed | tee $cache; fi } # Around — retry with backoff intercept around flaky_api { repeat 3; do intercept_proceed; [[ $? == 0 ]] && break; sleep 1; done } # Fat binary — stryke code at machine code speed intercept after 'make *' { @pmaps { notify "done: $_" } @(slack email) }

Variables available in advice:

VariableAvailableDescription
$INTERCEPT_NAMEallCommand name
$INTERCEPT_ARGSallArguments as space-separated string
$INTERCEPT_CMDallFull command string
$INTERCEPT_MSafterExecution time in milliseconds (nanosecond source)
$INTERCEPT_USafterExecution time in microseconds
$?afterExit status

[0x04] RKYV ZERO-COPY CACHE LAYER

Cached bytecode lives in rkyv archives mmap'd by clients — zero-copy deserialization, no allocator pressure on hot paths. The daemon owns all writes; clients only mmap. The single queryable mirror is catalog.db (SQLite) for dbview introspection — never touched on the hot path.

images/{hash}-{slug}.rkyv

Per-source-root rkyv shards. Compiled bytecode for autoloads, plugins, sourced scripts. Daemon writes; clients mmap zero-copy. Validates with rkyv's bytecheck on first access; format-versioned for migration safety.

index.rkyv

Top-level shard index: source-root path → shard hash + mtime. Daemon rewrites on changes; clients mmap to resolve which shard to load.

catalog.db (SQLite mirror)

Daemon-hydrated FTS5-indexed mirror of all rkyv contents. Used ONLY by dbview and zcache introspection — never read on the hot path. Clients have ZERO SQLite handles. The daemon's own FTS5 history (history.db) sits next to it; the shell's user-facing history is zshrs_history (flat zsh-extended-history-format text) plus a sibling FTS5 index zshrs_history.db, both in $ZSHRS_HOME.

Browse caches without SQL:

dbview # list all tables + row counts (hits catalog.db) dbview autoloads # dump autoloads: name, body size, ast size dbview autoloads _git # single row: source, body, ast status, preview dbview comps git # search comps for "git" dbview executables rustc # search PATH cache dbview history docker # search history zcache rebuild # force daemon to recompile every shard zcache verify # bytecheck every rkyv archive

[0x04b] FILE LAYOUT & BINARIES

One directory holds every zshrs file: $ZSHRS_HOME (defaults to ~/.zshrs/). All four binaries — zshrs, zshrs-daemon, zshrs-recorder, zd — seed the directory and three default configs on first run via CachePaths::ensure_default_configs. Idempotent — never overwrites user edits. Auto-migrates legacy daemon.tomlzshrs-daemon.toml and the legacy macOS history at ~/Library/Application Support/zshrs/history.db~/.zshrs/zshrs_history.db.

~/.zshrs/ ├── zshrs.toml # shell config [log] [daemon] [shell.skip_configs] ├── zshrs-daemon.toml # daemon config [log] [http] [http.tokens] ├── zshrs-recorder.toml # recorder config [log] ├── zshrs.log # shell tracing ├── zshrs-daemon.log # daemon tracing ├── zshrs-recorder.log # recorder tracing ├── zshrs_history # flat zsh-extended-history-format text ├── zshrs_history.db # FTS5 index sibling ├── catalog.db # daemon canonical-state mirror (FTS5) ├── history.db # daemon's own FTS5 history ├── cache.db # daemon KV cache ├── plugins.db ├── images/ # rkyv canonical shards (per source root) ├── replay/ # non-deterministic .zshrc fragments ├── artifacts/ # content-addressed artifact cache ├── snapshots/ # tag-based canonical-state snapshots ├── jobs/ # supervisor stdout/stderr captures ├── daemon.sock # Unix domain socket ├── daemon.pid # singleton flock └── index.rkyv # shard registry

Three log files (one per binary)

FileOwnerLevel source
zshrs.logshell[log] level in zshrs.toml (env $ZSHRS_LOG wins)
zshrs-daemon.logdaemon[log] level in zshrs-daemon.toml
zshrs-recorder.logrecorder[log] level in zshrs-recorder.toml

Helper paths::is_zshrs_log_file matches all three for rotation, tail, and clear. Default daemon startup emits 7 INFO lines; with level = "trace" an additional ~5 startup diagnostics print (resolved env, pidlock, db sizes, ticker spawn, schedule spawn, http listener address).

[shell] skip_configs = "auto"

When the daemon is up AND has a recorded zshrs canonical shard, the shell SKIPS sourcing /etc/zshenv + ~/.{zshenv,zprofile,zshrc,zlogin} entirely and rebuilds executor state from the rkyv shard. canonical_apply wires alias/galias/salias/env/params/setopt/path/fpath/named_dirs/autoload_functions/zstyle/bindkey/compdef/zle widgets. Inline functions are deferred until the recorder ships bytecode in the shard.

[0x04c] RECORDER (zshrs-recorder)

Separate binary — not a flag on zshrs. Sources the full zsh login chain in order, skipping any missing file silently:

1. /etc/zshenv 2. ${ZDOTDIR:-$HOME}/.zshenv 3. /etc/zprofile 4. ${ZDOTDIR:-$HOME}/.zprofile 5. /etc/zshrc 6. ${ZDOTDIR:-$HOME}/.zshrc 7. /etc/zlogin 8. ${ZDOTDIR:-$HOME}/.zlogin

-f PATH overrides the chain to source one file. End-of-run ships a recorder_ingest IPC bundle to the daemon, which folds it into the canonical shard for the matching source root.

[0x04d] zd — TWO SURFACES

SurfaceTransportLatencyUse case
zd binaryHTTP via ureq to [http].listen2.64 ms (fork+exec)bash, fish, CI, anything outside zshrs
zd builtin (in-process)local Unix socket via Client0.48 ms (5.5× faster)inside zshrs — same arg surface

Default HTTP listener: 127.0.0.1:7733, seeded into zshrs-daemon.toml on first run so zd health works out of the box. Default token comes from $DAEMON_TOKEN. Loopback bind requires no token; non-loopback bind refuses to start until [http.tokens] is populated. GET /openapi (alias /openapi.json) returns an OpenAPI 3.1 document auto-derived from OP_NAMES — 108 paths today (4 meta + 101 ops + 3 streams), with ErrPayload in components and bearerAuth declared only when tokens are configured.

zd subcommands

zd health # liveness probe zd ops # enumerate every op zd metrics # Prometheus exposition zd ping [echo...] zd info zd call OP [JSON_BODY] # raw op invocation zd doctor [--json] # pretty health table: perms, db integrity, shards, # fsnotify, pidlock, jobs, legacy litter; exit 1 on any fail zd config get KEY # runtime knob plumbing zd config set KEY VALUE zd config list zd snapshot save TAG [--notes N] # canonical-state freeze zd snapshot list zd snapshot load TAG zd snapshot diff A B zd cache <put|get|del|list|stats> zd job <submit|list|status|output|kill|cancel|wait> zd lock <try|acquire|release|list> zd publish TOPIC [DATA] [--json '...'] zd events / zd watch / zd defs zd artifact <put|get|list|gc> zd schedule <add|add-once|list|remove> zd export TARGET FORMAT [--json] # raw export — NO json envelope by default zd view TARGET [FORMAT]

zd export aliases pdf > out.pdf produces a real PDF — payload is streamed direct for sh / csv / json / yaml / text / pdf. --json opt-in restores the JSON envelope.

[0x04e] NEW z* BUILTINS

BuiltinDescription
zlock try NAMECross-process named lock — non-blocking try
zlock acquire NAME [--timeout S]Poll-wait acquire with optional timeout
zlock release NAME TOKENRelease by token
zlock listList all held locks
zpublish TOPIC / TOPIC DATA / TOPIC --json '{...}'Producer side of the pub/sub bus (consumer is zsubscribe)
zwhere SUBSYS NAMEQuery daemon canonical state for "where did this alias / function / zstyle come from" with file:line attribution

Full builtin family: zcache, zls, zid, zping, ztag, zuntag, zsend, znotify, zsubscribe, zunsubscribe, zjob, zsync, zask, zhistory, zsource, zcomplete, zsuggest, zcmd-result, zlog, zwhere, zd, zlock, zpublish.

zjob — bidirectional ptmx attach

zjob submit [--pty] [--cwd DIR] [--tag T...] [--env K=V...] -- CMD ARGS... zjob list [--state running|exited|killed|failed] [--tag T] [--limit N] zjob status <id> zjob output <id> [--follow] [--stderr] [--lines N] zjob attach <id> # read-only file-tail for non-pty; # bidirectional raw-mode pump for --pty zjob kill <id> [--signal NAME] zjob cancel <id> [--grace SECS] zjob wait <id> [--timeout SECS]

With --pty the daemon calls nix::pty::openpty() and the child runs with the slave on 0/1/2 via pre_exec dup2; the master fd lives in JobMeta. Two new IPC ops drive it:

  • job_input {id, bytes_b64} — base64-decode, write to master
  • job_resize {id, rows, cols}TIOCSWINSZ for SIGWINCH propagation

On zjob attach against a pty job: termios cfmakeraw on stdin, a stdin reader thread batches keystrokes and fires job_input, output is broadcast as job:N.stdout events with bytes_b64 payload (decoded back to the user's tty), and SIGWINCH is forwarded as job_resize. Ctrl-] is the detach key. Termios is restored on exit via a Drop guard. Use case: background jobs that block on stdin (deploy scripts asking y/n, REPLs in background) — submit with --pty, attach later, type, detach, attach again.

zsync up --all

Now wired (was a stub). The shell-side enumerator (src/extensions/overlay_snapshot.rs) snapshots every overlay table and ships push_canonical per subsystem. Covers alias/galias/salias, setopt, params (vars+arrays+assoc unioned), env, path, manpath, fpath, named_dir, compdef, zstyle. Skipped: function (needs source-text round-trip), bindkey (lives in ZleManager), zmodload (no canonical "currently loaded" list).

zsync up --all # promote every overlay subsystem zsync up <subsystem> KEY VALUE zsync up <subsystem> --json '<obj>' zsync pull <subsystem> zsync diff <subsystem> --overlay '<obj>' zsync watch <subsystem>...

[0x05] BYTECODE CACHING

Every autoload function in fpath gets compiled to fusevm bytecodes during compinit. Compiled chunks are serialized via rkyv into per-source-root shards and written by the daemon. Subsequent loads mmap the archive zero-copy, validate via bytecheck, and dispatch directly — no lex, no parse, no compile, no allocator hit.

First call to _git (daemon path): fpath/_git (source text) │ ▼ ShellParser::parse_script() ~1ms │ ▼ ShellCompiler::compile() ~0.5ms │ ▼ fusevm::Chunk (bytecodes) │ ├──► VM::run() native dispatch │ └──► rkyv::to_bytes() ~0.1ms (zero-copy archive) │ ▼ images/{hash}-fpath.rkyv (daemon writes) Every subsequent call (client path, ZERO daemon RTT): images/{hash}-fpath.rkyv mmap (kernel page cache) │ ▼ rkyv::access::<ArchivedChunk> ~0 µs (zero-copy access) │ ▼ fusevm::Chunk (no lexer, no parser, no compiler, no alloc) │ ▼ VM::run() native bytecode dispatch

[0x06] FUSEVM BYTECODE TARGET

100% lowered. Every shell construct compiles to fusevm bytecodes — a language-agnostic VM with fused superinstructions and Cranelift JIT. The same VM that powers stryke (which beats LuaJIT on benchmarks). No tree-walking interpreter remains. Hot bytecodes compile to native x86-64 machine code.

stryke source ───► stryke compiler ──┐ ├──► fusevm::Op ──► VM::run() ──► JIT ──► native zshrs source ───► shell compiler ──┘ │ ├── Linear JIT: straight-line code ├── Block JIT: loops, conditionals └── Cranelift codegen → x86-64

Shell constructs already lowered to fusevm bytecodes:

Arithmetic

$(( )) — full precedence, ternary, assignment, hex/octal, bitwise

Loops

for, for(()), while, until, repeat

Conditionals

if/elif/else, case, [[ ]] with all file tests and comparisons

Commands

Exec, ExecBg, pipelines, redirects, here-docs, here-strings

Functions

Definition, Call/Return, PushFrame/PopFrame

Fused Ops

AccumSumLoop, SlotIncLtIntJumpBack, PreIncSlotVoid — single-dispatch loop execution

[0x06b] COMPLETION CODEPATH — ZSH vs ZSHRS

Both shells share the same entry — zshrs ports callcompfunc faithfully — then split where each completion function actually runs. zsh re-interprets the whole framework as shell on every tab and walks $fpath to find the leaf; zshrs runs the framework as native Rust and resolves the leaf from an index. This is a structural codepath trace, not a benchmark — the claim is about which work exists on each path.

zle complete-word → callcompfunc ZSH · INTERPRETED, EVERY TAB ZSHRS · NATIVE SPINE + CACHED LEAF spine resolve leaf load + run leaf emit COST spine · interpreted _main_complete → _complete → _normal → _dispatch ~1,517 shell lines walked · re-run every tab NATIVE spine · compiled dispatch_compsys → Rust fn ptr machine code, in the binary · no wordcode walk router.rs:57 · compcore.rs:776 FS SCAN resolve leaf scan $fpath for _git 51 dirs · stat per dir until hit against a ~48k-file completion corpus MMAP resolve leaf rkyv shard · mmap zero-copy rkyv-mmap'd shards ~/.zshrs/*.rkyv · no $fpath walk getfpfunc → try_dump_file · exec.rs:811 PARSE load + run · interpreted autoload + parse _git, then _arguments → _describe → _tags parse on first tab · interpret specs JIT load + run · bytecode fusevm bytecode + JIT hot chunks → native · .fjit disk cache warm across launches · Cargo.toml:103 emit compadd C builtin NATIVE emit bin_compadd native matches on screen

Same flow, expanded to every frame — the shapes are the point: zsh descends a deep interpreted stack; zshrs forks once at dispatch_compsys into native fn-pointers and a fusevm-Chunk leaf.

ZSH — INTERPRETED STACK (deep) ZSHRS — NATIVE DISPATCH + VM LEAF (shallow) docompletion callcompfunc compcore.c:544 doshfunc(_main_complete) INTERP _main_complete _complete _normal → _dispatch _git FPATH SCAN + PARSE _arguments _describe · _tags _description · _message compadd C BUILTIN every frame tree-walked · re-run each tab callcompfunc compcore.rs body_runner compcore.rs:766 dispatch_compsys FORK · router.rs:57 NATIVE Some → Rust fn ptrs _main_complete · _complete _normal · _dispatch · _arguments _describe · _tags _description · _message VM else → leaf dispatch_function_call → fusevm Chunk bytecode + JIT ·fjit disk cache · warm bin_compadd NATIVE 3 frames to native/VM · no interpreted descent

This is every autoloaded function, not just completers — in zsh that is the majority of the function surface (all of compsys, ZLE widgets, prompt themes, vcs_info, hooks). On first call zsh must find it in $fpath then lex → parse → compile → interpret it; zshrs mmaps the already-compiled fusevm bytecode straight from the rkyv shard — the whole compile pipeline is skipped.

FIRST CALL OF ANY AUTOLOADED FN · fpath search + compile vs bytecode already in rkyv zsh · every step, on first call search $fpath read file lex parse compile interpret zshrs · bytecode ready in ~/.zshrs/*.rkyv mmap rkyv shard fusevm Chunk skipped — no lex · no parse · no compile run bytecode
Stagezsh — every <tab>zshrs
Spineinterpret ~1,517 shell lines (_main_complete_dispatch)native Rust fn pointer — compiled into the binary
Resolve leafscan $fpath — 51 dirs, stat per dir until hitrkyv-mmap'd shard (~/.zshrs/*.rkyv) + .zwc digest, mmap — no fpath walk
Load + run leafautoload + parse on first tab, interpret specsfusevm bytecode + JIT · .fjit disk cache, warm across launches
Emitcompadd (C builtin)bin_compadd (native)

Sources: callcompfunc compcore.c:544,:991 · body-runner fork compcore.rs:766–782 (native dispatch :776) · router router.rs:57–61 · leaf resolution exec.rs:811 (getfpfunc/try_dump_file) · JIT disk cache Cargo.toml:103. Figures from this machine's fpath depth and completion corpus.

[0x06c] HOT-LOOP EXECUTION — SIGIL SCAN vs JIT

Inside a loop, zsh re-does parameter expansion on every pass: prefork runs stringsubst/paramsubst, which scan each word character-by-character for sigils ($, ${...}, expansion flags) and re-expand, then the wordcode is walked again. The scan is O(tokens) work repeated ×N. zshrs resolves sigils to fusevm bytecode ops once at compile time; the hot loop is then JIT-compiled to native code (Cranelift, cached in .fjit), so each pass runs machine code with no re-scan.

ZSH — INTERPRET, RE-SCAN EVERY PASS ZSHRS — COMPILE ONCE, JIT THE HOT LOOP parse → wordcode (token strings, unresolved) compile → fusevm bytecode (sigils → ops) ONCE loop body ×N iterations loop body ×N iterations walk wordcode sigil scan each token ×N prefork / paramsubst expand glob exec sigils re-scanned every pass · O(tokens) × N fused loop ops (AccumSumLoop…) hot? → tracing JIT · Cranelift JIT run native (·fjit cached) no per-token sigil scan in the loop — expansion resolved once at compile time, hot path runs as cached native code scanned once at compile · O(1) per pass
Per loop passzshzshrs
Sigil / expansion scanre-scanned each pass (stringsubst / paramsubst)resolved to ops once at compile
Dispatchwalk wordcode (prefork → exec)fused loop ops, single-dispatch
Hot loopinterpreted every iterationtracing JIT → native (Cranelift), .fjit cached
Cost per passO(tokens)O(1)

Sources: zsh per-exec expansion prefork Src/exec.c:2545, stringsubst/paramsubst Src/subst.c · zshrs tracing JIT src/fusevm_bridge.rs:1203–1212 (enable_tracing_jit), fused loop ops (AccumSumLoop, SlotIncLtIntJumpBack).

[0x07] EXCLUSIVE BUILTINS

Parallel Primitives (VM-executed, zero fork)

BuiltinDescription
async / awaitShip work to pool, collect result
pmapParallel map with ordered output — compiles to bytecode, runs on VM, zero forks
pgrepParallel filter — compiles to bytecode, runs on VM, zero forks
peachParallel for-each, unordered — compiles to bytecode, runs on VM, zero forks
barrierRun all commands in parallel, wait for all

AOP / Debugging

BuiltinDescription
interceptAOP before/after/around advice on any command. Glob pattern matching. Nanosecond timing.
intercept_proceedCall original command from around advice.
doctorFull diagnostic: worker pool metrics, cache stats, bytecode coverage, startup health.
dbviewBrowse the daemon's catalog.db mirror without SQL. Tables: autoloads, comps, executables, history, plugins. Hot path uses rkyv mmap directly.
profileIn-process command profiling. Nanosecond accuracy. No fork overhead in measurement.

Unit Test Framework

Port of the strykelang test framework (stryke testzshrs --ztest). Worker-pool runner — one persistent --ztest-worker subprocess per CPU, fork-on-receive per test file, JSON-over-pipe wire protocol, per-test fd 2 capture so concurrent workers can’t tear each other’s lines. Test discovery: test_* / t_* prefix × .zsh/.sh/.zshrs suffix under t/ or tests/.

BuiltinDescription
zassert_eq / zassert_neString equality / inequality
zassert_ok / zassert_err / zassert_true / zassert_falseTruthiness (non-empty AND not "0" → truthy)
zassert_gt / zassert_lt / zassert_ge / zassert_leNumeric ordering
zassert_matchRegex match (Rust regex syntax)
zassert_containsSubstring containment
zassert_nearFloat approximate equality (epsilon)
zassert_diesPasses when given shell command exits non-zero
ztest_skipMark current assertion skipped (yellow ↓)
ztest_run / run_testsPrint summary, roll counters into totals
zshrs --ztest [-j N] [-q] [paths…]Worker-pool runner (default workers = num_cpus)
zshrs --ztest-workerPersistent worker subprocess (JSON over stdin/stdout)

Coreutils (Anti-Fork)

BuiltinDescriptionSpeedup vs fork
catConcatenate files — no fork2000-5000x
head / tailFirst/last N lines — no fork2000-5000x
wcLine/word/char count — no fork2000-5000x
sort / uniqSort and dedupe — no fork2000-5000x
findWalk directories — no fork2000-5000x
cut / tr / revText manipulation — no fork2000-5000x
seq / teeNumber sequences, copy stdin — no fork2000-5000x
dateCurrent date/time — direct strftime3000-8000x
sleepDelay — std::thread::sleep2000-5000x
mktempCreate temp file/dir — no fork2000-5000x
hostname / unameSystem info — direct syscall3000-8000x
id / whoamiUser info — direct syscall3000-8000x
touch / realpathFile ops — no fork2000-5000x
basename / dirnamePath manipulation — no fork2000-5000x
zgetattr / zsetattrxattr ops — direct syscall2000-5000x

[0x07b] SHELL LANGUAGE FEATURES

Every shell construct compiles to fusevm bytecode — no tree-walker dispatch lives in zshrs. The categories below are summaries; the full reference documents each entry with a runnable code example.

Control Flow

if, while, until, for, for ((;;)), case, select, coproc, break, continue, return, ;/&&/||/&.

Indexed Arrays

arr=(a b c), arr+=(d), ${arr[1]} (1-based), ${arr[-1]}, ${arr[@]} (argv splice), ${#arr[@]}.

Associative Arrays

typeset -A m, m[key]=val, ${m[key]}, ${(k)m} (keys), ${(v)m} (values).

Parameter Expansion

${var:-x}, ${var:=x}, ${var:?msg}, ${var:+x}, ${#var}, ${var:o:l}, ${var#pat}, ${var/pat/repl}, ${var:u}/:l.

Zsh Flags

(L)/(U) case, (j: :) join, (s. .) split, (f) newline-split, (o)/(O) sort, (P) indirect, (@) force-array, (k)/(v), (#). Stack: (jL), (s:,:U).

Redirects & Pipelines

> >> < <<EOF <<< 2>&1 &> &>> | |& ! <(cmd) >(cmd).

Background & Async

cmd & (fork + setsid), $!, wait, jobs/fg/bg; async/await (worker pool, no fork).

Coprocesses

coproc { body } creates two pipes, forks, registers $COPROC=[read_fd, write_fd]. Read from /dev/fd/${COPROC[1]}, write to /dev/fd/${COPROC[2]}.

Eval & Indirect Dispatch

eval 'echo $x' defers expansion correctly (single-quoted specials honored). cmd=ls; $cmd routes through host intercepts.

Arithmetic

$((expr)), (( cond )), let; full integer expression grammar compiled inline (no runtime parser).

Glob & Brace

*.rs, **/*.rs (parallel walk), {a,b,c}, {1..10}, glob qualifiers *(.x) *(N).

Tilde & Cmd-Sub

~, ~user, ~+/~-; $(cmd) (in-process pipe-capture), backticks.

For the complete catalog — every supported builtin, keyword, parameter-expansion form, ZshFlag, AOP primitive, parallel primitive, and anti-fork coreutils replacement with a code example for each — see the FULL REFERENCE.

[0x08] INSTALL

# Via Homebrew tap (auto-bumped by each release) brew tap MenkeTechnologies/menketech brew install zshrs # core: zshrs + zd # OR the umbrella: zshrs + zd + zshrs-recorder + zshrs-daemon brew install zshrs-all # From crates.io cargo install zshrs # From source — lean build, pure shell, no stryke dependency git clone https://github.com/MenkeTechnologies/zshrs cd zshrs && cargo build --release # target/release/zshrs
# Set as login shell sudo sh -c 'echo "$(which zshrs)" >> /etc/shells' chsh -s "$(which zshrs)" # Tab completion for zshrs itself cp completions/_zshrs /usr/local/share/zsh/site-functions/

[0x09] DIAGNOSTICS

# Full health check zshrs --doctor # In-session diagnostics doctor # Browse cache dbview dbview autoloads _git # Profile a command profile 'compinit' profile -s 'for i in {1..1000}; do echo $i > /dev/null; done' # Show intercepts intercept list

[0x09c] GRAMMAR & SYNTAX — CANONICAL TABLES

Every reserved keyword, builtin, setopt option, special variable, parameter flag, glob qualifier, operator, history expansion, and word modifier recognized by zshrs. Sourced directly from upstream zsh C tables (Src/hashtable.c::reswds[], Src/builtin.c::builtins[], Src/Modules/*.c, Src/options.c::optns[], Src/params.c::special_params[]) plus the zshrs extension builtins in daemon/builtins.rs + src/extensions/. Single source of truth lives at data/grammar/canonical.json; regenerate this section via python3 scripts/gen_grammar_docs.py.

50
Keywords
157
Builtins
196
Options
88
Special vars
38
Param flags
44
Glob quals
55
Operators
10
History exp.
16
Modifiers

Generated 2026-05-31 from data/grammar/canonical.json.

Reserved keywords (50)

Source: src/zsh/Src/hashtable.c::reswds[] + zshrs lexer extensions.

Control flow (20)

[[ ]] always case do done elif else end esac fi for foreach if in repeat select then until while

Declaration (10)

declare export float integer let local readonly set shift typeset

Function (1)

function

Grouping (2)

{ }

I/O / source (5)

. eval exec source trap

Loop control (5)

break continue exit logout return

Modifier (precommand) (6)

builtin command coproc nocorrect noglob time

Operator-like (1)

!

Builtins (157)

Every command implemented inside the shell — no fork/exec required.

POSIX / zsh core (74)

Source: src/zsh/Src/builtin.c::builtins[].

. : [ alias autoload bg break bye cd chdir continue declare dirs disable disown echo emulate enable eval exit export false fc fg float functions getln getopts hash hashinfo history integer jobs kill let local logout mem patdebug popd print printf pushd pushln pwd r read readonly rehash return set setopt shift source suspend test times trap true ttyctl type typeset umask unalias unfunction unhash unset unsetopt wait whence where which zcompile zmodload

zsh modules (59)

Source: src/zsh/Src/Modules/*.c (curses, datetime, pcre, files, stat, terminfo, zftp, zselect, zsystem, zutil, …).

cap chgrp chmod chown clone echotc echoti example getcap ln local log mkdir mv nameref pcre_compile pcre_match pcre_study private rm rmdir setcap stat strftime sync syserror sysopen sysread sysseek syswrite zcurses zdelattr zf_chgrp zf_chmod zf_chown zf_ln zf_mkdir zf_mv zf_rm zf_rmdir zf_sync zformat zftp zgdbmpath zgetattr zlistattr zparseopts zprof zpty zregexparse zselect zsetattr zsocket zstat zstyle zsystem ztcp ztie zuntie

zshrs extensions (24)

Source: daemon/builtins.rs + src/extensions/ext_builtins.rs.

zask zcache zcompdump zcomplete zd zhistory zid zjob zlock zlog zls znotify zping zpublish zsend zsource zsubscribe zsuggest zsync ztag zunsubscribe zuntag zwc zwhere

setopt options (196)

Source: src/zsh/Src/options.c::optns[]. All upstream zsh options recognized by name; use setopt / unsetopt to toggle, or NO_ prefix (e.g. NO_GLOB).

ALIASES ALIASFUNCDEF ALLEXPORT ALWAYSLASTPROMPT ALWAYSTOEND APPENDCREATE APPENDHISTORY AUTOCD AUTOCONTINUE AUTOLIST AUTOMENU AUTONAMEDIRS AUTOPARAMKEYS AUTOPARAMSLASH AUTOPUSHD AUTOREMOVESLASH AUTORESUME BADPATTERN BANGHIST BAREGLOBQUAL BASHAUTOLIST BASHREMATCH BEEP BGNICE BRACECCL BRACEEXPAND BSDECHO CASEGLOB CASEMATCH CASEPATHS CBASES CDABLEVARS CDSILENT CHASEDOTS CHASELINKS CHECKJOBS CHECKRUNNINGJOBS CLOBBER CLOBBEREMPTY COMBININGCHARS COMPLETEALIASES COMPLETEINWORD CONTINUEONERROR CORRECT CORRECTALL CPRECEDENCES CSHJUNKIEHISTORY CSHJUNKIELOOPS CSHJUNKIEQUOTES CSHNULLCMD CSHNULLGLOB DEBUGBEFORECMD DOTGLOB DVORAK EMACS EQUALS ERREXIT ERRRETURN EVALLINENO EXEC EXTENDEDGLOB EXTENDEDHISTORY FLOWCONTROL FORCEFLOAT FUNCTIONARGZERO GLOB GLOBALEXPORT GLOBALRCS GLOBASSIGN GLOBCOMPLETE GLOBDOTS GLOBSTARSHORT GLOBSUBST HASHALL HASHCMDS HASHDIRS HASHEXECUTABLESONLY HASHLISTALL HISTALLOWCLOBBER HISTAPPEND HISTBEEP HISTEXPAND HISTEXPIREDUPSFIRST HISTFCNTLLOCK HISTFINDNODUPS HISTIGNOREALLDUPS HISTIGNOREDUPS HISTIGNORESPACE HISTLEXWORDS HISTNOFUNCTIONS HISTNOSTORE HISTREDUCEBLANKS HISTSAVEBYCOPY HISTSAVENODUPS HISTSUBSTPATTERN HISTVERIFY HUP IGNOREBRACES IGNORECLOSEBRACES IGNOREEOF INCAPPENDHISTORY INCAPPENDHISTORYTIME INTERACTIVE INTERACTIVECOMMENTS KSHARRAYS KSHAUTOLOAD KSHGLOB KSHOPTIONPRINT KSHTYPESET KSHZEROSUBSCRIPT LISTAMBIGUOUS LISTBEEP LISTPACKED LISTROWSFIRST LISTTYPES LOCALLOOPS LOCALOPTIONS LOCALPATTERNS LOCALTRAPS LOG LOGIN LONGLISTJOBS MAGICEQUALSUBST MAILWARN MAILWARNING MARKDIRS MENUCOMPLETE MONITOR MULTIBYTE MULTIFUNCDEF MULTIOS NOMATCH NOTIFY NULLGLOB NUMERICGLOBSORT OCTALZEROES ONECMD OVERSTRIKE PATHDIRS PATHSCRIPT PHYSICAL PIPEFAIL POSIXALIASES POSIXARGZERO POSIXBUILTINS POSIXCD POSIXIDENTIFIERS POSIXJOBS POSIXSTRINGS POSIXTRAPS PRINTEIGHTBIT PRINTEXITVALUE PRIVILEGED PROMPTBANG PROMPTCR PROMPTPERCENT PROMPTSP PROMPTSUBST PROMPTVARS PUSHDIGNOREDUPS PUSHDMINUS PUSHDSILENT PUSHDTOHOME RCEXPANDPARAM RCQUOTES RCS RECEXACT REMATCHPCRE RMSTARSILENT RMSTARWAIT SHAREHISTORY SHFILEEXPANSION SHGLOB SHINSTDIN SHNULLCMD SHOPTIONLETTERS SHORTLOOPS SHORTREPEAT SHWORDSPLIT SINGLECOMMAND SINGLELINEZLE SOURCETRACE STDIN SUNKEYBOARDHACK TRACKALL TRANSIENTRPROMPT TRAPSASYNC TYPESETSILENT TYPESETTOUNSET UNSET VERBOSE VI WARNCREATEGLOBAL WARNNESTEDVAR XTRACE ZLE

Special variables (88)

Source: src/zsh/Src/params.c::special_params[]. Includes scalar ($?, $RANDOM) and array forms ($path, $pipestatus); upper-case and lower-case names are linked pairs where applicable.

$! $# $$ $* $- $0 $? $@ $ARGC $CDPATH $COLUMNS $EGID $ERRNO $EUID $FIGNORE $FPATH $FUNCNEST $GID $HISTCHARS $HISTCMD $HISTSIZE $HOME $IFS $KEYBOARD_HACK $LANG $LC_ALL $LC_COLLATE $LC_CTYPE $LC_MESSAGES $LC_NUMERIC $LC_TIME $LINENO $LINES $MAILPATH $MANPATH $MODULE_PATH $NULLCMD $OPTARG $OPTIND $PATH $POSTEDIT $PPID $PROMPT $PROMPT2 $PROMPT3 $PROMPT4 $PS1 $PS2 $PS3 $PS4 $PSVAR $RANDOM $READNULLCMD $RPROMPT $RPROMPT2 $RPS1 $RPS2 $SAVEHIST $SECONDS $SHLVL $SPROMPT $TERM $TERMINFO $TERMINFO_DIRS $TRY_BLOCK_ERROR $TRY_BLOCK_INTERRUPT $TTYIDLE $UID $USERNAME $WORDCHARS $ZLE_RPROMPT_INDENT $ZSH_EVAL_CONTEXT $ZSH_SUBSHELL $_ $argv $cdpath $fignore $fpath $histchars $mailpath $manpath $module_path $path $pipestatus $prompt $psvar $status $zsh_eval_context

Parameter expansion flags (38)

Single-letter modifiers inside ${(X)var}. Composable: ${(jL)arr} = join lowercase.

FlagMeaning
${(@)var}array-context retain $@ semantics; quoting-preserving even in scalar context
${(A)var}create as an array
${(a)var}sort by array index
${(c)var}count words in a parameter (e.g., scalar split count)
${(C)var}capitalize words
${(D)var}treat as DIRECTORY name (apply directory substitution like ~/...)
${(e)var}perform parameter expansion / arithmetic / etc. on the result
${(f)var}split result at newlines
${(F)var}join array elements with newlines
${(g)var}process escape sequences like print does (g:o: process octals, g:c: process \c)
${(i)var}case-insensitive sort
${(j)var}join array with separator: ${(j:sep:)arr}
${(k)var}for assoc arrays: keys ${(k)hash}
${(K)var}subscript flags: use keys
${(L)var}lowercase
${(M)var}match: use longest match (also for case-insensitivity in sort)
${(n)var}numeric sort
${(o)var}sort ascending
${(O)var}sort descending
${(p)var}interpret embedded escape sequences in j/s separator
${(P)var}treat value as parameter name → indirect (P)
${(q)var}quote the result (q-/q+/qq/qqq variants for shell-quote levels)
${(Q)var}remove quoting
${(r)var}right-justify within field width: ${(r:N::pad:)var}
${(l)var}left-justify within field width: ${(l:N::pad:)var}
${(s)var}split at separator: ${(s:sep:)var}
${(S)var}subscript: search subscript ranges
${(t)var}test parameter type
${(u)var}unique (dedupe array)
${(U)var}uppercase
${(v)var}for assoc arrays: values ${(v)hash}
${(V)var}make invisible / control chars visible
${(w)var}split into words
${(W)var}split into words (alternate)
${(z)var}split as the shell would (z-tokens)
${(#)var}expand result as arithmetic; numeric value
${(%)var}expand prompt percent escapes in result
${(~)var}treat values as patterns (e.g., for /pat/repl)

Glob qualifiers (44)

Modifiers inside *(X). Composable; use ^ to negate the group, , to OR-combine groups.

QualMeaning
*(/)directories only
*(.)regular files only
*(@)symbolic links only
*(=)sockets only
*(p)named pipes (FIFOs) only
*(*)executable plain files only
*(%)device special files only
*(%b)block special files
*(%c)character special files
*(r)owner-readable
*(w)owner-writable
*(x)owner-executable
*(A)group-readable
*(I)group-writable
*(E)group-executable
*(R)world-readable
*(W)world-writable
*(X)world-executable
*(s)setuid (S_ISUID)
*(S)setgid (S_ISGID)
*(t)sticky bit
*(d N)device number N
*(l[+-=]N)exactly / less-than / greater-than N hard links
*(U)owned by EUID
*(G)owned by EGID
*(u N)owned by uid N
*(g N)group gid N
*(f spec)permission mask: f:o+w: e.g.
*(L [+-=] N)size: blocks / k / m / p suffix (Lk / Lm / Lp)
*(a [Mwhms] [+-=] N)access time
*(m [Mwhms] [+-=] N)modify time
*(c [Mwhms] [+-=] N)ctime
*(o [name|size|links|mtime|atime|ctime])sort ascending
*(O [name|size|links|mtime|atime|ctime])sort descending
*([N,M])select range of matches
*(e:str:)external test: each match passed to expression
*(+func)external test: each match passed to function
*(N)nullglob: silently drop unmatched pattern
*(D)include dotfiles
*(Y N)limit to N results
*(M)include directory names as if trailing slash
*(:mod)apply history modifier (e.g., :t :h :r :e)
*(^)negate the qualifier list
*(,)OR-combine qualifier groups

Operators / redirections / substitution forms (55)

Lexer-recognized tokens. Grouped by kind: pipeline / list / case / redirect / procsub / subst / arith / cond / assign / compare / glob / tilde / brace / expansion / string / extension.

SymbolKindMeaning
|pipelinePipeline. stdout of LHS → stdin of RHS.
|&pipelinePipeline with stderr merged (= |2>&1).
&&listLogical AND: run RHS only if LHS exit==0.
||listLogical OR: run RHS only if LHS exit!=0.
;listSequence: run RHS after LHS regardless of status.
&listBackground: run LHS async; sets $!.
;;caseEnd case arm.
;;&caseFall through and test next case pattern.
;|caseFall through without test.
!negNegate exit status (reserved word).
>redirectStdout redirect (overwrite).
>>redirectStdout append.
<redirectStdin redirect.
<<redirectHeredoc; body terminated by marker.
<<-redirectHeredoc, strip leading tabs from body.
<<<redirectHere-string; literal text as stdin.
>|redirectStdout force-overwrite (bypass NO_CLOBBER).
>!redirectSame as >|; force-overwrite.
&>redirectRedirect both stdout and stderr (bash-compat).
&>>redirectAppend both stdout and stderr.
2>&1redirectDuplicate fd2 to fd1 (stderr → stdout).
>&-redirectClose fd.
<>redirectOpen for read+write.
<(procsubProcess substitution: <(cmd) is a path readable from cmd's stdout.
>(procsubProcess substitution: >(cmd) is a path writable into cmd's stdin.
=(procsubZsh-only =(cmd): tempfile capture.
$(substCommand substitution: $(cmd) captures cmd's stdout.
${substParameter expansion: ${var}.
$((substArithmetic expansion: $((expr)).
((arithArithmetic command. ((expr)) exits 0 iff expr != 0.
))arithClose arithmetic command.
[[condOpen conditional command: [[ expr ]].
]]condClose conditional command.
=assignAssignment. Also: equality in [[ ]].
+=assignAppend assignment (scalar concat / array push).
-=assignNumeric subtract-assign (in (( ))).
:=assign${var:=default}: assign default if unset/empty.
?=assign(arith) ternary.
==compareEquality in (( )) / [[ ]].
!=compareInequality.
=~compareRegex match in [[ ]] (POSIX ERE / PCRE depending on opts).
*globGlob: match any sequence (including empty).
**globRecursive glob (matches dir/subdir/.../ levels).
?globGlob: match one character.
~tildeTilde expansion: ~ → $HOME, ~user, ~+ / ~- / ~N for dirstack.
{a,b,c}braceBrace expansion: comma-separated list.
{1..10}braceBrace expansion: numeric range.
{a..z}braceBrace expansion: character range.
${~var}expansionTreat result of var as pattern.
${^var}expansionArray element rcexpansion.
${=var}expansionWord-split on IFS.
$'…'stringANSI-C quoted string: \n \t \xNN etc.
$"…"stringLocale-translated string.
`…`substBacktick command substitution (legacy form of $()).
@{}extensionZshrs @-prefix: dispatch to stryke embedded scripting.

History expansions (10)

Recall previous commands / words. Lexer-stage expansion before parsing.

FormMeaning
!!previous command
!Ncommand N in history
!-Ncommand N back
!?strmost recent containing str
!strmost recent starting with str
!$last word of previous command
!^first arg of previous command
!*all args of previous command
!:NNth word of previous command
^old^newquick substitute in previous command

Word modifiers (16)

Trailing :X modifiers applied to history words, filename expansions, and parameter results.

ModifierMeaning
:hhead: dirname of path
:ttail: basename of path
:rroot: strip extension
:eextension: keep only extension
:llowercase
:uuppercase
:qquote for shell re-input
:Qremove quoting
:s/old/new/substitute first match
:gs/old/new/global substitute
:aabsolutize (resolve relative path)
:Aabsolutize and resolve symlinks
:Pphysical resolved path
:xsplit into words on whitespace
:wselect words
:Ffollow symlinks (in conjunction with above)

[0xFF] THE PHILOSOPHY

Shells haven't fundamentally improved since the 1990s. bash is a GNU rewrite of the Bourne shell from 1979. zsh added features but kept the fork-based C architecture. fish focused on UX and abandoned POSIX. nushell reinvented the data model but lost compatibility.

zshrs takes a different approach: keep everything that makes zsh powerful — glob qualifiers, parameter expansion flags, the completion system, ZLE, zstyle, modules — and replace the runtime with modern systems engineering. Rust instead of C. Thread pool instead of fork. rkyv mmap'd zero-copy archives instead of flat files. Bytecode VM instead of tree-walker. AOP instead of monkey-patching.

The result is the first shell that gets faster as you add more plugins, because the plugin cache means each plugin is only parsed once. The first shell where **/*.rs scales with your CPU count. The first shell where you can intercept any command with nanosecond-accurate timing and zero overhead.

The result is the first shell where every command — interactive or scripted — compiles to bytecodes and executes on a VM with fused superinstructions. The first shell where autoload functions load from pre-compiled bytecodes in microseconds. The first shell where source ~/.zshrc can skip the lexer, parser, and compiler entirely because the bytecodes are mmap'd zero-copy from rkyv archives the daemon already validated.

Since the Bourne shell at Bell Labs in 1970, through csh, ksh, bash, zsh and fish, no Unix shell has emitted native machine code. Nushell reached bytecode in 2024 (IR in 0.96.0, default in 0.98.0) and still interprets it, per parse, with no on-disk cache. zshrs is the first to JIT to native code and the first to persist its bytecode across processes. Shell scripts at machine code speed. Achieved in alpha.

THE FIRST JIT-COMPILED UNIX SHELL. THE MOST POWERFUL SHELL EVER CREATED.