# cargo-brief
Visibility-aware Rust API extractor — pseudo-Rust output for AI agent consumption.
## Why?
AI coding agents need to understand crate APIs without reading full source files. HTML docs waste tokens on navigation chrome, and `cargo doc --json` is too verbose. `cargo-brief` outputs concise pseudo-Rust that fits in a context window:
- Function bodies replaced with `;`
- Only items visible from the caller's perspective
- Doc comments preserved verbatim
- Compact module hierarchy
## Installation
```sh
cargo install cargo-brief
```
**Requires the nightly toolchain** (for `rustdoc --output-format json`):
```sh
rustup toolchain install nightly
```
## Usage
```sh
cargo brief <target> [module_path] [OPTIONS]
```
The first argument is flexible — it can be a crate name, `self`, a `crate::module` path, or even a file path:
| `my-crate` | Named crate (hyphen/underscore normalized) |
| `self` | Current package (detected from cwd) |
| `self::module` | Current package, specific module |
| `crate::module` | Named crate + module in one arg |
| `src/foo.rs` | File path → auto-converted to module path |
| `unknown_name` | If not a workspace package → treated as self module |
### Examples
```sh
# Show the full API of a crate in your workspace
cargo brief my-crate --recursive
# Inspect the current package
cargo brief self --recursive
# Show a specific module (multiple syntaxes)
cargo brief my-crate utils::helpers
cargo brief self::utils
cargo brief src/utils.rs
# Show only what's visible from an external crate
cargo brief my-crate --at-package other-crate
# Limit recursion depth
cargo brief my-crate --depth 2
# Exclude certain item kinds
cargo brief my-crate --no-macros --no-traits
# Search for items by name (fuzzy / imprecise search)
cargo brief my-crate --search spawn
cargo brief --crates hecs --search "World spawn"
cargo brief self --search "config timeout"
```
## Options
| `<target>` | Target to inspect: crate name, `self`, `crate::module`, or file path |
| `[module_path]` | Module path within the crate (e.g., `my_mod::submod` or `src/my_mod.rs`) |
| `--at-package <pkg>` | Caller's package name (for visibility resolution) |
| `--at-mod <path>` | Caller's module path (determines what is visible) |
| `--depth <n>` | How many submodule levels to recurse into (default: 1) |
| `--recursive` | Recurse into all submodules (no depth limit) |
| `--all` | Show all item kinds including blanket/auto-trait impls |
| `--no-structs` | Exclude structs |
| `--no-enums` | Exclude enums |
| `--no-traits` | Exclude traits |
| `--no-functions` | Exclude free functions |
| `--no-aliases` | Exclude type aliases |
| `--no-constants` | Exclude constants and statics |
| `--no-unions` | Exclude unions |
| `--no-macros` | Exclude macros |
| `--search <pattern>` | Search leaf items by name (case-insensitive, multi-word AND) |
| `--toolchain <name>` | Nightly toolchain name (default: `nightly`) |
| `--manifest-path <path>` | Path to Cargo.toml |
## Search Mode
`--search` finds leaf items whose full path contains the given pattern (case-insensitive). Multiple words are AND-matched — all must appear somewhere in the path.
```sh
cargo brief --crates hecs --search component
```
```rust
// crate hecs — search: "component" (11 results)
/// A collection of component types
struct query::ComponentSet;
enum world::ComponentError;
field world::ArchetypeInfo::component_count: u32;
variant world::WorldError::ComponentNotFound;
variant world::WorldError::ComponentAlreadyBorrowed;
/// Insert a component into an entity
fn world::World::insert_component<T: Component>(&mut self, entity: Entity, component: T) -> Result<()>;
fn world::World::remove_component<T: Component>(&mut self, entity: Entity) -> Result<T>;
fn world::World::get_component<T: Component>(&self, entity: Entity) -> Result<ComponentRef<'_, T>>;
type world::ComponentId = u32;
const world::MAX_COMPONENTS: usize = ..;
macro component_bundle!;
```
**Leaf items** included in search: functions, methods, struct fields, enum variants, constants, statics, type aliases, macros, associated types/consts. Container types (struct, enum, trait, union) appear when their name matches directly.
## Output Format
```rust
// crate my_crate
mod utils {
/// Computes the hash of the input.
pub fn hash(input: &[u8]) -> u64;
pub struct Config {
pub timeout: Duration,
pub retries: u32,
// ... private fields
}
impl Config {
pub fn new() -> Self;
pub fn with_timeout(self, timeout: Duration) -> Self;
}
pub trait Processor: Send + Sync {
type Output;
fn process(&self, input: &[u8]) -> Self::Output;
}
}
```
## AI Agent Setup
### Claude Code
Add a note to your project's `CLAUDE.md` so the AI knows to use cargo-brief when exploring crate APIs:
```markdown
## Exploring Crate APIs
Use `cargo brief` to inspect crate interfaces instead of reading source files directly:
# Current package API
cargo brief self --recursive
# Specific module (by name or file path)
cargo brief self::some_module --recursive
cargo brief src/some_module.rs --recursive
# Named crate in workspace
cargo brief <crate> --recursive
# Multi-workspace: specify manifest path
cargo brief <crate> --manifest-path path/to/Cargo.toml --recursive
# External visibility only (what other crates can see)
cargo brief <crate> --at-package consumer-crate --recursive
# Search for items by name (imprecise / fuzzy)
cargo brief <crate> --search "spawn entity"
cargo brief --crates serde --search "deserialize"
```
### Generic LLM Agent
Pipe the output directly into your agent's context:
```sh
# Current package API
# Specific module
Or use it as a tool call that returns the output as a string to the agent.
## License
MPL-2.0