drft
A structural integrity checker for linked file systems. drft treats a directory of files as a dependency graph -- files are nodes, links are edges -- and validates the graph against configurable rules.
Install
Or download a prebuilt binary from GitHub Releases.
The binary is called drft.
Quick start
# Check a directory for issues (no setup required)
# Initialize a config file
# Snapshot the current state
# Check for staleness (files changed since last lock)
# Verify lockfile is current (for CI)
What it does
drft discovers files, runs configurable parsers to extract links between them, and builds a dependency graph. It then validates that graph against a set of rules:
| Rule | Default | Description |
|---|---|---|
broken-link |
warn | Link target does not exist |
cycle |
warn | Circular dependency detected |
directory-link |
warn | Link points to a directory, not a file |
stale |
error | Dependency changed since last lock |
containment |
warn | Link escapes the graph boundary |
encapsulation |
warn | Link reaches into a child graph's non-interface files |
orphan |
off | File has no inbound links |
indirect-link |
off | Link target is a symlink |
See the full documentation for details on parsers, analyses, and rules.
Commands
drft check
Validate the graph against all enabled rules.
drft lock
Snapshot file hashes to drft.lock. This enables staleness detection -- when a file changes, its dependents are flagged.
drft graph
Export the dependency graph.
drft impact
Show what depends on the given files (transitively).
drft init
Create a default drft.toml config file.
Configuration
drft.toml in the graph root:
# Glob patterns for files to exclude from discovery
= ["drafts/*", "archive/*"]
# Public interface — nodes accessible from parent graphs.
# Presence of this section enables encapsulation.
[]
= ["overview.md", "api/*.md"]
# Parsers — which file types to parse and how
[]
= true # built-in, all defaults
[] # custom (has command)
= "*.tsx"
= "./scripts/parse-tsx-links.sh"
# Rule severities: "error", "warn", or "off"
# Table form for per-rule options or custom rules
[]
= "error"
= "error"
= "error"
[]
= "warn"
= ["README.md", "CLAUDE.md"]
[]
= "./scripts/max-fan-out.sh"
= "warn"
drft automatically respects .gitignore.
Graph nesting
A directory with a drft.toml or drft.lock becomes a graph. Child directories with their own config are child graphs -- they appear as Graph nodes in the parent graph and are checked independently.
project/
drft.lock # root graph
drft.toml
index.md
docs/
overview.md
research/
drft.toml # child graph (Graph node in parent)
drft.lock
overview.md
internal.md
Use --recursive to check or lock all graphs in one command. Use [interface] in drft.toml to declare a graph's public interface and control which files are visible to the parent.
Output
Text format (default):
error[broken-link]: index.md -> gone.md (file not found)
error[stale]: index.md (stale via setup.md)
warn[cycle]: cycle detected: a.md -> b.md -> c.md -> a.md
JSON format (--format json):
Every JSON diagnostic includes a fix field with actionable instructions.
DOT format (--format dot) for graph export:
digraph
Graph JSON follows the JSON Graph Format specification.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Clean (warnings may be present) |
| 1 | Rule violations at error severity, or lock --check found lockfile out of date |
| 2 | Usage or configuration error |
Custom rules
Custom rules are scripts that receive the dependency graph as JSON on stdin and emit diagnostics as newline-delimited JSON on stdout:
[]
= "./scripts/max-fan-out.sh"
= "warn"
#!/bin/sh
# Flag nodes with more than 5 outbound links
See examples/custom-rules for complete examples.
Security note: Custom rules and custom parsers execute arbitrary shell commands defined in drft.toml. Review the [rules] and [parsers] sections before running drft in untrusted repositories, the same as you would review npm scripts or Makefiles.
LLM integration
drft is designed to work with LLMs as both a validation tool during editing sessions and a CI gate.
JSON output
All commands support --format json. The check command returns a summary envelope:
Every diagnostic includes a fix field with actionable instructions an LLM can follow directly.
Impact analysis
Before editing a file, check what depends on it:
Claude Code hooks
Add to your project's .claude/settings.json to run drft automatically after markdown edits:
If drft is installed globally (cargo install drft-cli), use drft instead of npx drft. For npm projects with drft-cli as a devDependency, npx drft ensures it resolves from node_modules.
CI
Run check first — it catches broken links, cycles, and stale files. Then lock --check verifies the lockfile is committed (structural consistency). Both exit with code 1 on failure.
The workflow: edit → drft check (see what's stale) → review impacted files → drft lock (acknowledge the changes) → commit. Lock is the "I've reviewed the impacts" step.
License
MIT