Skip to content
Back to skills

Vex

ASecurity

Reference for the `vex` code-search CLI — symbol search, usages (text + scope-bound `--strict`), structural AST patterns, call graph, semantic search, and content grep. Activates when picking the right vex subcommand, recalling filter flags, or checking the cross-file binder coverage matrix. Use whenever you'd otherwise reach for Read / Grep / Glob to locate code.

  • 11 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 6, 2026
ai-agentstypescriptpythonrustgojavaswiftkotlinc++c#bash

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned October 6, 2026

npx -y skills add tenatarika/vex --skill vex --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Vex?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Vex
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tenatarika-vex/badge)](https://www.skillsdirectory.com/skills/tenatarika-vex)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: vex
description: Reference for the `vex` code-search CLI — symbol search, usages (text + scope-bound `--strict`), structural AST patterns, call graph, semantic search, and content grep. Activates when picking the right vex subcommand, recalling filter flags, or checking the cross-file binder coverage matrix. Use whenever you'd otherwise reach for Read / Grep / Glob to locate code.
---

# vex — Code Search Reference

`vex` is a fast hybrid structural + semantic code search CLI. Source is parsed via tree-sitter, symbols stored in an mmap'd binary format. Supports exact name lookup (FST + bloom), structural AST patterns, persistent call graph, semantic similarity (HNSW + ONNX), and regex content search.

**Always prefer `vex` over Read / Grep / Glob for code lookup** — faster (~4ms typical), scope-aware, and avoids dragging whole files into context.

## Core Commands

### Exact symbol lookup (you know the name)

- `vex check "SymbolName"` — **does it exist?** Reports hit/miss only, no locations; bypasses the ranker. Always reach here FIRST when you have a literal name to find. `vex check "A" "B" "C"` for batch. Returns only yes/no per symbol (path:line lookup requires `vex show`).
- `vex show "SymbolName"` — extract the symbol body (**use INSTEAD of `Read` for specific symbols**). `vex show "A" "B" "C"` for multiple in one call.
- `vex usages "SymbolName" --strict` — **type-aware refs from the scope binder**; drops string-literal / comment / wrong-scope noise. Cross-file imports resolved for Rust, TypeScript, Python, C#, C++, Go, Java, Kotlin (others fall back to text-scan).
- `vex usages "SymbolName"` — same lookup without the binder; text-scan baseline.

### Fuzzy / keyword exploration (you don't know the exact name)

- `vex search "timeout retry"` — multi-word / keyword search via FST + BM25 + semantic RRF fusion. Use for "find me something about X". **Returns ranked NEIGHBORS** when no symbol literally matches — that's the design, not a bug. For "I know the name, just find it" use `vex check` instead.
- `vex search "handle alert" --semantic` — search by meaning (requires `--semantic` index).
- `vex grep "pattern"` — regex content search; use when you need TEXT (string literals, comments, config values), not symbols.
- `vex outline path/to/file.py` — file structure overview.

## Structural Patterns (AST)

- `vex pattern 'class $NAME(BaseModel):' --lang <lang> --why` — AST pattern matching with diagnostic trace
- Metavariables:
  - `$X` — single token / node
  - `$$ARGS` — multi-node (argument list, method params)
  - `$$$BODY` — multi-node (block, function body)
  - `$_` — wildcard (ignored in back-references)
  - Same name = back-reference (must agree across captures)
- Composition: ` && ` (intersect — captures must agree) and ` || ` (union). `&&` binds tighter than `||`. Both must be space-flanked at depth 0.
- `--why` — JSON trace on stderr explaining indexed vs live-scan fallback.

## Call Graph

- `vex implementations "BaseClass"` — types extending a class/interface (includes generic-parameterised bases)
- `vex subtypes "BaseClass"` — transitive descendants via extends/implements (requires v8+ index with hierarchy edges; no live-walk fallback)
- `vex callers "function_name"` / `vex callees "function_name"` — direct edges from the persistent call graph (~4ms)
- `vex paths A B` — all caller chains from A to B (multi-hop, max 6 hops)
- `vex reachable Target` — every symbol that transitively calls Target
- `vex tests-for Target` — test functions that transitively cover `Target` (post-filters `reachable` by test-path globs + name heuristic; rows carry a `framework` label so an agent can pick the right runner). `--include-fixtures` to also surface test-path helpers. `--test-pattern '<glob>'` (repeatable) REPLACES the default pattern set. Empty result → exit 1 (can mean "no test files" or "symbol not covered").

## Architecture (symbol clusters)

- `vex modules` — de-facto modules: clusters of symbols that call/reference each other (computed by `vex index`; label = dominant path prefix, size, cohesion, hub symbols). `--min-size 3`, `--sort size|cohesion`, `--members N`, `--include`/`--exclude`/`--exclude-tests` (scope members and hubs; out-of-scope hubs are dropped), `--limit 50` (≥1; with a symbol it caps matches, `symbols_total` in JSON; `--min-size` is list-mode only).
- `vex modules "Symbol"` — that symbol's status (`clustered` / `unclustered` / `not_eligible` / `new_since_build`) and its cluster with up to 25 members (path, then line). Alias: `vex clusters`.
- Empty result → exit `1` + `results.empty_reason` (`clusters_not_built`, `no_clusters_found`, `filtered_all`, `symbol_not_found`, `symbol_unclustered`); exit `2` if the cluster section is corrupt. `--no-clusters` at index time skips them.
- After `vex update` clusters are frozen: JSON `stale: true` / `new_since_build: N`, text ends with a `!` line. Run `vex index` to recompute. `--workspace` groups by repo.

## Diff & Similarity

- `vex diff --base <rev>` — symbol-level diff (added / removed / moved / body-changed) over files touched on the branch. `--base` is required.
- `vex similar "SymbolName"` / `vex duplicates` — semantic similarity (requires `--semantic` index)
- `--explain` — adds identifier-overlap reasoning + unified diff to `similar` / `duplicates`
- `--why` — JSON trace on stderr for `similar` and `duplicates` (shows threshold applied, filter results)

## Filters

Apply to most search-shaped commands:

- `--include '<glob>'` / `--exclude '<glob>'` — repeatable path globs (case-sensitive; `--exclude` wins). The older `--filter-path` / `--filter` is a single path-substring filter.
- `--exclude-tests` — drop test files (from `src/util/test_paths.rs` predicate: `tests/`, `test/`, `*_test.*`, `test_*.py`, `*.spec.ts`, `__tests__/`, `tests.rs`, `*Tests.swift`, `*_spec.rb`; same set as `tests-for`); composes with include/exclude; MCP `exclude_tests: true`. Path-only: Rust `#[cfg(test)] mod tests` inside a non-test file is NOT excluded.
- `--kind fn,struct` — boost or restrict to result kinds (multi-value; aliases: `def` = all definitions, `comment` = markdown headings, `test` = test-path symbols)
- `--visibility public|pub|private|priv|protected|internal` — symbol metadata post-filter (matches explicit visibility in signature)
- `--async-only` / `--no-async` / `--static-only` / `--sealed-only` — language-agnostic metadata gates
- `--threshold 0.8` (a.k.a. `--min-score`) — score cutoff for `similar` / `duplicates`
- `--exclude-generated` — drop machine-generated files (protoc / sqlc / bindgen / Diesel /
  OpenAPI Generator banners) from `search` results. Use when vendored `*.pb.go` /
  `*_pb2.py` bury real hits. Header heuristic: a generator that writes no banner is not
  detected, and a generated file someone hand-edited still carries its banner and will be
  hidden. If it suppresses everything, vex says so on stderr.
- `--why` — JSON trace on stderr (on `search`, `pattern`, `usages`, `similar`, `duplicates`; via MCP surfaces as `_meta.why`)
- `--format compact` / `--format json` — token-efficient output for automated workflows

## Cross-Language (polyglot repos, microservices)

**vex has no cross-language symbol edges, by design.** `usages` / `callers` stop at the
language boundary — a TS `fetch` call and the Go handler that serves it share no symbol.

To cross the boundary, search for the **shared string**, then pivot back to structure:

```bash
vex grep 'api/v1/invoices'        # route path — matches the TS call site AND the Go route
vex grep 'invoice.created'        # queue topic
vex grep 'CreateInvoiceRequest'   # proto message → its generated stubs in every language
vex show InvoiceHandler           # then pivot to structure once you know the name
```

Match the **stable prefix**, not the full path: route params differ per framework
(`/invoices/{id}` vs `/invoices/:id` vs an interpolated template literal). `vex grep` is
backed by a trigram skip-index, so a full-repo string scan is cheap.

Add `--exclude-generated` to `search` when vendored stubs bury the hand-written code.

Full walkthrough: `docs/COOKBOOK.md` → Recipe 6.

## Rules of Thumb

- **`vex check <Symbol>` instead of `Grep <Symbol>`** when you know the exact name. Reports hit/miss only and bypasses the ranker; `vex show` gets the location and body. `vex search` may surface neighbors (callers, imports) if the symbol isn't defined locally — fine for fuzzy exploration, wrong for "does it exist".
- **`vex show` instead of `Read`** when you need a specific function or class.
- **`vex grep` instead of `Grep`** when searching string literals, comments, or config values (text, not symbols).
- **`vex usages --strict` for refactor work** — text-scan refs lie about scope; `--strict` reads the persistent reference-edges section built from the scope binder.
- **`vex search "keyword phrase"`** is for FUZZY / multi-word / "find me something about X" — not for exact identifier lookup. v1.15.0+ prints a stderr drift hint when an identifier-shaped query returns 0 FST hits, suggesting `check`/`show`/`usages --strict`.
- **Re-index after a format-version bump**: `vex update` is incremental but won't recover from a bump; run `vex index` after upgrading the binary.

## Indexing

```bash
vex index             # structural only (fast)
vex index --semantic  # with embeddings (slower, enables semantic search + similar/duplicates)
vex update            # incremental update — only changed files
```

## Cross-File Binder Coverage (for `usages --strict`)

| Language   | Import resolved cross-file                                              |
|------------|-------------------------------------------------------------------------|
| Rust       | `use foo::Bar;`                                                         |
| TypeScript | `import { Bar } from './foo'` (named / default / namespace / type-only) |
| Python     | `import foo`, `from foo import Bar` (incl. aliases)                     |
| C#         | `using A.B.C;` (simple / `static` / `Alias = ...` / `global`)           |
| C++        | `using std::vector;`, `using V = T;`, `namespace alias = ns;` (quoted `#include "..."` also resolves cross-file; `#include <...>` system headers and `using namespace` stay unresolved) |
| Go         | `import "math/rand"`, `import mr "math/rand"`                           |
| Java       | `import a.b.C;`, `import static a.b.C.m;`                               |
| Kotlin     | `import a.b.C`, `import a.b.C as D`                                     |

Wildcard forms fall back to text-scan refs: C++ `#include`, C++ `using namespace`, Python `from x import *`, Rust `use foo::*`, Java `import a.b.*;`, Kotlin `import a.b.*`, Go dot / blank imports.

## Common Pitfalls

- **Meaningful-identifier filter**: pure-lowercase identifiers without `_` (e.g. `compute`, `calc`, `total`) are rejected by the indexer, so `usages --strict` on those bails to text-scan. Use snake_case / mixed-case symbols when designing test fixtures.
- **Cache directory**: keyed by `xxh3` of the canonical project path — mismatched paths land in different cache dirs, so don't `cd` through symlinks if you expect to hit warm cache.
- **Bench / format bump recovery**: `vex update` is incremental and **will not** rebuild a format-incompatible index. After a `vex` binary upgrade that bumps the format version, run `vex index` once to recreate.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…