mcpField
V
vex

vex

本地服务
github.com开发工具121 周前更新

关于 vex

vex 是面向开发者和编码 Agent 的本地代码索引与搜索工具。它结合符号查询、文本词项排序和语义搜索,并支持调用关系查询与 AST 模式匹配,基于预建索引返回紧凑结果。

主要功能

  1. 01结构、BM25 与语义混合搜索
  2. 02查找符号并提取其代码体
  3. 03查询调用方、被调用方和实现
  4. 04匹配 AST 模式
  5. 05在支持的语言中严格搜索引用
  6. 06为 Agent 工作流提供精简结果

适用场景

  1. 01在已索引代码库中快速定位符号
  2. 02探索代码时追踪调用关系
  3. 03名称未知时按概念查找代码
  4. 04在支持的语言中匹配语法模式

MCP 相关

项目包含 vex-mcp 这一 MCP 服务,向 MCP 客户端提供 vex 代码搜索能力。它运行 vex 命令行工具,需将其安装并加入 PATH,或通过 VEX_BIN 指定路径。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库
安装包
mcpbvex-mcp-aarch64-apple-darwin.mcpb1.28.0
mcpbvex-mcp-x86_64-unknown-linux-gnu.mcpb1.28.0
mcpbvex-mcp-x86_64-pc-windows-msvc.mcpb1.28.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

Vex

许可证:MIT CI Rust 命令 语言 测试

快速的结构化与语义混合代码搜索。Vector(向量)+ index(索引)。

为什么选择 Vex? · 对比 · 安装 · 快速开始 · 命令 · 配置 · 搜索原理 · 基准测试 · 支持的语言 · 集成 · 测试 · 架构

$ vex check "TelemetryProcessor"           # 4ms — does it exist? where? (exact name)
$ vex show "TelemetryProcessor"            # extract the class body (not the whole file)
$ vex usages "Config" --strict             # who references this symbol? (binder-resolved, no noise)
$ vex callers "process_event"              # who calls this function? (~4ms; covers module-scope + Python/Java decorators)
$ vex implementations "BaseService"        # who extends/implements this?
$ vex search "timeout retry"               # fuzzy / multi-word — BM25 finds rare body terms
$ vex search "handle alert" --semantic     # find by meaning, not just name
$ vex pattern 'fn $NAME($$$) -> Result' --lang rust    # AST pattern matching (like ast-grep)
$ vex similar "PaymentService"             # semantically close symbols
$ vex duplicates --threshold 0.95          # near-duplicate pairs
$ vex bundle --mode symbol --symbol Foo    # body + callers + callees + similar in 1 call

选对工具:对于“Foo 是否存在?”,使用 vex check;对于“帮我找一些关于重试的内容”,使用 vex search。search 会进行排序融合——当没有符号完全匹配时,它会显示相邻项(调用者/导入项),这很适合探索,却不适合按确切名称查找。v1.15.0 会在标识符形式的 search 返回 0 个 FST 命中时输出 stderr 提示。

为什么选择 Vex?

  • 索引后搜索约需 4–5 ms——基于 FST 的 O(query_len) 查找,而非 O(symbols);无论项目大小如何,耗时都保持不变。需要预先构建索引。索引只需构建一次(典型项目耗时数百毫秒),且构建的内容多于普通文本索引——FST + BM25 + 调用图 + 类型层次结构 + 三元组跳过索引——因此以较慢的构建速度换取成本低得多、内容更丰富的查询(参见基准测试)
  • 三通道混合搜索——结构化 FST(名称)+ BM25(正文中的罕见词)+ 语义 HNSW(含义),通过 Reciprocal Rank Fusion 融合。既能在不知道确切名称时找到符号,也能避免仅使用通用语义搜索时产生的大量噪声
  • 持久化调用图——vex callers/vex callees 从索引时构建的持久化索引读取(约 4 ms),而非实时扫描 tree-sitter 语法树(耗时数秒):callers 是按名称键控的 FST,callees 是密集 CSR 索引(v9+)。模块作用域表达式会通过合成的 <module:path> 调用者报告(阶段 14.1);Python + Java 函数/方法装饰器(阶段 14.2)、Kotlin 注解 + C# 方法/构造函数特性(阶段 14.2.2),以及 TypeScript 方法装饰器 + Rust 外部属性(阶段 14.2.1)都会向目标生成正向边。类级装饰器仍不可见——参见 docs/LIMITATIONS.md
  • 可插拔的嵌入器——Embedder trait + 注册表;可将 MiniLM-L6-v2 替换为未来面向代码的模型(BGE、CodeBERT),无需修改调用点
  • 节省 Token——紧凑输出在平均查找中通常比 grep 少用 6 倍 Token(压缩后的 JS/CSS 最多可少用 217 倍);vex show 只提取符号正文,而非整个文件
  • 支持索引 20 种语言——通过 tree-sitter 实现,分为三个覆盖层级:8 种绑定语言支持类型感知的 --strict usages(Rust / TypeScript / Python / C# / C++ / Go / Java / Kotlin);16 种 T1+T2a 语言支持索引模式预筛选;全部 20 种语言均支持基础结构化 + 语义搜索(支持语言矩阵参见支持的语言)
  • 单个二进制文件,无需配置——无需 LSP 服务、数据库或 Docker。只需运行 vex index && vex check Foo

Vex 不是什么

vex 是一个静态分析索引工具,不是语言服务器。请如实设定预期:

  • 不能替代 LSP。 不支持跳转到第三方包中的定义、重命名重构、类型检查或悬停文档。需要这些功能时,请继续使用 LSP。
  • vex search 返回的是排序融合结果,而非精确名称查找。 结构化 FST、BM25 和语义搜索通过 RRF 融合后,按相关性排序返回结果——如果索引中没有名为 Foo 的符号(它从依赖项导入、已删除或拼写有误),BM25 可能会将调用方/导入项作为定义返回。对于精确符号问题(“它是否存在?”“显示其函数体”“谁调用了它?”),请使用 vex check Foo / vex show Foo / vex usages Foo --strict——这些命令会绕过排序器。v1.15.0 会在类似标识符的查询没有 FST 命中时,向 stderr 打印一行提示。
  • 无法识别动态分派。 装饰器路由(@router.get("/path"))、通过字符串解析的工厂函数(uvicorn.run("main:app"))、反射(getattr(obj, name)())和宏展开引用对所有 vex 命令都不可见。vex grep '\bname\b' 是文本搜索的替代方案。
  • vex callers 对函数作用域外的支持不一致。 模块级表达式(如 app = create_app())会通过合成的 <module:path> 调用方报告(Phase 14.1)。Python + Java 函数/方法装饰器(Phase 14.2)、Kotlin 注解 + C# 方法/构造函数特性(Phase 14.2.2),以及 TypeScript 方法装饰器 + Rust 函数/方法上的外部属性(Phase 14.2.1)会生成正向边——vex callers GetMapping 会列出所有 Spring 处理程序,vex callers HttpGet 会列出所有 ASP.NET 操作,vex callers test 会列出所有 #[tokio::test]。类级装饰器(14.6)仍在路线图中。
  • vex usages 的质量因语言而异。 8 种受绑定器支持的语言可通过 --strict 获得适用于重构的引用;其余 11 种语言使用标识符扫描器,误报率更高。

完整的支持范围矩阵、具体复现示例以及各类查询的建议解决方法,请参阅 docs/LIMITATIONS.md。在 Python/FastAPI/Django 代码库上评估 vex 前,请先阅读该文档——框架模式是目前最常见的缺口。

对比

vexripgrepast-indexast-grepSerena
搜索内容符号定义所有文本符号定义AST 模式符号(通过 LSP)
需要索引吗?是(约 0.3-1s)否是(构建更快)否否
搜索速度约 4-5ms(预构建 FST,恒定)随代码库规模变化(小型约 8ms → 大型 100ms+)约 8-12ms(SQLite)约 30ms(扫描)取决于 LSP
语义搜索HNSW + 嵌入--------
模式匹配fn $NAME($$$)仅正则表达式--fn $NAME($$$)仅正则表达式
索引大小比 ast-index 小约 1.5-2 倍无索引SQLite + FTS5无索引无索引
Token 效率比 rg 少 6-217 倍基准比 rg 少约 3 倍不适用不适用
符号函数体提取vex show--------
语言19 种任意10+ 种10+ 种40+ 种(LSP)
重构--------重命名、移动、内联
运行时依赖无无无无Python + LSP

注意:vex 的搜索速度以索引已构建为前提。Ripgrep 和 ast-grep 无需预先建立索引,可立即用于任意目录。两者的取舍会随着使用次数而摊销:如果你会多次搜索同一代码库(这在 Agent 工作流中很常见),一次性索引成本很快就能收回。

最适合:在重视 Token 效率的 AI Agent 工作流中快速搜索符号。它不能替代基于 LSP 的工具(不支持重构,也不能跳转到依赖项中的定义)。

安装

# Install script (macOS arm64, Linux x86_64): vex + vex-mcp into ~/.local/bin
curl -fsSL https://raw.githubusercontent.com/tenatarika/vex/main/install.sh | sh

# Windows (PowerShell): vex + vex-mcp into %LOCALAPPDATA%\Programs\vex, added to PATH
irm https://raw.githubusercontent.com/tenatarika/vex/main/install.ps1 | iex

# Homebrew (macOS/Linux)
brew tap tenatarika/tap
brew install vex

# cargo-binstall: prebuilt release binaries, no compiler needed
cargo binstall vex-search                # → ~/.cargo/bin/vex
cargo binstall vex-search-mcp            # → ~/.cargo/bin/vex-mcp (MCP server)

# crates.io (compiles from source; the crate is `vex-search`, the binary is `vex`)
cargo install vex-search --locked        # → ~/.cargo/bin/vex
cargo install vex-search-mcp --locked    # → ~/.cargo/bin/vex-mcp (MCP server)

# From source (any platform with a Rust toolchain)
git clone https://github.com/tenatarika/vex.git
cd vex
cargo build --release
cp target/release/vex ~/.local/bin/

安装脚本接受 VEX_VERSION=1.28.0 来固定版本,接受 VEX_INSTALL_DIR 来指定其他安装位置,并接受 VEX_NO_MCP=1 来跳过 vex-mcp。在其他平台、musl 或 glibc 版本低于 2.34 的系统上,脚本会停止运行并提示从源码构建。cargo binstall 会在有对应版本时使用相同的发行版压缩包,其他情况下则从源码构建;在 Windows 上,它会安装不含压缩包中 DirectML.dll 的 vex.exe,因此 GPU 索引会退回到 CPU。

运行 cargo install vex-search(以及任何源码构建)所需条件:

  • 构建时需要网络连接:构建过程会下载预构建的 ONNX Runtime。预构建版本仅适用于 aarch64-apple-darwin、x86_64/aarch64-unknown-linux-gnu 和 x86_64/aarch64-pc-windows-msvc;在其他目标平台(Intel macOS、musl 等)上,请将 ORT_LIB_LOCATION 指向本地构建的 ONNX Runtime。
  • C/C++ 工具链(Xcode Command Line Tools、build-essential 或 MSVC Build Tools),用于构建 tree-sitter 语法。 GCC 必须为 12 或更高版本:GCC 11(Ubuntu 22.04 的默认版本)无法编译捆绑的 numkong 向量内核。请安装 gcc-12 g++-12,并使用 CC=gcc-12 CXX=g++-12 cargo install vex-search --locked 进行构建。
  • Linux:libssl-dev 和 pkg-config(Fedora:openssl-devel)。HTTP 技术栈通过 native-tls 链接 OpenSSL。
  • 首次运行 vex index --semantic 时会下载约 86 MB 的嵌入模型;结构搜索无需下载。
  • vex-search-mcp 只会安装 MCP 服务。它会运行 vex CLI,因此也要安装 vex-search 并确保 vex 位于 PATH 中,或者将 VEX_BIN 设为其完整路径。
Linux

每个 GitHub Release 都提供适用于 x86_64-unknown-linux-gnu 的预构建 vex。它需要 glibc 2.34 或更高版本(Ubuntu 22.04+、Debian 12+、RHEL / Rocky / AlmaLinux 9+);在较旧的系统上,请从源码构建:

curl -fsSL https://raw.githubusercontent.com/tenatarika/vex/main/install.sh | sh
# or by hand:
curl -L https://github.com/tenatarika/vex/releases/latest/download/vex-x86_64-unknown-linux-gnu.tar.gz | tar -xz
mv vex ~/.local/bin/      # or: sudo mv vex /usr/local/bin/
vex --version

此版本在 ubuntu-22.04 GitHub runner 上构建(链接 glibc,且已链接 libstdc++)。对于使用较旧 glibc 的发行版、基于 musl 的发行版(Alpine、未配置 nix-ld 的 NixOS),或 aarch64 Linux(Graviton、Pi 5、Ampere),请通过 cargo build --release 从源码构建。

Windows

每个 GitHub Release 都提供预构建的 vex.exe。通过 PowerShell 安装的最快方法:

irm https://raw.githubusercontent.com/tenatarika/vex/main/install.ps1 | iex

它会将 vex.exe、vex-mcp.exe 和 DirectML.dll 放入 %LOCALAPPDATA%\Programs\vex(每用户文件夹),并将其添加到用户的 PATH。VEX_VERSION、VEX_INSTALL_DIR 和 VEX_NO_MCP=1 的用法与 install.sh 相同。也可以手动安装:

  1. 从最新发行版下载 vex-x86_64-pc-windows-msvc.tar.gz
  2. 将 vex.exe 解压到稳定的位置(例如 C:\Users\<you>\bin\)——可在较新的 PowerShell 中运行 tar -xzf vex-x86_64-pc-windows-msvc.tar.gz,也可右键使用 7-Zip / WinRAR。安全提示:vex.exe 会从自身所在文件夹加载捆绑的 DirectML.dll,因此在多用户计算机或共享驱动器上,建议使用其他用户无法写入的目录(例如 C:\Program Files\vex\——但这样一来,运行 vex self-update 时需要提升权限的 shell)。请参阅 GPU_SUPPORT.md §6。
  3. 将该文件夹添加到 PATH(系统属性 → 环境变量 → 编辑 Path → 添加该文件夹)
  4. 打开新的终端并运行 vex --version

要更新,请运行 vex self-update——它会获取最新发行版,为你的平台选择合适的压缩包、验证其签名,并就地替换二进制文件。在 Windows 上,它还会安装/刷新捆绑的 DirectML.dll 旁置文件(文件内容相同时会跳过;如果旧版自更新程序删除了该文件,则会重新安装——直到 v1.16.0 的更新程序只会解压二进制文件)。macOS 和 Linux 也可以使用同一命令。

预构建二进制文件已内置 GPU 加速——Windows 使用 DirectML(适用于任何 DX12 GPU,仅需驱动;压缩包中捆绑了 redistributable DirectML.dll),macOS arm64 使用 CoreML。NVIDIA CUDA 需要通过源码构建选择启用。运行 vex gpu 进行检查,并参阅 GPU 加速。

快速开始

# Index a project (structural only — fast)
vex index --path /path/to/project

# Index with semantic embeddings (slower first time, downloads 86 MB model)
vex index --path /path/to/project --semantic

# Exact-name lookup (does this symbol exist?)
vex check "PaymentService"

# Extract a symbol's body (no whole-file read)
vex show "PaymentService"

# Fuzzy / multi-word search (returns ranked neighbors when no symbol matches)
vex search "payment processing" --semantic

# Find all usages of a symbol (--strict drops string-literal / comment / wrong-scope noise)
vex usages "IndexReader" --strict

# File structure outline
vex outline src/main.rs

# Find implementations of a trait/interface
vex implementations "Iterator"

# Callgraph: who calls / is called by a function (fast path via persistent index)
vex callers "process_event"
vex callees "process_event"

# Multi-hop call graph (v1.7)
vex paths "main" "process_event"          # all caller chains from main → process_event
vex reachable "process_event"             # everything that transitively reaches it
vex tests-for "process_event"             # tests covering process_event (path globs + name heuristic; framework label per row)

# Symbol-level diff against a branch (v1.7)
vex diff --base main                      # what symbols did this branch change?

# Historical view of a symbol — every commit that touched it (v1.15.0; v1.16.0 expanded)
vex index --history                       # build the persistent history sidecar once
vex history "PaymentService"              # ~10ms — every version reachable from HEAD
vex history "PaymentService" --diff       # unified diffs between consecutive versions
vex history "Foo" --since 2026-01-01 --author alice --kind function
vex history "deleted_symbol" --exact-presence    # exact commit set where each blob lived (revert-aware)

# Semantic similarity by existing symbol — explain what's actually similar (v1.7)
vex similar "PaymentService" --limit 5 --min-score 0.7 --explain

# Near-duplicate pairs with reasoning (v1.7)
vex duplicates --min-score 0.95 --min-body-lines 5 --explain

# Search with per-call scope + metadata filters (v1.7)
vex search "Repository" --include 'src/**' --exclude '**/*.gen.*' --visibility public --async-only

# Why did the search return these results? (v1.7)
vex search "Foo" --why 2>trace.json

# Bundle: 4 round-trips → 1 envelope (v1.9, Phase 13.2)
vex bundle --mode symbol --symbol PaymentService          # body + callers + callees + similar
vex bundle --mode pr-impact --base origin/main            # changed symbols + transitive callers + tests
vex bundle --mode project --top-n 30                      # top-N by reverse call-graph indegree

# Diff-context filters on every search-shaped command (v1.9, Phase 13.7-D3)
vex search "Repository" --since-branched                  # only files changed since branching from main
vex usages "Config" --since HEAD~3                        # refs within the last 3 commits
vex callers "Foo" --changed-only                          # working-tree changes only

# Extract just a symbol's body — replaces Read for a specific function/class
vex show "PaymentService"                                 # full body of the class / fn
vex show "Foo" "Bar" "Baz"                                # multiple symbols in one call

# Smart show truncation for token efficiency (v1.9, Phase 13.3)
vex show "BigClass" --signature-only                      # just the signature line
vex show "PaymentService" --head 20                       # first 20 lines of the body
vex show "Foo" --no-body                                  # signature + docstring, no body

# Ranking-eval harness — CI regression guard (v1.9, Phase 13.12)
vex eval --bench benches/ranking_golden/queries.toml      # nDCG@10 / recall@10 / MRR per query
vex eval --min-ndcg 0.85                                  # fail if mean nDCG drops below threshold

# Capability discovery for MCP clients (v1.9, Phase 13.0)
vex capabilities                                          # JSON: protocol_version, signals, bundle_modes, …

# Fast existence check
vex check "Foo" "Bar" "Baz"

# Incremental update (re-parses only changed files, reuses unchanged from index)
vex update

# Watch mode (re-indexes on file changes)
vex watch

# Multi-repo: treat a set of sibling repos as one workspace (v1.22.0)
vex index --workspace                     # build every member of .vex-workspace.toml
vex search "RetryPolicy" --workspace      # fan out, results grouped by repo
vex usages Config --strict --workspace    # cross-repo strict refs (v7+ index)
vex watch --workspace                     # keep every member incrementally fresh

# Show index stats
vex status

# GPU doctor — is the compiled EP actually engaging on this machine? (v1.16.0)
vex gpu                                   # probes the compiled-in EP with strict registration
vex gpu cuda                              # narrow to one EP
vex gpu --enable                          # persist working device to VEX_DEVICE

# Shell completions
vex completions zsh > ~/.zfunc/_vex

命令

命令描述
vex index [--path .] [--semantic] [--embedder ID] [--history [--history-depth N]] [--no-clusters] [--no-pattern-index] [--drop-semantic] [--gpu/--device]构建完整索引。--semantic 生成嵌入向量 + HNSW + BM25。--embedder 选择嵌入模型(默认值为 minilm-l6-v2)。--no-clusters 跳过符号聚类(v9)。--no-pattern-index 跳过模式骨架部分(v6)。--drop-semantic(与 --no-semantic 一起使用)删除磁盘上的语义构件(HNSW + 哈希索引 + 嵌入模型缓存)。--gpu/--device 控制 GPU 加速(支持 GPU 的构建版本)。--history(v1.15.0) 构建 Phase 14.8 持久化历史符号部分(<index_dir>/index.git_history),使 vex history <Symbol> 能以 FST 查找的速度运行。--history-depth N 将遍历限制为最新的 N 次提交(全局限制,而非逐文件限制)。
vex search <query> [--semantic] [--no-bm25] [--limit N] [--kind def,fn,…] [--visibility V] [--async-only] [--code-only] [--exclude-generated] [--why]混合搜索:结构化搜索 + BM25 + 语义搜索(启用 --semantic 时)。采用三路 RRF 融合。--kind 支持多个值(规范名称 + 元选择器 def/comment/test/ref)。元数据后置过滤器可按签名关键词缩小范围。v1.20.0(D4):每个结果的 signals 块现在会在排名序号旁提供原始 bm25_score + semantic_cosine,让 Agent 能读取绝对相关性质量;语义通道未运行时,_meta.vex.dev/semantic_channel 会报告 "not_requested" / "index_lacks_vectors";面向代码意图的查询中,--code-only 会排除 *.md/*.markdown/*.txt/*.rst/*.adoc 中的命中;--exclude-generated 会排除机器生成的文件(protobuf 存根、sqlc 输出、bindgen 绑定、ORM 架构),根据生成器的头部横幅识别——适用于将生成代码纳入版本控制的仓库;该启发式会倾向于漏报,而不是隐藏手写代码(参见 docs/LIMITATIONS.md §10.2)。--why 会将 JSON 跟踪信息附加到 stderr。v1.15.0 搜索偏移提示:当查询形似标识符(compile_query、Foo、_internal)且结构化 FST 找不到任何匹配项时,vex 会在 stderr 打印一行提示,指向 vex check / vex show / vex usages --strict——这通常是“从依赖项导入”的情况,否则 BM25 可能会将调用方显示成定义。参见 docs/COOKBOOK.md 常见问题。
vex show <symbol> [--limit N] [--context N] [--kind fn] [--visibility V] [--async-only] [--signature-only | --head N | --no-body]从源代码中提取符号主体(相比读取完整文件可节省 token)。支持与 search 相同的元数据和类型过滤器。智能截断选项(自 v1.9 起)——--signature-only 仅保留声明行,--head N 保留主体的前 N 行,--no-body 仅返回签名 + 文档字符串。选项互斥。
vex similar <name> [--limit N] [--min-score T] [--explain]查找与现有符号语义上接近的符号(HNSW 最近邻)。--explain 会为每个匹配项添加标识符 Jaccard 相似度 + 截断后的统一差异。--min-score 是 --threshold 的别名。
vex duplicates [--min-score T] [--min-body-lines N] [--explain]根据嵌入相似度列出近似重复的符号对。--explain 会显示主体的实际差异。
vex usages <name> [--limit N] [--strict] [--include-self] [--include-docs]查找符号的所有引用/用法。非严格模式使用 FST 查找;v1.20.0 默认会剔除符号自身定义行中的匹配,以及 *.md/*.markdown/*.txt/*.rst/*.adoc 中的匹配——使用 --include-self / --include-docs 可恢复 v1.20 之前的宽泛搜索行为。--strict 会从 v5 的 reference_edges 部分读取由绑定器解析的引用(Rust / TypeScript / Python / C# / C++ / Go / Java / Kotlin)。
vex impact <name> [--depth N] [--exclude-docs]一次调用即可生成删除安全性影响范围报告(自 v1.20.0 起)。整合四个引用通道——严格引用(绑定器解析)、FST 引用、grep \b<Name>\b 和直接调用图调用方——给出单一判定(safe / unsafe / uncertain)及每个通道的证据样本。在提议删除或重命名符号之前使用;一次调用即可替代手动执行 usages→grep→callers 的步骤。判定规则:严格引用或调用图调用方报告数量 >0 时为 unsafe(绑定器/图确认存在真实用法);只有文本通道命中时为 uncertain(可能是字符串分派 / 注释 / 装饰器);所有通道均返回零时才为 safe。--depth N(1..16)向后遍历调用图,以显示深度 ≥ 2 的间接调用方;--exclude-docs 会排除文档格式中的提及(*.md/*.txt/…),因此仅出现在 CHANGELOG 中的符号会变为 safe。
vex pattern '<pat>' --lang <lang> [--why]使用元变量($NAME、$_、$$$,以及 v6 命名的多行形式 $$$BODY / $$ARGS)进行 AST 模式匹配。重复的元变量会强制执行反向引用。以空格分隔的 && / `
vex outline <file> [--kind fn]显示文件结构,可按符号类型筛选。
vex implementations <name>查找扩展或实现基类、trait 或接口的类型(包括带泛型参数的情况:class Foo : Repository<T>)。基于索引(v8 层级部分)——通过 find_hierarchy_edges_by_symbol FST 和二分查找实现,仅当索引缺少该部分时才回退到原有的实时 tree-sitter 遍历。基准测试(benches/hierarchy.rs,150 个实现者):基于索引约 265 ns,实时遍历约 22.7 ms——快约 85,000 倍。
vex subtypes <name> [--depth N]沿 extends/implements 边进行传递式向下闭包(直接子级、孙级等),并为每行标注 BFS 跳数。遍历时排除 Uses(trait/mixin 组合)——混入某个 trait 并不意味着你是该 trait 所组合的一切的子类型。仅使用索引,不回退到实时遍历(需要包含 v8 层级部分的索引)。基准测试:遍历 20 跳传递链约需 1.1 µs。
vex modules [SYMBOL] [--min-size N] [--members N] [--sort size|cohesion]实际模块:由相互调用/引用的符号构成的集群,在运行 vex index 时计算(基于调用 + 引用 + 层级边的确定性 Leiden-CPM 算法;需要 v9 索引)。不带 SYMBOL 时,列出集群及其标签(占主导地位的路径前缀)、大小、内聚度和枢纽符号;带 SYMBOL 时,显示该符号所属的集群及其成员。--include/--exclude/--exclude-tests 可限定成员范围。如果索引中没有集群(v9 之前的索引、--no-clusters),则会以 empty_reason 退出,状态码为 1。隐藏别名:vex clusters。参见符号集群。
vex callers <name>查找函数的直接调用方(通过持久化调用图快速查找;缺少索引时回退到实时 tree-sitter 扫描)。
vex callees <name>查找函数的直接被调用方(同样使用快速查找)。
vex paths <from> <to> [--max-hops N]在持久化调用图中枚举从 from 到 to 的所有调用链。使用带环路防护的有界 DFS;--max-hops 默认值为 6。
vex reachable <target> [--max-hops N] [--limit N]返回其被调用方可到达 target 的符号传递集合,并为每行标注 BFS 深度。用于影响范围分析。
vex tests-for <target> [--max-hops N] [--limit N] [--test-pattern <glob>] [--include-fixtures]查找通过传递调用覆盖 <target> 的测试函数。在 vex reachable 结果上执行后置过滤:向后遍历调用图,保留位于已识别测试路径 glob 下的行(Rust / Python / TS-JS / Go / Java / Kotlin / C# / C++),并为每行标注 framework(pytest、jest、go-test 等),以便 Agent 选择正确的运行器。可重复使用的 --test-pattern <glob> 会替换默认集合;--include-fixtures 会放宽名称前缀过滤条件,并额外允许测试路径辅助函数向前跳一跳。
vex diff --base <rev> [--limit N]比较任意 git 修订版本与工作树,提供符号级差异:新增 / 删除 / 文件内移动 / 主体变更。采用 git diff --no-renames 语义,因此 git mv 会显示移动的两端。
vex bundle --mode <symbol|pr-impact|project> [...]统一的多来源汇总(自 v1.9 起)——用一次调用替代 4 次往返(show → callers → callees → similar)。--mode symbol --symbol Foo 返回主体 + 调用方 + 被调用方 + 语义相似项。--mode pr-impact --base origin/main 返回变更符号 + 传递调用方(默认深度为 2)+ 测试。--mode project [--top-n 30] 按反向调用图入度返回前 N 项(实验性——参见 docs/MCP-SCHEMA.md#bundle-modes-v19,了解响应形状和各模式对应的 mode_hints 键)。始终输出 v1 信封 { protocol_version, capabilities, _meta, results }。
vex check <name> [name...]快速检查索引中是否存在指定符号。
vex grep <pattern> [--filter-path path/]正则内容搜索(无需索引)。
vex update [--path .] [--semantic] [--embedder ID] [--history | --no-history]增量更新——仅重新解析有变更的文件,并从现有索引中复用未变更的符号。--history(v1.15.0) 通过清单保持启用:如果先前构建包含历史部分,vex update 会通过三分支遍历器保持其最新状态(无新提交时快速跳过;线性历史时增量更新;强制推送时完整重建)。--no-history 会移除该部分并将清单字段设为 null。
vex watch [--path .] [--semantic] [--embedder ID]监控文件系统,并在变更时自动重新索引。
vex status [--path .] [--coverage]显示索引统计信息:符号数量、大小、嵌入向量、调用图、BM25、GPU 支持情况。--coverage 会增加文件覆盖率诊断:按语言统计已索引文件、已发现但未索引的文件(含原因),以及磁盘上缺失的清单条目。
vex gpu [device] [--enable]诊断 GPU 加速:打印编译进此二进制文件的执行提供程序,并主动探测其是否在此计算机上启用(静默回退到 CPU 时会显示 FAILED 和设置修复说明)。vex gpu cuda 会探测一个 EP;GPU 成功启用时,--enable 会将可用设备持久化到 VEX_DEVICE(Windows 上通过 setx 设置用户环境变量;macOS/Linux 上会打印需要添加的 export 行)。参见 GPU 加速。
vex completions <shell>生成 shell 补全(bash、zsh、fish)。
vex init [--agents-md] [--agents-md-only]在当前目录中创建默认的 .vex.toml。--agents-md 还会为 Agent 工具写入 AGENTS.md。--agents-md-only 仅写入 AGENTS.md(跳过 .vex.toml)。
vex mcp <install|uninstall|list> --agent <id|all> [--dry-run] [--force]管理编码 Agent 配置中的 vex-mcp 服务条目。install 会以幂等方式注册;uninstall 会移除;list 会显示当前条目。--agent all 会分发到所有受支持的 Agent。--dry-run 仅预览,不写入。--force 会覆盖现有条目。
vex capabilities打印机器可读的能力矩阵(自 v1.9 起):protocol_version、signals、why、scope_filters、metadata_filters、empty_reason、bundle_modes、auto_update、async_update、history_diff、structured_result_kind、result_completeness、symbol_clusters。MCP / Agent 客户端会在启动时探测一次,而无需重新读取帮助文本。
vex eval [--bench PATH] [--min-ndcg F] [--json]使用人工整理的黄金查询集运行排序评估测试框架(自 v1.9 起);报告每个查询及汇总的 nDCG@10 / recall@10 / MRR。用作 CI 回归保护——当平均 nDCG 低于 --min-ndcg 时失败。默认黄金查询集:benches/ranking_golden/queries.toml。
vex history <Symbol> [--depth N] [--limit N] [--branch REV] [--no-index] [--since YYYY-MM-DD] [--until YYYY-MM-DD] [--author SUBSTR] [--kind KIND] [--diff] [--exact-presence]新增(v1.15.0);v1.16.0 扩展(Phase 14.9)。 返回从所选分支顶端可追溯的符号所有历史版本。此前运行过 vex index --history 时,查询会使用持久化 FST 旁路文件(约 10 ms——在 tokio 规模的仓库中比遍历器快 1640 倍)。如果没有该部分,则会调用 git log(约需数秒)。索引模式还能找到名称已从 HEAD 中删除的符号——遍历器无法做到。v1.16.0 新增: 日期/作者/类型过滤器(按字典序比较 YYYY-MM-DD);--diff 会渲染连续版本之间的统一差异(仅签名行会改变形状,组首保留完整签名);--exact-presence 会枚举每个条目 blob 所在的确切提交集合(可识别回滚,并受 --exact-presence-max-commits 限制);索引路径上的标识符形查询支持长度 ≥ 3 的前缀 FST 回退;JSON 信封已迁移到标准 ResponseEnvelope 形状(破坏性变更:影响读取 results.items[] 的 MCP 使用方)。完整流水线和使用指南请参见 docs/HISTORY-INDEX.md。
vex self-update [--check] [--yes]将 vex 更新到最新的 GitHub 发布版本。就地替换正在运行的二进制文件。支持 Linux、macOS 和 Windows。
每查询过滤器(适用于所有搜索类命令)

所有搜索类命令都接受范围过滤器。具体过滤器因命令而异:

  • --include <glob> / --exclude <glob>(可重复,采用 gitignore 语法)——按调用指定路径范围,无需重新索引。--exclude 优先于 --include。示例:vex search Foo --include 'src/**' --exclude '**/*.gen.*'。--exclude-tests(MCP:exclude_tests: true)会排除测试文件(tests/、*_test.*、test_*.py、*.spec.ts、__tests__/、tests.rs 等——与 vex tests-for 使用的集合相同);它可与 --include / --exclude 组合使用,会记录在 --why 跟踪信息中,并由所有接受范围标志的命令应用(search、usages、callers / callees、impact、grep、show、pattern、implementations / subtypes、similar / duplicates、modules、paths、reachable、diff、bundle 的 pr-impact 模式——在该模式下,它会过滤已更改文件以及传递调用者 / 测试行;bundle 的 symbol / project 模式会忽略所有范围过滤器)。vex tests-for 不接受 --exclude-tests(退出码为 2):它会列出测试函数,因此该标志总会导致没有结果。仅基于路径:非测试文件中 #[cfg(test)] mod tests 块内的 Rust 单元测试不会被排除。
  • --filter-path <substring>(别名:--filter)——对 search、show、usages、grep、similar、duplicates 按路径子字符串过滤。可与 glob 过滤器组合,采用 AND 逻辑。

vex search / vex show 还接受:

  • --visibility <public|private|protected|internal>——仅保留签名中带有显式关键字的符号。不会推断默认可见性(裸 Rust fn foo() 不匹配 --visibility private)。
  • --async-only / --no-async——仅保留或排除异步 / Kotlin suspend 符号。
  • --static-only、--sealed-only——仅保留静态类成员,或 sealed(或 Java final)类型。
推理标志
  • vex search --why 会将 JSON 跟踪信息输出到 stderr(结果列表仍输出到 stdout):normalized_query、各通道命中数(FST / BM25 / semantic)、启用的回退机制(fuzzy)以及当前过滤器快照。
  • vex pattern --why 会在结果列表之后将 JSON ScanTrace 输出到 stderr:mode(indexed / live_scan)、root_kind_inferred、candidate_files / total_files,以及跳过索引预过滤时的 fallback_reason(no-index、no-skeleton-section、empty-section、grammar-drift、partial-section、index-open-error)。MCP 调用方可在 _meta.why 中看到相同的 JSON。
  • vex similar --explain / vex duplicates --explain 会添加 jaccard 重叠分数,以及两个代码主体之间经过截断的统一差异,以便你在采取操作前判断语义聚类出的两个符号是否确为重复项。
多仓库工作区(--workspace)——v1.22.0

在 .vex-workspace.toml 中声明一组同级仓库,然后对任意命令使用 --workspace,即可将操作分发到所有仓库,并按仓库分组:

# .vex-workspace.toml (at the directory that contains the repos)
members = ["./api", "./worker", "./shared-lib"]
vex index --workspace                   # build every member into its own per-repo index
vex update --workspace                  # incremental refresh, per-repo changed/deleted counts
vex usages Config --strict --workspace  # cross-repo strict refs, grouped by repo
  • index、update、search、grep、check、usages、impact、callers、callees、reachable、modules 和 watch 均接受 --workspace。每个成员保留各自的 .vex.toml(排除项 / embedder / sections / 缓存)。
  • 默认情况下,引用解析和调用图解析均按仓库进行。唯一例外是 vex usages <name> --strict --workspace:它会通过 gtags 风格的名称回退机制,将仓库 B 中的引用解析到仓库 A 中定义的符号(显示为 name-resolved 子级)。需要 v7+ 索引——升级后请重新运行 vex index。
  • 如果某个成员缺少所需功能(旧索引不支持 --strict,或 reachable 所需的调用图),系统会将该仓库报告为不可用,而不会中止整个分发操作。--workspace 与 --why 不能同时使用。请参阅 docs/MULTIREPO.md 和 LIMITATIONS §7。

配置

在项目根目录中创建 .vex.toml,以自定义 vex 的行为:

vex init  # generates .vex.toml with commented defaults
# .vex.toml

# Glob patterns to exclude from indexing (gitignore syntax, on top of .gitignore)
exclude = [
    "vendor/**",
    "node_modules/**",
    "*.generated.go",
]

# Output format — "compact" (default since v1.10.1; single-line records),
# "text" (verbose multi-line), or "json" (envelope for MCP / tools).
# format = "text"

# Enable semantic embeddings by default
semantic = true

# Automatically update index before search if stale
# auto_update = false

# With auto_update, refresh in the background rather than blocking the query:
# answers come from the index on disk, the response is flagged stale, and the
# rebuild lands for the next query. Trades freshness for latency.
# async_update = false

# GPU device for semantic indexing (GPU-enabled builds only). "auto" uses the
# compiled-in GPU EP when it initializes, else CPU; or "cpu"/"cuda"/"directml"/
# "coreml". `gpu = true/false` is shorthand for auto/cpu. See GPU Acceleration.
# device = "auto"
# gpu = true

# Embedder model: minilm-l6-v2 (default), jina-code, bge-base-en-v1.5,
# bge-large-en-v1.5, mxbai-large. Changing it requires a reindex.
# Set globally across projects with the VEX_EMBEDDER env var (this file wins).
# embedder = "minilm-l6-v2"

# VCS backend for diff-scoping (--since/--since-branched/--changed-only).
# "auto" (default) detects .git/.svn/.arc; "git" | "none" | "arc" | "svn".
# git, arc (Yandex Arc), and svn (Subversion) are all functional backends;
# svn declines --since-branched (no merge-base). "none" disables diff-scoping.
# Overridden by the --vcs flag and the $VEX_VCS env var. See docs/VCS-BACKENDS.md.
# vcs = "auto"

CLI 标志始终优先于配置值。若配置启用了语义模式,可使用 --no-semantic 明确禁用。环境变量 VEX_DEVICE 和 VEX_EMBEDDER 会作为所有项目通用的全局默认值(优先级最低,低于 .vex.toml)——请参阅 GPU 加速。

将配置排除在仓库之外

不想在仓库中放置 .vex.toml(无法将其加入 .gitignore、使用共享检出等)?vex 不会自行创建该文件——只有 vex init 会写入它——你有两种方法可以将配置保存在外部:

  • --config <path> / $VEX_CONFIG — 指定磁盘上任意位置的配置文件。它会完全取代仓库内的查找方式,因此仓库可保持整洁:
    vex --config ~/vex/this-repo.toml search Foo
    export VEX_CONFIG=~/vex/this-repo.toml   # or set it once per shell
    
    --config 的优先级高于 $VEX_CONFIG;路径不存在或无效时会直接报错(vex 不会静默回退)。该文件中的相对路径会相对于文件所在目录解析。
  • 父目录 — 配置查找会从项目目录逐级向上,直到文件系统根目录,因此放在任一祖先目录中的 .vex.toml(例如 ~/work/.vex.toml,或用于全机器默认配置的 ~/.vex.toml)都会应用于其下的所有仓库,而无需在仓库中放置配置文件。

索引本身绝不会写入仓库——它位于缓存目录中(--cache-dir / $VEX_CACHE_DIR / 平台缓存),因此只需妥善放置配置,即可保持仓库整洁。

过期检测

Vex 会检测索引是否过期,并在搜索前发出警告:

$ vex search "Config"
Warning: index may be stale (HEAD changed). Run `vex update`.

工作原理:每次搜索时,vex 都会比较索引创建时保存的 git HEAD 与当前 HEAD(约 0.1ms,仅执行一次 git rev-parse)。如果 HEAD 已更改,索引即为过期。对于非 git 仓库,则会回退到 mtime 比较;自 v1.11 (H11) 起,当 mtime 检测触发时,vex 会流式计算文件的 xxh3_64 内容哈希,并与清单进行比较。如果哈希匹配,说明这次触碰只是表面变化(git checkout、无操作的 rustfmt、rsync --times),文件仍保持 Fresh;只有内容确实发生变化时,才会再次触发索引。

自动更新:跳过警告并就地更新:

# Per-command
vex search "Config" --auto-update

# Always (in .vex.toml)
auto_update = true

# Disable staleness check entirely
vex search "Config" --no-stale-check

GPU 加速

语义索引(--semantic)可在 GPU 上运行嵌入模型,在完整索引或冷启动索引时能大幅提速。在一台 RTX 3080 上,对包含 2.8 万个符号的 C++ 模块进行测试时,与 CPU 相比,默认 MiniLM 模型的嵌入速度在 CUDA 下快 51 倍,在 DirectML 下快 29 倍(完整基准测试和设计说明见 docs/GPU_SUPPORT.md)。

分为两个层面:二进制文件和设备:

  • 预编译二进制文件内置仅需驱动的 GPU EP: Windows → DirectML(适用于任何 DX12 GPU — NVIDIA/AMD/Intel;归档中附带运行库 DirectML.dll),macOS arm64 → CoreML。无需 SDK,也无需额外安装。Linux 预编译版本仅支持 CPU。
  • CUDA 需通过源码构建选择启用(在 NVIDIA 上速度最快,比 DirectML 快约 1.75 倍):cargo install --git https://github.com/tenatarika/vex vex-search --features gpu-cuda。需要将 CUDA 12 运行时和 cuDNN 9 放入 PATH(仅有 NVIDIA 驱动还不够 — 它只包含 nvcuda.dll,不包含运行时/cuDNN)。其他平台的源码构建选项:--features gpu-coreml / gpu-directml。

选择设备(vex index / vex update):

  • --gpu / --no-gpu — 强制使用 GPU(自动选择)或 CPU。
  • --device cpu|auto|cuda|directml|coreml — 选择特定执行提供程序。
  • 默认值为自动:如果已编译的 EP 初始化成功,就使用它;否则静默回退到 CPU。在启用了 CUDA 的二进制文件中,自动模式优先选择 CUDA → DirectML → CoreML;标准 Windows 预编译版本仅编译了 DirectML,因此无论电脑是否支持 CUDA,自动模式都会使用 DirectML。
  • 小规模增量 vex update 会继续使用 CPU(GPU 预热不值得只为处理几个符号);冷启动或大型 --semantic 构建会使用 GPU。

GPU 是否真的在使用? 运行 vex gpu — 它会报告已编译的 EP 并主动进行探测,因此静默回退到 CPU 时会显示 FAILED,并提供针对性的设置补救建议。vex gpu cuda 会探测单个 EP;vex gpu --enable 会将可用设备持久化到 VEX_DEVICE(在 Windows 上通过 setx 设置用户环境变量;在 macOS/Linux 上会打印要添加的 export 行)。如果 VEX_DEVICE 设定的 GPU EP 已过时,而后续构建(例如仅支持 CPU 的构建)不包含该 EP,程序会回退到 CPU,而不会报错。

环境变量
变量作用
VEX_DEVICE所有项目的全局默认设备(cpu/auto/cuda/directml/coreml)。优先级低于 --device/--gpu 和 .vex.toml。
VEX_EMBEDDER全局默认嵌入器 ID(例如 jina-code)。优先级低于 --embedder 和 .vex.toml。未知 ID 会回退到默认嵌入器,并发出警告。
VEX_GPU_STRICT=1将 ORT 静默回退到 CPU 的行为改为硬错误 — 用于确认 GPU 是否已启用。(vex gpu 会在内部请求相同的严格模式,不会触及环境变量。)
VEX_GPU_MEM_LIMIT=<bytes>高级选项:限制 GPU arena 的 VRAM 上限。请设置足够大的值(≥ 工作集),否则长上下文批次会因显存不足而失败。
VEX_GPU_ATTN_BUDGET=<n>高级选项:调整基于长度的批次大小(count × max_len² 预算)。

输出格式

# Compact single-line records — default since v1.10.1 (token-efficient, agent-friendly)
vex search "Foo"

# Verbose multi-line / human-readable
vex search "Foo" --format text

# JSON envelope (for MCP / tool integration; what `vex-mcp` parses)
vex search "Foo" --format json

如果希望在终端中显示详细的多行视图,可在 .vex.toml 中通过 format = "text" 固定其他默认格式。

JSON 封装(v1.11.0 — 对裸数组解析器有破坏性变更)

每个 --format json 子命令都会将其负载封装在 Phase 13 封装中。形状统一,可通过 protocol_version 轻松识别:

{
  "protocol_version": "v1",
  "capabilities": { /* see `vex capabilities` */ },
  "_meta": { "vex.dev/index_age_ms": 1200, "ttlMs": 30000, "cacheScope": "project" },
  "results": [ /* the actual data, shape depends on the subcommand */ ]
}

在 v1.11 之前,只有 search 和 bundle 会返回此封装;其他 子命令(show、usages、pattern、grep、implementations、 callers、callees、paths、reachable、tests-for、check、 similar、duplicates、diff、outline、index、update、 status、eval)返回的是裸数组/对象。迁移:原先的 jq '.[0].name' 或 data[0]['name'] 现在需要改为 jq '.results[0].name' / data['results'][0]['name']。在迁移期间,可通过 response.get('protocol_version') == 'v1' 检测封装, 以同时支持两种格式。

搜索原理

结构搜索(默认)

使用倒排索引按符号名称搜索,并拆分 CamelCase:

  • "PaymentService" — 完全匹配
  • "Payment" — 前缀匹配,可找到 PaymentService、PaymentGateway
  • "payment" — 不区分大小写,也会通过 CamelCase 词元查找
语义搜索(--semantic)

使用 MiniLM-L6-v2 将查询转换为嵌入(384 维向量),并查找含义相近的符号:

  • "parse source code files" 可找到 parse_file、extract_refs、parse_file_symbols
  • "database storage" 可找到 populate_db、create_10k_db、add_root_persists_to_db
  • "find implementations of an interface" 可找到 find_implementations、test_interface_extends
BM25 通道(索引包含 BM25 数据时自动启用)

基于符号主体词元(标识符、签名、文档字符串)执行经典的 Okapi BM25(K1=1.2、B=0.75)搜索。它弥补了“精确名称”(结构搜索)与“广义含义”(语义搜索)之间的差距:可查找不属于任何符号名称的稀有主体词,例如 timeout、retry、singlestore、idempotency_key。从 v1.11(阶段 8.4)起,还会从 TOML / YAML / HTML / CSS 值中提取主体词元,因此 vex search "production endpoint" --semantic 可以匹配包含 endpoint = "https://..." 的 [server] 表。传入 --no-bm25 可在单次调用中禁用此功能。

混合搜索(3 路 RRF)

当索引包含全部三个通道(通过 --semantic 构建)时,vex search 会使用倒数排名融合合并结构、BM25 和语义搜索结果。至少被 2 个通道命中的符号会标为 Hybrid;仅被一个通道命中的符号则保留原有匹配类型。一次查询即可同时减少结构搜索噪声和语义搜索模糊结果。

用法(FST)

引用存储在 FST(有限状态转换器)中,可通过 mmap 零复制查找,并支持前缀搜索。

符号簇(vex modules)

vex index 会将符号分组成相互调用或引用的代码簇(基于调用、引用和层级边执行确定性的 Leiden-CPM 算法;不含随机性,因此对同一代码树建立的两个索引结果一致)。vex modules 可读取这些分组:

vex modules                         # clusters of >= 3 symbols, largest first
vex modules --members 5 --sort cohesion --include 'src/**'
vex modules IndexReader             # the cluster of one symbol, with its members
vex modules --format text           # text output (shown below)
Modules — leiden-cpm/1 γ=1/8 · 475 clusters (≥3, showing 3) · 1,469 unclustered · 1,464 not eligible
  #60  src/cli/                     39 symbols  cohesion 0.49  hubs: OutputFormat, print_envelope, default_meta_for
  #23  crates/vex-mcp/src/tools/    38 symbols  cohesion 0.68  hubs: opt_bool, build_command, opt_u64
  #286 src/pattern/matcher/tests.rs 32 symbols  cohesion 0.78  hubs: parse_pattern, find_matches, Segment

(以上输出来自本文编写时的此代码库;簇 ID 是本节中的序号,而非排名。)

  • 簇的 label 是最深的路径前缀,该前缀下的文件至少包含其 60 % 成员。cohesion 是边权重 internal / (internal + cut);枢纽是簇内连接最紧密的三个成员。
  • --include/--exclude/--exclude-tests 会筛选成员和枢纽(超出范围的枢纽会被移除);只要至少有一个成员在范围内,就会显示该簇,其 size 为范围内成员数(JSON 中的 size_at_build 保留构建时的数量)。
  • 符号模式会为每个匹配项报告 status:clustered(已分簇)、unclustered(孤立)、not_eligible(标题、模块、标记语言/配置语言)或 new_since_build(构建后新增)。
  • 簇由 vex index 计算,并在 vex update 期间保留且冻结。更新后,JSON 中会包含 stale: true 和 new_since_build,文本输出末尾会出现一行 !;运行 vex index 可重新计算。这与 _meta.vex.dev/stale 不同,后者仍表示“索引早于工作树”。
  • --limit 必须至少为 1;与 SYMBOL 一起使用时,它会限制匹配的符号数量(JSON symbols_total 会报告未限制的总数),并忽略 --min-size。退出代码:有结果时为 0,结果为空时为 1(原因会显示在 results.empty_reason 和标准错误中),簇区段损坏时为 2。使用 --workspace 时,簇按仓库划分,且 --limit 对每个仓库分别生效。
类型感知的引用(--strict)

vex usages --strict <name> 会读取由 LSP 风格的作用域绑定器写入的 v5 reference_edges 区段。对于有绑定器的语言(Rust、TypeScript、Python、C#、C++、Go、Java、Kotlin),每个引用都会在索引时根据文件内作用域链和导入/使用关系图进行解析,然后关联到用户实际指向的全局符号,而不只是任何提及该名称的行。

这对用户的影响:

  • 注释、文档字符串、字符串字面量和正则表达式正文中的标识符会被排除(此过滤对所有用户启用,并非只有 --strict 才启用)。
  • 被 let / const / 函数参数遮蔽的名称会解析到内部作用域,而不是外部作用域。
  • use ext::Foo; / import { Foo } from './ext' / from ext import Foo 会使对 Foo 的引用跨文件解析到索引中定义它的位置。对于 C++,带引号的 #include "..."(v1.14+)会通过 BFS 遍历传递包含关系图,将 Foo 解析到任何可达头文件中定义的符号。系统头文件 <vector> / <string> 和宏包含(#include MY_HEADER)按设计不会解析。
  • 在索引中导入但从未定义的名称会保持为 Unresolved,且不会生成边——这比巧合匹配更好。

不带 --strict 时,vex usages 仍通过旧版引用 FST 支持所有受支持的语言;--strict 只是在八种带绑定器的语言中以召回覆盖面换取精确度。早于绑定器的 v3 / v4 索引会中止,并提示「请重新运行 vex index」。

结构模式(vex pattern)

按代码形状而非文本匹配。适用于 vex 能解析的所有语言;基于索引的预筛选(通过 v6 pattern_skeletons 区段)可加快 15 种会生成骨架的语言中的候选项筛选(除 Bash、Lua、YAML 和 TOML 外的所有语言会实时扫描)。

语法:

  • $NAME — 捕获单个标识符或括号平衡的表达式。同名标记变量出现两次时会强制反向引用:record($X, $X) 匹配 record(state, state),不匹配 record(state, other)。
  • $_ — 通配符(匹配但不捕获)。
  • $$$ — 匿名省略号(匹配到下一个字面内容前的任意内容;可跨越换行符)。
  • $$$BODY / $$ARGS — 命名多行省略号。功能与 $$$ 完全相同,但会以给定名称捕获匹配的文本;$$$BODY 适用于代码块正文,$$ARGS 适用于参数列表。反向引用相等规则同样适用。
  • &&(两侧有空格)— AND 组合。两个子模式必须在同一文件中匹配,且共享的元变量名称必须在两者中捕获相同文本:struct $S && impl $S 匹配同时包含这两种形状且 $S 相同的文件。
  • ||(两侧有空格)— OR 组合(取并集,按 (path, line) 去重)。&& 的优先级高于 ||。
  • 组合运算符仅在括号 / 引号深度为 0 时生效,因此 record($X, $X) 和 f($X && $Y) 会保持为单个模式。

基于索引的预筛选:存在 v6 索引时,模式开头的字面关键字(fn、struct、class、def、impl 等)会映射到 tree-sitter 节点类型,vex pattern 只遍历持久化骨架中包含该类型的文件。匹配前会去掉关键字前的可见性 / async / export 修饰符(pub async fn $F 会正确推断为 function_item)。遇到语法漂移、缺少区段或 vex update 后区段不完整时,会回退到实时扫描——--why 会报告具体原因。

示例:

# Multi-line function body with named captures
vex pattern 'fn $NAME($$ARGS) -> Result<$T, $E> { $$$BODY }' --lang rust

# Both struct and impl for the same type in one file
vex pattern 'struct $S && impl $S' --lang rust

# Interface OR class with the same name
vex pattern 'interface $N || class $N' --lang typescript

# See which mode and what narrowing happened
vex pattern 'fn $N($$$)' --lang rust --why 2>trace.json

基准测试

与 ast-index v3.31.0(SQLite + FTS5)和 ripgrep 15.1.0 对比。

方法说明: 索引数据于 2026-08-12 在 Apple Silicon(macOS)上重新测量,使用 vex v1.25.5、release 构建、冷缓存和非语义索引。搜索数据与 2026-07-11 / v1.25.1 的运行结果相同(v1.25.2-v1.25.5 中没有改动搜索路径的内容),取 10 次运行的平均值。可使用 ./benches/bench.sh 复现(如需更大的语料库,可将 VEX_BENCH_LARGE_PROJECTS 指向自己的代码库)。数据因机器而异——应关注比例,而非绝对数值。

索引
项目vexast-indexvex 大小ast-index 大小
小型(vex 本身,6.8K 个符号)442 ms254 ms4.6 MB6.7 MB
中型(ast-index 代码库,2.3K 个符号)204 ms109 ms1.6 MB3.4 MB

客观解读: vex 的索引速度比 ast-index 慢约 1.7-1.9 倍——它在索引时构建的内容多得多(FST + BM25 + 持久化调用图 + 已解析的引用边 + v8 类型层次结构分区 + 三元组跳过索引 + 模式骨架 + 符号簇(v9)),而 ast-index 构建的是 SQLite + FTS5 存储。这笔一次性开销换来了下文所示的常数时间查询;生成的索引在磁盘上仍小约 1.4-2 倍(mmap + FST 对比 SQLite)。这些数据测于 v1.25.5(v9 之前);v9 增加了聚类处理流程,仍待重新测量。上次在 v1.25.1 测量时,差距约为 2.5-3 倍;v1.25.5 是差距缩小的主要原因——与 v1.25.4 的单变量 A/B 测试显示,在两个语料库上的冷索引耗时分别降低了 34% 和 31%,因为现在每个文件只解析一次,并由所有提取器共享语法树,而不是让每个提取器都重新解析。使用 --semantic 索引的项目会更慢(需生成 ONNX 嵌入),生成的索引也更大。

搜索:vex 与 ast-index、ripgrep 对比

中型项目(ast-index 代码库,31K 行 Rust,10 次运行的平均值):

查询vexast-indexrg -wvex 对比 rg
search4.7 ms8.3 ms9.2 ms2.0x
SymbolKind4.6 ms8.2 ms8.6 ms1.9x
parse_file4.6 ms7.9 ms8.7 ms1.9x
IndexReader4.7 ms11.7 ms8.6 ms1.8x

核心结论:无论项目规模如何,vex 搜索耗时都稳定在约 4-5 ms(FST 的复杂度为 O(query_len))——这正是较慢的索引构建所换来的优势。与 ripgrep 的对比并非完全同类:rg 会扫描原始文本而不使用索引,因此耗时会随语料库大小增长(在这个 31K 行的代码库上只需个位数毫秒,在大型代码库上则需 100 ms 以上);而 vex 执行的是预构建 FST 的查找。持久的优势在于摊销后的效率和查询结果的性质:vex 只返回符号定义(精准且节省 token),而 rg 会返回每处文本匹配(噪声多,在大模型上下文中成本高)。

模式匹配(仅 vex 支持)

中型项目(ast-index 代码库,Rust):

模式耗时匹配数
fn $NAME($$$) -> Result31 ms50
pub struct $NAME27 ms44
fn $NAME($$$)29 ms50

ast-index 和 ripgrep 不支持 AST 模式匹配。

语义搜索

示例(展示语义能力,并非延迟基准测试)——结构化搜索返回 0 条结果,而语义搜索能找到相关符号的查询:

查询结构化搜索语义搜索
“解析源代码文件”019
“数据库存储”020
“查找接口的实现”020
“文件系统目录遍历器”020
“处理错误和异常”020
HNSW 与暴力搜索(语义向量搜索)

以下延迟数据是在早期版本中测得的,未在 v1.25.1 阶段重新测试——请将其视为扩展趋势(HNSW 保持平稳,暴力搜索随规模线性增长),而非当前的绝对数值。

语义搜索会先通过 ONNX 将查询嵌入为向量(约 55 ms),然后搜索存储的向量。HNSW(usearch)以 O(log N) 的近似最近邻搜索取代 O(N) 的暴力扫描:

符号数暴力搜索HNSW加速比
333约 3 ms约 3 ms1 倍
11K约 8 ms约 3 ms2.3 倍
20K约 11 ms约 3 ms4 倍
100K(预计)约 55 ms约 3 ms约 18 倍

无论索引大小如何,HNSW 的耗时都保持在约 3 ms。暴力搜索则随规模线性增长。语义搜索的总延迟主要由 ONNX 嵌入(约 55 ms)决定,因此对于小型代码库,端到端加速效果有限;但在大规模场景下至关重要。

模式延迟
仅结构搜索约 4 ms
混合搜索(结构搜索 + 语义搜索)约 58 ms(HNSW)/ 约 66 ms(暴力搜索)
LLM 令牌效率

AI Agent 搜索代码时,输出会直接进入上下文窗口。基于 grep 的工具会返回每一处文本匹配——包括注释、字符串、变量用法以及压缩文件中的匹配项——消耗令牌却没有增加有效信息。

vex 仅以紧凑的单行格式返回符号定义,大幅减少令牌消耗:

vex 紧凑格式rg(grep)减少比例
7 次符号查询(典型情况)约 220 个令牌约 1,300 个令牌6 倍
命中压缩 JS/CSS 文件的查询约 270 个令牌约 58,700 个令牌217 倍

示例——在大型项目中搜索类名:

# rg: 20 matches across imports, usage sites, comments, tests (2,045 chars)
$ rg -w "PreAggregatedConfig" .
./models.py:3602:class PreAggregatedConfig(models.Model):
./models.py:3610:    pre_aggregated_config = PreAggregatedConfig.objects.get(...)
./serializers.py:48:from .models import PreAggregatedConfig
./tests.py:12:    config = PreAggregatedConfig(...)
... (16 more lines)

# vex: 1 definition (93 chars)
$ vex search "PreAggregatedConfig" --format compact
C PreAggregatedConfig models.py:3602 class PreAggregatedConfig(models.Model):

对于每项任务需要进行 10–20 次代码查询的 Agent,与 grep 相比,vex 每次会话可节省 5,000–20,000 个令牌,从而降低成本,并为推理留出更多上下文窗口空间。

支持的语言

通过 tree-sitter 索引了 20 种语言。能力列说明:

  • Binder — vex usages --strict 是否会通过 LSP 风格的作用域链解析引用(阶段 11.1)?cross-file 包含 use / import 解析;in-file 会在文件内解析,但将导入视为未解析。其余语言会回退到普通 vex usages 使用的基于行的扫描器。
  • Patterns — vex pattern 是否使用 v6 索引预筛选器(阶段 11.4)?indexed 表示持久化的骨架区段会在查询时缩小候选文件范围;live-scan 表示每次查询时,tree-sitter 都会遍历所有匹配语言的文件。20 种语言都支持 vex pattern 语法($NAME、$$$BODY、&& / ||);预筛选器只会加快 16 种会生成骨架区段的语言的发现速度(除了 Bash、Lua、YAML 和 TOML 之外的所有语言)。
语言扩展名符号导入BinderPatterns
Rust.rs函数、结构体、枚举、特征、实现、类型、常量use 声明跨文件索引
TypeScript/JS.ts、.tsx、.js、.jsx、.mjs、.cjs类、接口、枚举、函数、箭头函数、类型别名import跨文件索引
Python.py类、函数(包括异步函数和带装饰器的函数)import、from..import跨文件索引
C#.cs类、接口、结构体、枚举、方法、属性using跨文件索引
C/C++.cpp、.cc、.cxx、.hpp、.hxx、.h(不索引 .c 文件,仅通过 C++ 索引 .h)类、结构体、函数、方法、模板、枚举#include跨文件(v1.14 会遍历带引号的 #include "...";类方法仍在文件内解析)索引
Go.go函数、方法、结构体、接口import跨文件索引
Java.java类、接口、枚举、方法、构造函数import跨文件索引
Kotlin.kt、.kts类、接口、对象、函数、属性import跨文件索引
Ruby.rb类、模块、方法——索引
Swift.swift类、结构体、枚举、Actor、协议、函数import—索引
PHP.php、.phtml类、接口、特征、方法、函数use、require—索引
SQL.sql表、视图、函数、触发器、索引、架构、类型、序列ALTER TABLE 引用—索引
Markdown.md、.markdown标题(章节结构)——索引
Bash.sh、.bash函数——实时扫描
Lua.lua函数、本地函数、表require—实时扫描
CSS.css规则、选择器、@keyframes——索引
HTML.html、.htm自定义元素(带连字符的标签名)——索引
YAML.yaml、.yml顶层键——实时扫描
TOML.toml裸键、点分键、表——实时扫描
Dart(索引支持).dart类、枚举、混入、扩展、函数、方法、构造函数、别名导入 URI—(基于名称的引用)索引

请参阅 docs/SUPPORTED_LANGUAGES.md,了解语法版本、ABI 级别,以及添加语言或升级语法的操作手册。将语言加入索引 Patterns 层级只需在 src/pattern/skeleton/kinds.rs 中编辑一次允许列表——阶段 11.4 后续推广(Go → Java → Kotlin → C# → C++ → Swift → PHP → Ruby,以及 SQL / Markdown / CSS / HTML)已完成;目前仅 Bash、Lua、YAML 和 TOML 仍使用实时扫描。

索引位置

macOS:     ~/Library/Caches/vex/<hash>/index.vex
Linux:     $XDG_CACHE_HOME/vex/<hash>/index.vex (fallback: ~/.cache/vex/<hash>/index.vex)
Windows:   %LOCALAPPDATA%\vex\<hash>\index.vex   (fallback: %USERPROFILE%\AppData\Local\vex\<hash>\index.vex)

每个项目都会根据其规范项目根路径的哈希值(xxh3)获得独立索引。可用以下方式覆盖:

  • --cache-dir <path> — 将 vex 指向自定义缓存目录
  • $VEX_CACHE_DIR — 环境变量(优先级低于 --cache-dir)
  • .vex.toml 中的 cache_dir — 配置文件(优先级最低)

已知限制

vex 是一款静态分析工具——有些实际调用点和引用从原理上就无法被发现。主要缺口如下:

  • 函数作用域之外的 vex callers——模块级表达式会通过合成的 <module:path> 调用方报告(Phase 14.1)。Python 和 Java 的函数/方法装饰器(Phase 14.2)、Kotlin 注解和 C# 方法/构造函数特性(Phase 14.2.2),以及 TypeScript 方法装饰器和 Rust 函数/方法上的外部属性(Phase 14.2.1)会生成正向边。剩余缺口:类级装饰器(Phase 14.6);Rust #[derive(...)] 会被有意过滤。
  • vex usages 的质量因语言而异。 Rust、TypeScript、Python、C# 和 C++ 支持 --strict(来自 v5 reference_edges 部分、由绑定器解析的引用,Phase 11.1)。其他语言使用基于行的标识符扫描,误报率较高。
  • 动态分派不可见。 通过字符串解析的工厂(uvicorn.run("main:app"))、任务队列(celery_task.delay())、反射(getattr(obj, name)())——这些都不会生成边。
  • 变通方法:vex grep '\bname\b' 是完备的文本回退方案。速度较慢(约 50 ms),但不会漏掉匹配项。

完整的覆盖矩阵、复现步骤以及各查询类型的建议,请参阅 docs/LIMITATIONS.md。

故障排除

查看内部警告

Vex 通过 tracing crate 在 parse/store 边界发出结构化日志——例如语法加载失败、mmap 重新打开、清单不匹配等。默认情况下,RUST_LOG 未设置,因此只有最关键的诊断信息会输出到 stderr。

如果搜索结果出乎意料,或索引命令行为异常,请提高日志级别:

RUST_LOG=vex=warn vex search Foo
RUST_LOG=vex=info vex index   # noisier — file-level progress

若要了解搜索引擎实际执行了什么(各通道的命中数、模糊回退是否启用、应用了哪些筛选条件),请改用结构化跟踪:

vex search Foo --why 2>trace.json   # trace lands on stderr as JSON

--why/why: true 的 JSON 格式请参阅 docs/MCP-SCHEMA.md。

集成

Claude Code(CLI 集成)

推荐通过 CLAUDE.md 规则将 vex 集成到 Claude Code 中(见下文)。Vex 以 CLI 工具的形式运行——Claude Code 通过 Bash 直接调用它,无需 MCP 服务。

设置:

# Install vex
brew tap tenatarika/tap && brew install vex

# In your project
cd /path/to/project
vex init              # create .vex.toml
vex index             # build index (add --semantic for meaning-based search;
                      # add --history for `vex history <Symbol>` archaeology queries — v1.15.0/v1.16.0)

然后添加 .vex.toml 配置以自动更新索引,确保 Claude 始终搜索最新的索引:

# .vex.toml
auto_update = true
# format = "compact"   # already the default since v1.10.1 — set "text" if you'd rather see verbose output

**多仓库(v1.22.0):**如果 Claude Code 同时处理多个仓库,可在它们的共同父目录中放置 .vex-workspace.toml,并告诉 Claude 在调用 vex 时添加 --workspace——例如,使用 vex usages Config --strict --workspace 跨所有仓库追踪某个符号的引用,或使用 vex check Foo --workspace 查看哪些仓库定义了它。结果会按仓库分组。请参阅多仓库工作区。

Claude Code(MCP 服务)

另外,vex 还包含一个 MCP 服务(vex-mcp),可将所有命令作为 MCP 工具提供。**注意:**Homebrew 只安装 vex(不安装 vex-mcp)。从 v1.11.2 开始,每个版本都会随 vex 一起提供预编译的 vex-mcp 二进制文件,适用于构建矩阵涵盖的三个目标三元组:aarch64-apple-darwin(macOS Apple Silicon)、x86_64-unknown-linux-gnu(Linux)和 x86_64-pc-windows-msvc(Windows)。Intel Mac 和其他目标三元组仍需按下文从源码构建。

最简单的设置方式(v1.15.0+):

vex mcp install --agent claude-code

此操作会为你运行 claude mcp add --scope user --transport stdio vex --env VEX_ROOT=<root> -- <vex-mcp>(v1.27.1+)。如果 claude CLI 不在 PATH 中,则会打印该命令,但不会写入任何内容。v1.27.1 之前的版本会写入 ~/.claude/claude_desktop_config.json,而 Claude Code 不会读取此文件;升级后请重新运行该命令。

手动设置:

# 1. Download the prebuilt for your platform from
#    https://github.com/tenatarika/vex/releases/latest
#    e.g. vex-mcp-aarch64-apple-darwin.tar.gz / vex-mcp-x86_64-pc-windows-msvc.tar.gz
# 2. Extract and put the binary on PATH (or remember the full path).

# Source build (if you prefer or are on an unsupported triple)
cargo build --release -p vex-search-mcp

# Register with Claude Code (user scope; Claude Code keeps it in ~/.claude.json)
claude mcp add --scope user --transport stdio vex \
  --env VEX_ROOT=/path/to/your/project --env VEX_DEVICE=auto \
  -- /path/to/vex-mcp

# Or project scope: commit a .mcp.json at the project root
# (see integrations/claude-code/mcp.json)

VEX_DEVICE(v1.16.0)会在二进制文件使用 gpu-cuda / gpu-directml / gpu-coreml 构建时选择 GPU 执行提供程序;当 MCP 驱动的 index / update 调用在大型仓库上重建语义嵌入时,这一点很重要(在 MiniLM-L6 上,CUDA / DirectML 相比 CPU 分别快 51× / 29×)。在仅支持 CPU 的构建中,auto 是安全的(会静默降级)。运行一次 vex gpu,确认 EP 确实已启用。

MCP 工具(28 个):

  • search — 三路混合搜索(结构 + BM25 + 语义);接受 filter / include / exclude / kind / context_path / no_bm25 / --why / 元数据过滤器 / 差异范围(since / since_branched / changed_only)
  • find_symbol — 精确名称查找
  • find_similar — 根据自由格式描述进行语义搜索
  • similar — 查找现有符号的最近邻(explain 会添加 Jaccard + 差异信息);差异范围
  • duplicates — 查找近似重复的符号对(explain 会显示差异);差异范围
  • show — 从源代码中提取符号正文;Phase 13.3 截断标志(signature_only / head / no_body / collapsed,互斥)
  • outline — 文件结构
  • usages — 查找符号的所有引用;filter_path / strict / why
  • impact — 删除安全性影响范围(判定 + 各通道证据);depth / exclude_docs
  • grep — 正则表达式内容搜索
  • pattern — 使用元变量反向引用进行 AST 模式匹配;差异范围;--why
  • implementations — 查找扩展基类 / trait / 接口的类型(包括泛型);差异范围
  • subtypes — 沿 extends/implements 边进行传递闭包向下遍历(直接子类、孙类……),并标注深度;仅使用索引(不回退到实时遍历);depth / 差异范围
  • modules — 实际模块:相互调用 / 引用的符号集群(v9 索引,在完整 vex index 时计算);列出集群(标签、大小、内聚度、中心节点),或传入 symbol 查询其所属集群;limit / min_size / members / sort / 作用范围 / workspace;对于较旧索引或使用 --no-clusters 时,返回空结果并附带 empty_reason
  • callers / callees — 直接调用图导航(通过持久化索引的快速路径);差异范围
  • paths — 列举两个函数之间的调用链
  • reachable — 目标的传递调用者
  • tests_for — 传递覆盖目标的测试函数(标注框架)
  • diff — 比较 git 版本与工作树之间的符号级差异
  • check — 快速检查符号是否存在
  • bundle — 统一的多源数据包(mode: symbol | pr-impact | project),Phase 13 信封
  • eval — 排名评估工具(bench / min_ndcg);MCP 默认启用 json: true,因此 Agent 可获取结构化的 EvalReport
  • capabilities — 机器可读的能力矩阵(protocol_version、signals、bundle_modes、history_diff (v1.16.0)、symbol_clusters 等)
  • index / update — 构建 / 重建索引;v1.16.0 新增 gpu: bool / device: cpu|auto|cuda|directml|coreml 参数(仅限启用 GPU 的构建),使 Agent 无需修改环境变量或配置,即可按调用选择使用 GPU 进行语义嵌入
  • status — 索引统计信息(现包含 gpu_support / default_device (v1.16.0))
  • history — 符号在各提交中的历史版本 (自 v1.20.0 起为 MCP 工具,D5);depth / limit / since / until / author / kind / diff / exact_presence

注意: vex history 和 vex tests-for 已在 v1.20.0(D5)升级为一等 MCP 工具;早期文档将 history 称为“仅限 CLI”的说法已过时。两者与其他所有 vex 命令一样,都会输出相同的 --format json 信封。 多仓库(v1.22.0): 十一个工具——search、grep、check、usages、impact、callers、callees、reachable、modules、index、update——均接受 workspace: boolean 参数,可将调用分发到每个 .vex-workspace.toml 成员,并在 structuredContent.results 下返回分组后的 {workspace, repos:[...]} 负载。将 project_root 指向 .vex-workspace.toml 所在目录或其上级目录。find_symbol 不在其中(请使用 check/search);工作区模式下会忽略 why。详见 docs/MULTIREPO-PHASE8-mcp.md。

MCP ↔ CLI 对等(v1.10): 现在,每个支持路径的工具,其架构都与 CLI 的功能范围一致。CLI 支持的所有位置均开放了 Glob 筛选器(include / exclude)、子串 filter、kind 提升、context_path 邻近提示、no_bm25、Phase 13.3 截断、diff 范围和 no_stale_check——Agent 无需再切换到 bash 来处理“main 之后 crates/api/ 下的 Rust 文件”这类范围筛选。

架构遵循规范词汇(query / symbol / symbols / path / pattern / filter / include / exclude);v1.7 之前的别名(name、file、names 等)仍然可用,并会在 JSON-RPC 响应中通过 _meta.deprecated_args: [...] 发出提示。格式错误的 JSON-RPC 输入现在会返回符合规范的 -32700 Parse error 响应(v1.9.2 修复),并在 data 字段中回显出错行的 512 个码点;stdin 上的管道断开 / EOF 会正常关闭服务,而不是丢弃正在处理的工具调用。详见 docs/MCP-SCHEMA.md。

其他兼容 MCP 的客户端(Cursor、Codex CLI、Windsurf、Cline、Continue.dev、Zed)请参阅下方的其他 MCP 客户端——使用相同的 vex-mcp 二进制文件,但配置文件不同。

其他 MCP 客户端

同一个 vex-mcp 二进制文件适用于任何兼容 MCP 的客户端。二进制文件的安装方式与上面的 Claude Code 部分相同;只有各客户端的配置文件位置和格式不同。

一行式设置(v1.15.0+):

vex mcp install --agent cursor       # or any of: claude-code, codex-cli, windsurf, cline, continue, zed
vex mcp install --agent all          # fan out across every supported agent
vex mcp install --agent cursor --dry-run   # preview the post-merge config without writing

对于基于文件的 Agent,vex mcp install 会读取现有的 Agent 配置,在不影响其他条目的情况下合并单个 vex 服务条目,并以原子方式写回。对于 Claude Code,它会运行 claude mcp add,而不是编辑文件。此操作具有幂等性——如果条目已匹配,重新运行时会跳过且不做任何更改(--force 可覆盖此行为)。vex mcp uninstall --agent <X> 会移除该条目;vex mcp list 会列出各 Agent 当前的条目。下文记录的其他 Agent 配置文件,正是 vex mcp install 写入的内容——手动编辑、配置自动安装程序尚不支持的 Agent,或处理默认结构以外的情况时,请参阅 integrations/。

最常用配置的可直接复制粘贴片段位于 integrations/:

Agent配置片段磁盘上的目标文件
Claude Codeintegrations/claude-code/(项目级作用域使用 mcp.json)通过 claude mcp add 注册(用户级作用域)或 <project>/.mcp.json
Cursorintegrations/cursor/mcp.json~/.cursor/mcp.json 或 <project>/.cursor/mcp.json
Codex CLI (OpenAI)integrations/codex-cli/config.toml~/.codex/config.toml 或 <project>/.codex/config.toml
Windsurf (Codeium)integrations/windsurf/mcp_config.json~/.codeium/windsurf/mcp_config.json
Cline (CLI)integrations/cline/mcp.json~/.cline/mcp.json(VS Code 扩展:通过面板 UI 配置)
Continue.devintegrations/continue/vex.yaml./.continue/mcpServers/vex.yaml(项目级)
Zedintegrations/zed/settings.json~/.config/zed/settings.json

各 Agent 的注意事项(自动批准标志、超时覆盖、Agent 模式要求)记录在 integrations/README.md 中。

MCP Registry(从 v1.27.2 起):vex 已作为 io.github.tenatarika/vex 收录在官方 MCP Registry 中。每个版本都会为每个平台附带一个 MCP Bundle(vex-mcp-<target>.mcpb,macOS arm64 / Linux x86_64 / Windows x86_64),其中包含 vex-mcp 和 vex;支持 MCPB 的客户端只需询问一次项目根目录,无需在 PATH 中添加其他内容。Bundle 的安装更新由客户端负责,而非 vex self-update(该命令在 Bundle 内会拒绝运行)。

Glama:也已收录在 Glama MCP 目录中,该目录会构建此服务并对其工具进行评级。

Glama 上的 vex MCP 服务

Agent 配方与工作流

将 vex-mcp 接入 Agent 后,接下来的问题是该如何向 Agent 提问,才能让它按正确顺序选择合适的工具。docs/COOKBOOK.md 收录了常见调用链的配方——代码考古、通过 usages --strict 验证的跨文件重构、通过 bundle(mode="pr-impact") 进行 PR 影响分析、清理死代码与重复内容,以及多仓库编排。每个配方都会说明工具调用顺序、这样安排的原因,以及能可靠触发该调用链的 Agent 提示词。

文档与集成:

Shell 集成
# Shell completions (tab-completion for commands and flags)
vex completions bash > ~/.bash_completion.d/vex   # Bash
vex completions zsh > ~/.zfunc/_vex               # Zsh (add ~/.zfunc to fpath)
vex completions fish > ~/.config/fish/completions/vex.fish  # Fish

# Aliases — add to .zshrc / .bashrc
alias vx="vex search"
alias vxu="vex usages"
alias vxi="vex index --path ."
alias vxs="vex index --path . --semantic"
alias vxw="vex watch"
CLAUDE.md 集成

将以下内容添加到项目的 CLAUDE.md,让 Claude Code 使用 vex 而不是 grep:

## Code Search

Before first use in a project, run `vex init` to generate `.vex.toml`, then `vex index` to build the index.
Set `auto_update = true` in `.vex.toml` so the index stays fresh automatically.

Use vex for code search instead of grep or manual file reading:

- `vex check "SymbolName"` — exact-name lookup: does it exist? (~4ms)
- `vex search "SymbolName"` — fuzzy symbol search: find definitions by name or meaning
- `vex search "description" --semantic` — search by meaning (requires --semantic index)
- `vex search "rare_term"` — BM25 channel finds rare terms in symbol bodies (auto-on when index has BM25 data)
- `vex show "SymbolName"` — extract symbol body (use INSTEAD of Read for specific symbols)
- `vex show "A" "B" "C"` — extract multiple symbols at once
- `vex usages "SymbolName"` — find all references
- `vex usages "SymbolName" --strict` — refactor-grade refs (binder-resolved, high precision)
- `vex impact "SymbolName"` — delete-safety blast-radius report (safe/unsafe/uncertain)
- `vex modules [SYMBOL]` — de-facto code clusters (symbol communities)
- `vex pattern 'class $NAME(BaseModel):' --lang python` — AST pattern matching with metavariables
- `vex pattern 'fn $N($$ARGS) -> Result<$T, $E> { $$$BODY }' --lang rust` — multi-line `$$$BODY` / `$$ARGS` capture
- `vex pattern 'struct $S && impl $S' --lang rust` — AND composition (back-ref `$S` must agree across both shapes)
- `vex pattern 'interface $N || class $N' --lang typescript` — OR composition (union, deduped by `(path, line)`)
- `vex pattern '<pat>' --lang <lang> --why` — emit ScanTrace on stderr (mode / candidate vs total / fallback reason)
- `vex outline path/to/file.py` — file structure overview
- `vex implementations "BaseService"` — find types extending a class/interface
- `vex subtypes "BaseService"` — transitive-down closure over extends/implements edges (direct children, grandchildren, …)
- `vex callers "function_name"` — find all callers (~4ms via persistent call graph)
- `vex callees "function_name"` — find all callees (~4ms via persistent call graph)
- `vex paths "from" "to"` — enumerate caller chains between two functions (multi-hop)
- `vex reachable "Target"` — transitive callers of a target (blast-radius analysis)
- `vex tests-for "SymbolName"` — test functions that cover a symbol (framework-labeled)
- `vex history "SymbolName"` — historical versions of a symbol across commits
- `vex similar "SymbolName"` — semantically close symbols (requires --semantic index)
- `vex duplicates --threshold 0.95` — near-duplicate symbol pairs
- `vex diff --base main` — symbol-level diff against a branch (added / removed / moved / body-changed)
- `vex bundle --mode symbol --symbol Foo` — single-call body + callers + callees + similar (replaces 4 round-trips)
- `vex bundle --mode pr-impact --base origin/main` — changed symbols + transitive callers + tests on the current branch

Many search-shaped commands support `--filter-path "path/"` (alias `--filter`) to narrow results to a directory (e.g. `search`, `show`, `usages`, `grep`, `similar`, `duplicates`). Most search-shaped commands also accept `--since <rev>` / `--since-branched` / `--changed-only` for diff-scoping.

### Rules
- **Always prefer `vex show` over `Read`** when you need a specific function or class
- **Always prefer `vex search` over `Grep`** when looking for symbol definitions
- **Use `vex grep` instead of `Grep`** for searching inside string literals, comments, or config values
- **Use `--format compact`** for token-efficient output in automated workflows
- **Use `--kind fn`** to boost results matching a specific symbol kind (fn, struct, trait, class, etc.)
- **Use `--context-path`** with the path of the file you are currently editing to boost nearby results
- **Run `vex update` after modifying source files** if `auto_update` is not enabled in `.vex.toml`
- **Use `vex pattern ... --why`** to debug match counts — the trace tells you whether the indexed prefilter ran or fell back to live-scan, and why
- **Indexed pattern prefilter requires a full `vex index`** — after `vex update` the section is partial and `vex pattern` automatically degrades to live-scan (reason `partial-section` in `--why`)

### Indexing
- `vex index` — full structural index + pattern skeleton section (v6)
- `vex index --semantic` — with embeddings (slower, enables semantic search)
- `vex update` — incremental update (only changed files)
- `vex index --no-pattern-index` — skip the v6 pattern skeleton section if you don't use `vex pattern` (sticky across `vex update`)
- `vex index --no-clusters` — skip computing symbol clusters (v9). `vex update` keeps the opt-out, but the next plain `vex index` computes clusters again

测试

单元与集成测试
cargo nextest run --workspace          # ~4,000 tests — unit, integration, property-based, adversarial
cargo test --doc                       # doctests
cargo clippy -- -D warnings            # zero warnings policy

(建议使用 nextest ≥ 0.9.145——旧版本在 macOS 上会报告虚假的 LEAK;使用 cargo nextest self update 更新。)

测试覆盖范围包括:

  • 各语言语法回归(新增):所有 20 种受支持语言均有对应的 tests/<lang>_query_test.rs——在升级 tree-sitter 语法 crate 时检测 ABI 不匹配和 AST 节点重命名
  • 二进制格式:往返测试、拒绝损坏/截断/版本错误的数据、越界访问、字符串池去重、空索引
  • 对抗性格式:20 项精心构造的索引测试——偏移溢出、错误的 magic/version、对齐攻击、记录截断
  • 向量:384 维 f32 嵌入的写入/读取往返测试
  • FST:refs FST 往返测试、前缀搜索、符号 FST 精确/前缀/模糊搜索
  • 搜索:结构化搜索、模糊搜索(Levenshtein)、RRF 融合、带有类型/路径/邻近度加权的重排序
  • 重排序压力测试:NaN/Infinity/零分数、10K 条结果、边缘上下文路径
  • 基于属性的测试(proptest):重排序保持长度不变、输出有序、无 NaN/负分数、融合满足交换律
  • 增量更新:未更改内容复用、删除内容移除、文件重命名、符号在文件间移动、空文件
  • 并发:并行索引/更新(锁序列化)、并发读取器、重建索引期间读取
  • 多语言:Rust、Python、Go、Kotlin、TypeScript、C++、跨语言同名、扩展名错误、1K 符号文件、深层嵌套、错误恢复
  • Unicode:BOM、混合 CRLF、Unicode 标识符、空字节、空文件/仅含空白的文件
  • 路径边界情况:路径含空格、深层嵌套(20 层)、符号链接、绝对路径与相对路径、Windows 反斜杠
  • 调用图:Rust、Python、Go、TypeScript、Java 的调用方/被调用方
  • 持久化调用图(v1.5):格式 v4 往返测试、调用方/被调用方 FST 查找、去重、隔离不同文件中的同名符号、区分同一文件中的同名符号、增量更新保留边、v3 时回退到实时扫描
  • 相似项/重复项(v1.5):排除自身、阈值筛选、规范化配对去重、函数体长度筛选、空索引处理
  • 可插拔嵌入器(v1.5):注册表查找、不匹配检测(包括对旧于 9.1 清单的向后兼容)、配置与 CLI 优先级、写入器变量 vector_dim
  • BM25 通道(v1.5):写入器/读取器往返测试、流水线输出、IDF 区分度、短文档优先、带 Hybrid 标签的三路 RRF、MatchType 标记、Unicode 词元
  • 陈旧检测:git HEAD 比较、脏文件检测、mtime 回退
模糊测试

模糊测试使用 cargo-fuzz(libFuzzer + AddressSanitizer),对所有接收不可信输入的解析器进行测试——包括二进制索引格式、sidecar 文件、面向用户的模式语法,以及 JSON 清单:

# Install (once)
cargo install cargo-fuzz

# Generate seed corpus for every target
bash fuzz/generate_seeds.sh

# Run (requires nightly)
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_index_reader        -- -max_total_time=120
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_refs_fst            -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_symbol_fst          -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_bloom_load          -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_pattern_parser      -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_manifest_load       -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_marker_load         -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_tokenize_document   -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_hash_index_load     -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_incremental_hnsw    -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_rename_chains_load  -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_state_load          -- -max_total_time=60

18 个模糊测试目标覆盖了读取器的所有 unsafe 路径,以及所有接收对抗性输入的文本/sidecar 解析器:

目标模糊测试内容覆盖接口
fuzz_index_reader将任意字节作为 .vex 文件header()、symbol()、vector()、read_string()、file_paths()、ref_edge API
fuzz_refs_fst任意 FST 和 posting 字节RefReader::find()、find_by_prefix()、find_ref_edges_by_symbol
fuzz_symbol_fst任意 FST 和 posting 字节SymbolFstReader::find()、find_fuzzy()、search_with_fallback()
fuzz_bloom_load (v1.12.0)任意 index.bloom sidecarSymbolBloom::load,随后进行 may_contain 探测
fuzz_pattern_parser (v1.12.0)将任意 UTF-8 文本作为模式字符串parse_composite_pattern(元变量、&& / `
fuzz_manifest_load (v1.12.0)将任意 JSON 作为 manifest.jsonManifest::load(128 MiB 大小上限 + JSON 解析)
fuzz_marker_load (v1.13.0)将任意文本作为 <onnx>.sha256.markerverify_with_marker 解析器 + 决策树
fuzz_tokenize_document (v1.13.0)将任意 UTF-8 文本作为 BM25 输入tokenize_document(共享所有权的 String 重构之后)
fuzz_hash_index_load (v1.14.1)将任意字节作为 index.hashes sidecarhash_index::load(VEXH magic、MAX_COUNT 防护、截断)
fuzz_incremental_hnsw (v1.15.0)对抗性的 new_hashes 切片build_hnsw_incremental_at(重复项、墓碑、去重并跳过)
fuzz_rename_chains_load (v1.17.0)将任意字节作为 index.rename_chains sidecarrename_chains::load(VEXR v1、MinHash + LSH 重放)
fuzz_state_load (v1.18)将任意字节作为 index.state sidecarincremental_state::load(VEXS v1、256 MiB 上限、bincode 负载)
fuzz_unresolved_refs (v1.22.0)任意 FST、posting 和 edge 字节UnresolvedRefReader(v7 unresolved_refs 区段、多仓库严格回退)
fuzz_kotlin_binder (v1.23.0)将任意字节作为 Kotlin 源码tree-sitter 解析 → 符号提取 → Kotlin bind_refs
fuzz_unresolved_hierarchy (v1.25.0)任意 FST、posting 和 edge 字节UnresolvedHierarchyReader(v8 unresolved_hierarchy 区段)
fuzz_csr (v1.27.0)任意 offsets / edge_idx 字节以及 n / mCsrView::new + neighbors(v9 CSR、callees 和 ref-edge 形态)
fuzz_leiden (v1.27.0)将任意字节解码为 ≤256 个节点的图确定性 Leiden-CPM(运行两次:输出相同、每个簇均连通)
fuzz_cluster_section (v1.27.0)将任意字节作为索引文件打开ClusterSectionReader 入口点(index.vex 中的 v9 clusters 区段)

最近一次系统级审计(Q4-A/B 收尾,2026-06-17):当时存在的全部 11 个目标共计 约 7,600 万次 执行,0 次崩溃 / panic / AddressSanitizer 命中 / 泄漏(每个目标 61 秒,libFuzzer + ASan + nightly)。在此过程中,在对抗性的 ref_edge 字节触发的死代码路径中发现一个潜在的 FST panic,并在 干净运行之前修复——find_ref_edges_by_symbol 和 RefReader::find_by_prefix 现在会用 catch_unwind 包装内部 FST 遍历,因此损坏的 sidecar 会返回 Err,而不是导致进程退出。此前的基线: v1.14.1(2026-06-05)在 9 个目标上运行了 580 万次迭代,结果干净; v1.15.2 发布门禁(2026-06-08)在 信号最强的 4 个目标上运行了约 85.3 万次专项执行,结果干净。

模糊测试在整个项目生命周期中发现并修复了 8 个真实缺陷:

  • v1.x:针对精心构造的 symbol_count 进行越界读取;在奇数 symbols_offset 上解引用未对齐的指针;未检查超出文件大小的节偏移(强化二进制读取器)。
  • v1.12.0:SymbolBloom::load 接受了 n_bits = 0、k_num = 0 的 sidecar。其一致性检查通过,但之后在 bloomfilter::Bloom::check 中因 hash % 0 触发 panic。修复:加载时拒绝退化尺寸。
  • v1.12.0:SymbolBloom::load 接受了最高约 21 亿的 k_num,导致每次 may_contain 调用都循环 110 秒以上(DoS,而非 panic)。修复:加载时将 k_num 限制为 k_num <= MAX_K_NUM = 64。
  • Phase 11.1.10 / Q4-A(2026-06-17):find_ref_edges_by_symbol 中的 FST 遍历会因精心构造的 refs FST 触发 panic,绕过生产环境中的 catch_unwind(libfuzzer-sys 的 panic hook 会在用户代码拦截之前触发)。修复:在内部遍历外层增加独立的 catch_unwind,并返回 Err。同时对 Manifest::load 进行了纵深防御强化:128 MiB 的预读大小上限可在 serde 分配数 GB 堆内存之前拦截恶意 JSON(Q4-B 审计后续措施;纵深防御,威胁模型针对用户拥有的文件)。
  • v1.23.0:一个 451 字节的格式错误 Kotlin 输入使 tree-sitter 的 GLR 错误恢复进入超线性时间和内存消耗(334 秒、超过 2 GB;DoS,而非崩溃;由 fuzz_kotlin_binder 发现)。修复:所有生产解析都通过 parser_pool::parse_text 执行,该函数会根据输入大小限制所有语言的进度回调调用次数。
  • v1.23.0:tree_sitter::Node::utf8_text() 在格式错误的输入导致 tree-sitter 生成越过 EOF 的节点时触发 panic(由 fuzz_kotlin_binder 发现)。修复:在提取器、绑定器和模式预过滤器中,使用带边界检查的 NodeTextExt(node_text / node_text_opt)替代原始 utf8_text。

v1.13.0 / v1.14.1 新增的内容在新代码中未发现缺陷——由审查推动的 hash_index::save / load 中 MAX_COUNT 检查是在模糊测试运行前添加的纵深防御措施(rust-reviewer + code-reviewer 指出了 save 中会截断的 as u32 类型转换),而持续运行 300 万 / 580 万次迭代也证实了这些检查有效。

架构

CLI (clap) → Pipeline (rayon, 500-file chunks) → Tree-sitter
                                      ↓
                           Binary format v9 (mmap, zero-copy)
                                      ↓
       ┌──────────────────┬──────────────┬──────────────┬──────────────┬─────────────┬──────────────┐
       ↓                  ↓              ↓              ↓              ↓             ↓              ↓
  Symbol FST         Refs FST        BM25 doc       HNSW vectors  Call graph   Hierarchy     Clusters
  (structural)    (cross-file refs) (body tokens)   (semantic)   (callers FST / (v8 edges)   (v9 Leiden-
                                                                  callees CSR)                 CPM)
                                      ↓
                       Embedder trait → fastembed / MiniLM-L6 (default)

Per-project sidecars (in <index_dir>/):
  · index.vex             — primary index (format v9)
  · manifest.json         — metadata: embedder, sections, version, staleness tracking
  · index.bloom           — symbol-name bloom filter (`vex check` skips FST lookups for definitely-missing names)
  · index.trigram         — per-file trigram bloom so `vex grep` skips non-matching files (v1.24.1)
  · index.hnsw            — semantic vectors (HNSW graph)
  · index.bodytokens      — per-symbol terms for BM25 + semantic context (B1.2)
  · index.git_history     — historical symbol presence (Phase 14.8, FST + git-walk fallback)
  · index.rename_chains   — MinHash+LSH rename tracking across commits (Phase 14.10)
  · index.state           — incremental state: imported_by reverse map + writer-provenance sentinels (audit C1)

Shared cross-project (in user cache root, e.g. ~/Library/Caches/vex/blobs/):
  · {sha}.bin shards      — content-addressed parse cache, keyed by git blob SHA (Phase 14.7)

Search pipeline:
  search   → Symbol FST + BM25 + HNSW  → N-way RRF fusion → Hybrid tag on cross-channel hits
  usages   → Refs FST + posting lists → zero-copy, --strict adds type-aware filter (v1.14.1)
  history  → walks git tree, follows rename chains via Jaccard + greedy 1:1 (Phase 14.10)
  similar  → HNSW nearest neighbors (hash-keyed for content-stable IDs)
  show     → tree-sitter node boundaries → symbol body extraction
  pattern  → AST-aware structural matcher with skeleton index
  • 无 SQLite — 自定义二进制格式 v6,零拷贝 mmap 读取;读取器接受 v3+,以保持向后兼容
  • 符号 FST — 持久化倒排索引,查找复杂度为 O(query_len)
  • Refs FST + ref_edges — 将符号引用存储为 FST,并在写入时解析跨文件边(store::writer 中的 Pass-2);支持达到重构级别的 usages --strict
  • 持久化调用图 — CallEdge 记录 + 按名称键控的 callers FST + 密集 callees CSR 索引(v9+;较旧索引使用 FST);在索引时构建,查询约需 4 毫秒,而实时扫描 tree-sitter 需要数秒
  • BM25 通道 — 对 body_tokens 使用 Okapi BM25,存在对应节时自动启用
  • HNSW — 通过 usearch 实现近似最近邻搜索,语义搜索复杂度为 O(log N);条目按哈希键控,使内容稳定 ID 在重新索引后保持不变
  • 可插拔嵌入器 — Embedder trait + 注册表;在清单中记录身份,并在搜索时检测不匹配
  • 历史索引 — 每次提交中的符号存在情况 + 基于 MinHash 的重命名链跟踪(解决 LIMITATIONS §4c #2 中的 1:1 重命名问题)
  • 并行解析 — 使用 rayon,每批处理 500 个文件;blob-SHA 解析缓存(在用户缓存根目录下的项目间共享)会跳过重新索引时未变文件的重新解析
  • 增量更新 — 使用 xxh3 进行内容哈希;vex update 仅重新解析变更文件(从现有索引重建未变符号 + 调用边)
  • 监视模式 — 使用 notify crate,防抖间隔为 500 毫秒
  • N 路 RRF 融合 — fuse_many 合并结构化、BM25 和语义排序列表,并将跨通道命中项标记为 Hybrid
  • 排序评估框架 — 在捆绑的黄金集上运行 vex eval;CI 回归门槛依据平均 nDCG@10、各查询类型下限和各通道归因(Phase 13.12 / 13.12.1)

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。