Merge nucleic/sleek-thistle-egret-fyej into dev

This commit is contained in:
2026-07-18 05:19:31 -07:00
commit b6be87b72d
677 changed files with 102939 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
# Reference
These documents serve as reference material for the `brush` project.
* [Configuration file](configuration.md)
* [Experimental features](experimental.md)
* [Integration testing](integration-testing.md)
* [Minimum Supported Rust Version (MSRV) policy](msrv-policy.md)
* [Compatibility](compatibility.md)
+209
View File
@@ -0,0 +1,209 @@
# `bash` Compatibility Reference
This document details `brush`'s compatibility with `bash`, including supported features, known limitations, and how to report issues.
## Overview
`brush` aims for high compatibility with `bash`. We validate this through **1700+ compatibility test cases** that compare behavior against `bash` as an oracle.
**Compatibility snapshot:** Production-ready for most use cases. Your `.bashrc`, aliases, functions, and completions should "just work."
## Fully Supported Features ✅
### Shell Syntax & Control Flow
- `if`/`then`/`elif`/`else`/`fi` conditionals
- `for`, `while`, `until` loops
- Arithmetic `for` loops: `for ((i=0; i<10; i++))`
- `case`/`esac` pattern matching
- `&&`, `||` conditional execution
- Subshells `()` and command grouping `{}`
- Pipelines and pipeline negation `!`
- Coprocesses: `coproc { ... }` and `coproc NAME { ... }`
### Expansions
- Brace expansion: `{a,b,c}`, `{1..10}`, `{a..z}`
- Parameter expansion: `${var:-default}`, `${var:+set}`, `${var#pattern}`, `${var%pattern}`, `${var//find/replace}`, etc.
- Command substitution: `$(cmd)`, `` `cmd` ``
- Arithmetic expansion: `$((expr))`
- Process substitution: `<(cmd)`, `>(cmd)`
- Tilde expansion: `~`, `~user`
- Globbing: `*`, `?`, `[...]`
- Extended globbing: `?(pat)`, `*(pat)`, `+(pat)`, `@(pat)`, `!(pat)`
- `globstar`: `**` recursive matching
### Builtins (50+)
- **I/O:** `echo`, `printf`, `read`, `mapfile`/`readarray`
- **Variables:** `declare`, `local`, `export`, `unset`, `readonly`, `typeset`
- **Control:** `break`, `continue`, `return`, `exit`
- **Navigation:** `cd`, `pushd`, `popd`, `dirs`, `pwd`
- **Jobs:** `jobs`, `fg`, `bg`, `wait`, `kill`
- **Completion:** `complete`, `compgen`, `compopt`
- **History:** `history`, `fc`
- **Testing:** `test`, `[`, `[[`
- **Sourcing & introspection:** `.`, `source`, `eval`, `caller`
- **Misc:** `alias`, `unalias`, `hash`, `type`, `command`, `builtin`, `enable`, `help`, `times`, `ulimit`, `umask`, `trap`, `shopt`, `set`, `shift`, `getopts`
### Arrays
- Indexed arrays: `arr=(a b c)`, `${arr[0]}`, `${arr[@]}`
- Associative arrays: `declare -A map`
- Array slicing: `${arr[@]:start:length}`
- Array operations: `${#arr[@]}`, `${!arr[@]}`, `${!arr[*]}`
### Job Control
- Background execution: `cmd &`
- Suspend/resume: Ctrl+Z, `fg`, `bg`
- Job listing: `jobs`
- Process groups and pipelines
### Dynamic Variables
- `RANDOM`, `SRANDOM`
- `LINENO`, `FUNCNAME`, `BASH_SOURCE`
- `EPOCHSECONDS`, `EPOCHREALTIME`
- `SECONDS`
- `PWD`, `OLDPWD`
- `BASH_VERSINFO`, `BASH_VERSION`
### Programmable Completion
- Compatible with [`bash-completion`](https://github.com/scop/bash-completion)
- Git, Docker, systemctl, etc. completions work out of the box
- `complete`, `compgen`, `compopt` builtins
### Redirection
- Standard: `>`, `>>`, `<`, `2>&1`
- Here documents: `<<EOF`, `<<-EOF` (tab-stripped), `<<<` (here strings)
- File descriptor manipulation: `>&n`, `<&n`, `n>&m`
- Process substitution redirects: `>(cmd)`, `<(cmd)`
- Clobber control: `>|`, `set -o noclobber`
## Partially Supported Features 🔷
### Traps
| Status | Feature |
|--------|----------|
| ✅ | `DEBUG` trap |
| ✅ | `ERR` trap |
| ✅ | `EXIT` trap |
| 🔷 | Signal traps (`SIGINT`, `SIGTERM`, etc.) — in progress |
### Key Bindings (`bind`)
| Status | Feature |
|--------|---------|
| ✅ | Basic `bind` support |
| ✅ | `bind -x` for custom key-bound commands |
| 🔷 | Advanced bind features — in progress |
### Shell Options
| Status | Feature |
|--------|---------|
| ✅ | Common options: `errexit`, `pipefail`, `extglob`, `globstar`, `noclobber`, `nounset`, `failglob` |
| 🔷 | Less common options — in progress |
## Not Yet Supported Features 🚧
These features are on our roadmap but not yet implemented:
### `select` Statement
The `select` builtin for creating menu-driven scripts is not yet implemented.
```bash
# Not yet supported
select opt in "Option A" "Option B" "Quit"; do
case $opt in
"Option A") echo "A";;
"Option B") echo "B";;
"Quit") break;;
esac
done
```
### `wait -n`
The `wait -n` option to wait for the next background job to complete is not implemented.
```bash
# Not yet supported
job1 &
job2 &
wait -n # Wait for whichever finishes first
```
### `BASH_COMMAND` Variable
The special variable `BASH_COMMAND` that contains the currently executing command is currently only available in trap contexts.
### `disown` and `logout`
These job control builtins are not yet implemented.
## Known Edge Cases
These areas have known differences from `bash` in edge cases. Most users won't encounter these, but they're documented for completeness.
### IFS (Input Field Separator)
There are ~10 known edge cases where IFS word splitting behavior differs from `bash`, particularly around:
- Non-whitespace IFS characters and empty field creation
- Mixed whitespace and non-whitespace IFS
- Leading/trailing delimiter handling
### `printf` Format Specifiers
Some advanced `printf` format specifiers behave differently (~8 known cases), particularly features not supported by the underlying `uucore` library.
### Arithmetic Expressions
- Division by zero handling may differ in `errexit` mode
- `$(( exit N ))` syntax edge case
### Aliases
Some complex alias expansion scenarios differ from `bash` (see GitHub issues #57, #286).
## Test Suite Statistics
- **Total test cases:** 1700+
- **Known failures:** ~125
- **Most failures are edge cases** in IFS handling and printf
The test suite runs on every PR and compares behavior against `bash` as an oracle.
## Version Compatibility
`brush` targets compatibility with **`bash` 5.3+**. Behavior may differ from older `bash` versions (3.x, 4.x) in some areas.
## Reporting Compatibility Issues
Found a script that works in `bash` but not in `brush`?
1. **Check existing issues:** [GitHub Issues](https://github.com/reubeno/brush/issues)
2. **Create a minimal reproducer:** Reduce to the smallest failing script
3. **File an issue** with:
- The script or command that fails
- Expected behavior (what `bash` does)
- Actual behavior (what `brush` does)
- Your platform (Linux/macOS/etc.)
## Tracking Progress
- **GitHub Issues:** Track specific compatibility work
- **Test Suite:** 1700+ tests run on every PR
- **This Document:** Updated as features are implemented
## Related Resources
- [`bash` Reference Manual](https://www.gnu.org/software/bash/manual/)
- [POSIX Shell Specification](https://pubs.opengroup.org/onlinepubs/9699919799/)
- [`brush` Test Cases](https://github.com/reubeno/brush/tree/main/brush-shell/tests/cases)
+118
View File
@@ -0,0 +1,118 @@
# Configuration File
brush supports an optional TOML configuration file that allows you to customize shell behavior without command-line arguments.
## File Location
`brush` looks for the configuration file at:
- **Linux/macOS**: `${XDG_CONFIG_HOME}/brush/config.toml`*
- **Windows**: `%APPDATA%\brush\config.toml`
> [!NOTE]
> On Linux/macOS falls back to `~/.config/brush/config.toml` if `XDG_CONFIG_HOME` is undefined.
You can override this location with the `--config` flag:
```bash
brush --config /path/to/custom/config.toml
```
To disable configuration file loading entirely, use:
```bash
brush --no-config
```
## Configuration Priority
Settings are applied in the following order (later values override earlier ones):
1. **Defaults** - Built-in default values
2. **Configuration file** - Values from `config.toml`
3. **Command-line arguments** - Flags passed to brush
## File Format
The configuration file uses [TOML](https://toml.io/) format. All settings are optional; brush uses sensible defaults for any unspecified values.
### Example Configuration
```toml
[ui]
syntax-highlighting = true
[experimental]
zsh-hooks = true
terminal-shell-integration = true
```
## Available Settings
### `[ui]` Section
User interface settings.
| Setting | Type | Default | CLI flag | Description |
|-----------------------|---------|---------|-------------------------|----------------------------------------------|
| `syntax-highlighting` | boolean | see below | `--enable-highlighting` | Enable syntax highlighting in the input line |
> The default value of `syntax-highlighting` depends on how `brush-shell`
> was built: `true` when built with the `experimental` Cargo feature,
> `false` otherwise. CLI flags take precedence over the configuration
> file.
### `[experimental]` Section
Experimental features that may change or be removed in future versions.
Each setting has an equivalent command-line flag; CLI flags take
precedence over the configuration file. See the
[experimental features reference](experimental.md) for details on each
feature.
| Setting | Type | Default | CLI flag | Description |
|------------------------------|---------|---------|----------------------------------|---------------------------------------|
| `zsh-hooks` | boolean | `false` | `--enable-zsh-hooks` | Enable zsh-style preexec/precmd hooks |
| `terminal-shell-integration` | boolean | `false` | `--enable-terminal-integration` | Enable terminal shell integration |
## JSON Schema
A JSON Schema for the configuration file is available at [`schemas/config.schema.json`](../../schemas/config.schema.json). This can be used with editors that support schema-based validation and autocompletion for TOML files.
### Using the Schema with VS Code
To enable schema validation in VS Code with the [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml) extension, add this to your `config.toml`:
```toml
#:schema https://raw.githubusercontent.com/reubeno/brush/main/schemas/config.schema.json
[ui]
syntax-highlighting = true
```
The `#:schema` directive tells the editor where to find the schema for validation and autocompletion.
### Using the Schema with Other Editors
Many editors support JSON Schema for TOML files. Consult your editor's documentation for how to associate a schema with a file. You can reference the schema via:
- **URL**: `https://raw.githubusercontent.com/reubeno/brush/main/schemas/config.schema.json`
- **Local path**: Point to `schemas/config.schema.json` in your brush source checkout
## Sample Configuration
A sample configuration file is available at [`samples/config.toml`](../../samples/config.toml) in the brush repository. You can copy this file to get started:
```bash
# Linux/macOS
mkdir -p ~/.config/brush
cp samples/config.toml ~/.config/brush/config.toml
```
## Forward Compatibility
brush ignores unknown settings in the configuration file. This allows configuration files to be shared across different versions of brush without causing errors.
## Error Handling
If the configuration file cannot be read or parsed, brush logs an error message and continues with default settings. The shell will still start normally.
+149
View File
@@ -0,0 +1,149 @@
# Experimental Features
`brush` ships several features that are intentionally marked as
**experimental**. They are usable today, but their interface or behavior
may evolve based on feedback before being stabilized. This page is the
canonical index of what's currently experimental and how to opt in.
Experimental features fall into two categories:
1. **Build-time experiments** — additional functionality gated behind
Cargo feature flags. To get them, you build `brush-shell` with the
relevant feature(s) enabled.
2. **Run-time experiments** — features that ship in standard builds but
are off by default and enabled through configuration.
> Names, defaults, and semantics of experimental features may change
> between releases.
---
## Build-time experiments (Cargo features)
These are flags on the `brush-shell` crate. You can enable them
individually, or pull in the whole set with the umbrella `experimental`
feature.
```bash
# Enable everything experimental
cargo install --locked brush-shell --features experimental
# Or pick and choose
cargo install --locked brush-shell --features experimental-bundled-coreutils
```
### `experimental-bundled-coreutils`
Bundles a configurable subset of [`uutils/coreutils`](https://github.com/uutils/coreutils)
implementations directly into `brush-shell` as builtins. Useful when:
- shipping `brush` into containers, embedded systems, or other
environments where a standalone coreutils package is inconvenient or
unavailable,
- distributing a single self-contained `brush` binary that doesn't
rely on host utilities being present.
When enabled (via the umbrella `experimental` feature, or directly), the
full set of supported utilities is bundled. When building the
`brush-coreutils-builtins` crate directly, individual utilities can be
selected via `coreutils.<name>` features (e.g., `coreutils.cat`,
`coreutils.ls`); the `coreutils.all` feature enables all of them.
Bundled utilities run in-process and take precedence over external
executables of the same name on `PATH` when invoked unqualified. As with
any builtin, you can bypass the in-process implementation with
`command <name>` or by giving an explicit path.
### `experimental-builtins`
Pulls in the [`brush-experimental-builtins`](../../brush-experimental-builtins)
crate, which provides additional builtins that are too new or too
narrow-purpose to ship in the default builtin set. Currently this
includes:
- **`save`** — serializes the current shell state to JSON on stdout.
Primarily intended for debugging and tooling. ⚠️ The serialized state
may include sensitive information (variable values, command history,
environment).
### `experimental-load`
Enables `serde`-based serialization support in `brush-core`, and adds a
`--load <FILE>` command-line flag that restores shell state from a JSON
file previously produced by the experimental [`save`](#experimental-builtins)
builtin (or by other tooling that emits the same format). State loaded
this way overrides non-UI command-line options. Useful for tooling
built on top of `brush-core` that wants to snapshot or transfer shell
state.
### `experimental-parser`
Switches on the in-development [`winnow`](https://crates.io/crates/winnow)-based
parser scaffolding in `brush-parser`, and adds an `--experimental-parser`
command-line flag that selects it at runtime. This parser is not yet the
production parser; the existing PEG parser remains the default. Enable
this only if you're contributing to or experimenting with the parser
work.
---
## Run-time experiments (configuration)
These features ship in standard builds but are off by default. Each one
can be enabled either through the optional [TOML configuration
file](configuration.md) (under the `[experimental]` section, persistent
across sessions) or through a command-line flag at startup (one-shot).
When both are specified, command-line flags take precedence over the
configuration file.
```toml
[experimental]
zsh-hooks = true
terminal-shell-integration = true
```
| Feature | TOML setting (`[experimental]`) | Command-line flag |
|---------|---------------------------------|-------------------|
| zsh-style hooks | `zsh-hooks = true` | `--enable-zsh-hooks` |
| Terminal shell integration | `terminal-shell-integration = true` | `--enable-terminal-integration` |
### `zsh-hooks`
Enables zsh-style `preexec` and `precmd` hook functions. When set:
- A function named `preexec` (if defined) is invoked before each
interactively-entered command runs, with the command line as `$1`.
- A function named `precmd` (if defined) is invoked just before each
prompt is displayed.
This is convenient for prompt frameworks, command timing, and
integrations that expect zsh-style hook conventions. Equivalent
behavior in stock bash typically requires `DEBUG`/`PROMPT_COMMAND`
plumbing.
Enable persistently with `zsh-hooks = true` under `[experimental]` in
`config.toml`, or per-invocation with `brush --enable-zsh-hooks`.
### `terminal-shell-integration`
Emits standard terminal shell-integration escape sequences (semantic
prompt and command boundary marking) that modern terminal emulators —
including VS Code, iTerm2, WezTerm, and others — use to enable features
like command navigation, exit-status display, and selective output
copying. This is off by default to avoid emitting escape sequences in
terminals that don't recognize them.
Enable persistently with `terminal-shell-integration = true` under
`[experimental]` in `config.toml`, or per-invocation with
`brush --enable-terminal-integration`.
---
## Reporting feedback
Experimental features are the place where your feedback is most valuable
— they exist precisely because we want to iterate on them before
stabilizing. If you try one and find a rough edge, missing capability,
or behavior that surprises you, please [file an
issue](https://github.com/reubeno/brush/issues).
+17
View File
@@ -0,0 +1,17 @@
# Integration testing
Our approach to integration testing relies heavily on using test oracles to provide the "correct" answers/expectations for test cases. In practice, we use existing alternate shell implementations as oracles.
Test cases are defined in YAML files. The test cases defined in a given file comprise a test case set. Running the integration tests for this project executes test case sets in parallel.
```yaml
name: "Example tests"
cases:
- name: "Basic usage"
stdin: |
echo hi
```
This defines a new test case set with the name "Example tests". It contains one defined test case called "Basic usage". This test case will launch the shell without any additional custom arguments (beyond a few standard ones to disable processing default profiles and rc files), write "echo hi" (with a trailing newline) to stdin of the shell, and then close that stream. The test harness will capture the shell's stdout, stderr, and exit code. After repeating these steps with the test oracle, each of these 3 data are compared. An error is flagged if any of the 3 differ.
Test cases are run with the working directory initialized to a temporary directory. The contents of the temporary directory are inspected after the shell-under-test has exited, and compared against their counterparts in the oracle's run. This enables easy checking of files created, deleted, or mutated as side effects of running the test case.
+37
View File
@@ -0,0 +1,37 @@
# Minimum Supported Rust Version (MSRV) Policy
## Overview
The `brush` project maintains a conservative MSRV policy to balance two key concerns:
1. **Binary distribution**: Users building `brush` from source should not need a bleeding-edge compiler
2. **Library usage**: Downstream projects depending on `brush` crates should not face aggressive MSRV increases
## Policy
### When We Update MSRV
We **do not** update MSRV proactively. Updates only occur when:
- A meaningful set of language features or capabilities becomes available that provides clear value to the project
- The return-on-investment justifies the potential impact on users and downstream dependencies
### MSRV Age Requirements
When we do update MSRV, we move to a Rust version that is **at least 4-6 months old** from the time of the update. This ensures:
- Sufficient time for the Rust version to stabilize
- Wide availability in package managers and development environments
- Reduced friction for users building from source
### Communication
MSRV changes are always:
- Explicitly documented in release notes
- Considered a notable change requiring user awareness
- Announced with clear justification for the update
## Rationale
This conservative approach recognizes that `brush` serves dual purposes: as a standalone binary tool and as a library for integration into other projects. Both use cases benefit from stability and predictability in compiler requirements.