Changelog
Canonical Quirl project documentation synced from CHANGELOG.md.
Notable user-visible changes to Quirl are recorded here. The format follows Keep a Changelog, and releases use Semantic Versioning.
Content under [Unreleased] remains candidate work. Release preparation moves
it into a versioned entry for review; publication follows only after the exact
candidate and artifacts pass the release checklist.
The GitHub Releases page records
which versions are published.
[Unreleased]
Fixed
- Errors a
$(...)substitution writes to standard error now appear, as in Bash and Zsh. They were discarded, so a failingeval "$(ssh-agent -s)"ended silently.
[0.5.1] - 2026-10-04
Fixed
eval "$(ssh-agent -s)"in the interactive shell could lose the agent it started, sossh-addthen reported "Connection refused". The terminal cleanup that ends a foreground command could kill the agent before it detached.evalandsourcenow run outside that cleanup, so daemons they start keep running.- Typing a pasted loop or
{ ...; }group no longer shows an "unsupported dialect control form" error while it runs fine. The editor shows a quiet hint that the line runs through/bin/sh; function definitions keep an error that explains why they cannot persist. - Tab no longer ranks a fuzzy catalog match above a candidate that extends
what you typed from Zsh:
git log --oneoffers--onelinealone.
[0.5.0] - 2026-10-04
Added
- Quirl works as a login shell for SSH, file transfer, Git, editors, and
agents.
quirl -c 'code' [name [arg...]]runs the code exactly as/bin/sh -cwould, sossh host cmd,scp,rsync, and Git over SSH work against a host whose shell is Quirl.-l,--login,-i,-s, and clusters such as-ilcare accepted, as editors use to read your environment. - An interactive login session (
-quirlas argv0, orquirl -l) adopts the environment/bin/sh -lsets up from/etc/profileand~/.profile, such as macOSpath_helperentries. evalandsource(or.) run shell setup code in/bin/shand keep the exported variables and working directory it leaves behind.eval "$(ssh-agent -s)",eval "$(brew shellenv)",source .venv/bin/activate, andsource .envnow work. Functions and aliases the code defines stay inside it.- Pasted loops, conditionals,
{ ...; }groups,[[ ... ]], and brace expansion such as{a,b}run in Normal mode without abash { ... }wrapper. Quirl runs the line in/bin/shand keeps the exports and working directory it leaves. Function definitions explain that functions do not persist. $PPIDnames the shell's parent process.helpcompletes command names instead of file names.clearempties the interactive shell's output, and Ctrl-L clears the screen as in Zsh while earlier output stays reachable with PageUp. Both used to leave the transcript in place.- Data tables in the interactive shell color their values: numbers and sizes in the number color, times in the secondary color, record keys in the accent, and row numbers, empty values, and nested summaries dimmed.
NAME=valuesets a shell variable,NAME=value commandsets a variable for one command,export NAMEexports an existing variable, andunset NAMEremoves one.- Data mode speaks more of Nushell's vocabulary:
sort-by <field> [-r],first <n>,last [n],skip <n>,reverse,reject <field>...,uniq,group-by <field>,columns, andmath sum|min|max|avg.mathadds exactly, without floating-point rounding, and sizes keep their unit. whereaccepts size literals:ls | where size > 10kB. Decimal (kB,MB) and binary (KiB,MiB) units are understood.- Tab completes
docker,kubectl,gh,helm,podman, and other Cobra-based tools by asking the program itself (PROGRAM __complete) when Zsh has no function for it. Only a fixed list of known Cobra programs is asked, within 1.5 seconds; setQUIRL_TOOL_COMPLETION=offto disable it. - Live Zsh completion finds more completion functions: rustup's
_cargo(cargo boffersbuildandbench), Debian'svendor-completions, Nix profiles, and the per-user~/.zfunc,~/.zsh/completions, and~/.local/share/zsh/site-functionsdirectories.
Changed
- Quirl builds with Rust 1.99.0. Captured command output is decoded without an extra copy of the bytes.
${NAME:=word}and${NAME=word}set an unexported shell variable, as insh, instead of exporting it.- Subcommands such as
quirl catalog | headend quietly with status 141 when the reader closes the pipe, instead of panicking. - Git branches and other Zsh answers that contain
/no longer show a folder icon unless they name an existing path. - Comparing values of different kinds explains itself, such as "cannot
compare a size with a string", and suggests writing
10kBwithout quotes. - A lone size or time, such as the result of
math sumover file sizes, prints like its table cell (213.1 kB) instead of raw bytes. - A script piped into a bare
quirlnow runs as POSIX shell code through/bin/sh -s, as with any shell. It used to run as Lua; usequirl run --lang lua -for that. - Data mode in the interactive shell renders records as tables, as
quirl dataalready did, instead of one JSON line per row. Record streams render in batches of up to 256 rows, so the first rows appear early. - Data tables fit the terminal. Wide columns shrink and end in
…, and columns that still do not fit collapse into one…column. Redirectedquirl dataoutput keeps every cell intact. - A single record renders as a vertical key/value table, and nested values in
cells read
{record 2 fields},[table 3 rows], or[list 4 items]instead of raw JSON. lstables show thekindcolumn with its real values, such asdirectory, sowhere kind == "directory"filters what the table shows. The column was labeledtypeand abbreviated values todirandlink.quirl completenow runs the interactive completion engine, so it lists what Tab offers, including directories forcdand$VARIABLEnames.
Fixed
openkeeps the field order of JSON and TOML documents, as YAML already did, instead of sorting fields alphabetically, so table columns appear in the order the file lists them.mode dand other commands whose argument has a fixed set of values no longer offer file names next to those values. Their menu descriptions read "One ofnormal,data,ai, ortoggle" instead of internal wording.
[0.4.0] - 2026-09-30
Added
- Tab completes like Zsh. A unique match is inserted at once with a trailing
space, or a
/for directories. Ambiguous matches extend the word by their shared prefix while the menu lists the alternatives. Accepting a menu entry adds the separating space too. Further Tabs cycle through the menu and put each candidate on the line, like Zsh's menu completion. - Explicit Tab asks the installed Zsh's own completion functions for argument
values. Git branches, Make targets, SSH hosts, process IDs, and every other
Zsh-supported argument now complete in Quirl, with Zsh's descriptions in the
menu. Requests run inside the existing bounded completion boundary without
user startup files, and automatic as-you-type completion never starts a
shell. Results keep Zsh's display order: groups in creation order, each
sorted unless Zsh added it unsorted. When Zsh answers, its choice replaces the plain file listing,
so
git addoffers changed files only. SetQUIRL_ZSH_COMPLETION=offto disable the live source. quirl completeincludes the same live Zsh argument candidates.$NAMEand${NAMEcomplete environment variable names, showing each value. As with Zsh'sAUTO_PARAM_SLASH, a variable that names a directory completes with a trailing/.
Changed
- Completion ranks exact-case prefixes above case-insensitive prefixes and
fuzzy matches, and shows only the best tier, alphabetically with an exact
match first. Command completion advances one word at a time:
gioffersgit, not everygit …subcommand. - Ctrl-C at the rich prompt leaves the abandoned input in the transcript,
marked
^C, and sets$?to 130, as in Zsh. It no longer prints a separate cancellation block.
Fixed
- A command could hang forever on macOS when another thread started a process at the same moment: a pipe that was briefly inheritable leaked into the other child, so the reader never saw end-of-file. Process launches and pipe and terminal creation are now serialized process-wide.
- Catalog discovery no longer fails on typical Homebrew installations. Fingerprinting and importing completion sources used to charge the same bytes twice against one budget. An unreadable or oversized completion source is now skipped with a diagnostic instead of disabling all discovered completions.
quirl index buildfollows symlinked completion files, as automatic discovery already did, and tolerates man pages whose searchable description exceeds 16 KiB.- The completion cache can live below root-owned system links such as macOS's
/tmpand/var. Links owned by other users are still rejected. - Command completion no longer offers shell expressions such as
$1or2>/dev/nullthat Bash completion scripts register as computed names. - Valid flags such as
git log --onelineno longer draw an "unknown flag" warning when a command's options come from an imported, possibly partial, declaration. Only built-in, Lua-declared, and plugin-declared option lists are treated as complete. - BSD man pages (mdoc) produce readable option summaries: nested
Fl Flbecomes--name, and enclosure macros,Ns, standards references, escaped literals, and delimiters render asmandocshows them.
[0.3.0] - 2026-09-06
Added
- Managed Git project cloning through
quirl projects clone, using GHQ-compatible<root>/<host>/<repository-path>directories and existingGHQ_ROOTorghq.rootsettings.quirl projects rootshows the effective parent directory. Existing matching checkouts are reused without pulling or overwriting them. - Rich Normal mode offers an optional managed location for a straightforward
git clone URL. The original command remains the default; users can choose a managed location once, opt in for future eligible clones, or dismiss further suggestions. Explicit destinations and scripts keep their Git behavior. Completed projects are immediately available in the project picker, with an explicit Alt-Q u action to open them.quirl projects policyinspects or setsask,managed, oroffwithout editing Lua configuration.
Fixed
- Interactive native commands retain the previous command's exit status in
$?across prompt turns, including status 130 after cancellation. Internal project metadata probes do not replace the user-visible status. Cancelled Bash and Zsh islands also report 130; deadlines remain ordinary failures. - Rich Normal mode on Unix runs foreground programs in an embedded terminal,
including unknown tools, wrappers,
tdx, andbunx tokscale@latest. Screen redraws, keyboard input, local terminal queries, resizing, and Ctrl-C work without an application allowlist. Explicit pipes and redirections retain their semantics; completed primary-screen output remains in the transcript and unread type-ahead returns as editable prompt text. - Native Unix commands expand unquoted
~and~/from the session home, includingcdarguments and redirection paths, while preserving quoted and escaped literal tildes and enforcing the existing expansion limit. - Filesystem completion accepts Enter immediately and opens children after a
directory selection. Escape then Enter executes the selected directory;
already complete filenames still execute on the first Enter. Home-directory
candidates follow the session's current
HOMEvalue. Background catalog loading preserves an Escape-dismissed popup until the next edit or explicit completion request. - Completion and documentation panels reserve space below the editor, scrolling older transcript lines upward instead of covering recent command output.
- Foreground execution shows output and progress without an empty command prompt or shell input cursor. The next prompt appears only after execution finishes and its output has been drained.
- Plain shell startup skips constructing the command-line option tree. Invocations with arguments retain full validation, and redirected stdin keeps its existing execution behavior.
[0.2.0] - 2026-09-05
Changed
- Quirl now embeds vendored Lua 5.5.1 through
mlua'slua55bindings. The restricted standard library, typed Rust boundary, execution budgets, and supervised-worker containment remain unchanged. Lua 5.5 language incompatibilities apply to scripts, including read-only numeric and genericforcontrol variables.
Added
- Conversational Codex command assistance with typed plans, executable-plan validation, cancellation, and explicit review before shell execution.
- A Miller-column directory explorer and an environment explorer in the rich terminal interface, plus native Quirl LSP completion and Neovim integration.
- A project picker discovers Git repositories in the background, keeps a bounded local cache, and jumps to a selected repository without blocking typing.
- Replayable keyboard-session simulation with seeded workflows, styled terminal captures, resize and Unicode input, precise clipboard-protocol checks, and bounded sustained-session resource checks on macOS and Linux.
- A recorded VHS terminal demo and an asciinema/
asgtext-tour SVG, embedded in the README and website to show semantic completion, native pipelines, typed data, and Lua in action.scripts/record-tour.shreproduces the tour recording;scripts/demo.shtakes an opt-inQUIRL_DEMO_PACE_SECONDSpace for it without changingcargo xtask demo's default (unpaced) behavior. The VHS take emits synchronized GIF, MP4, and WebM assets and gives typed JSON, itsopen/where/sort/selectpipeline, local AI, explicit compatibility boundaries, and measured release evidence their own visual beats. - A feature-tour blog post explains how Normal, Data, and AI modes, explicit Bash compatibility, and the bounded Lua SDK fit together.
Fixed
- Zsh completion initialization skips insecure function directories instead of waiting for an interactive security confirmation inside the capture PTY. Safe completions continue to work within the existing request deadline.
- Model loading reads unknown-token metadata directly instead of serializing the entire tokenizer, preserving inference across all four supported models while avoiding a temporary copy of the vocabulary and processing graph.
- CI verifies coordination retries and asynchronous completion ordering independently of scheduler delays, and gives the real Zsh completion fixture a separate bounded startup budget. Trusted-plugin contract tests use an explicit fixture deadline while retaining production resource limits and verifying registration parity and process-grant rejection. Release jobs now enforce size and latency budgets on every native artifact and preserve the measured results even when a budget fails.
- Release performance sessions isolate project discovery and persisted state from the developer's home while retaining the default enabled behavior. Each sample now shares one total deadline, terminal I/O is nonblocking, and cleanup does not write to a terminal after its child exits. Failed samples stop sampling and leave the evidence gate failed. Successful samples now exit normally after their timing endpoints, within the original deadline, before the next session reuses the fixture state. Failed measurements preserve the exact packaged executable for investigation while continuing to block aggregation and publication.
- Terminal dimension queries use the native platform API directly. Redirected
or unavailable terminals no longer trigger an external
tputfallback during prompt rendering or surface selection. - The rich editor repaints when it regains input control after command execution, removing kernel-echoed type-ahead that could leave stale characters in spaces. Queued input remains intact and ordinary edits retain incremental rendering.
- Contained subprocess groups terminate when their owner's keepalive pipe closes, including abrupt shell termination. Background probes and joined pipeline stages no longer depend on the parent reaching Rust cleanup.
- The recording recipe installs verified native command assets and waits for interactive discovery before rebuilding the local search index. Its search scene requires actual hybrid retrieval and presents suggestions as candidates for review.
- Background catalog lock contention preserves queued refresh generations and local completion requests. A bounded, cancellable retry replaces the lost work and minute-long delay caused by treating contention as completion.
- Rich paste rejects oversized edits atomically. The simple fallback preserves pasted newlines until submission, renders pasted terminal controls safely, bounds source and undo retention, and restores the terminal on input failure.
- Rapid Vi typing followed by Escape preserves source order. Numeric repeats, terminal escape/paste accumulation, input queues and editor batches now have explicit resource bounds; incomplete terminal sequences expire when idle.
- Interactive startup remains usable while command intelligence loads. Help, completion, resizing, retained output, history recovery, and secondary-text contrast now have focused user-session regressions. The project worker now admits its database and publishes cached repositories before scanning, with an explicit loading state in the picker. Initial Git and Rust prompt probes start after the first rich frame is flushed.
- Child-process cancellation, stopped jobs, pipeline cleanup, runtime protocol admission, SQLite history limits, atomic file writes, data parsing, extension boundaries, and plugin persistence reject invalid or excessive input earlier.
- The PTY harness handles bidirectional backpressure without extending deadlines and checks visible labels in reconstructed screens instead of raw byte layout.
- Demo recipes report the actual Lua runtime and distinguish local command search from authenticated Codex planning. Earlier embedded recordings and their performance cards are explicitly labeled as historical.
- The homepage demo now exposes playback controls when autoplay or reduced motion leaves it paused, reserves its aspect ratio, and shares a stable content rail with centered hero copy. The decorative headline treatment no longer moves text. README playback now has an explicit raw GIF plus MP4 link.
scripts/demo.tape's startup wait condition, which previously matched only an officialv0.1.0release build and timed out against development builds; it now matches either build identity. The closing beat now ends on a successfulbash { ... }dialect island instead of the native-mode process-substitution error.
Upgrade notes
- Release binary size is advisory by default, prioritizing shell usability
and retaining all features. Exact byte counts, independent digests, and the
warning above 8 MiB remain; growth without a clear user benefit warrants
review. An explicit
--max-binary-bytesstill enforces a caller maximum. Every latency, identity, and cleanup gate remains unchanged. See ADR 0036. - Linux and macOS remain the supported interactive platforms. Windows and Wasm execution are not part of this release scope.
- Lua extensions should be reviewed for Lua 5.5 language changes. The sandbox still exposes only the restricted SDK and standard libraries.
- Configuration schema v5 adds project-discovery settings. Older configurations
retain their migration path;
quirl config migrate path/to/config.lua --dry-runpreviews the required source changes without rewriting the file. - Resource-limit failures never authorize execution of rejected input. Rich oversized paste preserves the edit; simple-editor input overflow exits the session after restoring terminal modes.
- Accelerated session tests and styled terminal captures are supporting evidence, not a claim of continuous 100-hour uptime or native IME/clipboard compatibility.
[0.1.0] - 2026-08-22
Added
- A native C1-core command graph on Linux and macOS with byte pipelines, redirects, boolean lists, bounded expansions, background jobs, and job control.
- An explicit typed-data mode, shared semantic catalog, completion and picker surfaces, durable history, generated documentation, and a stdio language server.
- One sandboxed Lua 5.4 SDK for configuration, scripts, prompt segments, completions, tests, and trusted in-process plugins.
- Permission-locked plugin packages, typed extension events, live views, recovery records, bounded out-of-process adapter initialization, and typed trusted-Lua plugin command dispatch.
- Plain, Unicode, and opt-in Nerd Font prompt profiles with
NO_COLORandTERM=dumbfallbacks. - A Ratatui inline surface selected by default on capable TTYs, with a Quirl-owned editor, context and status rows, syntax highlighting, advisory diagnostics, documented completion, typed overlays, autosuggestions, and transient prompts that preserve normal scrollback and PTY handoff.
- Reproducible performance gates, a text-only product tour, and a real-PTY demo recipe for release review.
- A repository-local Cargo
xtaskwith typed check, test, SDK, demo, and release commands; no separate task-runner installation is required. - Bounded seeded native C1 generation with reproducible differential comparison against available Bash and Zsh reference shells.
getandwherecell paths accept a non-negative integer segment to index into lists (get items.0.name) and may end with a trailing?to make the path optional, returningNothing(forget) or a non-match (forwhere) instead of failing when a field or index is absent.
Security
- Lua VMs enforce memory, instruction, deadline, and cancellation policies and expose only a restricted standard library.
- Plugin sources are integrity checked and receive only their locked grants; isolated adapters have exact launch grants, deadlines, output bounds, and process-tree containment.
- Terminal-derived and extension-derived text is sanitized before rendering.
Changed
- The capable-TTY default is now the rich Ratatui inline frame. Reedline remains the explicit and automatic simple-terminal fallback; its removal is not part of this change.
- Advanced the release-candidate config contract to v4. It retains v3's validated shared semantic themes, changes the polished defaults to a compact welcome banner, automatic completion after one character, and an active Rust toolchain right-prompt segment, and deterministically migrates unversioned v0 plus explicit v1/v2/v3 configurations before validation. Future versions fail closed.
- Added a release-only website gate with non-mutating generated-mirror
freshness, semantic release-evidence attribution, lint, route type checking,
and a production build. It uses the exact
website/package-lock.jsondependency graph and is not part of narrow Rust-only checks. - Runtime assets now use an exact-version website channel with immutable, content-addressed payloads and per-asset provenance. Completion knowledge can be refreshed independently of binary releases while retaining the compatible command model, required license notice, the previous valid local generation, and strict binary-version, size, format, and SHA-256 admission checks.
Recordis now an insertion-ordered mapping instead of an alphabetized one:selectemits fields in the requested order and table rendering unions columns across rows in first-seen order, instead of both always sorting columns alphabetically.wherenow fails with a diagnostic when a row does not have a field named in its predicate, instead of silently treating the row as a non-match; use the new?optional-path marker to opt back into tolerant matching.quirl config webno longer requires an explicit<file>argument; omitting it defaults to the discoveredconfig.lua(QUIRL_CONFIG_DIR,XDG_CONFIG_HOME, or~/.config/quirl), matching the resolution the shell already uses at startup.- Typing a
quirlCLI-only subcommand name (config,plugin,catalog,sdk) or a barequirldirectly in the interactive shell now fails with a specific hint instead of a generic "not found on PATH" message. - The rich surface now tails bounded stdout and stderr while a foreground command runs. Its input row shows a dimmed animated spinner instead of the prompt indicator, silent commands still repaint elapsed time on liveness ticks, and carriage-return progress updates replace their live line rather than filling the transcript.
Known limitations
- Interactive 0.1 support targets Linux and macOS. Windows remains a best-effort portability target without native terminal validation.
- Here-documents, process substitution, loops, functions, conditionals, and dialect control forms require an explicit Bash or Zsh island.
- Wasm components are validated but not executed, and publishing remains a local dry-run workflow rather than a remote registry.
- The rich and Reedline editor cores intentionally coexist. Rich-keymap parity and a replacement minimal fallback are required before Reedline can be removed.
- Native JSON literals and
from jsonstill normalize field order toserde_json's default (alphabetical) parse order; enabling itspreserve_orderfeature to carry source JSON field order all the way through is a separate, wider-blast-radius change affecting everyserde_json::Valueconsumer in the workspace, not yet made.
Release evidence status — historical. Artifact evidence for measured candidate
23fd5d36907fc816bdafd9aa3c2dcb3afb69feb5and artifact9a893a5f1a0b49d62712f331c88966113d910d94efa9651dc4feffe9fd55b637is historical. Evidence commit14e70939d039d96c195f57452a0e1ec3928194afdocuments that measurement. It is evidence only for that named artifact, not for a later candidate. This historical record does not assert the release-readiness or human-review state of a later candidate.