what it does
Bodies out. Signatures in.
contasty walks a directory (respecting .gitignore), parses each supported
source file with ast-grep, and elides function
bodies, constant values, and long strings — keeping declarations, signatures, and types
intact. The result is one Markdown (or JSON) bundle, ready to paste into an LLM context window.
your source
use crate::cart::Cart;
/// Charge the user, emit a receipt.
pub fn checkout(c: &Cart) -> Receipt {
let t = c.total();
charge(t)?;
Receipt::new(t)
}contasty output
pub fn checkout(c: &Cart) -> Receipt {}
↓ comments & imports gone, body reduced, signature kept ↓
reduce to taste
Four ingredients you can leave out
--strip picks what to remove, per path, find-style. Comments, imports, and
bodies go by default; test signatures stay. Try to toggle them off/on.
use crate::db::Pool;
// open a pooled connection
pub fn connect(p: &Pool) -> Conn {
let c = p.acquire();
c.ping()?;
c
}
#[cfg(test)]
mod tests {
#[test]
fn pings() {}
}
comments stripped by default
Doc and line comments dropped. Re-add with --strip=!comments.
imports stripped by default
Use / import / require lines removed — the indentation goes too, no blank stub.
body stripped by default
Function, method, and closure bodies elided to an empty {} — still reparseable.
tests kept by default
Test signatures stay so the agent sees intent. Drop them with --strip=tests.
27 flavors
Every grammar ast-grep bundles, except Markdown
Each language is data, not code: a YAML ast-grep rule file matched against the tree. 18 do full body elision; the rest strip comments, imports, long strings, and values.
Need another? Add a language with a compiled .so tree-sitter grammar plus a rule
file — no rebuild.
context facts
See the reduction
contasty --stats reports original-vs-compacted line counts and a rough
token figure instead of the bundle. Fewer tokens, same shape — the agent still sees
every signature and type.
contasty --stats src/Context Facts
per src/ directory · example run
~tokens is a dependency-free estimate (~bytes ÷ 4), not a model tokenizer count. Use it for relative comparison.
get a taste
Install
# from crates.io
cargo install contasty
# with nix — run without installing, or add it to your profile
nix run github:mlavrinenko/contasty -- src/
nix profile install github:mlavrinenko/contasty
# strip a tree, write Markdown context
contasty src/ > context.md
# default-strip src/, keep tests/ whole, JSON output
contasty src/ --strip=none tests/ --format=json
Or grab a pre-built binary from the latest release. Single static binary — no Node, no runtime.
taste test
contasty vs repomix --compress
Same idea — walk, elide, print one document. Different strengths.
| contasty | repomix --compress | |
|---|---|---|
| Languages with body elision | 18 of 27 built-ins | 16 |
| Add a language, no rebuild | yes | no |
| Extend / override strip rules | yes | no |
| Gate comments / imports / tests / body | yes | partial |
| Optional reformat of result | yes | no |
| Runtime | single static binary | Node.js |
| Token counting | no (by design) | yes, multi-tokenizer |
| Remote repos / MCP server | no | yes |
Full table, plus ctx / aider repo-map comparisons, in the README.
the pantry
More to taste
Query files
Saved, reusable *.cty.yaml selectors in gitignore syntax — import & union scopes.
Custom rules
Extend or override any language's strip rules from contasty.toml.
Dynamic grammars
Bring a compiled .so tree-sitter grammar for a language ast-grep doesn't bundle.
Reformatting
Optional cosmetic tidy of the stripped output via embedded Topiary or a shell-out.
docs/reformatting.md →.gitignore modes
--ignore=enable|disable|reverse, per path — include ignored files or only them.