defendend/Claude-ast-index-search
defendend/Claude-ast-index-search · 1 plugin
Marketplace Cli allows AST search, ~40-50% economy tokens. Claude/Codex/Cursor searches (suitable for any AI agent that can use Bash)
Install
The repo has no one-line install. Follow its README.
Plugins 1
After adding the marketplace, install one with /plugin install <name>@ast-index-marketplace.
- 1ast-indexFast code search for Android/Kotlin/Java, iOS/Swift/ObjC, Dart/Flutter, TypeScript/JavaScript, Rust, Ruby, C#, PHP, Python, Go, Perl, C++, Protocol Buffers, and WSDL/XSD projects
/plugin install ast-index@ast-index-marketplace
Files
ast-index v3.56.0
Structural, AST-aware code navigation CLI for large, multi-language
repositories. It builds a local SQLite index of symbols, references, imports,
modules, dependencies, and inheritance so humans and agents can move through
code by exact structure instead of grep-style text matches.
What It Gives You
- Navigate classes, functions, files, imports, usages, callers, implementations,
inheritance, modules, and dependency paths. - Start broad with
explore, then jump to exact definitions withsymbol,
class,outline, andrefs. - Keep the index current with
ast-index updateafter the firstrebuild. - Give coding agents compact, parseable context instead of raw file dumps.
- Save ~40-50% of agent tokens on large repositories by returning structural
slices of code instead of whole files.
Languages: Kotlin, Java, Swift, Objective-C, TypeScript, JavaScript, Vue,
Svelte, CSS, SCSS, Less, Rust, Zig, C#, Python, Go, C, C++, Scala, PHP, Ruby,
Perl, Dart, Protocol Buffers, WSDL, XSD, BSL (1C:Enterprise), Lua, Bash, Elixir,
SQL, R, Matlab, Groovy, Common Lisp, GDScript. Project type is auto-detected.
How To
# Install
brew tap defendend/ast-index
brew install ast-index
# Build an index once per project
cd /path/to/project
ast-index rebuild
# Ask code questions
ast-index explore "payment flow"
ast-index search ViewModel
ast-index class BaseFragment
ast-index usages Repository
ast-index implementations Presenter
ast-index deps app
# Opt-in data: Git history and the symbol graph (neither runs on rebuild/update)
ast-index hotspots --collect # rank files by churn, fixes, authors
ast-index graph build # symbol-to-symbol dependency graph
ast-index graph dependents ApplicationService # who depends on it, before a change
ast-index search Service --rank proven # which match is safe to copyUse ast-index update after edits or branch switches. Hooks can queue a
trailing-debounced refresh without losing edits that arrive during an update:
ast-index update --background --debounce-ms 500Index-reading commands wait (with a bounded timeout) for an already queued
generation, so they do not observe stale results. In monorepos with nested
project markers, add --walk-up or AST_INDEX_WALK_UP=1 to reuse the root
index.
Guides: User guide
for everyday workflow;
command setup guide
for install/options/examples;
CodeGraph comparison
for a dated, source-backed feature comparison.
Performance
Benchmarks on large Android project (~29k files, ~300k symbols):
| Command | ast-index | grep | Speedup |
|---|---|---|---|
| imports | 0.3ms | 90ms | 260x |
| dependents | 2ms | 100ms | 100x |
| deps | 3ms | 90ms | 90x |
| class | 1ms | 90ms | 90x |
| search | 11ms | 280ms | 14x |
| usages | 8ms | 90ms | 12x |
Installation
Homebrew (macOS/Linux)
brew tap defendend/ast-index
brew install ast-indexCargo (crates.io)
Requires a Rust toolchain.
cargo install ast-index --lockedTo build the unreleased default branch instead:
cargo install --locked --git https://github.com/defendend/Claude-ast-index-search ast-indexBoth build from source. For prebuilt release binaries, use Homebrew, npm, or Winget.
Winget (Windows)
winget install --id defendend.ast-indexMigration from kotlin-index
If you have the old kotlin-index installed:
brew uninstall kotlin-index
brew untap defendend/kotlin-index
brew tap defendend/ast-index
brew install ast-indexFrom source
git clone https://github.com/defendend/Claude-ast-index-search.git
cd Claude-ast-index-search
cargo build --release
# Binary: target/release/ast-index (~50 MB)Troubleshooting: Syntax errors on install
If brew install ast-index fails with merge conflict errors (<<<<<<< HEAD), reset your local tap:
cd /opt/homebrew/Library/Taps/defendend/homebrew-ast-index
git fetch origin
git reset --hard origin/main
brew install ast-indexMonorepo Workflow
If your repo has subdirectories with their own VCS markers (git submodules,
subtrees, nested Cargo.toml / settings.gradle), read-commands normally
stop at the nearest marker — they won't reuse a parent-level index even
if one exists. Pass --walk-up, or set AST_INDEX_WALK_UP=1, to tell
the lookup to prefer any existing parent DB over nested markers:
# once, in the root
cd /monorepo && ast-index rebuild
# later, from any subproject — reuse the root index
AST_INDEX_WALK_UP=1 ast-index search ViewModel
# or per-call:
ast-index --walk-up search ViewModelThis is opt-in by design: silently preferring a far-away parent DB could
surface a stale or misconfigured index from an earlier accidental
rebuild higher up. With the flag you explicitly say "trust the parent".
Worktrees And Intentional Cross-Root Workspaces
Independent git worktrees get independent indexes because their canonical
root paths differ. Rebuild once inside each worktree; do not attach worktrees
to one another.
For source trees that intentionally form one workspace, create the primary
index first, attach a named subtree, then index the attached files:
cd /path/to/application
ast-index rebuild
ast-index subtree add shared ../shared-library
ast-index update # or: ast-index rebuild
ast-index subtree listUse --subtree shared to query only that attachment and --local to query
only the primary project. The legacy root commands remain compatibility
aliases; new automation should use subtree add/remove/list.
AI Agent Integration
Claude Code Plugin
# Option 1: via marketplace
claude plugin marketplace add defendend/Claude-ast-index-search
claude plugin install ast-index
# Option 2: if ast-index is already installed
ast-index install-claude-pluginRestart Claude Code to activate.
Update: brew upgrade ast-index && claude plugin update ast-index.
Uninstall: claude plugin uninstall ast-index.
The Claude plugin ships /initialize as the default setup command. It
auto-detects project stack(s), including KMP and polyglot repos, then writes
.claude/settings.json and .claude/rules/ast-index.md. Use
/initialize-android, /initialize-ios, /initialize-web, /initialize-rust,
/initialize-csharp, or /initialize-ruby only as manual overrides.
See examples/.claude/rules/ast-index.md
for a template rules file that teaches the agent to use ast-index for
structural navigation, outline before reading large files, and pass the same
instructions to subagents. Adapt before dropping into your project's
.claude/rules/.
Codex Skill / Plugin
Codex can use the shared ast-index skill directly. For local development,
symlink or copy the skill directory into Codex's global skills directory:
mkdir -p ~/.codex/skills
ln -s /absolute/path/to/Claude-ast-index-search/plugin/skills/ast-index ~/.codex/skills/ast-indexThis repository also includes a Codex plugin manifest at
plugin/.codex-plugin/plugin.json and a
repo marketplace at .agents/plugins/marketplace.json
for Codex builds that support plugin marketplaces.
If your Codex build supports plugin marketplaces, restart Codex in this repo
and install ast-index from the repo marketplace. For a remote marketplace,
add the repository:
codex plugin marketplace add defendend/Claude-ast-index-searchThe Codex package exposes the same ast-index skill. Command-style project
setup is kept out of the Codex manifest because Codex uses skills and local
project configuration as first-class components.
Cursor Skill / Plugin
Cursor can use the shared skill directly:
mkdir -p ~/.cursor/skills
ln -s /absolute/path/to/Claude-ast-index-search/plugin/skills/ast-index ~/.cursor/skills/ast-indexThis repository also includes a Cursor plugin manifest at
plugin/.cursor-plugin/plugin.json and a
multi-plugin marketplace at .cursor-plugin/marketplace.json.
For local Cursor testing:
mkdir -p ~/.cursor/plugins/local
ln -s /absolute/path/to/Claude-ast-index-search/plugin ~/.cursor/plugins/local/ast-indexReload Cursor after creating the symlink. The Cursor plugin package exposes the
shared ast-index skill, a project rule in plugin/rules/, and a Cursor-specific
initialize-ast-index command that writes .cursor/rules/ast-index.mdc.
Gemini CLI
gemini skills install https://github.com/defendend/Claude-ast-index-search.git --path plugin/skills/ast-index💝 Support Development
Commands (47+)
Run ast-index rebuild once per project, then use ast-index update to keep
the index fresh.
ast-index explore <QUERY...> # One-shot context: ranked symbols, outline/source, neighbours, tests (--rwr for graph)
ast-index search <QUERY> # Universal structural search
ast-index search <QUERY> --rank <PRESET> # Re-rank by history + graph: proven, hotspots, risky, central
ast-index file <PATTERN> # Find files
ast-index symbol <NAME> # Find symbols
ast-index symbol <NAME> --with-content # Include matched symbol bodies
ast-index search <QUERY> --with-content # Include matched symbol bodies
ast-index class <NAME> # Find classes/interfaces
ast-index outline <FILE> # Symbols in file
ast-index imports <FILE> # Imports in file
ast-index refs <SYMBOL> # Definitions + imports + usages
ast-index usages <SYMBOL> # Symbol usages
ast-index callers <FUNCTION> # Function call sites
ast-index implementations <PARENT> # Find implementations
ast-index implementations <PARENT> --with-content # Include implementation bodies
ast-index hierarchy <CLASS> # Class hierarchy tree
ast-index changed [--base BRANCH] # Branch-level changed files (A/M/D/R)
ast-index hotspots [--collect] # Rank files by Git history (churn, fixes, authors)
ast-index graph <SUBCOMMAND> # Symbol graph: build, dependents, impact, path, cycles, top, metrics
ast-index todo [PATTERN] # TODO/FIXME/HACK comments
ast-index deprecated [QUERY] # Deprecated itemsPaginated JSON schema v2
Limited search commands now report completeness explicitly. symbol, class,
implementations, usages, and callers use this shape:
{
"schema_version": 2,
"items": [],
"pagination": {
"total": 0,
"returned": 0,
"truncated": false,
"limit": 50
}
}For symbol --with-content and implementations --with-content, each item
additionally contains content, truncated, and end_line. content is a
line-numbered source body read from disk, capped at 60 lines; truncated tells
whether the range extends past those lines, and end_line is the indexed end
of the symbol. search --with-content adds the same fields to its symbols
array, including ranked search. Text output marks truncated bodies. Without
the flag, response shapes are unchanged. The index stores parser supplied
start and end lines, not body text. Existing indexes gain ranges as files are
reindexed; rebuild fills them immediately.
search and refs keep their named result arrays and provide one pagination
object per array under pagination. Consumers migrating from bare arrays must
read items for single-result-set commands and must check truncated before
treating a response as complete. This is limit-based pagination, not a cursor:
rerun with a larger --limit when more results are required.
The cache-independent changed command retains its separate JSON schema v1;
its schema version did not change with this pagination migration.
Changed files on the current branch
changed asks the detected version-control repository for the files changed from
merge-base(base, HEAD) to HEAD. Without --base, Git resolves
origin/HEAD, then tries origin/main, origin/master, main, master, and
trunk; other supported backends select their conventional mainline. It reads
version-control state directly, so it works without an
ast-index database and does not require rebuild or update. Results are
scoped to the current working directory, while paths remain
repository-relative. Staged and unstaged working-tree edits are not included.
# Compact text summary
ast-index changed
# Stable schema v1; the VCS timeout defaults to 30000 ms
ast-index --format json changed --base origin/main --timeout-ms 30000
# Print the detected root, scope, exact VCS argv, and timing to stderr
ast-index changed --verboseText output uses A (added), M (modified), D (deleted), and R (renamed):
Changed files against origin/main (3):
M README.md
R docs/old-guide.md -> docs/setup-guide.md
M docs/generated\nname.md
Control characters and backslashes in text paths are escaped, so every change
stays on one output line. Use JSON instead of parsing this human-readable
summary in scripts.
JSON output preserves rename metadata:
{
"schema_version": 1,
"vcs": "git",
"base": "origin/main",
"head": "HEAD",
"scope": null,
"changes": [
{ "status": "M", "path": "README.md" },
{ "status": "R", "path": "docs/setup-guide.md", "old_path": "docs/old-guide.md" }
]
}At the repository root, scope is null; from a nested working directory it
is that repository-relative directory path.
This is a fast file summary for branch review, not a changed-symbol report and
not a replacement for your version-control system's diff command when patch
hunks are needed.
Git hotspots
Two files can look equally good to copy from and have very different histories:
one absorbed 28 commits in six months with 54% of them bugfixes, the other was
written once and left alone for 86 days. hotspots puts that history next to
the code.
Per file, collected from the Git log: commit count, churn (added + deleted
lines, plus churn relative to the file's current size), bugfix share, distinct
author count, age of the first commit, and time since the last change.
Thresholds are percentiles within this repository, not constants. 28
commits is a lot for a small library and unremarkable in a monorepo, so
churn:high means p90+ here. Raw numbers are always printed next to the
label.
# Collect history into the index, then show the top 20
ast-index hotspots --collect
# Report from already-collected data (no Git subprocess at all)
ast-index hotspots --limit 50 --sort fixes
# Narrow to a directory; percentiles still come from the whole repository
ast-index hotspots --path src/parsers --min-commits 5
# Discard the cursor and rescan the full history
ast-index hotspots --collect --full
# Stable paginated schema v2
ast-index --format json hotspots --limit 10Collection is never implicit: rebuild and update do not run it, so
indexing cost is unchanged for everyone who does not ask for this. The first
--collect walks the whole history; every later one reads only what changed.
History is stored per commit, so a branch switch, rebase, force-push or reset
subtracts the commits HEAD no longer reaches and adds the new ones, and the
numbers equal a fresh full collection at that HEAD. Line counts come from the
working tree: an uncommitted edit counts once a later --collect recomputes
that file (a commit or checkout changes it) or on --full.
Signals live in their own tables, keyed by project-relative path, so they
also cover files the parsers never look at (fixtures, configs, migrations).
They depend on the repository, not on the index, so rebuild carries them
into the new index and the next --collect stays incremental. It leaves them
behind, says so, and the next --collect reads the history again only when
they were collected by an older version or from another working tree or
scope, or cannot be read back intact.
Git hotspots — 289 file(s) with history, 370 commit(s) analyzed, HEAD 50db069dbb, sorted by score:
Labels are percentiles within this repository (high = p90+, elevated = p75+).
src/indexer.rs
score 90 · commits 70 (p98) · fixes 17/70 = 24% (p72) · churn +8558/-3259 (p99, 2.2x file)
authors 6 (p99) · age 244d · last change 0d ago · 5298 lines
churn:high rewritten-often authors:many veteran
Labels: churn:high / churn:elevated, rewritten-often (high churn per
current line, only for files of 10+ lines), fixes:high / fixes:elevated
(only for files with at least four commits, so 1-of-1 is never "100% bugs"),
authors:many, veteran. score is the mean of the commit, churn and
bugfix-ratio percentiles.
Bugfix detection is a heuristic over commit subjects: leading tracker keys
([ABC-123], ABC-123:, #42, [ABC-1][ABC-2]) are stripped first, then
English and Russian bugfix vocabulary is matched on word boundaries — so
prefix is not a fix, Исправить падение is, and so is [HOTFIX][ABC-123]
(only keys are stripped, not tags like [HOTFIX] or [FIX]). Merge commits
are excluded. Renames follow the file: history recorded under the old path
moves onto the new one.
--sort accepts score (default), commits, churn, relative-churn,
fixes, authors, recent. fixes orders by the bugfix share discounted
for thin history — the lower bound of its 95% Wilson interval, so 11 fixes in
17 commits rank above 2 in 2 — and lists files with fewer than four commits
after the rest. --subtree is rejected: the signals describe the project's own
working tree.
Symbol graph and ranked search
graph build turns indexed references into symbol-to-symbol edges, each with
the confidence of its resolution (local, scoped, import, unique,
ambiguous). Like history collection it is explicit: rebuild and update
never run it, and queries flag a graph gone stale (--refresh rebuilds first).
ast-index graph build
ast-index graph dependents ApplicationService # incoming edges
ast-index graph impact PaymentGateway --depth 3 # transitive dependents per hop
ast-index graph path OrdersController Invoice # how one reaches the other
ast-index graph top --kind class --exclude-tests # most central symbolssearch --rank <preset> re-orders the Files and Symbols of a search by the
collected Git history and the graph: proven (safe to copy), hotspots
(keeps being fixed), risky (many dependents and unstable history), central
(PageRank). Exact name matches stay first; a preset whose data is missing is
not applied and the output names the command that collects it. Formulas and
their backtest are in the
user guide.
Module analysis
ast-index module <PATTERN> # Find modules
ast-index deps <MODULE> # Module dependencies
ast-index dependents <MODULE> # Dependent modules
ast-index unused-deps <MODULE> # Find unused dependencies (v3.2: +transitive, XML, resources)
ast-index api <MODULE> # Public API of modulemodule-route — dependency path between two modules
Show how module A reaches module B through the dependency graph:
# Shortest path (default)
ast-index module-route --from core.utils --to features.payments.api
# All simple paths, filtered to api edges only
ast-index module-route --from app --to core.db --all --via-kind api
# JSON output — machine-readable, no ANSI
ast-index module-route --from app --to core.db --format json
# Mermaid diagram (paste into any markdown renderer)
ast-index module-route --from app --to core.db --format mermaid
# Graphviz DOT
ast-index module-route --from app --to core.db --format dot
# Gradle-style module names work too
ast-index module-route --from :app --to :core:utilsOptions:
--all— return all simple paths instead of the single shortest--via-kind <api|implementation|all>— filter traversal to one edge kind (default:all)--max-paths <N>— cap on returned paths when--allis set (default: 50)--max-depth <N>— cap on path length in hops (default: 20)--timeout-ms <N>— wall-clock guard in milliseconds (default: 5000)
XML & Resource analysis
ast-index xml-usages <CLASS> # Find class usages in XML layouts
ast-index resource-usages <RES> # Find resource usages (@drawable/ic_name, R.string.x)
ast-index resource-usages --unused --module <MODULE> # Find unused resourcesiOS-specific commands
ast-index storyboard-usages <CLASS> # Class usages in storyboards/xibs
ast-index asset-usages [ASSET] # iOS asset usages (xcassets)
ast-index asset-usages --unused --module <MODULE> # Find unused assets
ast-index swiftui [QUERY] # @State/@Binding/@Published props
ast-index async-funcs [QUERY] # Swift async functions
ast-index publishers [QUERY] # Combine publishers
ast-index main-actor [QUERY] # @MainActor usagesPerl-specific commands
ast-index perl-exports [QUERY] # Find @EXPORT/@EXPORT_OK
ast-index perl-subs [QUERY] # Find subroutines
ast-index perl-pod [QUERY] # Find POD documentation (=head1, =item, etc.)
ast-index perl-tests [QUERY] # Find Test::More assertions (ok, is, like, etc.)
ast-index perl-imports [QUERY] # Find use/require statementsIndex management
ast-index rebuild [--type TYPE] # Full reindex
ast-index update # Incremental update
ast-index stats # Index statistics
ast-index version # Version infoLanguage-Specific Features
TypeScript/JavaScript (new in v3.9)
Supported elements:
- Classes, interfaces, type aliases, enums
- Class methods (constructor, getters/setters, static, async)
- Class fields/properties, private
#members, abstract methods - Functions (regular, arrow, async)
- React components and hooks (
useXxx) - Vue SFC (
<script>extraction) - Svelte components
- Decorators (@controller, @Injectable, etc.)
- Namespaces, constants, imports/exports
ast-index class "Component" # Find React/Vue components
ast-index search "use" # Find React hooks
ast-index search "@Controller" # Find NestJS controllers
ast-index class "Props" # Find prop interfacesRust (new in v3.9)
Supported elements:
- Structs, enums, traits
- Impl blocks (
impl Trait for Type) - Functions, macros (
macro_rules!) - Type aliases, constants, statics
- Modules, use statements
- Derive attributes
ast-index class "Service" # Find structs
ast-index class "Repository" # Find traits
ast-index search "impl" # Find impl blocks
ast-index search "macro_rules" # Find macrosRuby (new in v3.9)
Supported elements:
- Classes, modules
- Methods (def, def self.)
- RSpec DSL (describe, it, let)
- Rails patterns (has_many, validates, scope, callbacks)
- Require statements, include/extend
ast-index class "Controller" # Find controllers
ast-index search "has_many" # Find associations
ast-index search "describe" # Find RSpec tests
ast-index search "scope" # Find scopesC# (new in v3.9)
Supported elements:
- Classes, interfaces, structs, records
- Enums, delegates, events
- Methods, properties, fields
- ASP.NET attributes (@APIController, @httpget, etc.)
- Unity attributes (@SerializeField)
- Namespaces, using statements
ast-index class "Controller" # Find ASP.NET controllers
ast-index class "IRepository" # Find interfaces
ast-index search "[HttpGet]" # Find API endpoints
ast-index search "MonoBehaviour" # Find Unity scriptsDart/Flutter (new in v3.10)
Supported elements:
- Classes with Dart 3 modifiers (abstract, sealed, final, base, interface, mixin class)
- Mixins, extensions, extension types
- Enhanced enums with implements/with
- Functions, constructors, factory constructors
- Getters/setters, typedefs, properties
- Imports/exports
ast-index class "Widget" # Find widget classes
ast-index class "Provider" # Find providers
ast-index search "mixin" # Find mixins
ast-index implementations "State" # Find State implementations
ast-index outline "main.dart" # Show file structure
ast-index imports "app.dart" # Show importsPython
ast-index class "ClassName" # Find Python classes
ast-index symbol "function" # Find functions
ast-index outline "file.py" # Show file structure
ast-index imports "file.py" # Show importsGo
ast-index class "StructName" # Find structs/interfaces
ast-index symbol "FuncName" # Find functions
ast-index outline "file.go" # Show file structure
ast-index imports "file.go" # Show importsConfiguration File
Create .ast-index.yaml in your project root to configure ast-index:
# Additional directories to index
roots:
- "../shared-lib"
- "../common-modules"
# Directories to exclude from indexing
exclude:
- "vendor"
- "build"
- "node_modules"
# Index only these directories (allow-list; everything else is skipped)
include:
- "app"
- "packages/shared"
# Include files ignored by .gitignore
no_ignore: falseAll fields are optional. CLI flags override config file values.
Examples
Monorepo with shared libraries:
roots:
- "../core"
- "../network"Project with generated code to skip:
exclude:
- "generated"
- "proto/gen"Changelog
3.56.0
This release merges the symbol graph, Git hotspots and ranked search work
contributed in #72
(which also carried #28),
the --with-content responses from
#73, and
follow-up fixes.
graph— a symbol dependency graph —graph buildresolves indexed
references into symbol-to-symbol edges and stores them with per-edge
resolution confidence (local,scoped,import,unique,ambiguous).
Queries on top of it:dependents,dependencies,impact(transitive
dependents),path,cycles,top,statusandmetrics(fan-in, fan-out,
PageRank). Ruby references resolve through namespaces, lexical nesting,
constant receivers, inheritance and mixins; JavaScript/TypeScript references
through the module's own imports. Metrics count resolved edges only and
report ambiguous ones separately, so a name defined in hundreds of places
does not inflate a symbol's centrality. The graph is opt-in:rebuildand
updatenever build it, and queries flag a stale graph after the index
changes and accept--refresh.hotspots— per-file Git history signals — a new command ranks files by
what their history says about them: commit count, churn (absolute and
relative to the file's current size), bugfix share, distinct authors, age
and time since the last change. Thresholds are percentiles within the
repository being indexed, not absolute constants, so "high churn" means
something in a 300-file library and in a 30 000-file monorepo alike; raw
numbers are printed next to every label. Text and--format jsonoutput,
paginated JSON schema v2.- Collection is opt-in —
rebuildandupdatenever walk the log;
hotspots --collectdoes. search --rank— re-rank results by history and structure — four
presets re-order the Files and Symbols sections of a search:proven(calm,
old, idle and actually used — safe to copy),hotspots(the file's hotspot
score),risky(transitive dependents × hotspot score) andcentral
(PageRank). Every result carries its dossier: the score and its terms, raw
history and graph numbers with repository percentiles and labels, and the
relevance position it came from. Relevance stays in charge: exact name
matches stay above partial ones, the pool is the head of the plain order, and
the preset only weighs in inside a tier. History is labelled as per-file,
third-party code is never scored and goes last, and a preset whose data is
missing is not applied — the output nameshotspots --collector
graph buildinstead of ranking by zeros. Formulas were chosen by
backtesting next-year bugfixes on a 40k-file monorepo.- MCP exposes the symbol graph, Git hotspots and ranked search — seven new
tools:graph_dependents(direct dependents, or the transitive blast radius
withdepth≥ 2),graph_dependencies,graph_path,graph_metrics(the
most central symbols, or metrics of given ones),graph_cycles,
graph_buildandhotspots;searchtakesrank(proven,hotspots,
risky,central). Descriptions tell the agent when to reach for each one —
before changing a symbol, when choosing which match to copy — and what has to
be collected first; graph queries acceptrefresh: trueto build a missing or
stale graph in place. History collection stays a CLI command, since its first
run outlasts common MCP client timeouts, and the tools name it when history is
missing. Output is compact text: a ranked search prints each file's history
and each symbol's graph numbers once, about a quarter of the JSON size. symbol,searchandimplementationstake--with-content— each
matched symbol is followed by its line-numbered source body, read from disk
at the range the parser recorded (end_line), so an agent gets the
definition in one call instead of a follow-upsed/rg. Bodies are capped
at 60 lines; a longer one ends with... truncated at line N; symbol ends at line M, and JSON items carrycontent,truncatedandend_line. Without
the flag the output is unchanged. The MCPsymbol,searchand
implementationstools acceptwith_content: true.- Symbol ranges for every tree-sitter language —
end_lineused to be
filled only for Ruby and TypeScript/JavaScript, socall-tree,
explore --rwrandgraphfell back to "the last definition above the
line" everywhere else. Python, Go, Rust, Java, Kotlin, Swift, C#, C/C++, PHP,
Scala, Dart, Lua, Elixir, Zig, Objective-C, Groovy, Bash, SQL, R, MATLAB,
GDScript, Common Lisp, BSL and Protobuf now store the last line of every
definition, with classes, modules and namespaces enclosing their members.
The fallback had blamed the wrong definition for 3.6% of references in a Rust
codebase, 11% in a Go checkout and 18–42% in C++; module-level code no longer
gets an invented caller. Decorators and annotations stay outside the
definition they decorate, since they run in the enclosing scope. The graph
treats Rustimplblocks and Swift/Objective-C extensions as the namespace of
the type they extend, soType::new(…)andSelf::helper(…)resolve. CSS,
SCSS, Less and the regex-based parsers still report no ranges. Run
ast-index rebuildto fill ranges in an existing index. outlineprints line ranges and supports--format json—
:12-40 Invoice [class], rows in source order, JSON schema v1 with
end_line.- Tell code from prose by the syntax tree — names in comments, docstrings
and string literals are no longer references in Ruby, Python, JS/TS, C/C++,
Objective-C, Go, Rust, Java, C#, PHP, Swift, Scala, Dart, Lua, Groovy,
Elixir, Bash, R, Zig and Proto. Interpolated code stays, and so do strings
that name code: Ruby constant-path strings and%w[]words
(class_name: 'Invoice'), Python dotted class names ("pkg.models.User"),
JSimport('./Page'). References drop 7–51% per project. rebuildis about 40% faster — signatures looked up their source line
by scanning the file from the top for every symbol (quadratic in file
length) and copied the whole line before truncating it; Ruby and TypeScript
files were parsed twice (symbols, then references); and parse threads sat
idle while each chunk was written. Lines are now indexed once per file,
signatures capped before copying, each file parsed once, and writing
overlaps parsing in the same order, so the index is byte-for-byte the same:
9.3 → 5.5 s on a 40k-file monorepo.updatefinds changes 3–4× faster — the project root was canonicalized
once per file (a thread and arealpatheach), and files were stat'ed and
node_modulespackages walked one by one; now once per walk and in
parallel. A no-opupdateon a 40k-file project: 2.1 → 0.6 s; 20 changed
files: 1.8 → 0.4 s.- Minified JavaScript and CSS are left out —
.js/.mjs/.cjs/
.cssfiles named*.min.*or*-min.*, or whose first 64 KiB is minifier
output (lines of 1000+ bytes on average, 100+ of them outside string
literals), are no longer indexed, read by the grep-based commands (search
contents,callers,call-tree,todo, …) or parsed byoutline/
imports, which now say the file was skipped. A one-line stylesheet gave
each of its thousands of selectors the whole file as a signature: on a Rails
monorepo with a 589 KBapp.min.css,outlineof that file took 4.6 GB and
rebuildpeaked at 4–7 GB; now 11 MB and 0.21 GB.callers/call-tree
no longer answer from bundles (__webpack_require__). Source with a few long
strings, SVG paths or data URIs is unaffected, and TypeScript, JSX, SCSS and
.d.tsare never judged.updatedrops minified files an older index kept,
without a rebuild.AST_INDEX_SKIP_MINIFIED=0turns the filter off. - Rust
usedeclarations count as references again —use crate::models::Widget;is the first use ofWidgetin a file, not the
declaration of its own last segment;usagesandrefslist import lines
for Rust projects as before. Takes effect on the nextrebuild. - Build markers inside test fixtures no longer label the project — a
pom.xmlundertests/fixtures/or aPackage.swiftundersrc/test/
belongs to the sample project a test indexes;stats,mapand
detect-stacksignore markers undertest,tests,__tests__,
fixtures,__fixtures__,test-fixturesandtestdatadirectories. implementationsleaves out a namesake from another namespace — a
parent writtenLegacy::ApplicationServiceno longer counts as
ApplicationServicewhen the index defines both classes, and
< ::ApplicationServiceis a direct child; on a large Rails app
implementations ApplicationServicelists 917 classes instead of 972.
Package and namespace spellings of the same type (extends com.acme.Base,
: public acme::Base, a namespace the index does not define) still match.- A qualified declaration is no usage of itself —
class Billing::Invoiceno longer records a reference toInvoiceon its own line
(a C#namespace A.Blikewise);Billingstays a reference, and so does the
parent ofclass Admin::User < User. Takes effect on the nextrebuild. statsandmapname the project —rebuildrecords the stacks it
finds (Web (TypeScript/JavaScript) + Ruby),statsprints them as
Project:(JSONproject, which the MCPstatstool already expected)
andmapin its header. Every*.gemspecdirectory is a Ruby module
(engines.billing), so Rails engines show up inmodule,mapand
--module.conventionsmatches frameworks on whole name segments — a rule no
longer fires on any import that merely contains it: the Ruby gem
sequel-combineis not Swift Combine,preactnot React, and
../testing/setupnot Go'stesting, whileCombineExtis still Combine. Rails, Django, Flask, FastAPI,
Express, Sidekiq, Celery, ActiveRecord and Sequel are recognised.explorepicks tests from the mirrored directory — among test files
named after the source, one in the source's own directory comes first, then
the ones whose directories end like the source's
(spec/services/billing/charge_spec.rbforapp/services/billing/charge.rb,
src/test/java/a/b/XTest.javaforsrc/main/java/a/b/X.java); when none
shares a directory (a flattests/, separate*.Testsprojects) all are
listed as before. Up to 50 candidates per name are considered instead of 5.explore --rwrtakes callers from the symbol graph — whengraph build
ran and the index has not changed since, neighbours are the definitions
with a resolved edge to each seed instead of every reference sharing its
name, which for a namespaced class found none. A seed the graph resolves no
edge to (a call through a receiver of unknown type in Java, Swift, Go)
keeps the name-matched callers.- Performance table in the skill shows a large repository — next to a
small project;searchon a 40k-file monorepo takes 250–550 ms, not 10. explorefinds the class a question names — candidates now also come
from one bm25 ranking over all query words, from the words run together
(pdf to html serviceis thePdfToHtmlServicetoken the full-text index
keeps whole) and from files whose path holds every word; before, each word
sampled its first 40 matches in index order, so a common word such as
applicationnever reachedApplicationService. The type a file is named
after and the symbol the query spells out rank first, a word in a symbol's
own name counts above one in its namespace, and statements (has_many :x,
scope,include), namespace-only modules and question words (how,
does,work) rank low or are ignored. On a large Rails app 6 of 6 queries
naming a class put it first instead of 1 of 6, for about 30 ms more per
query on a 300k-symbol index.exploreoutlines types and modules — a class, module or statement is
shown as the outline of the definition around it (:34-53 process [function]rows with line ranges from the index, the chosen symbol marked
→, at most 40 rows) instead of the first lines of its source; functions
keep their source. JSONfiles[]carryoutlineandoutline_hiddenin
place ofsourcefor those, and the MCPsearchfallback renders them.- MCP
exploreanswers in compact text — the tool returned the raw CLI
JSON, with source escaped into strings, although its description promised
text by default; it now renders the report thesearchfallback prints under
anexplore: <query>line, less than half the size.format: "json"still
returns the JSON. - Definitions rank above imports — inside every relevance tier of
search
(plain,--fuzzy,--rank) definitions now come before imports, and imports
never enter the last-segment tier:search InstallRequirementin pip lists
the class first instead of 12th behind eleven imports, andsearch Resultin
a Rust project no longer answers with a wall ofuse anyhow::Result. - Schema columns named after the query rank first —
.now separates name
segments like::, sosearch email -t columnlistsusers.emailand every
other*.emailcolumn beforeemail_communicator_email_id-style columns. - Namespaced Ruby classes found by short or full name —
symbol,class,
refs,hierarchyandimplementationsfindclass Billing::Invoiceby
InvoiceorBilling::Invoice(Ruby stores the full name and no qualified
name, sohierarchyandclassused to answer "not found"); an exact short
name still wins.usages Billing::Invoicelists references toInvoiceon
lines that spell the full name out. provenfavours settled, living examples over abandoned ones — the score
is now mean(calm, mature, used) × substance × lineage: files under 10 lines
and empty class bodies score half, and so does code built on a base class the
project stopped extending; time since the last change is no longer scored. On
11 "which one to copy" queries the top five held 40 good examples of 55
instead of 22.- Test symbols after production code — in the partial-match tiers of
search, symbols in test files or namedtest_*/TestXfollow the rest;
exact names keep their place. usageslists production code first — references in test files follow
those in production files, each group by path and line (also inrefs); JSON
marks test references with"test": true.- Skip references inside the Rails schema dump —
t.string/t.integer
indb/schema.rbno longer count as uses of the project'sstring/
integermethods (≈3.7k references and 2.6k ambiguous graph edges fewer on a
large Rails app); tables and columns stay indexed. - C/C++ prototypes are declarations — a header prototype,
staticforward
declaration, class method declaration or function-pointer field no longer
counts as a use of the function. - Ruby callbacks link to their methods —
before_save :normalize,
validate :check,validates :email,if:/unless:,
rescue_from ... with:,alias_method,delegateand&:nameare
references; the graph links a model to its callback methods and validated
columns. - Compact
outlinefor schema dumps —db/schema.rbcolumns fold into a
count per table (164 KB → 17 KB on a 335-table schema);--fulllists every
column. call-treeshows same-named callers with their file — twoit "works"
blocks orexportmethods of different files used to print as(recursive)
without a path and still use up--limit. Every caller now prints with its
file, a caller named like one already expanded is marked(expanded above),
and(recursive)marks only a definition already on its own path.call-treeattributes calls to real definitions — an import or
annotation line (use,import,include Mod, Rails callbacks) no longer
owns a call, and a line declaring the function itself (Gofunc (s *T) Name(,
a JavaScript method,attr_reader :name) is no longer its own caller. The
same owner rule servesexplore --rwrand the symbol graph.callers/call-treestop reading symbols and paths as calls —
authorize(record, :update?)is no call ofupdate, anduse super::name;
orBilling::Namewithout a call is no call ofname; callbacks,delegate
and&:namestill count.- The symbol graph resolves Rust paths — files are modules,
crate::/
super::/self::andusedeclarations (grouped, aliased, globbed,
pub use) are followed, and workspace crates are reached by their
Cargo.tomlname. On this repository calls through a module path went from
0 to 1160 of 1161 resolved;graph dependents open_dblists 38 dependents
instead of none. graph dependentsandgraph impacttake--exclude-tests(MCP
graph_dependents:exclude_tests) — test dependents are left out of the
list and the counts, andimpactdoes not follow them.- Graph answers say what they merge and leave out — the definitions a bare
name matches are capped by--limitwith a hint to narrow the query, and a
column query explains that only reads inside the model are edges and points
tousages. - Faster graph queries — freshness is checked against an index write
generation and the highest row ids instead of counting and summing the
tables: about 35 ms off every graph query andsearch --rankon a large
monorepo. hotspots --sort fixesdiscounts thin history — files are ordered by the
lower bound of the 95% Wilson interval of their bugfix share, and files with
fewer than four commits follow the rest, so 11 fixes in 17 commits rank above
2 in 2.- Bracketed fix tags count as bugfixes — only tracker keys (
[ABC-123],
ABC-123:,#42, several in a row) are stripped before matching, so
[HOTFIX][ABC-123] …,[FIX],[BUGFIX]andхотфиксcount as fixes; the
store layout moved tocommits-v2, so the first--collectrecollects once. - MCP descriptions no longer overpromise —
usagesclaimed it never
matches comments or strings andcallersthat it attributes calls to the
calling function; both now say what they do (callerspoints to
call_tree), and the heaviest descriptions were trimmed:tools/list
27.3 → 24.4 KB. search,callersandcall-treeskip files that cannot match — the
index now keeps each file's distinct words (file_words, about +7–10% index
size), and the grep-based commands no longer open a file whose words lack
the searched literal while it is unchanged since indexing; edited and new
files are still searched. On a 40k-file Rails monoreposearchwent from
0.67 to 0.33 s,callers mergefrom 0.60 to 0.26 s,call-tree --depth 3
from 1.13 to 0.66 s, with the same results. An index built by an older
version is searched in full until its nextrebuild.callerschecks for definitions cheaply and in parallel — the "is this
line a definition?" test ran a capturing regex on one thread for every call
line; a capture-free pre-check now runs on the search threads.graph build2.5× faster — references are resolved per file in parallel
and import matches memoized, with an identical graph: 4.2 → 1.7 s.hotspots --collectreads history 5× faster —git logwindows run in
parallel (up to 8), and a window of old bulk-import commits no longer
overflows the 16 MB output cap and gets re-read at half size again and
again: a full collection of a 25k-commit history took 50 s, now 10 s, with
the same stored history.- The MCP server answers tool calls concurrently — several calls sent at
once used to wait for each other; responses may now arrive out of order,
matched byidas JSON-RPC allows. rebuildkeeps the collected Git history — the per-commit store, the
per-file signals and the collection cursor are copied into the new index
instead of being dropped, sohotspotsreports right after a rebuild and the
next--collectstays incremental: on a 25k-commit monorepo 0.2 s instead of
a full rescan of a minute or more, for about 0.4 s added to the rebuild.
History collected by an older version, from another working tree or scope,
or that cannot be read back intact is left behind with a note, and the next
--collectreads it again.--sub-projects/--includerebuilds lost it
too and now keep it.- snake_case calls are references in every language — the generic
reference extractor did not allow_in a called name, sousages,refs
and the graph missedupdate_profile(user)andself._compute()in Python,
Rust, Go, C/C++, PHP, Lua, TypeScript and every other language using it
(usages open_dbon a Rust codebase went from 0 to 50 hits). Reserved words
(sizeof (x),#if defined(X), Gofunc (r *T), Rustpub(crate),
PythonNone) are no longer recorded as references. In Ruby, a lowercase
name(inside a comment, string or heredoc is no longer a reference. - BSL and stylesheets use their own reference extractors again — the
trait default sent every language to the generic ASCII extractor, so BSL
(1C:Enterprise / OneScript) files had no Cyrillic references at all and
CSS, SCSS and Less recorded kebab-case fragments. BSL now records Cyrillic
calls, modules before.and types afterНовый, with the source line as
context; stylesheets record none. - One test-path rule for
explore, the symbol graph and--exclude-tests
(graph top,hotspots,search --rank): addstest_*.py,conftest.py
andFooTest/FooTests/FooSpecfiles in JVM/Swift/C#/PHP/C++, and stops
treating Ruby namespaces underapp//lib/(e.g.app/jobs/tests/) and
PascalCase JS component folders (components/Test/) as tests. - First open of a project no longer slows down quadratically with the
cache — the cache-migration scan now lists the lease directory once
instead of once per cached project (a firstrebuildbeside 5000 cached
projects dropped from about 70 s to about 1.4 s). - Cache GC removes lease lock files of caches that no longer exist —
.leaseskept a{key}.lockand{key}.publish.lockfor every cache ever
opened, so it grew without bound. The cleanup afterrebuild/updatenow
deletes them whatever their age, and only under the cache-layout lock after
locking both files itself, so a lock another process holds or is about to
take is never split across two files. Every command that resolves the index
gets faster on a cache that had accumulated them. - Rails schema tables and columns —
db/schema.rb(indexed even when
gitignored) yieldstableandcolumnsymbols (users.email;
search email -t column,outline db/schema.rb).graph buildmatches
tables to models by Active Record's rules —self.table_name, single-table
inheritance, nested models,table_name_prefix/isolate_namespace, the
pluralized class name — reports tables without a model and models without a
table, and resolves column readers and attribute methods called inside a
model (email,self.email,email?,saved_change_to_email?) as scoped
edges to the column (graph dependents users.email). - Ruby calls without parentheses are references —
usagesand the graph
now seerecv.name,name arg, snake_casename(...)and barenamecalls
that are not local variables; core collection and string methods called
without parentheses are skipped. RSpeclet/subjecthelpers resolve
within their spec file. On a Rails monorepo resolved graph edges grew from
66k to 186k; references nearly doubled for Ruby files. - Compound Ruby constants are indexed —
Billing::Import = Container.injector
becomes a constant named with its enclosing scopes like a class, and CamelCase
assignments (Types = Dry.Types()) are indexed as constants too. In the graph a constant defined in
an inner scope shadows an outer module of the same name, so
include Import[...]insidemodule Billingno longer links every such
class to an unrelated top-levelmodule Import. - Production code never resolves into test trees — a spec helper that
reopens a class to stub a method is no longer the target of production calls. - One definition of third-party code — only
node_modulesis left out of
the graph; a project'svendor/directory and its own.d.tsfiles are
graph nodes, while search ranking still demotes every.d.ts. hotspots --collectsurvives branch switches, rebases and resets —
history is kept per commit, so moving HEAD subtracts the commits it no longer
reaches and adds new ones, reusing diffs already read; when a merge joins or
leaves, files that differ between the two HEADs are recomputed too, so a file
only a merge edited keeps a correct line count. A switch on a 25k-commit
monorepo takes under a second instead of a ~1-minute rescan, and the numbers
equal a fresh full collection at the same HEAD (line counts come from the
working tree, so an uncommitted edit counts once the file is recomputed). The
git tables are ~40% smaller. The first--collectafter upgrading recollects
once.- Hotspot scores no longer collapse into ties —
hotspots --sort scoreand
search --rank hotspots|risky|provenorder by unrounded percentiles
(score_exactin JSON). --exclude-testsforhotspotsand ranked search —hotspots --exclude-testsandsearch --rank … --exclude-tests(MCP:exclude_tests)
hide spec/test files; percentiles still rank every file.rewritten-oftenonly for files of 10+ lines — relative churn is
computed only for such files, so one-line bundles, fixtures and gutted files
no longer read as "3000x file".- Namespaced definitions lead
search—search MergeServicehad no exact
hit forclass A::B::MergeService, and bm25 put the shorter
describe "A::B::MergeService"spec above the class. Names whose last::
segment equals the query (case-sensitive) now rank right below exact names;
statements such asinclude A::B::MergeServicedo not count.search --rank
uses the same tier (exact_last_segment). - Owning-symbol lookups stay within the file's root — with an attached
subtree holding a file under the same relative path,call-treeand
explore --rwrcould attribute a call to a method of the other root. - Drop the redundant
idx_symbols_fileindex — it is the leftmost prefix
ofidx_symbols_file_line_end; new databases no longer create it and
existing ones drop it on open.restoreaccepts backups with and without it;
a backup made by this version cannot be restored by older releases. - MCP
searchkeeps its explore fallback — a multi-word query without
literal matches now rendersfallback: explore — <reason>followed by
source, ranked symbols, graph neighbours and tests instead of an empty
Files:heading. callersandcall-treefind bare Ruby predicate and bang calls —
if next_page? && …andsave!without receiver or parentheses were
missed; definitions,#name?in documentation and!=/!~are still
skipped.- Tests no longer write to the user's index cache —
.cargo/config.toml
pointsAST_INDEX_CACHE_DIRattarget/ast-index-cachefor everything cargo
starts; an explicitly set variable still wins. - Name a wrapped default export after what it wraps —
export default injectIntl(Header),memo(Button)or
connect(mapState)(Page)was indexed under the wrapper's name, so every file
applyinginjectIntlclaimed a definition of it. Such an export is now
indexed asdefault(Header), likeexport default Header. A wrapped inline
function or class is indexed like an anonymous default export, under the
module name. A call that builds a value from its configuration
(createRouter({ … })) keeps the callee's name. - Name an
indexdefault export after its package, not its build
directory —node_modules/pkg/dist/index.d.tswas nameddist; the name
now comes from the nearest directory above build and source directories. importslists every TypeScript/JavaScript import — files were read
with a line pattern meant for Kotlin, so a multi-lineimport {printed as a
bare{, re-exports were not listed and a barrelindex.tsreported
"No imports found". Imports and re-exports are now read from the syntax
tree, one declaration per line. Import symbols in the index stay limited to
project-local specifiers.search --typeno longer takes a minute on a large index — with a kind
filter the bundled SQLite drove the query from the index onkind, scanning
every symbol of that kind and re-running the full-text match for each one:
search Job --type classtook about a minute on a 320k-symbol index. The
full-text match now leads and the kind filters its hits, about a second, with
the same results.call-treeprints the same tree on every run — which callers made it
under--limitdepended on the files the parallel scan happened to reach
first and on hash-map iteration order, so repeating a query could print a
different tree. Call lines are now taken in path order and attributed in
that order; files are still searched in parallel. Definition lines and files
outside--in-fileno longer use up a function's match budget, so every
Sidekiq worker definingperformno longer crowds out the calls, and
--in-fileno longer prints an empty tree while matching calls exist. The
repository is walked once per command rather than once per level: deep
trees got faster, while a single frequent name at--depth 1now pays for
the full walk up front.call-treestops expanding callers nothing can call — a call inside an
RSpec block or a Rails DSL call is owned by a symbol named after that block
(it "does nothing",let(:fields),attributes :id). Such an owner is
still printed as a caller, but its name never occurs in code as a call, so
looking up its callers cost a scan of the whole repository and never found
any. Only callers whose name is an identifier —Foo::Bar,save!,
valid?andname=included — are expanded now.call-treescans once per level — every function in the tree took a
scan of the whole repository to find its callers, so a level with nine
callers meant nine scans. Since calls resolve to the symbol that really
contains them, levels are wider andcall-treehad slowed down several
times over. The functions a level needs are now looked up together in one
scan that still gives each of them its own match budget, so the printed tree
is the same and a level costs about one scan.- Calls behind a keyword are no longer taken for definitions —
callers
andcall-treeskip definition lines by readingType name(as a
declaration, and a keyword in the type position fooled it:return foo(,
await foo(,new Foo(,if foo(,export default connect(and the like
were dropped as if they declaredfoo. Such lines now count as calls; real
typed declarations are still skipped. - Index anonymous
export defaultfunctions and classes —
export default () => {},export default function () {}and
export default class {}produced no symbol, so the function could not be
found and every call inside it had no owner incall-treeand
explore --rwr. Such a value is now indexed with its range under the name it
is imported by: the file name (hooks/useMap.js→useMap), or the
directory for anindexfile (Button/index.jsx→Button). A function is
classified like a declaration of that name, so a PascalCase component indexes
as a class;outlineshows the same name. Named default exports are
unchanged. - Rank project code above dependencies in
search— within a ranking
tier, hits innode_modulesor.d.tsfiles were ordered against project
hits by path alone, sonode_modules/…came ahead ofspec/orsystem/.
Project hits now lead within each tier. The tier still decides first, so a
library's exactuseStatestays above a project's partialuseStateModal.
A project-ownedvendor/directory still counts as project code;explore
shares the same definition. - Rank symbol searches by relevance — FTS symbol queries used to come back
in whatever order the engine produced, or sorted by name length, so a search
forApplicationServicelisted every shorter sibling and never the class
itself. Matches are now ordered by exact name first (case-sensitive, then
case-insensitive), then bybm25()weighted towards the symbol name over its
signature, then by name length and finally by path and line, so repeated runs
return the same order. The candidate poolexplorebuilds keeps its previous
unranked order — ranking it narrowed the pool to same-named symbols and made
its own scoring worse. - Attribute a call site to the function that really contains it —
call-treeandexplore --rwrused to blame the nearest definition line
above a reference, so a module-level call landed on the last method of the
file and anincludeor a constant could be reported as the caller. Both
now ask the index which symbol's line range encloses the reference and take
the innermost one. Languages whose parsers report no range keep the previous
behaviour.
3.55.0
- Tuist module helpers are understood — besides
targets: [...], target
declarations insidemodules: [...]are read, source directories are found
among common layouts (Sources/X,X/Sources,X/Api,Tests/X,
X/Tests), and nestedimplementation:/tests:configurations declare
XImpl/XTeststargets with their own dependencies. Workspaces that moved
to per-module folders no longer lose those modules from the graph. updaterefreshes the module graph when build files change — adding,
removing or editingbuild.gradle(.kts),Package.swift,Project.swift,
pom.xml,ya.makeand other build files now updatesdeps,dependents,
module-routeandunused-depswithout arebuild. Unchanged build files
cost nothing extra.rebuild --type moduleskeeps resources and usages — it used to delete
and re-insert every module, which cascaded into Android resources, XML
usages, iOS assets and storyboard usages. Modules are now synced in place and
keep their ids.rebuild --verboselists manifests whose targets could not be read, so
an incomplete module graph is visible instead of silent.
3.54.0
- Tuist projects get a module graph — targets declared in
Project.swift
(.target(...)or any project helper such as.spmSwiftFolderTarget(name: .Foo, ...))
are indexed as modules with theirdependencies:(.target,.external,
.project,.product), somodule,deps,dependents,module-route
andunused-depswork on Tuist workspaces. Previously such a workspace had
no module dependencies at all. - SwiftPM modules point at their sources and have dependencies — targets
resolve toSources/<Target>/Tests/<Target>(orpath:/sources:)
instead of<package>/<Target>,let targets = [...]declarations are read,
and target dependencies are indexed. Swift modules are named after the
target — theimportname — and only fall back to a package-qualified name
when two manifests declare the same target. Manifests are parsed with
tree-sitter. unused-depsunderstands Swift imports — animport Depin any file of
the module marks the dependency as used. Swiftimportdeclarations are now
indexed.apilists Swift public API — declarations markedpublic/open.- Swift local variables are no longer indexed as properties —
let/var
inside function bodies, closures and accessors are skipped, matching Kotlin. - Module-qualified supertypes resolve —
class A : ru.pkg.Base(),
extension Foo: Module.Protoare recorded with the simple parent name, so
hierarchyandimplementationsfind them (previously the parent became
ru/Module). injectfinds constructor injection — parameters of
@Inject constructor(...)and Java@Injectconstructors, plus fields whose
annotation is on its own line.--limitwith a filter no longer under-reports —flows,suspend,
deprecated,suppress,deeplinksapplied the query after the limit was
spent (e.g.flows --limit 5could return 1).suspendreports the function
name instead of an extension receiver.providesis an order of magnitude faster — scans only files that
declare@Provides/@Binds.unused-symbols --moduleaccepts a module name —features.surge.impl
or:core:utils, not just a path prefix.rebuild --type modules|deps|fileskeeps the rest of the index — a
partial rebuild used to publish an index without files and symbols.
3.53.0
searchno longer returns nothing for a multi-word query — when a
query with two or more terms has no literal match,searchnow hands it to
theexploreranking engine and prints relevance-ranked symbols with their
source, honouring--module/--in-file. Text output is labelled
No literal matches …; JSON stays a single document and carries
"fallback": "explore"plus areason. Single-term queries keep exact
literal semantics.callerswalks attached subtrees — call sites inside a subtree added
withsubtree addare found and shown as[name] /abs/path, matching
usagesandrefs.--subtree NAMErestricts to that subtree,--local
to the primary root. Previously both flags were accepted and ignored.exploreaccepts scope —--module/--in-filenarrow the candidate
set before ranking.- Agent guidance points intent queries at
explore— the skill and the
MCPsearchdescription now say to useexplorefor a question or a
description andsearchfor a known identifier.
3.52.0
- Index C++ functions that return a pointer or a reference — declarations
such asGrid* GetGrid(),Player& FindPlayer(), and
const Grid* const GetEmptyPhaseShift()are now indexed instead of being
skipped. - Install from crates.io — the installation guide now documents
cargo install ast-index --lockedas the released channel, with the Git
build kept as the way to install the unreleased default branch.
3.51.0
- Report truncated search results instead of hiding them —
search,
symbol,class,implementations,refs,usages, andcallersnow
printshowing N of Mwith a--limithint, and emit paginated JSON
schema v2 withtotal,returned,truncated, andlimit. - Keep the index fresh without blocking edits —
update --background --debounce-ms <ms>queues a coordinated, trailing-debounced generation and
returns immediately; index-reading commands wait for a queued generation
instead of answering from a stale index. - Scope watcher detection to the project — the new
watch-statuscommand
reports whether this project has an active watcher, so a watcher in one
repository no longer suppresses updates in another. Session-start and
post-edit hooks and the generated Git hooks use it, and the session-start
hook now runs asynchronously with a visible status message. - Index Kotlin files containing
suspend { }lambdas — a suspend-lambda
syntax error no longer drops the enclosing interface, nested classes, and
later declarations from the index, and localvalbindings are no longer
published as properties. - Count Kotlin references accurately — references in a symbol's own
declaring file are kept when external references exist, and matches inside
string literals, comments, and KDoc are excluded while executable string
interpolation is still indexed. - Resolve kebab-case Gradle type-safe project accessors —
deps,
dependents,unused-deps, andmodule-routenow link
projects.core.designIconto:core:design-icon, and report ambiguity
instead of picking an arbitrary module. - Rebuild on Windows — the staged index is synced through a
write-capable handle, fixingfailed to sync index file … (os error 5).
Concurrent runs are also recognized as ordinary lock contention instead of
failing withfailed to acquire index publication lock … (os error 33). - Detect nested project stacks —
detect-stacksperforms a bounded
recursive marker scan and recognizes standard nested Kotlin Multiplatform
layouts such ascomposeApp/src/commonMain. - Document worktrees and cross-root workspaces — independent git
worktrees keep independent indexes; intentional workspaces are attached
withrebuild, thensubtree add, thenupdate. - Compare ast-index and CodeGraph —
docs/comparison.mdadds a dated,
source-backed feature comparison with pinned snapshots of both projects.
3.50.0
- Review branch changes quickly without building an index — use
changed
from the CLI or MCP to read cache-independent branch changes with
added, modified, deleted, and renamed files, rename metadata,
working-directory scope, a bounded VCS timeout, Git base auto-detection, and
stable JSON schema v1. - Migrate
changedconsumers to the file-level contract — text output now
prints an A/M/D/R file summary instead of regex-derived declaration
pseudo-symbols. Scripts should request--format jsonand read
changes[].status,changes[].path, andchanges[].old_pathfor renames.
Library callers can use the deprecated Rust compatibility wrappers while
migrating to the new API.
See CHANGELOG.md for earlier releases.
{
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "ast-index-marketplace",
"description": "Plugins for Android/Kotlin/Java, iOS/Swift/ObjC, Dart/Flutter, TypeScript/JavaScript, Rust, Ruby, C#, PHP, Python, Go, Perl, Proto, WSDL, and C++ development",
"owner": {
"name": "defendend"
},
"plugins": [
{
"name": "ast-index",
"description": "Fast code search for Android/Kotlin/Java, iOS/Swift/ObjC, Dart/Flutter, TypeScript/JavaScript, Rust, Ruby, C#, PHP, Python, Go, Perl, C++, Protocol Buffers, and WSDL/XSD projects",
"author": {
"name": "defendend"
},
"version": "3.56.0",
"category": "development",
"homepage": "https://github.com/defendend/Claude-ast-index-search",
"source": "./plugin"
}
]
}Facts
- Kind
- Marketplace
- Repo
- defendend/Claude-ast-index-search
- Group
- Uncategorized
- Marketplace name
- ast-index-marketplace
- Owner
- defendend
- License
- MIT
- Language
- Rust
- Created
- 2026-01-19
- Forks
- 43
- Plugins
- 1
- 1f/prompts.chatf/prompts.chatf.k.a. Awesome ChatGPT Prompts. Share, discover, and collect prompts from the community. Free and open source — self-host for your organization with complete privacy.
- 2affaan-m/everything-claude-codeaffaan-m/everything-claude-codeThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.
- 3obra/superpowersobra/superpowersAn agentic skills framework & software development methodology that works.
- 4anthropics/skillsanthropics/skillsPublic repository for Agent Skills
- 5anthropics/claude-codeanthropics/claude-codeClaude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
- 6nextlevelbuilder/ui-ux-pro-max-skillnextlevelbuilder/ui-ux-pro-max-skillAn AI skill that provides design intelligence for building professional UI/UX across multiple platforms.