Thank you for your interest in contributing to tokmd! This project aims to be a robust code intelligence platform for humans, machines, and LLMs.
Please review our Code of Conduct before contributing.
If you are making a first contribution, start with the short Contributor Guide. This file remains the full reference for setup, testing, hooks, repository structure, and release guidance.
- Enter the dev shell:
nix develop
- Build:
cargo build
- Rust Toolchain: Ensure you have a recent stable Rust toolchain installed.
rustup update stable
- Clone & Build:
git clone https://github.com/EffortlessMetrics/tokmd.git cd tokmd cargo build
For repeated local rebuilds, sccache is supported as an opt-in wrapper rather than a repo default.
- Install sccache:
winget install Mozilla.sccache # or cargo install sccache --locked - Verify setup:
cargo sccache-check
- Run Cargo through the wrapper:
cargo with-sccache test --workspace --all-features cargo sccache-stats
The wrapper sets RUSTC_WRAPPER=sccache and defaults CARGO_INCREMENTAL=0 because incrementally compiled Rust crates do not produce sccache hits. Pass cargo xtask sccache --keep-incremental -- test ... if you want to preserve your current incremental setting.
If you want cache hits across multiple worktrees or checkout roots, use cargo xtask sccache --basedir <PATH> -- test ... so the wrapper sets SCCACHE_BASEDIRS explicitly.
Enable the project's git hooks for automated lint-fix and quality gating:
git config core.hooksPath .githooksThis is a one-time setup. Two hooks are provided:
- pre-commit — Runs
cargo xtask lint-fix(fmt + clippy --fix + clippy verify), restages fixed files, and runstypos --diff(if installed). Only triggers when.rs,Cargo.toml, orCargo.lockfiles are staged. - pre-push — Runs
cargo xtask gate --check(fmt check + cargo check + clippy + test compile-only) to catch issues before they reach CI.
You can bypass hooks with git commit --no-verify or git push --no-verify in emergencies.
Branch prefixes are descriptive hints, not a policy boundary. The repo currently has a mix of human-created and tool-created prefixes, and we do not require a single canonical style before a branch can merge.
Common prefixes in current use:
feat/for feature workfix/for bug fixesdocs/for documentation-only changesfeature/on some older or tool-generated branches
Prefer short, lowercase branch names that describe the change area, for example
feat/browser-runner-packaging or fix/nix-source-filter. The merge bar is the
same regardless of prefix: focused changes, passing checks, and an accurate PR
description.
The codebase uses a tiered microcrate architecture:
crates/
├── tokmd-types/ # Tier 0: Core data structures
├── tokmd-analysis-types/ # Tier 0: Analysis receipt types
├── tokmd-settings/ # Tier 0: Clap-free settings types
├── tokmd-envelope/ # Tier 0: Sensor report + FFI envelope contracts
├── tokmd-scan/ # Tier 1: tokei wrapper + walk helpers
├── tokmd-model/ # Tier 1: Aggregation logic
├── tokmd-sensor/ # Tier 1: Sensor trait + substrate module/builder
├── tokmd-format/ # Tier 2: Output rendering
├── tokmd-git/ # Tier 2: Git analysis
├── tokmd-analysis/ # Tier 3: Analysis orchestration
├── tokmd-analysis-api-surface/ # Tier 3: API surface analysis
├── tokmd-analysis-archetype/ # Tier 3: Archetype inference
├── tokmd-analysis-assets/ # Tier 3: Asset and dependency reports
├── tokmd-analysis-complexity/ # Tier 3: Cyclomatic/cognitive complexity
├── tokmd-analysis/src/content/ # Tier 3: Content scanning adapters
├── tokmd-analysis-derived/ # Tier 3: Core derived metrics
├── tokmd-analysis-entropy/ # Tier 3: High-entropy file detection
├── tokmd/src/analysis_explain/ # Tier 5: CLI metric explanation catalog
├── tokmd-analysis-fingerprint/ # Tier 3: Corporate fingerprint
├── tokmd-analysis/src/git/ # Tier 3: Git history adapters
├── tokmd-analysis-grid/ # Tier 3: Preset/feature matrix
├── tokmd-analysis-halstead/ # Tier 3: Halstead metrics
├── tokmd-analysis/src/imports/ # Tier 3: Import parsing + normalization
├── tokmd-analysis-license/ # Tier 3: License radar scanning
├── tokmd-analysis-maintainability/ # Tier 3: Maintainability index scoring
├── tokmd-analysis-near-dup/ # Tier 3: Near-duplicate detection
├── tokmd-analysis-topics/ # Tier 3: Topic-cloud extraction
├── tokmd-analysis-util/ # Tier 3: Shared analysis utilities
├── tokmd-gate/ # Tier 3: Policy evaluation
├── tokmd-core/ # Tier 4: Library facade + FFI
├── tokmd/ # Tier 5: CLI binary
├── tokmd-python/ # Tier 5: Python bindings (PyO3)
├── tokmd-node/ # Tier 5: Node.js bindings (napi-rs)
└── tokmd-wasm/ # Tier 5: Browser/worker bindings
Helper functionality that does not need an independent crates.io package now
lives as owner modules: module-key logic in tokmd-model, path/exclude/math and
tokeignore helpers in tokmd-scan, redaction/scan-args/badge/export-tree
rendering in tokmd-format, context policy/git helpers in tokmd-core, and
CLI/config/progress/tool-schema wiring in tokmd.
We prioritize deterministic outputs. This is critical because tokmd is used to generate receipts that must be stable over time.
Run standard unit tests for model logic and utility functions:
cargo testWe use insta for snapshot testing. These tests run the full CLI against fixtures.
Important: Line Endings & Bytes Our receipts include byte counts which are sensitive to line endings. To ensure cross-platform determinism:
- We enforce
text eol=lfin.gitattributesfor test fixtures and snapshots. - Always accept snapshots from an LF-normalized checkout (Linux/WSL/macOS or a normalized Windows git checkout).
- If you see byte count diffs (e.g.,
183vs172), check your line endings.
If you change output logic (e.g., new fields, different formatting):
- Run tests:
cargo test(they will fail with a diff). - Review changes:
cargo insta review(requirescargo-instainstalled). - Accept changes if they are intentional.
This guarantees that tokmd outputs (receipts) remain deterministic and stable.
Each crate may have its own tests in a tests/ directory. Run all tests with:
cargo test --workspaceProperty-based testing verifies that functions behave correctly across a wide range of randomly generated inputs. Instead of testing specific examples, you define properties that should always hold.
When to use: Functions with well-defined invariants (e.g., determinism, length constraints, cross-platform consistency).
Running property tests:
cargo test -p tokmd-scan properties # Run property tests for tokmd-scan
cargo test --workspace # Includes all property testsExample patterns (from path/redaction property tests):
use proptest::prelude::*;
proptest! {
/// Hash output is always exactly 16 hex characters.
#[test]
fn short_hash_length_is_16(input in ".*") {
let hash = short_hash(&input);
prop_assert_eq!(hash.len(), 16);
}
/// Same input always produces same hash (determinism).
#[test]
fn short_hash_is_deterministic(input in ".*") {
let h1 = short_hash(&input);
let h2 = short_hash(&input);
prop_assert_eq!(h1, h2, "Hash must be deterministic");
}
/// Unix and Windows paths produce identical hashes.
#[test]
fn short_hash_normalizes_separators(path in arb_path()) {
let unix_path = path.replace('\\', "/");
let windows_path = path.replace('/', "\\");
prop_assert_eq!(short_hash(&unix_path), short_hash(&windows_path));
}
}Key concepts:
- Define strategies to generate test data (e.g.,
arb_path()for path-like strings) - Use
prop_assert!andprop_assert_eq!instead of standard assertions - Use
prop_assume!to skip inputs that don't meet preconditions - Proptest will shrink failing cases to minimal reproducible examples
Fuzz testing bombards functions with arbitrary byte sequences to find crashes, panics, and edge cases. This is especially useful for parsing code and security-sensitive operations.
Prerequisites:
rustup install nightly
cargo +nightly install cargo-fuzzRunning fuzz targets:
cargo +nightly fuzz run fuzz_entropy --features content
cargo +nightly fuzz run fuzz_json_types --features types
cargo +nightly fuzz run fuzz_redact --features redactAvailable targets (see fuzz/README.md for complete list):
| Target | Feature | Description |
|---|---|---|
fuzz_entropy |
content |
Entropy calculation |
fuzz_json_types |
types |
JSON deserialization of receipt types |
fuzz_normalize_path |
model |
Path normalization |
fuzz_toml_config |
config |
tokmd.toml config parsing |
fuzz_redact |
redact |
Path redaction |
Using dictionaries for better coverage:
cargo +nightly fuzz run fuzz_json_types --features types -- -dict=fuzz/dict/json.dictLimiting input size:
cargo +nightly fuzz run fuzz_entropy --features content -- -max_len=4096For full documentation, see fuzz/README.md.
Mutation testing verifies test suite quality by introducing small changes (mutations) to the code and checking if tests catch them. Surviving mutations indicate gaps in test coverage.
Install:
cargo install cargo-mutantsRunning locally:
# Test mutations in a specific file
cargo mutants --file crates/tokmd-format/src/redact/mod.rs
# Test a specific crate (run from crate directory for proper test scoping)
cd crates/tokmd-format && cargo mutants
# Test with all features enabled
cargo mutants --all-featuresInterpreting results:
- Killed: Test suite caught the mutation (good)
- Timeout: Mutation caused tests to hang (usually acceptable)
- Unviable: Mutation caused compilation failure (neutral)
- Missed/Survived: Mutation was not detected (indicates a test gap)
Configuration (.cargo/mutants.toml):
all_features = true
gitignore = true
timeout_multiplier = 2.0
# Exclude test code from mutation
exclude_globs = ["**/tests/**", "fuzz/**"]
# Exclude boilerplate
exclude_re = ["impl.*Display", "fn main\\("]CI integration: Mutation testing runs automatically on PRs via .github/workflows/mutants.yml:
- Only tests changed
.rsfiles (up to 20 files) - Generates a summary JSON with survivors
- PRs with surviving mutations will fail CI
When mutations survive:
- Review the mutation to understand what behavior wasn't tested
- Add a test that specifically exercises that code path
- Re-run mutation testing to verify the mutation is now caught
- Run
cargo xtask lint-fixto auto-fix formatting and clippy issues. - Run
cargo fmt-fixfor a fast, Windows-safe fmt-only fix. - Run
cargo fmt-checkto verify formatting only. - Run
cargo xtask gate --checkto verify the full quality gate locally. cargo xtask gate --checknow uses a disposable tempCARGO_TARGET_DIRand forcesCARGO_INCREMENTAL=0unless you overrideCARGO_TARGET_DIRyourself, so repeated gate runs do not leave a hugetarget/tree behind.- On Unix-like systems,
cargo xtask gate --checkalso refuses to start when free disk drops below theTOKMD_MIN_FREE_GBthreshold. - The gate first prunes stale
tokmd-gate-target-*temp dirs left by earlier killed/cancelled runs (older thanTOKMD_GATE_STALE_HOURS, default3) so accumulated orphan target trees do not trip that guard on long-lived self-hosted runners (issue #309). - Run
cargo trim-target --checkto inspect reclaimabletarget/debugfootprint. - Run
cargo trim-targetto drop Windows PDBs and incremental state fromtarget/debugwithout a fullcargo clean. - If you need full local symbols on Windows for a debugging session, use
$env:RUSTFLAGS='-C debuginfo=2'; cargo test .... - Run
cargo sccache-checkto verify the optional local compiler cache. - Run
cargo with-sccache test --workspace --all-featuresfor cache-friendly local rebuilds. cargo with-sccache check|clippy|test ...now uses a disposable tempCARGO_TARGET_DIRby default when you have not already set one, so validation runs clean up after themselves.- The repo-native
sccachewrapper uses a deterministic per-workspaceSCCACHE_SERVER_PORT; setSCCACHE_SERVER_PORTyourself if you need to override it. - For cross-worktree cache reuse, run
cargo xtask sccache --basedir <PATH> -- test --workspace --all-features. - Expect the biggest
sccachewins on repeated library and dependency compiles; final binary and test-binary link steps still run uncached. - On Unix-like systems, both wrappers refuse to start when free disk drops below the
TOKMD_MIN_FREE_GBthreshold. Override that env var if you need a different floor for a larger machine.
-
Enricher implementations — Add new analysis enrichers in
crates/tokmd-analysis/src/:- Look at existing enrichers like
derived.rsorgit.rsfor patterns - Add new modules and wire them into
analysis.rs
- Look at existing enrichers like
-
Output format templates — Improve Markdown/SVG rendering in
crates/tokmd-format/src/analysis/ -
Language support — Extend import graph parsing for more languages
-
Documentation — Recipe examples, use cases, and tutorials
Enrichers are implemented as owner modules:
- Create a new module under
crates/tokmd-analysis/src/ - Add the data structures to
crates/tokmd-analysis-types/src/lib.rs - Wire it into the preset system in
crates/tokmd-analysis/src/analysis.rs - Add formatting support in
crates/tokmd-format/src/analysis/ - Add tests and update documentation
- Open an issue to discuss major changes first.
- Ensure your PR includes relevant tests.
- Update documentation if you change CLI behavior or flags.
- Reference the relevant section in
ROADMAP.md.
tokmd treats outputs as "receipts". If you modify the JSON output structure:
- Update struct definitions in
tokmd-typesortokmd-model. - Update formatting in
tokmd-format. - Update the formal schema in
docs/schema.json. - Increment
schema_versionfor breaking changes.
- Update struct definitions in
tokmd-analysis-types. - Update formatting in
tokmd-format::analysis. - Update
docs/SCHEMA.mddocumentation. - Increment
ANALYSIS_SCHEMA_VERSIONfor breaking changes.
Some features are gated to allow selective compilation:
git: Git history analysis (shells out togitcommand)content: File content scanning (entropy, TODOs, duplicates)walk: Filesystem traversal for assetshalstead: Halstead software science metrics (requirescontent+walk)
When adding new features with heavy dependencies, consider making them optional.
Publishing is handled via cargo xtask publish, which ensures correct dependency order, validates packaging, and handles propagation delays. See RELEASE.md for the full release process, including the CI-driven tag workflow.
# 1. Review the publish plan
cargo xtask publish --plan --verbose
# 2. Validate packaging (runs cargo package --list for each crate)
cargo xtask publish --dry-run
# 3. Publish for real (requires confirmation)
cargo xtask publish --yes
# 4. Publish and create git tag
cargo xtask publish --yes --tagThe xtask runs these checks before publishing:
- Clean git working directory
- Version consistency across all crates
- CHANGELOG.md contains the version
- All tests pass
Skip individual checks with --skip-git-check, --skip-version-check, --skip-changelog-check, --skip-tests, or all with --skip-checks.
If publishing fails partway through:
cargo xtask publish --from tokmd-format --yesjust publish-plan # cargo xtask publish --plan --verbose
just publish-dry # cargo xtask publish --dry-run
just publish # cargo xtask publish --yes
just publish-tag # cargo xtask publish --yes --tagNative bindings are available for Python and Node.js:
crates/
├── tokmd-core/ # FFI layer via ffi::run_json()
├── tokmd-python/ # PyO3 bindings → PyPI
└── tokmd-node/ # napi-rs bindings → npm
import tokmd
# All functions return native Python dicts
result = tokmd.lang(paths=["src"])
result = tokmd.analyze(paths=["."], preset="risk")- Install:
pip install tokmd - Functions:
lang(),module(),export(),analyze(),diff() - Releases GIL during long scans
const tokmd = require('tokmd');
// All functions return Promises
const result = await tokmd.lang({ paths: ['src'] });
const analysis = await tokmd.analyze({ paths: ['.'], preset: 'risk' });- Install:
npm install tokmd - Functions:
lang(),module(),export(),analyze(),diff() - Non-blocking via
spawn_blocking()
Both bindings use the unified FFI layer in tokmd-core:
pub fn run_json(mode: &str, args_json: &str) -> String
// Response: {"ok": bool, "data": {...}, "error": {...}}Design principles:
- JSON serialization at FFI boundary for simplicity
- Mirror the CLI's mental model (
lang,analyze,diff) - Return native language types (Python dicts, JS objects)
- Cross-platform wheels/prebuilds via CI matrix
If you use AI tools like Claude Code to contribute to this project, please refer to CLAUDE.md for project-specific guidance. This file contains:
- Build and test commands
- Architecture overview and crate hierarchy
- Critical patterns (deterministic output, path normalization, etc.)
- Testing strategies and snapshot management
- Key dependencies and documentation references
The CLAUDE.md file helps AI assistants understand the codebase conventions and produce contributions that align with project standards.