Contributing to Quirl
Canonical Quirl project documentation synced from CONTRIBUTING.md.
Thanks for helping make Quirl a better shell. Quirl is a fast-moving Rust prototype with a deliberately narrow Unix-first support contract. Small, well-tested changes that extend an existing pattern are especially welcome.
Before you start
- Read
AGENTS.mdfor the crate layering, error, Lua sandbox, generated-artifact, and test rules. These rules apply to human and automated contributions alike. - Check the relevant architecture decisions in
docs/decisionsbefore changing a settled boundary. - Use a public issue or discussion for product proposals. Report suspected
vulnerabilities privately as described in
SECURITY.md.
Quirl targets Linux and macOS for interactive use. Windows portability is kept contract-tested on a best-effort basis, but native Windows terminal behavior is not a release gate. The exact boundary is recorded in ADR 0010.
Set up the workspace
Install Rustup. The repository's rust-toolchain.toml
selects Rust 1.99.0; Cargo builds the repository-local xtask, and no separate
task runner or system Lua installation is needed. On Unix, that Rust-owned
xtask also runs the bounded real-PTY harness in xtask/src/pty.rs and
xtask/src/rich_pty.rs; no separate scripting-language toolchain is required.
Editors that do not bundle Rust Analyzer can use the server and standard-library sources matched to the pinned toolchain. These editor-only components stay optional so CI and command-line-only contributors do not download them:
rustup component add rust-analyzer rust-srcgit clone git@github.com:niklas-heer/quirl.git
cd quirl
cargo xtask check
cargo run -p quirl-clicargo xtask check is the canonical local gate and must pass before every
commit. It checks formatting, Quirl source formatting, Clippy, all workspace
tests, the real-PTY interaction matrix on Unix, and the guest-side Lua tests.
The workspace denies Clippy's pedantic and nursery groups behind an explicit
compatibility allowlist, plus production safety restrictions such as unchecked
casts, panics, placeholder macros, and unreachable branches. Test-only unwrap,
panic, and indexing allowances live in clippy.toml; do not weaken the
workspace policy to make production code compile.
Generated shell cases use a stable seed; replay or expand them with
cargo xtask test --seed <seed> --cases <count>. Stateful compatibility swarms
run with cargo xtask simulate --seed <seed> --sessions <count>, producing an
inspectable JSONL trace. The complete layered strategy is in
docs/testing-strategy.md. Pull requests and pushes
to main run the canonical gate in CI with bounded test parallelism; the larger
replayable Bash/Zsh simulation swarm runs separately each day. Include the local
cargo xtask check result in your pull request so failures are caught before CI.
Prefer package-targeted commands during iteration so research-only workspace
members such as quirl-bench are not rebuilt unnecessarily:
cargo check -p quirl-ui
cargo test -p quirl-ui
cargo build -p quirl-cliAt the workspace root, a bare cargo build defaults to quirl-cli; use an
explicit -p selection or --workspace when another scope is intended.
The default development profile keeps line numbers for workspace backtraces
while omitting dependency debug data. Use cargo build --profile debugging
when a full-debug build is needed.
For a faster edit-test loop, install the optional developer tools and use the checked-in project configuration:
cargo install --locked cargo-nextest --version 0.9.143
cargo install --locked bacon --version 3.25.0
cargo nextest run --workspace
bacon # Clippy on each relevant change
bacon nextest # Isolated tests with slow-test reportingNextest reports any test taking longer than two seconds as slow without killing
it, so timing regressions are visible while each test's own resource and cleanup
limits remain authoritative. Bacon also provides check-all and canonical
jobs. The latter runs the full cargo xtask check gate; neither tool replaces
that required pre-commit check.
The developer-only xtask binary uses color-eyre for readable error chains,
source locations, and panic reports. Set RUST_BACKTRACE=1 when a task failure
needs a backtrace. Product crates continue to return the stable, serializable
quirl_core::ShellError contract.
Useful focused tasks are typed Rust subcommands in xtask:
cargo xtask fmt
cargo xtask lint
cargo xtask test
cargo xtask demo
cargo xtask cleanDebug builds with tests grow to tens of gigabytes. cargo xtask clean removes
debug builds and test binaries but keeps release builds; cargo xtask clean --all
also removes release builds, Rustdoc, and the website's Next.js build.
The release recording is intentionally separate: cargo xtask demo-record <sha256>
consumes the already-built candidate only after verifying its digest and local
VHS/font prerequisites. It never rebuilds the artifact during capture.
Make a change
- Put functionality in the lowest crate that can own it; never invert the dependency graph to make a change compile.
- Reuse
quirl_core::ShellError,LuaPolicy,HOST_API, andCatalog::builtin()rather than introducing parallel mechanisms. - Keep tests beside the implementation in
#[cfg(test)] mod tests, and name them as behavior sentences in snake case. - Add adversarial tests for sandbox, resource-limit, terminal-safety, or parser boundary changes.
- Add a frozen regression before fixing a generated or differential failure, and include its seed and case index in the change description.
- Do not hand-edit
docs/quirl.lua. ChangeHOST_API, runcargo xtask sdk, and check in the regenerated file with its source change. - Do not add a feature flag, dependency edge,
unsafeblock, or significant architecture choice without documenting why it belongs.
For user-visible behavior, check both a normal terminal and the text fallback where practical:
NO_COLOR=1 cargo xtask demo
TERM=dumb NO_COLOR=1 cargo xtask demoPrompt glyphs and color must remain decorative; state and diagnostics need a legible plain-text representation.
Send the change
Use a present-tense Conventional Commit
subject, optionally scoped, such as fix(lua): bound completion output.
Keep commits focused and avoid unrelated generated or formatting churn.
A pull request should say:
- what user or developer problem it solves;
- which public contract, security boundary, or generated artifact it changes;
- what was tested, including the
cargo xtask checkresult; - what remains intentionally out of scope.
Release work follows docs/release-checklist.md.
Passing an older commit or a rebuilt artifact is not evidence for the current
release candidate.