clast-ai 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +112 -0
- package/README.md +313 -418
- package/dist/cli.js +11 -1
- package/dist/cli.js.map +1 -1
- package/dist/indexer/scan.d.ts +10 -1
- package/dist/indexer/scan.js +28 -5
- package/dist/indexer/scan.js.map +1 -1
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/tools/explore.js +5 -1
- package/dist/tools/explore.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
- package/skills/audit/SKILL.md +18 -25
- package/skills/deadcode/SKILL.md +12 -25
- package/skills/explore/SKILL.md +24 -43
- package/skills/impact/SKILL.md +10 -16
- package/skills/status/SKILL.md +2 -5
- package/src/cli.ts +12 -1
- package/src/indexer/scan.ts +29 -5
- package/src/server/index.ts +2 -1
- package/src/tools/explore.ts +6 -1
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -1,70 +1,68 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[tree-sitter](https://tree-sitter.github.io/), keeps a live index of symbols, calls, references, imports and
|
|
5
|
-
type relationships in SQLite, and exposes tools that answer navigation questions in one call instead of a
|
|
6
|
-
chain of greps and file reads.
|
|
3
|
+
# Clast
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
### Ask the code. Skip the grep marathon.
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
and what function bodies mention.
|
|
13
|
-
- **Exact symbol source** - the body of one function or class, read fresh from disk with line numbers,
|
|
14
|
-
together with its callers, callees, overrides and subtypes. No need to read the whole file.
|
|
15
|
-
- **Type-aware call graph** - calls are resolved with declared receiver types, imports, inheritance,
|
|
16
|
-
`this`/`self`/`super` and argument counts, so `record.topic()` goes to the right class, an overload
|
|
17
|
-
gets its own callers and `list.push()` is not mistaken for your `Stack.push`. Edges that are name-only
|
|
18
|
-
guesses are marked `?`.
|
|
19
|
-
- **References, type hierarchy, repo map, text search, git change impact** - see the tool list below.
|
|
20
|
-
- **Always fresh** - a file watcher re-indexes on save, and every query re-checks the files it is about to
|
|
21
|
-
quote, so line numbers and source are never stale, even right after the agent edits a file.
|
|
22
|
-
- **Fast** - parallel indexing in worker threads; warm start only stats files; queries take milliseconds.
|
|
23
|
-
- **No native compilation** - grammars are WASM, storage is Node's built-in `node:sqlite`.
|
|
7
|
+
A code index for coding agents. One call tells Claude who calls a function, what implements an<br>
|
|
8
|
+
interface, what breaks if you change it, and what nobody uses any more.
|
|
24
9
|
|
|
25
|
-
|
|
10
|
+
**Built for Claude Code** | **Works with Codex, Gemini CLI and any MCP client** | **14 languages** | **MIT licensed**
|
|
26
11
|
|
|
27
|
-
|
|
12
|
+
[Install](#install) / [What it does](#made-for-the-questions-grep-cannot-answer) / [Measured results](#measured-not-promised) / [First steps](#your-first-minute) / [Changelog](CHANGELOG.md)
|
|
28
13
|
|
|
29
|
-
|
|
14
|
+
</div>
|
|
30
15
|
|
|
31
|
-
|
|
16
|
+
An agent with only grep finds a name. To follow a call chain, list every subclass or count the
|
|
17
|
+
endpoints it has to search, read, and search again. Clast parses your repository with tree-sitter,
|
|
18
|
+
keeps a live index of symbols, calls, imports and type relationships, and answers those questions
|
|
19
|
+
directly, with exact `file:line` locations. The index follows every edit, so answers are never stale.
|
|
32
20
|
|
|
33
|
-
|
|
21
|
+
## Install
|
|
34
22
|
|
|
35
|
-
|
|
23
|
+
**Claude Code (recommended).** Two commands inside Claude Code, then restart:
|
|
36
24
|
|
|
37
25
|
```
|
|
38
26
|
/plugin marketplace add xls/clast
|
|
39
27
|
/plugin install clast@clast
|
|
40
28
|
```
|
|
41
29
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
The plugin brings the MCP server, a short skill that teaches Claude which tool answers which question,
|
|
45
|
-
and four slash commands:
|
|
30
|
+
The plugin brings the index server, a short skill that teaches Claude which tool answers which
|
|
31
|
+
question, and four slash commands:
|
|
46
32
|
|
|
47
33
|
| Command | What it does |
|
|
48
34
|
| --- | --- |
|
|
49
|
-
| `/clast:deadcode [path]` | Finds unused functions, methods and types, verifies
|
|
35
|
+
| `/clast:deadcode [path]` | Finds unused functions, methods and types, verifies them, and removes the ones you confirm |
|
|
50
36
|
| `/clast:impact <symbol>` | What must change with a symbol, every caller, and the tests to run |
|
|
51
37
|
| `/clast:audit [path]` | Inventory for due diligence or onboarding: size, structure, endpoints, database access, dependencies, dead code |
|
|
52
38
|
| `/clast:status` | State of the index |
|
|
53
39
|
|
|
54
|
-
|
|
55
|
-
|
|
40
|
+
Requires **Node.js 22.22 or newer**. The first start indexes the project in the background (seconds;
|
|
41
|
+
about half a minute for 15,000 files). Later starts are ready immediately.
|
|
56
42
|
|
|
57
|
-
|
|
43
|
+
**Codex, Gemini CLI and other agents.** Clast is a standard MCP server, so any agent that speaks MCP
|
|
44
|
+
can use it:
|
|
58
45
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
newer (0.1 ran on Node 18; check with `node --version`):
|
|
46
|
+
```sh
|
|
47
|
+
npm install -g clast-ai
|
|
62
48
|
|
|
49
|
+
codex mcp add clast -- clast-ai # Codex
|
|
50
|
+
gemini mcp add -s user clast clast-ai # Gemini CLI
|
|
63
51
|
```
|
|
64
|
-
|
|
65
|
-
|
|
52
|
+
|
|
53
|
+
Then give the agent the usage guide the Claude plugin ships as a skill:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
clast-ai guide >> AGENTS.md # or GEMINI.md, or your rules file
|
|
66
57
|
```
|
|
67
58
|
|
|
59
|
+
<details>
|
|
60
|
+
<summary><strong>Coming from an older install?</strong></summary>
|
|
61
|
+
|
|
62
|
+
If clast is already set up the old way (`npm install -g clast-ai` plus `claude mcp add`, or through
|
|
63
|
+
the old VS Code extension), install the plugin as above and restart. That is all, provided Node.js is
|
|
64
|
+
22.22 or newer (0.1 ran on Node 18; check with `node --version`).
|
|
65
|
+
|
|
68
66
|
The first session after that cleans up in the background, once per plugin version:
|
|
69
67
|
|
|
70
68
|
- clast servers registered with `claude mcp add`, for all projects or for single ones, are removed
|
|
@@ -81,10 +79,9 @@ from the file (`clast-ai migrate` names the file).
|
|
|
81
79
|
Optional: `npm uninstall -g clast-ai` removes the old global package. The plugin does not need it; it
|
|
82
80
|
only uses a global install that is at least as new as the plugin itself.
|
|
83
81
|
|
|
84
|
-
To see what would be removed, or to do it by hand
|
|
85
|
-
server and only want the old index gone):
|
|
82
|
+
To see what would be removed, or to do it by hand:
|
|
86
83
|
|
|
87
|
-
```
|
|
84
|
+
```sh
|
|
88
85
|
clast-ai migrate # report only
|
|
89
86
|
clast-ai migrate --apply # remove the leftovers
|
|
90
87
|
```
|
|
@@ -92,127 +89,249 @@ clast-ai migrate --apply # remove the leftovers
|
|
|
92
89
|
What was removed is logged to `migrate.log` in the plugin's data folder. `CLAST_NO_MIGRATE=1` switches
|
|
93
90
|
the automatic clean-up off.
|
|
94
91
|
|
|
95
|
-
|
|
92
|
+
</details>
|
|
96
93
|
|
|
97
|
-
|
|
94
|
+
<details>
|
|
95
|
+
<summary><strong>Updating, and how the plugin starts the server</strong></summary>
|
|
98
96
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
Update with `/plugin marketplace update clast`, then `/plugin update clast@clast`.
|
|
98
|
+
|
|
99
|
+
The plugin starts the server from a global `clast-ai` that is at least as new as the plugin, otherwise
|
|
100
|
+
through `npx clast-ai@<plugin version>` (downloaded on first use, cached afterwards).
|
|
101
|
+
|
|
102
|
+
</details>
|
|
103
|
+
|
|
104
|
+
<details>
|
|
105
|
+
<summary><strong>Cursor, VS Code and other editors</strong></summary>
|
|
106
|
+
|
|
107
|
+
Add the server to the editor's MCP configuration. These follow each editor's documented format; they
|
|
108
|
+
have not been tested the way Claude Code, Codex and Gemini CLI have.
|
|
109
|
+
|
|
110
|
+
Cursor, `.cursor/mcp.json` in the project (or `~/.cursor/mcp.json`):
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{ "mcpServers": { "clast": { "command": "clast-ai", "env": { "CLAST_PROJECT_DIR": "${workspaceFolder}" } } } }
|
|
102
114
|
```
|
|
103
115
|
|
|
104
|
-
|
|
105
|
-
commands. Use one of the two ways, not both: with both, every tool is listed twice (the plugin removes
|
|
106
|
-
a registration made this way when it is installed later).
|
|
116
|
+
VS Code, `.vscode/mcp.json`:
|
|
107
117
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
and ask to be called again. Later starts are ready immediately.
|
|
118
|
+
```json
|
|
119
|
+
{ "servers": { "clast": { "command": "clast-ai", "env": { "CLAST_PROJECT_DIR": "${workspaceFolder}" } } } }
|
|
120
|
+
```
|
|
112
121
|
|
|
113
|
-
|
|
122
|
+
Clast indexes the directory in `CLAST_PROJECT_DIR`, else `CLAUDE_PROJECT_DIR`, else the directory the
|
|
123
|
+
agent starts it in. Editors do not always start servers in the project, hence the variable above.
|
|
114
124
|
|
|
115
|
-
|
|
116
|
-
# from GitHub, without npm
|
|
117
|
-
npm install -g github:xls/clast
|
|
125
|
+
</details>
|
|
118
126
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
<details>
|
|
128
|
+
<summary><strong>Claude Code without the plugin, a team-wide setup, building from source</strong></summary>
|
|
129
|
+
|
|
130
|
+
**As a plain MCP server** (the tools, without the skill and the slash commands):
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
npm install -g clast-ai
|
|
134
|
+
claude mcp add -s user clast clast-ai
|
|
124
135
|
```
|
|
125
136
|
|
|
126
|
-
|
|
137
|
+
Use the plugin or this, not both: with both, every tool is listed twice.
|
|
138
|
+
|
|
139
|
+
**For a whole team**, put this in `.mcp.json` in the project root:
|
|
127
140
|
|
|
128
141
|
```json
|
|
129
142
|
{ "mcpServers": { "clast": { "command": "clast-ai" } } }
|
|
130
143
|
```
|
|
131
144
|
|
|
132
|
-
|
|
145
|
+
**From source** (also how to work on clast itself):
|
|
133
146
|
|
|
134
|
-
|
|
147
|
+
```sh
|
|
148
|
+
git clone https://github.com/xls/clast.git && cd clast
|
|
149
|
+
npm install && npm run build
|
|
150
|
+
claude --plugin-dir /path/to/clast # try the plugin for one session
|
|
151
|
+
claude mcp add -s user clast node /path/to/clast/dist/cli.js # or register the build
|
|
152
|
+
```
|
|
135
153
|
|
|
136
|
-
|
|
154
|
+
</details>
|
|
137
155
|
|
|
138
|
-
|
|
139
|
-
|------|---------|-------------|
|
|
140
|
-
| `clast_overview` | What is in this repo / directory? | First call in an unfamiliar codebase: languages, layout with sizes, most referenced symbols, hub files, entry points |
|
|
141
|
-
| `clast_file_summary` | What is in this file / directory? | Outline of a file (signatures, line ranges, doc summaries, imports, importers) or a directory listing with main symbols |
|
|
142
|
-
| `clast_search` | Where is X? What is it called? | Ranked symbol search by name, partial name, abbreviation, typo, qualified name or descriptive words (matched against names, signatures, docs and what function bodies mention) |
|
|
143
|
-
| `clast_get_context` | Show me X. | Source of one symbol plus callers, callees, overloads, overrides, subtypes. Batch several names in one call |
|
|
144
|
-
| `clast_call_graph` | Who calls X? What does X call? | Caller / callee tree to depth N, test callers separated, `within` path filter. Works for external names too |
|
|
145
|
-
| `clast_references` | Where is X used? | Every usage of an identifier (calls, types, imports, assignments) with the enclosing symbol of each line |
|
|
146
|
-
| `clast_hierarchy` | What implements / extends / overrides X? | Supertypes and transitive subtypes; Ruby mixins; Go interface satisfaction by method set (names plus parameter / result counts); the same method across related types; `within` path filter |
|
|
147
|
-
| `clast_grep` | Where does this text appear? | Literal or regex search over all text files; each hit is labelled with its enclosing function |
|
|
148
|
-
| `clast_changes` | What did this diff touch? | Symbols changed vs HEAD or a base ref, their callers outside the change, and the tests that exercise them |
|
|
149
|
-
| `clast_impact` | What breaks if I change X? Which tests do I run? | Declarations, overrides and implementations that must change together; every direct caller by file and line; how far it reaches through indirect callers; the test files that call it directly or through its callers |
|
|
150
|
-
| `clast_unused` | What is dead code? | Functions, methods and types nothing references (or only other unused code does), by file with line ranges; code used only by tests; names that only appear in strings or non-code files |
|
|
151
|
-
| `clast_stats` | How many X are there? What does this code base consist of? | Exact totals grouped by directory, file, kind or name; inventory, endpoints, database access, dependencies |
|
|
152
|
-
| `clast_status` | Is the index healthy? | Counts, languages, indexing progress, watcher state |
|
|
153
|
-
| `clast_reindex` | Rebuild | Rarely needed; incremental rescan or forced rebuild |
|
|
154
|
-
| `clast_comment` | Docs for X | Existing doc comment, or one generated by a local LLM (optional) |
|
|
156
|
+
## Made for the questions grep cannot answer
|
|
155
157
|
|
|
156
|
-
|
|
157
|
-
passed straight to an editor, and a hint about the next useful call when something is not found.
|
|
158
|
+
### Know what breaks before you change it
|
|
158
159
|
|
|
159
|
-
|
|
160
|
+
Ask about one function and get everything a change touches: the overrides and declarations that must
|
|
161
|
+
change with it, every caller with its line, and the tests to run. That includes tests that never name
|
|
162
|
+
the function but reach it through its callers.
|
|
160
163
|
|
|
161
|
-
|
|
164
|
+
```
|
|
165
|
+
> clast_impact Context.Render (gin)
|
|
166
|
+
|
|
167
|
+
method Context.Render context.go:1201-1215
|
|
168
|
+
direct callers: 18 resolved in 1 file (each must be checked when the signature changes):
|
|
169
|
+
context.go: Context.HTML :1222 (x2), Context.IndentedJSON :1235, Context.SecureJSON :1242, ...
|
|
170
|
+
indirect callers (through the direct ones, up to depth 3): 5 functions in 2 files
|
|
171
|
+
tests to run (11 files: 2 call it directly, 9 reach it through callers):
|
|
172
|
+
context_test.go direct (TestContextRenderIfErr, TestContextRenderSSE, +26)
|
|
173
|
+
auth_test.go via Context.String (TestBasicAuthSucceed, TestBasicAuth401, +3)
|
|
174
|
+
routes_test.go via Context.String (TestRouteQuery, TestRouterMethod, +4)
|
|
175
|
+
...
|
|
176
|
+
```
|
|
162
177
|
|
|
163
|
-
|
|
164
|
-
|------|---------|
|
|
165
|
-
| bare name | `parseConfig` |
|
|
166
|
-
| member | `Parser.parse`, `Parser::parse`, `Parser#parse` |
|
|
167
|
-
| inherited member | `Engine.GET` (defined on an embedded / base type) |
|
|
168
|
-
| package or module path | `django.shortcuts.render`, `page.Clear`, `Illuminate\Pipeline\Pipeline` |
|
|
169
|
-
| file and name | `src/db/index.ts:open` |
|
|
170
|
-
| file and line | `src/db/index.ts:120` (the symbol containing that line) |
|
|
178
|
+
### Find the code nobody uses
|
|
171
179
|
|
|
172
|
-
|
|
180
|
+
Agents write a lot of code and refactors leave helpers behind. `clast_unused` lists functions, methods
|
|
181
|
+
and types that nothing references, and what only that dead code uses, so a removed feature's helpers
|
|
182
|
+
are found with it. Code used only by tests and names that still appear in a string, a template or a
|
|
183
|
+
config file are listed separately, because those need a look before they go.
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
> clast_unused path: app (a C++ application)
|
|
187
|
+
|
|
188
|
+
unused code: 80 symbols, 415 lines in 51 files - the name is never referenced outside
|
|
189
|
+
its own definition, or only from other unused code (10594 symbols judged under app)
|
|
190
|
+
app/panels/agc_ui.cpp (6 symbols, 109 lines)
|
|
191
|
+
function reasonText :12-23 (used only by unused agcGear)
|
|
192
|
+
function ampNow :42-49 (used only by unused agcStep)
|
|
193
|
+
function agcStep :79-111 exported
|
|
194
|
+
function agcGear :126-169 exported
|
|
195
|
+
...
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Entry points, routes and tests marked by a decorator or attribute, subclasses of library types and
|
|
199
|
+
other things a framework calls for you are left out, and counted so you can see what was skipped.
|
|
200
|
+
|
|
201
|
+
### Follow the call chain in one step
|
|
173
202
|
|
|
174
|
-
|
|
203
|
+
Calls are resolved with declared types, imports, inheritance and argument counts, so `record.topic()`
|
|
204
|
+
goes to the right class and `list.push()` is not mistaken for your `Stack.push`. Ask for depth 2 or 3
|
|
205
|
+
and get the chain, not just the next hop. An edge that is only a name match is marked `?`, so the
|
|
206
|
+
agent knows what to verify.
|
|
175
207
|
|
|
176
208
|
```
|
|
177
|
-
>
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
callers (1 resolved):
|
|
185
|
-
main src/app.ts:10
|
|
186
|
-
calls (1):
|
|
187
|
-
circleArea src/math/area.ts:2-4
|
|
209
|
+
> clast_call_graph Engine.handleHTTPRequest depth: 2 (gin)
|
|
210
|
+
|
|
211
|
+
callers (2 resolved; depth 2):
|
|
212
|
+
Engine.ServeHTTP gin.go:701
|
|
213
|
+
Context.FileFromFS context.go:1352 ?
|
|
214
|
+
RouterGroup.createStaticHandler routergroup.go:273 ?
|
|
215
|
+
Engine.HandleContext gin.go:713
|
|
188
216
|
```
|
|
189
217
|
|
|
218
|
+
### Describe it. Clast finds it.
|
|
219
|
+
|
|
220
|
+
No name to search for? Describe the behaviour. Search covers names, signatures, doc comments and
|
|
221
|
+
what function bodies mention, and copes with partial names, abbreviations and typos.
|
|
222
|
+
|
|
190
223
|
```
|
|
191
|
-
>
|
|
192
|
-
|
|
193
|
-
method
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
leveldb.MemTableInserter.Delete db/write_batch.cc:126
|
|
198
|
-
leveldb.MemTableConstructor.FinishImpl table/table_test.cc:315
|
|
224
|
+
> clast_search "client ip from forwarded headers" (gin)
|
|
225
|
+
|
|
226
|
+
method Context.ClientIP context.go:997-1046
|
|
227
|
+
// ClientIP implements one best effort algorithm to return the real client IP.
|
|
228
|
+
method Engine.validateHeader gin.go:505-524
|
|
229
|
+
// validateHeader will parse X-Forwarded-For header and return the trusted client IP address
|
|
199
230
|
```
|
|
200
231
|
|
|
201
|
-
|
|
232
|
+
### Count it exactly
|
|
233
|
+
|
|
234
|
+
How many endpoints, how many receivers, which models does the database layer touch most? `clast_stats`
|
|
235
|
+
returns exact totals grouped by directory, file, kind or name, with production, test and vendored
|
|
236
|
+
code kept apart. `/clast:audit` turns that into a due-diligence inventory: size, structure, HTTP
|
|
237
|
+
endpoints, database access, dependencies and dead code.
|
|
238
|
+
|
|
239
|
+
### Read the function, not the file
|
|
240
|
+
|
|
241
|
+
`clast_get_context` returns exactly the lines of one function or class, read fresh from disk, with
|
|
242
|
+
its callers, callees and overrides. The agent spends its context on the code it needs.
|
|
243
|
+
|
|
244
|
+
### Always current, nothing to maintain
|
|
245
|
+
|
|
246
|
+
A watcher re-indexes files as they are saved, and every answer re-checks the files it quotes, so line
|
|
247
|
+
numbers are right even a second after the agent edited a file. The index is a cache in `.clast/`:
|
|
248
|
+
delete it whenever you like. Grammars are WebAssembly and storage is Node's built-in SQLite, so
|
|
249
|
+
there is nothing to compile.
|
|
250
|
+
|
|
251
|
+
**Languages:** TypeScript, TSX, JavaScript, Python, Java, C#, Go, Rust, C, C++, Ruby, PHP, Swift, Dart.
|
|
252
|
+
|
|
253
|
+
## Measured, not promised
|
|
254
|
+
|
|
255
|
+
Every figure comes from real headless Claude Code sessions: the same question asked once with clast
|
|
256
|
+
and once with only grep and file reads. The rows below are from OnAir (C++, 7.5k files, 1.6M lines);
|
|
257
|
+
the vscode results (TypeScript, 15.7k files) are in the full tables.
|
|
258
|
+
|
|
259
|
+
| Kind of work | Tool calls (clast / grep) | Time | Cost |
|
|
260
|
+
| --- | ---: | ---: | ---: |
|
|
261
|
+
| Find dead code in a directory | 2 / 16 | 20 s / 124 s | $0.15 / $1.89 |
|
|
262
|
+
| Multi-hop questions (call chains, subclasses, impact) | 1.2 / 5.9 | 12.5 s / 25.8 s | $0.056 / $0.212 |
|
|
263
|
+
| Code described by behaviour, in a repo the model has not seen | 1.8 / 4.2 | 12.0 s / 20.5 s | $0.068 / $0.197 |
|
|
264
|
+
| Counting ("how many receiver classes") | 1.0 / 3.3 | 11 s / 34 s | about $0.02 / $0.29 |
|
|
265
|
+
| Simple lookups (where is X, who calls X) | 1.7 / 1.7 | 12.7 s / 14.3 s | $0.063 / $0.096 |
|
|
266
|
+
| Cross-cutting refactors (rename a method, add a parameter everywhere) | 8.3 / 8.3 | 27 s / 26 s | $0.26 / $0.25 |
|
|
267
|
+
|
|
268
|
+
Both sides answered correctly almost everywhere; the difference is how much searching it took.
|
|
269
|
+
|
|
270
|
+
**Where it does not help.** Looking up one name is a tie: a current model with ripgrep does that in
|
|
271
|
+
one call. Writing code is a tie too: the agent reads the surrounding code before it edits, index or
|
|
272
|
+
not. Clast tells the agent to skip it for those edits, so it adds no calls there, only the fixed size of its tool definitions in the prompt. Clast earns its place on questions that take several hops, on counting, and on dead code. The
|
|
273
|
+
samples are small (two repositories, three runs per question), so read them as direction.
|
|
274
|
+
|
|
275
|
+
Full tables, method, answer-quality audits and index speed: [docs/benchmarks.md](docs/benchmarks.md).
|
|
276
|
+
|
|
277
|
+
## Your first minute
|
|
278
|
+
|
|
279
|
+
Open a project in Claude Code with the plugin installed, then try these:
|
|
280
|
+
|
|
281
|
+
1. **Get the lie of the land:** "Give me an overview of this repository."
|
|
282
|
+
2. **Trace something:** "Who ends up calling `saveOrder`, two levels up?"
|
|
283
|
+
3. **Plan a change:** `/clast:impact OrderService.save`
|
|
284
|
+
4. **Clean up:** `/clast:deadcode src/billing`
|
|
285
|
+
5. **Take stock:** `/clast:audit`
|
|
286
|
+
|
|
287
|
+
**Not sure it is running?** `/clast:status` shows the index, and `/mcp` lists the server with 15 tools.
|
|
288
|
+
|
|
289
|
+
## Reference
|
|
290
|
+
|
|
291
|
+
<details>
|
|
292
|
+
<summary><strong>All 15 tools</strong></summary>
|
|
293
|
+
|
|
294
|
+
| Tool | Answers |
|
|
295
|
+
|------|---------|
|
|
296
|
+
| `clast_overview` | What is in this repo or directory? Languages, layout, most referenced symbols, entry points |
|
|
297
|
+
| `clast_file_summary` | What is in this file or directory? Outline with signatures, line ranges, imports |
|
|
298
|
+
| `clast_search` | Where is X, what is it called? By name, partial name, abbreviation, typo, or a description |
|
|
299
|
+
| `clast_get_context` | Show me X: its source plus callers, callees, overloads, overrides, subtypes |
|
|
300
|
+
| `clast_call_graph` | Who calls X, what does X call, to depth N |
|
|
301
|
+
| `clast_references` | Where is X used at all: calls, types, imports, assignments |
|
|
302
|
+
| `clast_hierarchy` | What implements, extends or overrides X? Including Go interfaces by method set |
|
|
303
|
+
| `clast_impact` | What must change with X, who reaches it, which tests to run |
|
|
304
|
+
| `clast_unused` | What is dead code? |
|
|
305
|
+
| `clast_stats` | How many X? Inventory, endpoints, database access, dependencies |
|
|
306
|
+
| `clast_grep` | Where does this text appear? Each hit labelled with its function |
|
|
307
|
+
| `clast_changes` | What did this git diff touch, and who calls it? |
|
|
308
|
+
| `clast_status` | Is the index healthy? |
|
|
309
|
+
| `clast_reindex` | Rebuild (rarely needed) |
|
|
310
|
+
| `clast_comment` | Doc comment for X, optionally generated by a local model |
|
|
311
|
+
|
|
312
|
+
All tools return compact plain text: one line per fact, `path:line` locations, and a hint about the
|
|
313
|
+
next useful call when something is not found.
|
|
314
|
+
|
|
315
|
+
**Addressing symbols.** Anywhere a tool takes a name you can write:
|
|
202
316
|
|
|
203
|
-
|
|
317
|
+
| Form | Example |
|
|
318
|
+
|------|---------|
|
|
319
|
+
| bare name | `parseConfig` |
|
|
320
|
+
| member | `Parser.parse`, `Parser::parse`, `Parser#parse` |
|
|
321
|
+
| package or module path | `django.shortcuts.render`, `Illuminate\Pipeline\Pipeline` |
|
|
322
|
+
| file and name | `src/db/index.ts:open` |
|
|
323
|
+
| file and line | `src/db/index.ts:120` (the symbol containing that line) |
|
|
204
324
|
|
|
205
|
-
|
|
206
|
-
to prefer it over grep or reading files. Installed as a plugin, the bundled `explore` skill adds a
|
|
207
|
-
short workflow guide. You can also say it once: "use the clast tools to navigate the code".
|
|
325
|
+
When a bare name has many unrelated definitions the tools list them instead of guessing.
|
|
208
326
|
|
|
209
|
-
|
|
327
|
+
</details>
|
|
210
328
|
|
|
211
|
-
|
|
329
|
+
<details>
|
|
330
|
+
<summary><strong>Command line</strong></summary>
|
|
212
331
|
|
|
213
|
-
The same queries
|
|
332
|
+
The same queries work from a shell, for scripts, hooks and debugging:
|
|
214
333
|
|
|
215
|
-
```
|
|
334
|
+
```sh
|
|
216
335
|
clast-ai overview # repository map
|
|
217
336
|
clast-ai outline src/db # directory or file outline
|
|
218
337
|
clast-ai search "resolve import path" # ranked symbol search
|
|
@@ -220,19 +339,21 @@ clast-ai symbol Indexer.indexAll # source + callers + callees
|
|
|
220
339
|
clast-ai callers resolveImports --depth 2 --no-tests
|
|
221
340
|
clast-ai hierarchy Shape
|
|
222
341
|
clast-ai refs DEFAULT_CONFIG
|
|
223
|
-
clast-ai grep "TODO" --path src
|
|
224
|
-
clast-ai changes main # symbols changed vs main and their impact
|
|
225
342
|
clast-ai impact Shape.area # what must change with it, callers, tests to run
|
|
226
343
|
clast-ai unused src/billing # dead code under a directory (or the whole project)
|
|
344
|
+
clast-ai stats endpoints # exact counts: inventory, symbols, endpoints, database, ...
|
|
345
|
+
clast-ai changes main # symbols changed vs main and their impact
|
|
346
|
+
clast-ai guide # the usage guide for agents, as Markdown
|
|
347
|
+
clast-ai migrate # leftovers of older installs
|
|
227
348
|
clast-ai index --force # rebuild the index
|
|
228
|
-
clast-ai parse file.go # debug: what the extractor sees
|
|
229
349
|
```
|
|
230
350
|
|
|
231
351
|
Add `--root <dir>` to point at another project. `clast-ai` with no arguments runs the MCP server.
|
|
232
352
|
|
|
233
|
-
|
|
353
|
+
</details>
|
|
234
354
|
|
|
235
|
-
|
|
355
|
+
<details>
|
|
356
|
+
<summary><strong>Configuration and languages</strong></summary>
|
|
236
357
|
|
|
237
358
|
Optional. Create `clast.config.json` (or `.claude/clast.config.json`) in the project root:
|
|
238
359
|
|
|
@@ -258,10 +379,10 @@ Optional. Create `clast.config.json` (or `.claude/clast.config.json`) in the pro
|
|
|
258
379
|
| `workers` | `0` (auto) | Parser threads for bulk indexing; `1` disables workers |
|
|
259
380
|
| `watch.enabled` | `true` | Re-index files as they change |
|
|
260
381
|
| `watch.debounceMs` | `200` | Debounce for file change events |
|
|
261
|
-
| `llm.*` | disabled | OpenAI-compatible endpoint for `clast_comment`
|
|
382
|
+
| `llm.*` | disabled | OpenAI-compatible endpoint for `clast_comment` (Ollama, LM Studio, vLLM, OpenAI, OpenRouter) |
|
|
262
383
|
|
|
263
|
-
Environment: `CLAST_PROJECT_DIR` (project root), `CLAST_DB_PATH` (index location
|
|
264
|
-
|
|
384
|
+
Environment: `CLAST_PROJECT_DIR` (project root), `CLAST_DB_PATH` (index location), `CLAST_GRAMMARS_DIR`
|
|
385
|
+
(alternative grammar directory), `CLAST_NO_MIGRATE=1` (no automatic clean-up), `CLAST_DEBUG=1` (timings).
|
|
265
386
|
|
|
266
387
|
| Language | id | Extensions |
|
|
267
388
|
|----------|----|------------|
|
|
@@ -279,314 +400,88 @@ Environment: `CLAST_PROJECT_DIR` (project root), `CLAST_DB_PATH` (index location
|
|
|
279
400
|
| Swift | `swift` | `.swift` |
|
|
280
401
|
| Dart | `dart` | `.dart` |
|
|
281
402
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
against the file's imports), imports and packages in TS / Python / Java / PHP, C# generic arity and
|
|
310
|
-
partial classes, Ruby lexical constant lookup, Rust module paths.
|
|
311
|
-
5. **Stay fresh** - a watcher re-indexes saved files; in addition every tool re-checks the files it is
|
|
312
|
-
about to quote and re-parses them on the spot if they changed.
|
|
313
|
-
|
|
314
|
-
The index lives in `.clast/index.db` and is a pure cache: delete it at any time.
|
|
315
|
-
|
|
316
|
-
### Performance
|
|
317
|
-
|
|
318
|
-
Measured with `npm run bench` (Node 24, Windows 11, Ryzen 9950X3D, up to 12 parser workers). Cold index is a first run on an
|
|
319
|
-
empty index; warm rescan is a restart with nothing changed.
|
|
320
|
-
|
|
321
|
-
<!-- BENCH:START -->
|
|
322
|
-
| repo | files | symbols | cold index | warm rescan | db size | search (name) | search (words) | symbol + callers/callees | callers depth 2 | references |
|
|
323
|
-
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
324
|
-
| vscode | 15726 | 461365 | 26.1 s | 1895 ms | 357 MB | 168 ms | 42 ms | 28 ms | 38 ms | 11 ms |
|
|
325
|
-
| aspnetcore | 11470 | 145271 | 10.2 s | 1382 ms | 135 MB | 9.3 ms | 86 ms | 33 ms | 38 ms | 6.6 ms |
|
|
326
|
-
| kafka | 6481 | 126393 | 9.5 s | 863 ms | 144 MB | 26 ms | 33 ms | 25 ms | 962 ms | 5.6 ms |
|
|
327
|
-
| rails | 3580 | 61549 | 3.8 s | 352 ms | 52 MB | 8.7 ms | 15 ms | 22 ms | 35 ms | 3.0 ms |
|
|
328
|
-
| framework | 3180 | 44124 | 3.0 s | 337 ms | 38 MB | 1.4 ms | 46 ms | 23 ms | 23 ms | 2.2 ms |
|
|
329
|
-
| django | 2987 | 60131 | 3.1 s | 522 ms | 42 MB | 29 ms | 41 ms | 21 ms | 48 ms | 2.5 ms |
|
|
330
|
-
| fastapi | 1224 | 10049 | 0.8 s | 161 ms | 7 MB | 5.0 ms | 17 ms | 5.7 ms | 48 ms | 2.4 ms |
|
|
331
|
-
| hugo | 939 | 16896 | 1.1 s | 127 ms | 11 MB | 7.2 ms | 38 ms | 18 ms | 18 ms | 2.4 ms |
|
|
332
|
-
| redis | 919 | 35752 | 1.9 s | 103 ms | 23 MB | 5.7 ms | 16 ms | 7.0 ms | 26 ms | 2.2 ms |
|
|
333
|
-
| tokio | 816 | 12341 | 1.1 s | 85 ms | 11 MB | 3.9 ms | 17 ms | 2.0 ms | 4.2 ms | 1.4 ms |
|
|
334
|
-
| http | 362 | 3947 | 0.7 s | 75 ms | 3 MB | 1.3 ms | 9.6 ms | 3.5 ms | 1.6 ms | 5.3 ms |
|
|
335
|
-
| express | 142 | 439 | 0.4 s | 20 ms | 1 MB | 0.4 ms | 1.0 ms | 0.8 ms | 0.5 ms | 1.6 ms |
|
|
336
|
-
| leveldb | 133 | 3456 | 0.6 s | 14 ms | 2 MB | 2.2 ms | 15 ms | 3.3 ms | 1.8 ms | 2.5 ms |
|
|
337
|
-
| Alamofire | 107 | 4085 | 1.5 s | 23 ms | 4 MB | 2.4 ms | 7.1 ms | 1.5 ms | 530 ms | 1.3 ms |
|
|
338
|
-
| ripgrep | 103 | 4496 | 0.6 s | 13 ms | 3 MB | 1.3 ms | 13 ms | 1.3 ms | 1.4 ms | 0.5 ms |
|
|
339
|
-
| gin | 99 | 2157 | 0.4 s | 12 ms | 2 MB | 1.5 ms | 7.7 ms | 2.2 ms | 4.3 ms | 0.7 ms |
|
|
340
|
-
| spring-petclinic | 50 | 323 | 0.2 s | 11 ms | 1 MB | 0.8 ms | 0.7 ms | 0.6 ms | 0.6 ms | 1.3 ms |
|
|
341
|
-
|
|
342
|
-
Node v24.7.0, win32 x64. Query times are medians of 5 in-process runs on the most-called function of each repo.
|
|
343
|
-
<!-- BENCH:END -->
|
|
344
|
-
|
|
345
|
-
Worst case measured: `callers IDisposable.dispose` on vscode (10.7k call sites, 4.7k definitions of
|
|
346
|
-
`dispose`) takes 5-6 s. Scoped with `within` it is fast.
|
|
347
|
-
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
## Clast vs grep + read
|
|
351
|
-
|
|
352
|
-
Measured with real headless Claude Code sessions (`bench/ab.mjs`): every question runs in its own
|
|
353
|
-
fresh session, once with clast connected and once with only Read / Grep / Glob. Two large repositories:
|
|
354
|
-
vscode (TypeScript, 15.7k files) and OnAir (C++, 7.5k files, 1.6M lines). The index was fully built
|
|
355
|
-
before the first session. Answers are scored against facts verified in the source beforehand; an
|
|
356
|
-
implementation task is scored on its diff.
|
|
357
|
-
|
|
358
|
-
"Cost" is the API-price equivalent that Claude Code reports for the session. It weights fresh input and
|
|
359
|
-
output above cached prompt tokens, so it is the closest single number to "tokens that matter".
|
|
360
|
-
|
|
361
|
-
| Kind of work | Repo | Sessions per arm | Tool calls (clast / grep) | Wall time | Cost per question | Correct |
|
|
362
|
-
| --- | --- | ---: | ---: | ---: | ---: | ---: |
|
|
363
|
-
| Multi-hop questions (call chains, transitive subclasses, impact) | vscode | 18 | 1.3 / 3.1 | 16.0 s / 19.8 s | $0.063 / $0.126 | 18 / 18 |
|
|
364
|
-
| Multi-hop questions | OnAir | 18 | 1.2 / 5.9 | 12.5 s / 25.8 s | $0.056 / $0.212 | 18 / 18 |
|
|
365
|
-
| Simple lookups (where is X, who calls X, what implements X) | vscode | 30 | 1.4 / 1.4 | 13.9 s / 13.9 s | $0.053 / $0.059 | 30 / 29 |
|
|
366
|
-
| Simple lookups | OnAir | 30 | 1.7 / 1.7 | 12.7 s / 14.3 s | $0.063 / $0.096 | 30 / 30 |
|
|
367
|
-
| Counting ("how many receiver classes under core/") | OnAir | 3 | 1.0 / 3.3 | 11 s / 34 s | about $0.02 / $0.29 | 3 / 2 |
|
|
368
|
-
| Small implementation tasks (new provider, new enum value, new subclass) | vscode | 6 | 7.5 / 8.5 | 30 s / 31 s | $0.29 / $0.24 | 6 / 6 |
|
|
369
|
-
| Small implementation tasks | OnAir | 6 | 11.5 / 12.8 | 39 s / 40 s | $0.58 / $0.56 | 6 / 6 |
|
|
370
|
-
| Cross-cutting refactors (rename a method in 28 files, add a parameter and update all callers) | vscode | 4 | 21.8 / 22.0 | 47 s / 48 s | $0.45 / $0.37 | 4 / 4 |
|
|
371
|
-
| Cross-cutting refactors | OnAir | 4 | 9.8 / 7.8 | 26 s / 25 s | $0.25 / $0.24 | 4 / 4 |
|
|
372
|
-
| Before a change (tests that reach a function, files to edit for a signature change, dead functions in a file) | vscode | 9 | 2.3 / 2.2 | 19.3 s / 19.8 s | $0.098 / $0.154 | 9 / 9 |
|
|
373
|
-
| Before a change | OnAir | 9 | 1.7 / 3.6 | 14.3 s / 17.4 s | $0.126 / $0.173 | 9 / 9 |
|
|
374
|
-
| Find dead code in a directory ("five unused functions under app/", answers checked by hand) | OnAir | 2 | 2.0 / 16.0 | 20 s / 124 s | $0.15 / $1.89 | 2 / 2 |
|
|
375
|
-
| Questions that describe behaviour without naming anything ("what takes out the spike in the middle of the spectrum") | OnAir | 12 | 1.8 / 4.2 | 12.0 s / 20.5 s | $0.068 / $0.197 | 12 / 12 |
|
|
376
|
-
| Questions that describe behaviour | vscode | 12 | 1.0 / 1.0 | 11.7 s / 11.8 s | $0.048 / $0.044 | 12 / 12 |
|
|
377
|
-
|
|
378
|
-
What this says:
|
|
379
|
-
|
|
380
|
-
- **Multi-hop questions are where clast pays off**: half the cost and 20% less time on vscode, a
|
|
381
|
-
quarter of the cost and half the time on OnAir. One call returns a call chain or a full inheritance
|
|
382
|
-
tree that grep needs three to twelve rounds for, and the agent reads 70-85% less tool output.
|
|
383
|
-
- **Simple lookups are a tie.** A current model with ripgrep answers them in one or two calls; clast
|
|
384
|
-
does not make that faster, it only returns a little less text.
|
|
385
|
-
- **Counting is faster and exact** with `clast_stats`. With grep the agent tallied matches by hand and
|
|
386
|
-
was off by one in one of three runs.
|
|
387
|
-
- **Finding dead code is the largest gap measured**: one `clast_unused` call and a confirming grep
|
|
388
|
-
against 11-21 greps, a sixth of the time and a thirteenth of the cost. Every function either arm
|
|
389
|
-
named was verified unused, and all ten the grep sessions found are in clast's list.
|
|
390
|
-
- **Describing behaviour instead of naming it** works on a repository the model has never seen
|
|
391
|
-
(OnAir): search over names, comments and what function bodies mention finds the place in one or two
|
|
392
|
-
calls where grep needs four rounds of guessing words. On vscode it is a tie at one call, because the
|
|
393
|
-
model already knows where things are in vscode; that row measures the model's memory, not the tools.
|
|
394
|
-
- **Questions before a change are cheaper, not always faster.** With `clast_impact` and `clast_unused`
|
|
395
|
-
the sessions cost 27-36% less because the agent reads less tool output. The clear win is "which
|
|
396
|
-
tests reach this function when none names it": 1 call and 11 s against 5.7 calls and 22 s on OnAir.
|
|
397
|
-
"Which files do I edit for a signature change" is one grep and stays a tie.
|
|
398
|
-
- **Implementation work showed no benefit**, neither small features nor cross-cutting refactors. Both
|
|
399
|
-
arms made the same edits in the same files in the same time (refactors were also checked for
|
|
400
|
-
completeness: nothing of the old name or call shape left). Finding every occurrence of one name is
|
|
401
|
-
a single grep, and before editing the agent reads the neighbouring code either way. Clast costs a
|
|
402
|
-
little more here because its tool definitions are in the prompt.
|
|
403
|
-
- **Small samples.** Two repositories, three runs per question, questions written by the tool's
|
|
404
|
-
author. The figures show the direction, not a precise ratio.
|
|
405
|
-
|
|
406
|
-
One result changed the server itself. Claude Code defers MCP tools behind its tool search by default,
|
|
407
|
-
and in that state the agent never called clast: 0 of 60 sessions, every question answered with Grep.
|
|
408
|
-
The navigation tools now ask the client to keep them loaded, after which the agent used clast in every
|
|
409
|
-
question session where it helped. The cost is about 5k tokens of tool definitions and instructions in
|
|
410
|
-
the cached prompt (measured with 13 tools: 23.7k against 19.3k prompt tokens for a session that calls
|
|
411
|
-
no tool; `clast_impact` and `clast_unused` add about 0.6k).
|
|
412
|
-
|
|
413
|
-
Reproduce with `node bench/ab.mjs <repo> bench/ab/<set>.json --runs 3`. The question sets are in
|
|
414
|
-
`bench/ab/`.
|
|
415
|
-
|
|
416
|
-
### Answer quality
|
|
417
|
-
|
|
418
|
-
Reviewer agents asked deliberately hard questions on 17 real repositories and checked every answer
|
|
419
|
-
against the source. Over the last five rounds the share of first answers judged correct rose from
|
|
420
|
-
roughly 45-60% to 65-78%, with new questions each round.
|
|
421
|
-
|
|
422
|
-
Two further audits read the cited source lines for call edges that clast shows as certain (no `?`):
|
|
423
|
-
|
|
424
|
-
| Repo | Language | Wrong | Checked |
|
|
425
|
-
| --- | --- | ---: | ---: |
|
|
426
|
-
| kafka | Java | 0 | 57 |
|
|
427
|
-
| tokio | Rust | 0 | 17 |
|
|
428
|
-
| laravel | PHP | 0 | 19 |
|
|
429
|
-
| vscode | TypeScript | 1 | 45 |
|
|
430
|
-
| django | Python | 1 | 39 |
|
|
431
|
-
| aspnetcore | C# | 1 | 58 |
|
|
432
|
-
| hugo | Go | 1 | 19 |
|
|
433
|
-
| rails | Ruby | 4 | 18 |
|
|
434
|
-
|
|
435
|
-
The vscode and aspnetcore errors were fixed afterwards; the django, hugo and rails ones are open (see
|
|
436
|
-
Known limitations).
|
|
437
|
-
|
|
438
|
-
---
|
|
439
|
-
|
|
440
|
-
## Changelog
|
|
441
|
-
|
|
442
|
-
### 0.2.2
|
|
443
|
-
|
|
444
|
-
- The clean-up of older installs also finds servers registered as `node .../dist/server/index.js` (how
|
|
445
|
-
the 0.1 README registered a build from source) and reads the configuration from `CLAUDE_CONFIG_DIR`
|
|
446
|
-
when that is set. Both were found by installing the plugin into an empty configuration next to an
|
|
447
|
-
old-style registration.
|
|
448
|
-
|
|
449
|
-
### 0.2.1
|
|
450
|
-
|
|
451
|
-
- `clast-ai migrate`: finds and removes what older installs left behind (duplicate MCP registrations
|
|
452
|
-
next to the plugin, entries written by the old VS Code extension, the v0.1 index file). The plugin
|
|
453
|
-
runs it automatically, once per version, at the first session after an install or update.
|
|
454
|
-
- The plugin uses a global `clast-ai` only when it is at least as new as the plugin itself, so a
|
|
455
|
-
plugin update is not held back by an older global install.
|
|
456
|
-
|
|
457
|
-
### 0.2.0
|
|
458
|
-
|
|
459
|
-
New tools (7 to 13):
|
|
460
|
-
|
|
461
|
-
- `clast_references`: every use of a symbol, not just calls.
|
|
462
|
-
- `clast_hierarchy`: supertypes, subtypes, implementers and overrides, including Go structural
|
|
463
|
-
interfaces and Ruby mixins.
|
|
464
|
-
- `clast_overview`: map of a repository or directory.
|
|
465
|
-
- `clast_grep`: text search over all text files with the enclosing symbol on every hit.
|
|
466
|
-
- `clast_changes`: symbols touched by a git diff, plus their callers.
|
|
467
|
-
- `clast_impact`: for one symbol, what has to change with it (declarations, overrides, implementations,
|
|
468
|
-
overloads), every direct caller by file and line, the reach through indirect callers, and the test
|
|
469
|
-
files to run, including tests that only reach it through its callers.
|
|
470
|
-
- `clast_unused`: dead code. A name counts as unused when it is never referenced outside its own
|
|
471
|
-
definitions, or only from other unused code (so a deleted feature's helpers are found with it).
|
|
472
|
-
Reported separately: code used only by tests, names that still appear in a string, config or
|
|
473
|
-
template, and (Java, C#, C, C++, Rust) methods whose every call binds to another definition. Left
|
|
474
|
-
out, with counts: entry points, decorated or attribute-marked symbols, subclasses of library types
|
|
475
|
-
and their methods, object-literal members, exported Go methods, C# extension classes.
|
|
476
|
-
- `clast_stats`: exact counts grouped by directory, file, kind or name: inventory, symbols by name
|
|
477
|
-
pattern, direct and indirect subtypes, call sites by name and receiver, text matches, external
|
|
478
|
-
dependencies, and pattern-based inventories of HTTP / RPC endpoints and database access.
|
|
479
|
-
|
|
480
|
-
Call graph:
|
|
481
|
-
|
|
482
|
-
- Calls are resolved at query time using declared receiver types, imports and aliases, field types
|
|
483
|
-
across files, return types of call chains, inheritance and `super`.
|
|
484
|
-
- Edges are shown as certain, uncertain, or `?` (matched by name only).
|
|
485
|
-
- Argument counts separate overloads. A bare name means all overloads; `path:line` selects one.
|
|
486
|
-
- A call bound to a subclass override no longer counts as a caller of the base method.
|
|
487
|
-
- Language rules: Go packages and structural interfaces, Rust `impl Trait for Arc<T>` and std types,
|
|
488
|
-
C# generic arity / partial / static classes, Ruby mixins and bare calls, Java static imports, Python
|
|
489
|
-
submodule calls and imported instances, TypeScript renamed re-exports, C macro bodies, C++ namespace
|
|
490
|
-
functions.
|
|
491
|
-
|
|
492
|
-
Search:
|
|
493
|
-
|
|
494
|
-
- Ranked by exact name, prefix, abbreviation, typo and plain words.
|
|
495
|
-
- Descriptive queries also match what function bodies mention (marked `[body]`).
|
|
496
|
-
- Overloads fold into one hit.
|
|
497
|
-
|
|
498
|
-
Performance and storage:
|
|
499
|
-
|
|
500
|
-
- `node:sqlite` on disk (WAL, FTS5) replaces in-memory sql.js.
|
|
501
|
-
- Parsing runs in worker threads, with an inline fallback if a worker fails.
|
|
502
|
-
- The server connects first and indexes afterwards; on a first build, tools wait for the index.
|
|
503
|
-
- A watcher re-indexes changed files, and every tool re-checks the files it quotes.
|
|
504
|
-
|
|
505
|
-
Other:
|
|
506
|
-
|
|
507
|
-
- Swift and Dart added (14 languages).
|
|
508
|
-
- All dependencies updated (MCP SDK 1.32, web-tree-sitter 0.27).
|
|
509
|
-
- 291 tests, plus `bench/bench.mjs`, `bench/resolution.mjs` and the A/B harness `bench/ab.mjs`.
|
|
510
|
-
- Installable as a Claude Code plugin from this repository (`/plugin marketplace add xls/clast`,
|
|
511
|
-
`/plugin install clast@clast`): the MCP server, a usage skill, and the slash commands
|
|
512
|
-
`/clast:deadcode`, `/clast:impact`, `/clast:audit` and `/clast:status`. The old VS Code extension
|
|
513
|
-
scaffold that wrote the server into `.claude/settings.local.json` is removed.
|
|
514
|
-
- The navigation tools ask Claude Code to keep them loaded instead of deferring them behind tool
|
|
515
|
-
search; deferred, the agent never called them.
|
|
516
|
-
- Ruby classes and their ancestors are compared by qualified name (`Rails::Generators::Base` is not
|
|
517
|
-
`ActiveRecord::Base`; `Thor::Group` is not a project class called `Group`).
|
|
518
|
-
- C++ functions declared through a head-building macro (`FN(jstring, nativeStart)(args)`) are indexed
|
|
519
|
-
under their real name.
|
|
520
|
-
- C / C++ function pointers whose calling convention is a macro (`err_t (MY_CALL* find)(args)`) are
|
|
521
|
-
indexed as a field called `find`, not as a method called `err_t`.
|
|
522
|
-
|
|
523
|
-
### Upgrading from 0.1
|
|
524
|
-
|
|
525
|
-
How to upgrade: see [Upgrading from an older install](#upgrading-from-an-older-install). What changed:
|
|
526
|
-
|
|
527
|
-
- Node.js 22.22+ is required (was 18).
|
|
528
|
-
- The index moved from `.clast/clast.db` (sql.js) to `.clast/index.db` (`node:sqlite`). Nothing is
|
|
529
|
-
migrated: the index is a cache and is rebuilt on first start. The old file is ignored; the plugin
|
|
530
|
-
(or `clast-ai migrate --apply`) deletes it.
|
|
531
|
-
- A later schema change deletes and rebuilds the index automatically (the schema is at version 26).
|
|
532
|
-
- Tool output is compact text instead of JSON.
|
|
533
|
-
- `clast_call_graph` defaults to depth 1 and gained `within` and `includeTests`.
|
|
534
|
-
- The binary entry point is `dist/cli.js`; `dist/server/index.js` still starts the server for old configs.
|
|
535
|
-
|
|
536
|
-
---
|
|
537
|
-
|
|
538
|
-
## Known limitations
|
|
403
|
+
</details>
|
|
404
|
+
|
|
405
|
+
<details>
|
|
406
|
+
<summary><strong>How it works</strong></summary>
|
|
407
|
+
|
|
408
|
+
1. **Scan.** Walk the tree honouring `.gitignore` and `ignoredPaths`; compare size and modification
|
|
409
|
+
time with the index, so an unchanged file costs one `stat`.
|
|
410
|
+
2. **Parse.** Changed files are parsed by a pool of worker threads (tree-sitter WebAssembly). A
|
|
411
|
+
per-language spec turns the syntax tree into symbols, calls (with the receiver and its declared
|
|
412
|
+
type), imports, inheritance and identifier occurrences.
|
|
413
|
+
3. **Store.** SQLite (`node:sqlite`, WAL) with a full-text index over names, signatures and docs.
|
|
414
|
+
4. **Link at query time.** A call is stored as "symbol A calls name N on receiver R of type T" and
|
|
415
|
+
resolved when asked, so nothing goes stale when another file changes. Resolution uses the declared
|
|
416
|
+
receiver type, then imports and same-file definitions, then `this` / `self` and inheritance, then
|
|
417
|
+
evidence scoring for untyped receivers. Argument counts separate overloads. A call through an
|
|
418
|
+
interface or base type counts as a caller of the implementations it can reach; a call bound to a
|
|
419
|
+
subclass override does not count for the base method.
|
|
420
|
+
5. **Stay fresh.** A watcher re-indexes saved files, and every tool re-checks the files it quotes.
|
|
421
|
+
|
|
422
|
+
The index lives in `.clast/index.db` and takes about 12-25 MB per 1,000 source files. A cold index of
|
|
423
|
+
vscode (15.7k files) takes about 26 s; a restart with nothing changed about 2 s; most queries are
|
|
424
|
+
under 50 ms.
|
|
425
|
+
|
|
426
|
+
</details>
|
|
427
|
+
|
|
428
|
+
<details>
|
|
429
|
+
<summary><strong>Known limitations</strong></summary>
|
|
539
430
|
|
|
540
431
|
- **Untyped receivers**: `obj.save()` in dynamic code cannot be attributed. Such calls are listed as
|
|
541
432
|
"receiver type unknown", not guessed.
|
|
433
|
+
- **Dead code is judged by name**: a member whose name is also used by other symbols (`get`, `run`,
|
|
434
|
+
`close`) is never listed, so the list is incomplete for common method names. Calls built from
|
|
435
|
+
strings at run time are only caught when the prefix is a literal (`getattr(x, "as_" + vendor)`).
|
|
436
|
+
Code found by convention (Django `AppConfig` classes inside Django itself) can be listed wrongly.
|
|
437
|
+
Treat the list as candidates and let the compiler or the tests confirm a deletion.
|
|
542
438
|
- **Same-named types**: classes with the same short name in different PHP namespaces or Java packages
|
|
543
439
|
can be confused when the calling file gives no hint.
|
|
544
|
-
- **Ruby**: the least exercised language. The rails audit found 4 wrong edges of 18 with one cause
|
|
545
|
-
(classes called `Base` in different namespaces treated as one family); classes are now compared by
|
|
546
|
-
qualified name, which fixes that case, but Ruby has not been re-audited since. Methods defined by
|
|
547
|
-
metaprogramming are invisible.
|
|
548
440
|
- **Overloads**: those that differ only in parameter types, not count, are one target.
|
|
549
441
|
- **Generics and inference**: no generic substitution; TypeScript return types are known only when
|
|
550
442
|
annotated.
|
|
551
443
|
- **Framework conventions**: Django managers, Laravel facades and container lookups, and services
|
|
552
|
-
injected by string are not modelled.
|
|
444
|
+
injected by string are not modelled. Endpoint and database statistics are pattern-based.
|
|
553
445
|
- **Python**: a local variable that holds a class is not followed.
|
|
554
|
-
- **Go**:
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
keeping its members. Swift and Dart are new and less exercised.
|
|
558
|
-
- **Dead code is judged by name**: a member whose name is also used by other symbols (`get`, `run`,
|
|
559
|
-
`close`) is never listed, so the list is incomplete for common method names; calls built from
|
|
560
|
-
strings at run time are only caught when the prefix is a literal (`getattr(x, "as_" + vendor)`).
|
|
561
|
-
In a spot check of 8-12 random findings per repository (kafka, aspnetcore, vscode, django, tokio,
|
|
562
|
-
hugo, OnAir) against a word grep, the remaining wrong ones were convention-based discovery (Django
|
|
563
|
-
`AppConfig` classes inside Django itself). Treat the list as candidates and let the compiler or the
|
|
564
|
-
tests confirm a deletion.
|
|
446
|
+
- **Go**: the module path is inferred from imports, not from go.mod.
|
|
447
|
+
- **Ruby, Swift, Dart**: the least exercised languages. Ruby methods defined by metaprogramming are
|
|
448
|
+
invisible; the Swift grammar fails on some files.
|
|
565
449
|
- **Search is lexical**: a description that shares no word with the code finds the neighbourhood, not
|
|
566
450
|
always the function.
|
|
567
451
|
- **Not indexed**: Kotlin, Scala and other languages without a bundled grammar. `clast_grep` still
|
|
568
452
|
searches those files as text.
|
|
569
|
-
- **
|
|
570
|
-
|
|
453
|
+
- **Prompt cost**: the tool definitions add about 5k tokens to every session.
|
|
454
|
+
|
|
455
|
+
More detail and examples: [docs/v0.2-summary.md](docs/v0.2-summary.md).
|
|
456
|
+
|
|
457
|
+
</details>
|
|
571
458
|
|
|
572
|
-
|
|
459
|
+
## Try it. Tell us what you think.
|
|
573
460
|
|
|
574
|
-
|
|
461
|
+
Point clast at a code base you know well and ask it something you already know the answer to. If an
|
|
462
|
+
answer is wrong, a language construct is missed, or an agent you use needs something different,
|
|
463
|
+
[open an issue](https://github.com/xls/clast/issues). Include the language, the question you asked and
|
|
464
|
+
the clast version (`clast-ai --version`).
|
|
575
465
|
|
|
576
|
-
|
|
466
|
+
<details>
|
|
467
|
+
<summary><strong>Working on clast</strong></summary>
|
|
577
468
|
|
|
578
|
-
```
|
|
469
|
+
```sh
|
|
579
470
|
npm install
|
|
580
471
|
npm run build # tsc
|
|
581
|
-
npm test # node:test,
|
|
472
|
+
npm test # node:test, incl. an end-to-end MCP session
|
|
582
473
|
npm run bench -- /path/to/dir-with-repos
|
|
583
474
|
```
|
|
584
475
|
|
|
585
476
|
Adding a language: create `src/parser/langs/<name>.ts` exporting a `LangSpec` (see `typescript.ts` and
|
|
586
|
-
`python.ts`
|
|
587
|
-
|
|
588
|
-
|
|
477
|
+
`python.ts`), register it in `src/parser/registry.ts`, and add a test under `test/langs/`.
|
|
478
|
+
`clast-ai ast <file>` dumps the syntax tree and `clast-ai parse <file>` shows what your spec extracts.
|
|
479
|
+
The grammar must be available in `@repomix/tree-sitter-wasms`.
|
|
480
|
+
|
|
481
|
+
The A/B harness that produced the figures above is `bench/ab.mjs`; the question sets are in `bench/ab/`.
|
|
482
|
+
|
|
483
|
+
</details>
|
|
589
484
|
|
|
590
485
|
## License
|
|
591
486
|
|
|
592
|
-
MIT
|
|
487
|
+
[MIT](LICENSE). Free to use, modify and share.
|