This guide helps AI agents work efficiently on the brush codebase by providing essential context about architecture, patterns, and development workflows.
The brush project is organized into several key crates:
brush-core/: Core shell functionality, builtins, and runtimebrush-parser/: Shell script parsing (AST generation)brush-builtins/: Implementation of shell builtins (e.g., echo, cd)brush-interactive/: Interactive shell interfaces (readline, etc.)brush-shell/: Main CLI application and entry point
Critical files to understand first:
brush-core/src/shell.rs- MainShellstruct and creation logicbrush-core/src/lib.rs- Public API exportsbrush-shell/src/main.rs- CLI application entry point
Architecture patterns:
- Shell instances are created via
Shell::builder() - The project uses builder patterns for type-safe configuration
- We try to keep platform-specific code in
brush-coreunder thesysmodule - Follows Rust 2024 edition standards
brush-shell → brush-interactive → brush-core → brush-parser
↘ brush-builtins ↗
Recommended development workflow:
The project provides a cargo xtask command that centralizes common development tasks:
# Run quick inner-loop checks (~7s warm): fmt, build, lint, unit tests
cargo xtask ci quick
# Run the full workflow (~60s warm): quick + pre-commit hooks, schemas, integration tests
cargo xtask ci full
# Run with --continue-on-error to see all failures at once
cargo xtask ci full -k
# Add -v for verbose output showing exact commands being run
cargo xtask -v ci fullcargo xtask ci quick needs only the Rust toolchain. cargo xtask ci full also
needs prek on PATH (see CONTRIBUTING.md for install commands). If prek is
unavailable in your environment, run cargo xtask ci full --no-hooks; CI
still runs the hooks.
# Run unit tests (fast tests excluding integration binaries)
cargo xtask test unit
# Run integration tests (all workspace tests including compat tests)
cargo xtask test integration
# Run tests with coverage
cargo xtask test integration --coverage --coverage-output codecov.xmlFor finer-grained control:
- Quick validation:
cargo check --package <changed-package>- Fast syntax/type checking - Correctness validation:
cargo test --package <changed-package>- Target specific crates for faster feedback
- Compatibility tests:
cargo test --test brush-compat-tests- Bash compatibility validation - Full workspace tests:
cargo test --workspace- Complete test suite
Recommended: Run the xtask full workflow:
cargo xtask ci fullManual approach: Before considering work complete, run these validation steps:
- Compatibility tests:
cargo test --test brush-compat-tests - Linting:
cargo clippy - Formatting:
cargo fmt --check - Pre-commit hooks (spelling, links, workflows, dependency audit):
cargo xtask check hooks(needsprek) - Full test suite:
cargo test --workspace
When tests fail:
- Focus on failures in the area you changed first
- Compatibility test failures often indicate shell behavior changes
- Check if new functionality needs corresponding test cases
- Format/clippy failures should be fixed before proceeding
Common pitfalls:
- Test scope mistakes: Running full test suite too early instead of targeting specific areas first
- Skipping test-driven development: Add tests that specify desired behavior before implementing
Test-driven development approach:
- When possible, write tests first that specify the desired behavior
- Use unit tests for logic changes, compatibility tests for shell behavior changes
- Use these tests as validation that your implementation is working correctly
Pro tip: For specific compatibility test cases, use:
cargo test --test brush-compat-tests -- '<name of test case>'Fast iteration strategies:
- Target specific crates:
cargo test --package <changed-package> - Target specific test cases:
cargo test <test-name>orcargo test --test <test-file> - Requires knowledge of which tests best exercise the code being changed
Testing approach:
- Follow good software engineering practice: start by validating the specific area being changed, then iteratively move to incrementally broader sets of tests
Testing expectations for new public APIs:
- Unit tests are expected if feasible
- Examples are nice to have and worthwhile for sufficiently critical APIs
Test patterns and conventions:
- Compatibility tests: For any compatibility-related fixes, it's critical to add new test cases to the compat tests (see docs/how-to/run-tests.md and section 3 for when breaking changes apply)
Test categories:
- Unit tests: In
src/files with#[cfg(test)] - Integration tests: In
tests/directories - Examples: In
examples/directories (must be runnable) - Shell script tests: YAML-based test cases in
brush-shell/tests/cases/
Performance regression testing:
- Not a chief concern for most changes
- For performance-specific work, benchmarks are available (see docs/how-to/run-benchmarks.md)
- Performance sensitivity will be identified in the initial brief if relevant
Breaking change policy:
- Non-backwards compatible changes to public APIs are considered breaking
- Breaking changes are still in consideration, but need to be highlighted and carefully reviewed
- Any APIs exported from crates are considered public because all of the crates are published to crates.io
Adding new fields to public structs:
- New optional fields are fine to add as long as the struct implements the Default trait and as long as the defaulted value is a sensible one
When changing public APIs in brush-core (see section 3 for breaking change policy):
- Check
brush-shell/src/main.rsfor struct initialization sites - Check
brush-interactive/for any usage
Rustdoc documentation standards:
- At minimum we must have good rustdoc documentation for exported types, functions, traits, etc. as well as on all exported modules and crates
- Documentation for internal components should be a best-effort, nice to have thing
Examples for new features:
- Unless explicitly requested, only major feature additions warrant an example.
Documentation style:
- Follow general best practices for Rust
Examples should:
- Be self-contained and runnable with
cargo run --package brush-core --example <name> - Include comprehensive error handling
- Demonstrate both basic and advanced usage patterns
- Include output examples in comments when helpful
The project uses several tools for code quality:
Using xtask (Recommended):
The project provides a cargo xtask command that centralizes common development tasks:
# Run all checks (comprehensive)
cargo xtask ci full
# Individual checks
cargo xtask check fmt # Format check
cargo xtask check lint # Clippy
cargo xtask check build # Compilation check
cargo xtask check schemas # Schema drift check
cargo xtask check hooks # prek hooks: file hygiene, typos, zizmor, lychee, cargo-deny
cargo xtask check hooks deps # One hook, by id or alias
# Tests
cargo xtask test unit # Fast unit tests (excludes integration binaries)
cargo xtask test integration # All workspace tests (unit + compat)
# Analysis
cargo xtask analyze bench # Run benchmarksManual approach (Alternate):
- Standard cargo commands (e.g., check, test, build, run, clippy)
- You may need to reverse engineer some of the args looking at CI checks in .github/*.yml
Command frequency guidelines:
- Frequent (inner loop):
cargo xtask ci quick,cargo check,cargo test --package <pkg> - Regular (before commits):
cargo xtask ci quickorcargo fmt+cargo clippy - Occasional (outer loop):
cargo xtask test integrationorcargo test --workspace - Rare (before opening a PR):
cargo xtask ci full, which adds the pre-commit hooks, schemas, and integration tests
Pre-commit validation:
- Recommended:
cargo xtask ci quick - Before opening a PR:
cargo xtask ci full - Manual: Run
cargo fmtandcargo clippybefore committing
Outer loop validation:
cargo xtask check hooksshould pass (file hygiene, typos, zizmor, lychee, cargo-deny) - not for frequent use during development;cargo xtask ci full --no-hooksskips it whenprekis unavailable
Error handling patterns:
thiserroris used for implementing crate-specific errors- Use
anyhowonly in tests
Logging and tracing patterns:
- Use
tracingfor debug logging with predefined categories - Categories are defined in
trace_categories.rsmodules (e.g.,COMMANDS,COMPLETION,EXPANSION,FUNCTIONS,INPUT,JOBS,PARSE,PATTERN,UNIMPLEMENTED) - Usage pattern:
tracing::debug!(target: trace_categories::CATEGORY_NAME, "message") - Example:
tracing::debug!(target: trace_categories::JOBS, "Polling job {} for completion...", job_id)
Clone vs references:
- Avoid cloning by default, no reason to make extra copies
- Only use cloning when you really must capture a separate copy for async safety or similarly important reasons
Following the Linux kernel convention, when AI assistants or advanced coding tools have been used in the development of a change, include an Assisted-by: tag in the commit message. This provides transparency about the use of such tools in the development process.
The format for the Assisted-by tag is:
Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]
Example:
Assisted-by: Mistral Vibe:mistral-medium-3.5
When making changes to brush:
- Understand which crate(s) are affected
- Check if changes might break dependent crates
- Identify relevant test files and examples
- Run
cargo checkfrequently during development - Test changes with package-specific tests first (see section 2 for testing workflow)
- Update dependent crate usage if needed (see section 3 for compatibility considerations)
- Add/update examples for major feature additions only (see section 4)
- Run full test suite:
cargo test(see section 2 for complete testing workflow) - Format code:
cargo fmt(see section 5 for tool details) - Check linting:
cargo clippy - Use conventional commit format
- If using AI assistants or advanced coding tools, include an
Assisted-by:tag in the formatAGENT_NAME:MODEL_VERSION(following Linux kernel convention)
- Add rustdoc to exported APIs (see section 4 for documentation standards)
- Include working examples for major features only
- Update this guide if new patterns emerge