---
slug: "lspcmd"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/andreasjansson/lspcmd@main/README.md"
repo: "https://github.com/andreasjansson/lspcmd"
source_file: "README.md"
branch: "main"
---
# 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](#installation)
- [Quick start](#quick-start)
- [AI agent skill](#ai-agent-skill)
- [Commands](#commands)
  - [grep](#grep)
  - [files](#files)
  - [show](#show)
  - [refs](#refs)
  - [calls](#calls)
  - [graph](#graph)
  - [implementations](#implementations)
  - [supertypes / subtypes](#supertypes--subtypes)
  - [declaration](#declaration)
  - [rename](#rename)
  - [mv](#mv)
- [Symbol formats](#symbol-formats)
- [Daemon management](#daemon-management)
- [Workspace management](#workspace-management)
- [Configuration](#configuration)
- [Supported languages](#supported-languages)
- [Development](#development)
- [License](#license)

## Installation

### Using Homebrew (macOS)

```bash
brew install andreasjansson/tap/leta
```

### Using Cargo

```bash
cargo install leta
```

### From source

```bash
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:

```bash
# 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:

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

Get a full architectural overview:

```bash
leta graph                     # Full call graph as trees from entry points
```

Search for symbols:

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

Show symbol definitions:

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

Find references:

```bash
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:

```bash
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:

```bash
# 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.

```bash
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)
  -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:

```bash
# 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/"

# 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.

```bash
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.

```bash
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:

```bash
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.

```bash
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:

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

### calls

Show call hierarchy for a symbol.

```bash
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:

```bash
# 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
```

### graph

Show the full workspace call graph as trees rooted at entry points. This is
the most token-efficient way to understand the architecture of a codebase.
Test files are excluded by default.

```bash
leta graph [OPTIONS]

Options:
  --include-non-workspace  Include stdlib/dependency calls
  --include-tests          Include test files (excluded by default)
  -x, --exclude-path       Exclude paths matching regex (repeatable)
  -i, --include-path        Only include paths matching regex (repeatable)
  --include-orphans        Include symbols with no callers or callees
```

The output shows trees from entry points (functions nothing else calls),
largest subgraph first. Nodes already shown get a `↑` marker; recursive
calls get `↻`. Multi-language workspaces are grouped by language.

Examples:

```bash
# Full call graph
leta graph

# Only Go files
leta graph -i '\.go$'

# Include stdlib calls
leta graph --include-non-workspace

# Exclude generated code
leta graph -x generated -x proto
```

### implementations

Find implementations of an interface or abstract method.

```bash
leta implementations <SYMBOL> [OPTIONS]

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

Examples:

```bash
leta implementations Storage
leta implementations Storage.save
```

### supertypes / subtypes

Navigate type hierarchies.

```bash
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).

```bash
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.

```bash
leta rename <SYMBOL> <NEW_NAME>
```

Examples:

```bash
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.

```bash
leta mv <OLD_PATH> <NEW_PATH>
```

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

Examples:

```bash
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:

```bash
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.

```bash
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:

```bash
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`:

```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:

```bash
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` |
| OCaml | ocamllsp | `opam install ocaml-lsp-server` |
| Zig | zls | `brew install zls` |

## Development

```bash
# Run unit tests
./script/unit-test

# Run corpus tests
./script/corpus-test

# Run linter
./script/lint

# Format code
./script/format
```

## License

MIT
