$johnhartquist
Toggle theme
← Back to projects

groxide

Query Rust crate documentation from the terminal

RustRustdoc JSONCLI

I kept watching Claude do the same thing when it needed to look up a Rust API: run cargo rustdoc and parse the raw output, or fetch docs.rs pages and try to extract what it needed from the HTML. It worked, but it was slow and burned through tokens. The information was there — the access pattern was wrong.

Go has go doc. You type go doc sync.Mutex and get exactly what you need: signature, docs, methods. One command, clean output, no browser. Rust had nothing equivalent. So I built one.

The path is the query

$ grox tokio::sync::Mutex::lock

fn tokio::sync::mutex::Mutex::lock

pub async fn lock(&self) -> MutexGuard<'_, T>

Locks this mutex, causing the current task to yield until the lock has
been acquired. When the lock has been acquired, function returns a
[MutexGuard].

Cancel safety

This method uses a queue to fairly distribute locks in the order they
were requested. Cancelling a call to lock makes you lose your place in
the queue.

Examples

  use tokio::sync::Mutex;

  let mutex = Mutex::new(1);

  let mut n = mutex.lock().await;
  *n = 2;

No subcommands, no flags needed for the common case. grox serde::Deserialize shows the trait with its methods. grox tokio::sync lists the module contents. The output adapts by item kind — structs show their methods, modules show their children, functions show their signature and docs.

Finding the right crate

When you type grox tokio::sync::Mutex, groxide needs to figure out where tokio lives. It resolves through a priority chain:

  1. Current crate — is tokio the crate you're working in?
  2. Direct dependencies — is tokio in your Cargo.toml?
  3. Workspace members — is tokio another crate in your workspace?
  4. Transitive dependencies — is tokio pulled in by something else?
  5. Standard librarystd, core, alloc always work
  6. crates.io auto-fetch — download it, build the index, answer the query

That last step means groxide works anywhere. Outside a Rust project, grox axum::Router downloads axum from crates.io, generates rustdoc JSON, builds a queryable index, and gives you the answer. First query pays the build cost; after that, it's cached.

Fuzzy resolution

Queries don't need to be exact. A 5-stage pipeline handles the messiness of real usage:

exact path → case-insensitive → suffix match → name match → not found

grox mutex inside a tokio project finds tokio::sync::Mutex. grox Deserialize finds serde::Deserialize. If the query is ambiguous, groxide lists the candidates instead of guessing wrong:

$ grox Builder

Ambiguous query "Builder". 3 matches:

  struct  tokio::runtime::Builder
  struct  http::request::Builder
  struct  http::response::Builder

Built for agents

The design choices that matter most aren't visible in the output — they're about what's not there.

All documentation goes to stdout. All status messages (Building index for tokio 1.40.0...) go to stderr. An agent pipes stdout into its context window and ignores stderr. Clean separation means zero noise in the prompt.

Output is truncated to ~200-800 tokens per query by default. Progressive disclosure lets you drill deeper: crate → module → type → method. An agent that needs Mutex::lock doesn't pay for the entire tokio::sync module — it asks for exactly what it needs.

Full-text search works across an entire crate's documentation:

$ grox tokio -S "spawn"

  fn      tokio::spawn                  Spawns a new asynchronous task, returning a [JoinH...
  fn      tokio::task::spawn            Spawns a new asynchronous task, returning a [JoinH...
  fn      tokio::task::spawn_blocking   Runs the provided closure on a thread where blocki...
  fn      tokio::task::spawn_local      Spawns a !Send future on the current LocalSet.
  fn      tokio::runtime::Handle::spawn Spawns a future onto the Tokio runtime.

The binary is called grox. It ships as a crate on crates.io and includes an agent skill that teaches AI coding agents how to use it autonomously.