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)
...
- Installation
- Quick start
- AI agent skill
- Commands
- Symbol formats
- Daemon management
- Workspace management
- Configuration
- Supported languages
- Development
- License
brew install andreasjansson/tap/letacargo install letagit clone https://github.com/andreasjansson/leta
cd leta
cargo install --path crates/leta
cargo install --path crates/leta-daemonEnsure 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 # UbuntuInitialize a workspace before using leta:
cd /path/to/your/project
leta workspace addSearch for symbols:
leta grep "User" # Find symbols matching "User"
leta grep "^Test" -k function # Find test functionsShow symbol definitions:
leta show UserRepository # Show full class body
leta show UserRepository.add_user # Show method bodyFind references:
leta refs validate_email # Find all uses of validate_email
leta refs UserRepository -n 2 # With 2 lines of contextleta 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.
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.
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/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 mockWhen to use leta grep vs ripgrep:
- Use
leta grepfor: finding symbol definitions, filtering by kind, getting semantic matches - Use ripgrep for: searching file contents, string literals, comments, multi-word phrases
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
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 tooFind 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"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 5Find 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.saveNavigate 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]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 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" PersonMove/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.rsMost 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 fileleta 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 workspacesWorkspaces 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 directoryConfiguration 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 configLogs are stored in ~/.cache/leta/log/.
| 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 |
# Run unit tests
./script/unit-test
# Run corpus tests
./script/corpus-test
# Run linter
./script/lint
# Format code
./script/formatMIT