Skip to content

andreasjansson/leta

Repository files navigation

leta - LSP Enabled Tools for Agents

leta is a command-line LSP client for semantic code navigation. It provides fast symbol search, reference finding, call hierarchy analysis, and refactoring operations by leveraging language server protocols across multiple programming languages.

$ leta grep "Handler$" -k class
src/handlers/auth.py:15 [Class] AuthHandler
src/handlers/user.py:22 [Class] UserHandler
src/handlers/admin.py:8 [Class] AdminHandler

$ leta show UserHandler
src/handlers/user.py:22-67

class UserHandler:
    def __init__(self, db: Database):
        self.db = db

    def get_user(self, user_id: int) -> User:
        return self.db.query(User).get(user_id)
    ...

Table of contents

Installation

Using Homebrew (macOS)

brew install andreasjansson/tap/leta

Using Cargo

cargo install leta

From source

git clone https://github.com/andreasjansson/leta
cd leta
cargo install --path crates/leta
cargo install --path crates/leta-daemon

Language servers

Ensure you have language servers installed for your target languages:

# Python
pip install basedpyright

# TypeScript/JavaScript
npm install -g typescript-language-server typescript

# Go
go install golang.org/x/tools/gopls@latest

# Rust
rustup component add rust-analyzer

# Ruby
gem install ruby-lsp

# C/C++
brew install llvm  # macOS
apt install clangd  # Ubuntu

Quick start

Initialize a workspace before using leta:

cd /path/to/your/project
leta workspace add

Search for symbols:

leta grep "User"               # Find symbols matching "User"
leta grep "^Test" -k function  # Find test functions

Show symbol definitions:

leta show UserRepository           # Show full class body
leta show UserRepository.add_user  # Show method body

Find references:

leta refs validate_email      # Find all uses of validate_email
leta refs UserRepository -n 2 # With 2 lines of context

AI agent skill

leta includes a skill file that teaches AI coding agents (like Claude Code or OpenCode) how to use leta effectively. The skill instructs agents to prefer leta show over reading files, leta refs over grepping for usages, etc.

OpenCode

Copy the skill to your OpenCode skills directory:

cp -r skills/leta ~/.config/opencode/skills/

Then load the skill with /skill leta or configure it to load automatically.

Claude Code

Copy the skill to your Claude Code skills directory:

# Personal (available across all your projects):
cp -r skills/leta ~/.claude/skills/

# Project-specific (commit to version control):
cp -r skills/leta .claude/skills/

Commands

grep

Search for symbols by regex pattern. Unlike text search tools, leta grep searches symbol names semantically—it finds function definitions, class declarations, method names, etc.

leta grep <PATTERN> [PATH] [OPTIONS]

Options:
  -k, --kind <KIND>        Filter by symbol kind (comma-separated: class, function, method, etc.)
  -x, --exclude <EXCLUDE>  Exclude files matching regex (repeatable)
  -d, --docs               Include documentation for each symbol
  -C, --case-sensitive     Case-sensitive matching
  -N, --head <N>           Maximum results (0 = unlimited) [default: 500]

The optional PATH argument filters files by matching a regex against the relative file path. This is simpler and more powerful than glob patterns.

Examples:

# Find all classes ending with "Handler"
leta grep "Handler$" -k class

# Find functions in Python files only
leta grep "validate" '\.py$' -k function

# Find symbols in a specific directory
leta grep "User" "models/"

# Find symbols in test files
leta grep "test" "test/"

# Search with documentation
leta grep "parse" -k function -d

# Exclude test files
leta grep "User" -x test -x mock

When to use leta grep vs ripgrep:

  • Use leta grep for: finding symbol definitions, filtering by kind, getting semantic matches
  • Use ripgrep for: searching file contents, string literals, comments, multi-word phrases

files

Show source file tree with line counts.

leta files [PATH] [OPTIONS]

Options:
  -x, --exclude <EXCLUDE>  Exclude files matching regex (repeatable)
  -i, --include <INCLUDE>  Include default-excluded dirs (repeatable)
  -f, --filter <FILTER>    Only include files matching regex
  -N, --head <N>           Maximum files (0 = unlimited) [default: 500]

Example output:

src
├── handlers
│   ├── auth.py (2.3KB, 89 lines, 1 class, 5 methods)
│   └── user.py (3.1KB, 112 lines, 2 classes, 8 methods)
├── models
│   └── user.py (1.8KB, 67 lines, 1 class, 4 methods)
└── main.py (845B, 32 lines, 2 functions)

4 files, 8.0KB, 300 lines

show

Print the full definition of a symbol.

leta show <SYMBOL> [OPTIONS]

Options:
  -n, --context <N>  Lines of context around definition [default: 0]
  -N, --head <N>     Maximum lines (0 = unlimited) [default: 500]

Examples:

leta show UserRepository           # Show full class
leta show UserRepository.add_user  # Show method
leta show "*.py:User"              # Filter by file
leta show COUNTRY_CODES            # Multi-line constants work too

refs

Find all references to a symbol.

leta refs <SYMBOL> [OPTIONS]

Options:
  -n, --context <N>  Lines of context around each reference [default: 0]
  -N, --head <N>     Maximum results (0 = unlimited) [default: 500]

Examples:

leta refs UserRepository
leta refs validate_email -n 2
leta refs "models.py:User"

calls

Show call hierarchy for a symbol.

leta calls [OPTIONS]

Options:
  --from <SYMBOL>          Show what SYMBOL calls (outgoing)
  --to <SYMBOL>            Show what calls SYMBOL (incoming)
  --max-depth <N>          Maximum recursion depth [default: 3]
  --include-non-workspace  Include stdlib/dependency calls
  -N, --head <N>           Maximum results (0 = unlimited) [default: 500]

At least one of --from or --to is required. Use both to find a path.

Examples:

# What does main() call?
leta calls --from main

# What calls validate_email()?
leta calls --to validate_email

# Find call path from main to save
leta calls --from main --to save --max-depth 5

implementations

Find implementations of an interface or abstract method.

leta implementations <SYMBOL> [OPTIONS]

Options:
  -n, --context <N>  Lines of context [default: 0]
  -N, --head <N>     Maximum results (0 = unlimited) [default: 500]

Examples:

leta implementations Storage
leta implementations Storage.save

supertypes / subtypes

Navigate type hierarchies.

leta supertypes <SYMBOL> [OPTIONS]  # Find parent types
leta subtypes <SYMBOL> [OPTIONS]    # Find child types

Options:
  -n, --context <N>  Lines of context [default: 0]
  -N, --head <N>     Maximum results (0 = unlimited) [default: 500]

declaration

Find the declaration of a symbol (useful for languages that separate declaration from definition).

leta declaration <SYMBOL> [OPTIONS]

Options:
  -n, --context <N>  Lines of context [default: 0]
  -N, --head <N>     Maximum results (0 = unlimited) [default: 500]

rename

Rename a symbol across the entire workspace.

leta rename <SYMBOL> <NEW_NAME>

Examples:

leta rename old_function new_function
leta rename UserRepository.add_user insert_user
leta rename "user.py:User" Person

mv

Move/rename a file and update all imports.

leta mv <OLD_PATH> <NEW_PATH>

Supported by: TypeScript, Rust, Python (via basedpyright).

Examples:

leta mv src/user.ts src/models/user.ts
leta mv lib/utils.rs lib/helpers.rs

Symbol formats

Most commands accept symbols in these formats:

Format Description
SymbolName Find symbol by name
Parent.Symbol Qualified name (Class.method, module.function)
path:Symbol Filter by file path pattern
path:Parent.Symbol Combine path filter with qualified name
path:line:Symbol Exact file + line number (for edge cases)

Examples:

leta show UserRepository            # By name
leta show UserRepository.add_user   # Qualified
leta show "*.py:User"               # Path filter
leta show "models/user.py:User"     # Specific file

Daemon management

leta runs a background daemon that manages LSP server connections. The daemon starts automatically on first command and persists to make subsequent commands fast.

leta daemon start    # Start daemon
leta daemon stop     # Stop daemon
leta daemon restart  # Restart daemon
leta daemon info     # Show daemon status and active workspaces

Workspace management

Workspaces must be explicitly added before using leta:

leta workspace add              # Add current directory
leta workspace add /path        # Add specific path
leta workspace remove           # Remove current workspace
leta workspace restart          # Restart language servers
leta workspace info             # Show workspace info for current directory

Configuration

Configuration is stored in ~/.config/leta/config.toml:

[daemon]
log_level = "info"
request_timeout = 30
hover_cache_size = 268435456   # 256MB
symbol_cache_size = 268435456  # 256MB

[workspaces]
roots = ["/home/user/projects/myapp"]
excluded_languages = ["json", "yaml", "html"]

[formatting]
tab_size = 4
insert_spaces = true

[servers.python]
preferred = "basedpyright"

View configuration:

leta config

Logs are stored in ~/.cache/leta/log/.

Supported languages

Language Server Install
Python basedpyright pip install basedpyright
TypeScript/JavaScript typescript-language-server npm install -g typescript-language-server typescript
Go gopls go install golang.org/x/tools/gopls@latest
Rust rust-analyzer rustup component add rust-analyzer
Java jdtls brew install jdtls
Ruby ruby-lsp gem install ruby-lsp
C/C++ clangd brew install llvm
PHP intelephense npm install -g intelephense
Lua lua-language-server brew install lua-language-server
Zig zls brew install zls

Development

# Run unit tests
./script/unit-test

# Run corpus tests
./script/corpus-test

# Run linter
./script/lint

# Format code
./script/format

License

MIT

About

Command-line LSP client for agentic coders

Resources

License

Stars

Watchers

Forks

Packages

No packages published