GitHub - jahala/tilth

4 min read Original article ↗

tilth

Smart code reading for humans and AI agents.

tilth is what happens when you give ripgrep, tree-sitter, and cat a shared brain.

$ tilth src/auth.ts
# src/auth.ts (258 lines, ~3.4k tokens) [outline]

[1-12]   imports: express(2), jsonwebtoken, @/config
[14-22]  interface AuthConfig
[24-42]  fn validateToken(token: string): Claims | null
[44-89]  export fn handleAuth(req, res, next)
[91-258] export class AuthManager
  [99-130]  fn authenticate(credentials)
  [132-180] fn authorize(user, resource)

Small files come back whole. Large files get an outline. Drill in with --section:

$ tilth src/auth.ts --section 44-89
$ tilth docs/guide.md --section "## Installation"

Search finds definitions first

$ tilth handleAuth --scope src/
# Search: "handleAuth" in src/ — 6 matches (2 definitions, 4 usages)

## src/auth.ts:44 [definition]
  [24-42]  fn validateToken(token: string)
→ [44-89]  export fn handleAuth(req, res, next)
  [91-120] fn refreshSession(req, res)

## src/routes/api.ts:34 [usage]
→ [34]   router.use('/api/protected/*', handleAuth);

Tree-sitter finds where symbols are defined — not just where strings appear. Each match shows its surrounding file structure so you know what you're looking at without a second read.

Why

I built this because I watched AI agents make 6 tool calls to find one function. glob → read → "too big" → grep → read again → read another file. Each round-trip burns tokens and inference time.

tilth gives structural awareness in one call. The outline tells you what's in the file. The search tells you where things are defined. --section gets you exactly the lines you need.

Install

cargo install tilth
# or
npx tilth

Prebuilt binaries on the releases page.

MCP server

tilth install claude-code      # ~/.claude.json
tilth install cursor           # ~/.cursor/mcp.json
tilth install windsurf         # ~/.codeium/windsurf/mcp_config.json
tilth install vscode           # .vscode/mcp.json (project scope)
tilth install claude-desktop

Add --edit to enable hash-anchored file editing (see Edit mode):

tilth install claude-code --edit

Or call it from bash — see AGENTS.md for the agent prompt.

How it decides what to show

Input Behaviour
0 bytes [empty]
Binary [skipped] with mime type
Generated (lockfiles, .min.js) [generated]
< ~1500 tokens Full content with line numbers
> ~1500 tokens Structural outline with line ranges

Token-based, not line-based — a 1-line minified bundle gets outlined; a 120-line focused module prints whole.

Edit mode

Install with --edit to add tilth_edit and switch tilth_read to hashline output:

42:a3f|  let x = compute();
43:f1b|  return x;

tilth_edit uses these hashes as anchors. If the file changed since the last read, hashes won't match and the edit is rejected with current content shown:

{
  "path": "src/auth.ts",
  "edits": [
    { "start": "42:a3f", "content": "  let x = recompute();" },
    { "start": "44:b2c", "end": "46:e1d", "content": "" }
  ]
}

Large files still outline first — use section to get hashlined content for the part you need.

Inspired by The Harness Problem.

Usage

tilth <path>                      # read file (outline if large)
tilth <path> --section 45-89      # exact line range
tilth <path> --section "## Foo"   # markdown heading
tilth <path> --full               # force full content
tilth <symbol> --scope <dir>      # definitions + usages
tilth "TODO: fix" --scope <dir>   # content search
tilth "/<regex>/" --scope <dir>   # regex search
tilth "*.test.ts" --scope <dir>   # glob files
tilth --map --scope <dir>         # codebase skeleton

Speed

CLI times on x86_64 Mac, 26–1060 file codebases. Includes ~17ms process startup (MCP mode pays this once).

Operation ~30 files ~1000 files
File read + type detect ~18ms ~18ms
Code outline (400 lines) ~18ms ~18ms
Symbol search ~27ms
Content search ~26ms
Glob ~24ms
Map (codebase skeleton) ~21ms ~240ms

Search, content search, and glob use early termination — time is roughly constant regardless of codebase size.

What's inside

Rust. ~5,000 lines. No runtime dependencies.

  • tree-sitter — AST parsing for 9 languages (Rust, TypeScript, JavaScript, Python, Go, Java, C, C++, Ruby)
  • ripgrep internals (grep-regex, grep-searcher) — fast content search
  • ignore crate — parallel directory walking, searches all files including gitignored
  • memmap2 — memory-mapped file reads (no buffers)
  • DashMap — concurrent outline cache, invalidated by mtime

Search runs definitions and usages in parallel via rayon::join.

Name

tilth — the state of soil that's been prepared for planting. Your codebase is the soil; tilth gives it structure so you can find where to dig.

License

MIT