Blazing fast terminal file manager written in Rust, based on async I/O.
# AGENTS.md
## Rules
- Applies repo-wide. Keep changes scoped; do not create issues or pull requests, or post comments.
## Project
- Rust 2024 Cargo workspace containing all `yazi-*` crates; default members are `yazi-fm` (`yazi`) and `yazi-cli` (`ya`).
## Style
- Follow nearby code and use idiomatic Rust and Lua. In Rust, treat directories as modules and files as types: always prefer `mod_pub!` for directory exports and `mod_flat!` for file exports. Keep `mod.rs` and `lib.rs` limited to exports; put module-wide implementation in a same-named file (for example, `core/core.rs`). Rust uses `snake_case` for modules, functions, and fields and `PascalCase` for types, traits, and variants. Lua uses PascalCase component tables, `local M` plugin modules, `snake_case` methods/locals, and `_name` private fields.
- Separate distinct responsibilities and processing stages with a single blank line, following nearby code. Group related statements together; use blank lines between preparation, validation, I/O, fallback, progress reporting, cleanup, and return values when they mark a distinct step. Keep this spacing when writing or simplifying code; the smallest implementation must still be readable.
- Preserve established terms and type families: `Url`/`UrlBuf`/`UrlCow`, `PathDyn`/`PathBufDyn`/`PathCow`, `*Ref`, `*Arc`, `*Opt`, `*State`, `*Job`, `*Prog`, `File`, `Folder`, `Tab`, `Mgr`, and `Task`. Use `Url` for logical locations and `Path` for filesystem paths.
- `key()` identifies a file-list entry; `urn()` is the raw URL path tail. Use `key()` for list state and `urn()` for filesystem-path semantics; do not substitute them mechanically.
- Reuse established plugin and event names (`fetch`, `preload`, `peek`, `seek`, `spot`, `entry`, `setup`, `yank`, `hover`, and `select`) across Rust, Lua, and configuration.
- Use Rust prefixes (`as_`, `to_`, `into_`, `try_`, `is_`, `has_`) according to their usual semantics; prefer descriptive names.
- Name variables, modules, methods, and other symbols simply, elegantly, and expressively. Be creative while keeping names clear, consistent with established terminology, and idiomatic. When equivalent names are available, align the name's natural default with the type's default; for booleans, prefer a name whose `false` value represents the common state (for example, `seekless` when handles normally support seeking), so `Default` and `unwrap_or_default()` do not need special initialization.
- When passing arguments, use the parameter's conversion traits directly (such as `Into<_>` or `AsRef<_>`); avoid eager conversions like `.to_string()`, `.to_owned()`, and `.as_ref()` unless ownership, type inference, or semantics require them. At Lua boundaries, prefer `LuaString` to `String`, use `BorrowedBytes` when string semantics are unnecessary, and return binding errors as `(nil, error)` alongside a value (or `(false, error)` for no-value operations); successful calls return `(value, nil)` or `(true, nil)`. Reserve direct `Err` returns for invalid API usage that violates the call contract and can be handled with `pcall`.
- Let Rust infer types whenever the context is sufficient; when an annotation is needed, put it on the binding (`let value: Type = ...`) instead of turbofishing the expression.
- Prefer methods provided by `UrlLike`, `PathLike`, or `StrandLike` directly on the original value (for example, `buf.parent()` over `buf.as_url().parent()`), rather than converting it first with `as_url()`, `dyn_path()`, or `to_strand()`.
- Prefer `&*value` for dereferencing over `AsRef` when both are suitable.
- Prefer general-purpose traits and conversion APIs already provided by the codebase or its dependencies over manual construction or adapter closures; for example, use `into_lua()` where applicable. Unless method resolution is genuinely ambiguous, call `value.method()` instead of `Trait::method(value)`.
## Code Changes
- Search and reuse first. Prefer established types, variants, helpers, and data structures over adding new wrappers or abstractions; introduce a new type only when it represents a genuinely distinct responsibility or invariant. For new features, extend existing infrastructure or data structures with general, reusable capabilities when that keeps the final code concise and the total type count small.
- Use `gh` to read GitHub issues, pull requests, and their discussions.
- For refactors, inspect the whole target module, its callers, and the surrounding lifecycle first. Understand the system's established assumptions before adding local safeguards; distinguish required invariants from acceptable compromises, and ask when that boundary materially affects the design. Look for duplicated work, redundant I/O, underpowered return values, one-use wrappers, and reusable cross-platform abstractions; implement high-confidence, behavior-preserving simplifications while preserving error, fallback, and platform semantics.
- Prefer the simplest design that satisfies the requirements. Keep diffs minimal and avoid overengineering, unrelated refactors, speculative abstractions, and defensive handling for states the system already excludes. When evaluating code size, count hand-maintained Rust, Lua, and similar source logic; do not treat imports or generated/tool-managed files such as `Cargo.toml` and `Cargo.lock` as business-code complexity. When conditions are mutually exclusive and have a clear priority, express them as flat, peer-level `if`/`else if`/`else` branches in that priority order; do not compress the state checks into compound predicates merely to deduplicate a small result body. Prefer clear, flat control flow, expressions, and positive predicates; use standard combinators, early returns, ordered branches, and match guards to avoid nested conditionals, compound negation, and unnecessary wrapper syntax. Treat small, deliberate repetition in branch results as a good trade-off when it makes the decision structure easier to understand. Prefer available convenience macros when they simplify the code. When idiomatic and equivalent, prefer visually parallel forms such as `true as usize` over `usize::from(true)`. Comment only behavior the code cannot explain.
- Keep responsibility boundaries clear and cohesive. Prefer pure functions, explicit invariants, and idempotent operations when repeated calls are natural and idempotency removes coordination or state. Favor convention over configuration when invariants can eliminate state or coordination. Put reusable code in the lowest suitable shared layer; place business-independent adapters and wrappers that mainly compensate for a dependency's limitations in `yazi-shim`, and add that dependency there. Avoid unnecessary allocations. Prefer borrowed values and existing wrappers.
- Minimize total maintenance cost, not dependency count. Reuse suitable crates instead of hand-writing synchronization, resource cleanup, or platform adapters merely to avoid a dependency. A dependency already declared at workspace level may be used by any child crate as needed; adding it to a child crate is not a new workspace dependency. Evaluate new crates by the maintained code and correctness obligations they remove versus their integration, compatibility, security, and upkeep costs; add them when the overall trade-off is favorable.
- Initialize crates explicitly from the application entrypoint in dependency order; a module must not initialize another module as a side effect.
- Use stable Rust APIs; nightly is formatting-onlyβapply Rust formatting directly with `rustfmt +nightly **/*.rs`. Use only `pub`, `pub(super)`, and `pub(crate)`βnever `pub(in ...)`.
- Keep async I/O non-blocking, preserve platform/fork behavior, and follow existing error boundaries with `?`.
- For renames or refactors, update all related variables, functions, parameters, modules, methods, types, derived types, exports, tests, configuration keys, documentation, Lua bindings, and, when a type and file share a name, the file as well; do not preserve renamed terms through aliases or re-exports.
- When adding a changelog entry, leave the PR number blank for a human to fill in.
- Do not add tests or change test behavior unless requested.
## Review and Simplification
- For a review or simplification pass, inspect the whole target module, its callers, and its surrounding lifecycle before drawing conclusions. Use two passes: first establish ownership, cancellation, error, and state invariants; then compare each custom mechanism with the standard library, existing dependencies, and nearby code.
- Check every task and channel for a simpler native primitive (for example, a `JoinHandle` that can replace a result-only `oneshot`), and check wrappers, helpers, flags, `Option` values, defaults, conversions, clones, and manual cleanup for the same opportunity. Include `Drop`, async boundaries, and cancellation in the comparison.
- Before concluding a simplification review, enumerate every credible candidate. Report which candidates were applied and which were retained, with the behavioral reason and approximate diff impact; do not report only the changes that were ultimately made.
- Keep only changes with a clear semantic or readability advantage, or a measurable reduction in maintained code. Include blank lines in source line counts, but do not remove useful spacing merely to reduce that count. Preserve behavior, error propagation, ownership, cancellation, and platform semantics when a shorter form would weaken them.
## Validation
- Prefer targeted debug checks; use multiple `-p` flags for affected crates before the whole workspace.
- When investigating bugs, add temporary diagnostics when useful (`tracing` in Rust and `ya.dbg` in Lua), reproduce in a simulated terminal with `YAZI_LOG=debug`, and inspect the log file to pinpoint the cause. When debugging on a real terminal, use its IPC remote-control interface whenever supported. Remove temporary diagnostics before handoff.
```sh
cargo check -p <package>
cargo test -p <package>
cargo clippy -p <package>
rustfmt +nightly **/*.rs
stylua --color always --check .
```
- Use `cargo check` instead of `cargo build` unless artifacts are needed. Do not use `--release` unless requested; use `scripts/build.sh <target>` for release or cross-target packaging.
- Run relevant existing tests when needed, then inspect `git diff` and verify that only intended files changed.