Crates
Toolpath is a Rust workspace of focused, composable crates. The core crate has zero dependencies beyond serde; satellite crates add derivation sources and rendering.
toolpath-cli (binary: path)
+-- toolpath (core types)
+-- toolpath-git -> toolpath
+-- toolpath-claude -> toolpath
+-- toolpath-dot -> toolpath
No cross-dependencies between satellite crates. toolpath is the sole shared foundation.
toolpath
Core types, builders, and query API
The gravity well. All other crates depend on this; it depends on nothing except serde. Defines Step, Path, Graph, and the query functions to traverse them.
toolpath-git
Derive from git repository history
Reads git history via libgit2 and maps commits to Steps, branches to Paths. Single branch produces a Path; multiple branches produce a Graph.
toolpath-claude
Derive from Claude conversation logs
Reads Claude Code conversation data from ~/.claude/projects/ and maps messages, tool calls, and abandoned approaches to Toolpath documents.
toolpath-dot
Graphviz DOT visualization
Renders any Toolpath Document as a Graphviz diagram. Steps are color-coded by actor type, dead ends get red dashed borders, and the DAG structure is preserved visually.
toolpath-cli
Unified CLI (binary: path)
One binary called `path` that ties everything together: derive, query, render, merge, track, validate.
Using the libraries
Core types
use toolpath::v1::{Step, Path, Base, Document};
let step = Step::new("step-001", "human:alex", "2026-01-29T10:00:00Z")
.with_parent("step-000")
.with_raw_change("src/main.rs", "@@ -1,1 +1,1 @@\n-hello\n+world")
.with_intent("Fix greeting");
let path = Path::new(
"path-pr-42",
Some(Base::vcs("github:org/repo", "abc123")),
"step-001",
);
Query operations
use toolpath::v1::query;
let ancestors = query::ancestors(&path.steps, &path.path.head);
let dead_ends = query::dead_ends(&path.steps, &path.path.head);
let by_actor = query::filter_by_actor(&path.steps, "agent:");
let artifacts = query::all_artifacts(&path.steps);
Git derivation
use toolpath_git::{derive, DeriveConfig};
let repo = git2::Repository::open(".")?;
let config = DeriveConfig {
remote: "origin".into(),
title: None,
base: None,
};
let doc = derive(&repo, &["main".into()], &config)?;
DOT rendering
use toolpath::v1::Document;
use toolpath_dot::{render, RenderOptions};
let dot_string = render(&doc, &RenderOptions::default());
// Pipe through `dot -Tpng` for an image