# ty-find

> LSP adapter for AI coding agents — symbol name in, structured code intelligence out.

ty-find (`tyf`) lets AI coding agents query Python's type system by symbol name.
It wraps ty's LSP server so that `tyf show MyClass` returns the definition
location, type signature, and all references — without requiring file paths or
line numbers. A background daemon keeps responses under 100ms.

Also useful for humans who want fast, precise Python navigation from the terminal.

## Relationship to ty

tyf is an adapter over ty's LSP server; it does no Python analysis of its own.

- **tyf does not ship ty.** ty is a required peer dependency you install yourself
  (`uv add --dev ty`). tyf does not bundle or pin a copy — at runtime it binds to
  whatever ty is on PATH, falling back to `uvx ty`. The analysis you get depends on
  the installed ty version, not the tyf version.
- **ty is pre-release (`0.0.x`)** with frequent breaking changes, including to
  LSP/diagnostic behavior. A ty upgrade can change tyf output with no change to tyf.
  Keep ty current within the supported range; if results shift after a ty upgrade,
  check `ty --version` (or `uvx ty --version`) against the supported list.
- **Navigation, not diagnostics.** tyf surfaces ty's navigation/symbol knowledge —
  definitions, signatures, references, members — not ty's type-checking diagnostics.
  tyf never reports type errors, so the false-positive noise that affects `ty check`
  on dynamic frameworks (Pydantic, SQLAlchemy, etc.) does not appear in tyf output.

## Commands

### show — Definition, type signature, and usages of a symbol by name

The most useful command. Given a symbol name, it shows:
- Where it's defined (file + line)
- Its type signature
- Optionally, docstring (`--doc`) and every place it's used (`--references`)
- Or everything at once with `--all` (doc + refs + test refs)

No file path needed — it searches the whole project by name.
`inspect` still works as a hidden alias for backward compatibility.

When ty cannot resolve a type (e.g. a third-party library's stubs are not
installed), the `Signature` shows the literal source annotation the developer
wrote instead of `Unknown`; a symbol with no annotation is marked
`(unannotated)`.

```
tyf show MyClass
tyf show MyClass.get_data                # narrow to a specific class method
tyf show calculate_sum UserService       # multiple symbols at once
tyf show MyClass --references            # also list all usages
tyf show MyClass --doc                   # include docstring
tyf show MyClass --all                   # show everything: doc + refs + test refs
tyf show MyClass --file src/models.py    # narrow to one file
tyf --format json show MyClass           # JSON output for scripting
```

### find — Find where a symbol is defined by name

Given a symbol name, returns the file and line where it's defined.
Searches the whole project — no need to know which file it's in.
Use `--fuzzy` for partial/prefix matching with richer output (kind + container).

```
tyf find calculate_sum
tyf find Calculator.add                     # find a specific class method
tyf find calculate_sum multiply divide      # multiple symbols at once
tyf find handler --file src/routes.py       # narrow to one file
tyf find handle_ --fuzzy                    # fuzzy/prefix match
```

### refs — All usages of a symbol across the codebase

Find every location in the codebase that references a symbol. Supports both
name-based and position-based lookup. Essential before renaming or removing
code — tells you exactly what will break.

```
tyf refs my_func my_class                   # by name
tyf refs Calculator.add                     # refs for a specific method
tyf refs -f myfile.py -l 10 -c 5           # by position
tyf refs file.py:10:5 my_func              # mixed
... | tyf refs --stdin                      # piped input
```

### members — Public interface of a class

Shows the public interface of a class: methods with signatures, properties,
and class variables with types. Like `list` scoped to a class, with type info.
Excludes private/dunder members by default; `--all` includes everything.
Only shows directly-defined members, not inherited ones.

When ty cannot resolve a member's type (e.g. missing third-party stubs), the
literal source annotation is shown instead of `Unknown` (read from source, so
it works without the project's dependencies installed); a member with no
annotation is marked `(unannotated)`.

```
tyf members MyClass
tyf members MyClass UserService          # multiple classes at once
tyf members MyClass --all                # include __init__, __repr__, etc
tyf members MyClass --file src/models.py # narrow to one file
```

### list — All functions, classes, and variables defined in a file

Shows all functions, classes, and variables defined in a file — like a table
of contents. Useful for getting an overview of a file you haven't seen before.

```
tyf list src/services/user.py
```

### daemon — Manage the background server

The daemon starts automatically on first use and shuts down after 5 minutes
of inactivity. You usually don't need these commands.

```
tyf daemon start     # start manually
tyf daemon status    # check if running, uptime, cache info
tyf daemon stop      # stop the daemon
```

## Output Formats

All commands support `--format` (placed before the subcommand):
- `human` — default, readable text with source context
- `json` — structured JSON, good for piping to jq or scripting
- `csv` — tabular, good for spreadsheets or further processing
- `paths` — just file paths, one per line (for xargs, editors, etc.)

```
tyf --format json show MyClass
tyf --format paths find calculate_sum | head -1 | xargs code
```

## Why tyf?

**vs grep/ripgrep:**
- grep matches text; tyf understands Python's type system
- grep returns hits in comments, strings, and docstrings; tyf returns only real symbol references

**vs raw LSP (in editors):**
- LSP requires file:line:col positions to answer queries
- An LLM doesn't know positions without searching first — it would need to grep, which is imprecise
- tyf accepts symbol names directly, resolves positions internally, and returns structured results

Use **grep** for: string literals, config values, TODOs, non-Python files.

## Performance Tips

Install [ripgrep](https://github.com/BurntSushi/ripgrep) (`rg`) to speed up lookups for non-existent symbols. When a symbol isn't found on the first LSP attempt, tyf uses `rg` to quickly check if the symbol text exists in any `.py` file. If not, it skips the retry chain (~3 seconds) and returns immediately.

## Installation

Requires [ty](https://github.com/astral-sh/ty) (`uv add --dev ty`) — see Relationship to ty above.
Optional: [ripgrep](https://github.com/BurntSushi/ripgrep) for faster non-existent symbol lookups.

```
pip install ty-find
# or
uv add --dev ty-find
```

## Supported ty versions

Placeholder — pending the version-floor task. The concrete list of supported ty
versions is owned by a single source of truth (version-floor / CI task) that is not
in the repository yet, so no version numbers are listed here (and none are invented).
Policy: tyf is tested against specific pinned ty versions and supports a contiguous
range, from a documented floor up to the latest tested release. Until the source
lands, check your own version with `ty --version` (or `uvx ty --version`).

## Testing

tyf's integration tests drive a real `ty lsp` process (ty is never mocked): each test
spawns the actual tyf binary, which starts a real daemon and a real ty LSP server, and
asserts on structured output for fixtures at the repo root (`example.py`,
`test_project/`, `test_project2/`). Suites live in `tests/integration/`.

```
uv add --dev ty              # ty required for integration tests
cargo test --all-features    # unit + integration
cargo test --test test_basic # just the basic integration suite
```

CI runs `cargo test --all-features` with ty installed, plus a smoke-test workflow
(`benchmarks/smoke.sh`) against the release binary.

## Troubleshooting

- **Empty results for something that should exist** has two causes. (a) The symbol
  isn't statically visible (dynamic constructs: `getattr`, runtime-generated classes,
  metaprogramming). (b) ty couldn't resolve the environment (wrong interpreter, missing
  stubs, third-party package not installed). Diagnose with `ty check <file>`: unresolved
  imports mean an environment problem, not a tyf bug.
- **Which ty am I running, and is it supported?** Print `ty --version` (or
  `uvx ty --version`) and compare against the supported list.
- **Signatures show a source annotation or `(unannotated)`.** Expected, not a bug:
  when ty can't resolve a type (e.g. missing stubs), tyf falls back to the literal
  source annotation instead of `Unknown`; no annotation is shown as `(unannotated)`.
