clast-ai 0.1.1 → 0.2.0

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.
Files changed (290) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/.claude-plugin/plugin.json +11 -9
  3. package/README.md +449 -390
  4. package/bench/ab/onair-concept.json +22 -0
  5. package/bench/ab/onair-deadcode.json +7 -0
  6. package/bench/ab/onair-hard.json +32 -0
  7. package/bench/ab/onair-impact.json +17 -0
  8. package/bench/ab/onair-refactors.json +18 -0
  9. package/bench/ab/onair-survey.json +17 -0
  10. package/bench/ab/onair-tasks.json +20 -0
  11. package/bench/ab/onair.json +52 -0
  12. package/bench/ab/vscode-concept.json +22 -0
  13. package/bench/ab/vscode-hard.json +32 -0
  14. package/bench/ab/vscode-impact.json +17 -0
  15. package/bench/ab/vscode-refactors.json +18 -0
  16. package/bench/ab/vscode-tasks.json +20 -0
  17. package/bench/ab/vscode.json +52 -0
  18. package/bench/ab.mjs +213 -0
  19. package/bench/bench.mjs +98 -0
  20. package/bench/resolution.mjs +75 -0
  21. package/dist/cli.d.ts +2 -0
  22. package/dist/cli.js +257 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/config/defaults.d.ts +12 -2
  25. package/dist/config/defaults.js +38 -31
  26. package/dist/config/defaults.js.map +1 -1
  27. package/dist/config/index.d.ts +1 -1
  28. package/dist/config/index.js +26 -14
  29. package/dist/config/index.js.map +1 -1
  30. package/dist/db/index.d.ts +73 -13
  31. package/dist/db/index.js +481 -39
  32. package/dist/db/index.js.map +1 -1
  33. package/dist/engine.d.ts +15 -0
  34. package/dist/engine.js +28 -0
  35. package/dist/engine.js.map +1 -0
  36. package/dist/indexer/imports.d.ts +6 -0
  37. package/dist/indexer/imports.js +254 -0
  38. package/dist/indexer/imports.js.map +1 -0
  39. package/dist/indexer/index.d.ts +63 -0
  40. package/dist/indexer/index.js +360 -0
  41. package/dist/indexer/index.js.map +1 -0
  42. package/dist/indexer/process.d.ts +22 -0
  43. package/dist/indexer/process.js +42 -0
  44. package/dist/indexer/process.js.map +1 -0
  45. package/dist/indexer/scan.d.ts +43 -0
  46. package/dist/indexer/scan.js +223 -0
  47. package/dist/indexer/scan.js.map +1 -0
  48. package/dist/indexer/worker.d.ts +1 -0
  49. package/dist/indexer/worker.js +23 -0
  50. package/dist/indexer/worker.js.map +1 -0
  51. package/dist/llm/comment-generator.d.ts +17 -19
  52. package/dist/llm/comment-generator.js +46 -80
  53. package/dist/llm/comment-generator.js.map +1 -1
  54. package/dist/llm/index.d.ts +0 -1
  55. package/dist/llm/index.js.map +1 -1
  56. package/dist/llm/prompts.d.ts +0 -1
  57. package/dist/llm/prompts.js +16 -16
  58. package/dist/parser/extractor.d.ts +21 -16
  59. package/dist/parser/extractor.js +700 -228
  60. package/dist/parser/extractor.js.map +1 -1
  61. package/dist/parser/index.d.ts +17 -21
  62. package/dist/parser/index.js +115 -171
  63. package/dist/parser/index.js.map +1 -1
  64. package/dist/parser/langs/c.d.ts +3 -0
  65. package/dist/parser/langs/c.js +879 -0
  66. package/dist/parser/langs/c.js.map +1 -0
  67. package/dist/parser/langs/csharp.d.ts +2 -0
  68. package/dist/parser/langs/csharp.js +548 -0
  69. package/dist/parser/langs/csharp.js.map +1 -0
  70. package/dist/parser/langs/dart.d.ts +2 -0
  71. package/dist/parser/langs/dart.js +893 -0
  72. package/dist/parser/langs/dart.js.map +1 -0
  73. package/dist/parser/langs/go.d.ts +2 -0
  74. package/dist/parser/langs/go.js +455 -0
  75. package/dist/parser/langs/go.js.map +1 -0
  76. package/dist/parser/langs/java.d.ts +2 -0
  77. package/dist/parser/langs/java.js +390 -0
  78. package/dist/parser/langs/java.js.map +1 -0
  79. package/dist/parser/langs/php.d.ts +2 -0
  80. package/dist/parser/langs/php.js +486 -0
  81. package/dist/parser/langs/php.js.map +1 -0
  82. package/dist/parser/langs/python.d.ts +2 -0
  83. package/dist/parser/langs/python.js +216 -0
  84. package/dist/parser/langs/python.js.map +1 -0
  85. package/dist/parser/langs/ruby.d.ts +2 -0
  86. package/dist/parser/langs/ruby.js +602 -0
  87. package/dist/parser/langs/ruby.js.map +1 -0
  88. package/dist/parser/langs/rust.d.ts +2 -0
  89. package/dist/parser/langs/rust.js +931 -0
  90. package/dist/parser/langs/rust.js.map +1 -0
  91. package/dist/parser/langs/swift.d.ts +2 -0
  92. package/dist/parser/langs/swift.js +616 -0
  93. package/dist/parser/langs/swift.js.map +1 -0
  94. package/dist/parser/langs/typescript.d.ts +4 -0
  95. package/dist/parser/langs/typescript.js +535 -0
  96. package/dist/parser/langs/typescript.js.map +1 -0
  97. package/dist/parser/registry.d.ts +16 -0
  98. package/dist/parser/registry.js +87 -0
  99. package/dist/parser/registry.js.map +1 -0
  100. package/dist/parser/returns.d.ts +9 -0
  101. package/dist/parser/returns.js +152 -0
  102. package/dist/parser/returns.js.map +1 -0
  103. package/dist/parser/spec.d.ts +165 -0
  104. package/dist/parser/spec.js +188 -0
  105. package/dist/parser/spec.js.map +1 -0
  106. package/dist/query/arity.d.ts +27 -0
  107. package/dist/query/arity.js +187 -0
  108. package/dist/query/arity.js.map +1 -0
  109. package/dist/query/callgraph.d.ts +113 -0
  110. package/dist/query/callgraph.js +332 -0
  111. package/dist/query/callgraph.js.map +1 -0
  112. package/dist/query/format.d.ts +36 -0
  113. package/dist/query/format.js +136 -0
  114. package/dist/query/format.js.map +1 -0
  115. package/dist/query/hierarchy.d.ts +44 -0
  116. package/dist/query/hierarchy.js +441 -0
  117. package/dist/query/hierarchy.js.map +1 -0
  118. package/dist/query/resolve.d.ts +265 -0
  119. package/dist/query/resolve.js +1957 -0
  120. package/dist/query/resolve.js.map +1 -0
  121. package/dist/query/search.d.ts +35 -0
  122. package/dist/query/search.js +379 -0
  123. package/dist/query/search.js.map +1 -0
  124. package/dist/query/words.d.ts +30 -0
  125. package/dist/query/words.js +201 -0
  126. package/dist/query/words.js.map +1 -0
  127. package/dist/server/index.d.ts +1 -3
  128. package/dist/server/index.js +313 -73
  129. package/dist/server/index.js.map +1 -1
  130. package/dist/tools/context.d.ts +35 -0
  131. package/dist/tools/context.js +79 -0
  132. package/dist/tools/context.js.map +1 -0
  133. package/dist/tools/explore.d.ts +51 -0
  134. package/dist/tools/explore.js +779 -0
  135. package/dist/tools/explore.js.map +1 -0
  136. package/dist/tools/impact.d.ts +21 -0
  137. package/dist/tools/impact.js +743 -0
  138. package/dist/tools/impact.js.map +1 -0
  139. package/dist/tools/search.d.ts +59 -0
  140. package/dist/tools/search.js +843 -0
  141. package/dist/tools/search.js.map +1 -0
  142. package/dist/tools/stats.d.ts +26 -0
  143. package/dist/tools/stats.js +604 -0
  144. package/dist/tools/stats.js.map +1 -0
  145. package/dist/types.d.ts +95 -40
  146. package/dist/types.js +9 -1
  147. package/dist/types.js.map +1 -1
  148. package/dist/version.d.ts +2 -0
  149. package/dist/version.js +3 -0
  150. package/dist/version.js.map +1 -0
  151. package/dist/watcher/index.d.ts +18 -16
  152. package/dist/watcher/index.js +94 -77
  153. package/dist/watcher/index.js.map +1 -1
  154. package/package.json +21 -24
  155. package/plugin/server.mjs +50 -0
  156. package/skills/audit/SKILL.md +37 -0
  157. package/skills/deadcode/SKILL.md +33 -0
  158. package/skills/explore/SKILL.md +54 -0
  159. package/skills/impact/SKILL.md +24 -0
  160. package/skills/status/SKILL.md +11 -0
  161. package/src/cli.ts +256 -0
  162. package/src/config/defaults.ts +50 -32
  163. package/src/config/index.ts +23 -14
  164. package/src/db/index.ts +540 -43
  165. package/src/engine.ts +30 -0
  166. package/src/indexer/imports.ts +241 -0
  167. package/src/indexer/index.ts +369 -0
  168. package/src/indexer/process.ts +63 -0
  169. package/src/indexer/scan.ts +226 -0
  170. package/src/indexer/worker.ts +24 -0
  171. package/src/llm/comment-generator.ts +52 -103
  172. package/src/llm/index.ts +80 -80
  173. package/src/llm/prompts.ts +95 -95
  174. package/src/parser/extractor.ts +643 -256
  175. package/src/parser/index.ts +105 -205
  176. package/src/parser/langs/c.ts +860 -0
  177. package/src/parser/langs/csharp.ts +519 -0
  178. package/src/parser/langs/dart.ts +820 -0
  179. package/src/parser/langs/go.ts +437 -0
  180. package/src/parser/langs/java.ts +361 -0
  181. package/src/parser/langs/php.ts +450 -0
  182. package/src/parser/langs/python.ts +206 -0
  183. package/src/parser/langs/ruby.ts +554 -0
  184. package/src/parser/langs/rust.ts +904 -0
  185. package/src/parser/langs/swift.ts +590 -0
  186. package/src/parser/langs/typescript.ts +499 -0
  187. package/src/parser/registry.ts +88 -0
  188. package/src/parser/returns.ts +137 -0
  189. package/src/parser/spec.ts +309 -0
  190. package/src/query/arity.ts +173 -0
  191. package/src/query/callgraph.ts +473 -0
  192. package/src/query/format.ts +136 -0
  193. package/src/query/hierarchy.ts +447 -0
  194. package/src/query/resolve.ts +1972 -0
  195. package/src/query/search.ts +430 -0
  196. package/src/query/words.ts +183 -0
  197. package/src/server/index.ts +390 -79
  198. package/src/tools/context.ts +82 -0
  199. package/src/tools/explore.ts +860 -0
  200. package/src/tools/impact.ts +748 -0
  201. package/src/tools/search.ts +886 -0
  202. package/src/tools/stats.ts +633 -0
  203. package/src/types.ts +113 -45
  204. package/src/version.ts +2 -0
  205. package/src/watcher/index.ts +82 -88
  206. package/tsconfig.json +7 -4
  207. package/.mcp.json +0 -8
  208. package/commands/clast-status.md +0 -9
  209. package/dist/config/defaults.d.ts.map +0 -1
  210. package/dist/config/index.d.ts.map +0 -1
  211. package/dist/db/index.d.ts.map +0 -1
  212. package/dist/db/queries.d.ts +0 -44
  213. package/dist/db/queries.d.ts.map +0 -1
  214. package/dist/db/queries.js +0 -251
  215. package/dist/db/queries.js.map +0 -1
  216. package/dist/db/schema.d.ts +0 -3
  217. package/dist/db/schema.d.ts.map +0 -1
  218. package/dist/db/schema.js +0 -94
  219. package/dist/db/schema.js.map +0 -1
  220. package/dist/db/types.d.ts +0 -44
  221. package/dist/db/types.d.ts.map +0 -1
  222. package/dist/db/types.js +0 -2
  223. package/dist/db/types.js.map +0 -1
  224. package/dist/llm/comment-generator.d.ts.map +0 -1
  225. package/dist/llm/index.d.ts.map +0 -1
  226. package/dist/llm/prompts.d.ts.map +0 -1
  227. package/dist/parser/call-resolver.d.ts +0 -15
  228. package/dist/parser/call-resolver.d.ts.map +0 -1
  229. package/dist/parser/call-resolver.js +0 -16
  230. package/dist/parser/call-resolver.js.map +0 -1
  231. package/dist/parser/extractor.d.ts.map +0 -1
  232. package/dist/parser/index.d.ts.map +0 -1
  233. package/dist/parser/languages.d.ts +0 -11
  234. package/dist/parser/languages.d.ts.map +0 -1
  235. package/dist/parser/languages.js +0 -74
  236. package/dist/parser/languages.js.map +0 -1
  237. package/dist/server/index.d.ts.map +0 -1
  238. package/dist/server/register-tools.d.ts +0 -7
  239. package/dist/server/register-tools.d.ts.map +0 -1
  240. package/dist/server/register-tools.js +0 -17
  241. package/dist/server/register-tools.js.map +0 -1
  242. package/dist/server/tools/clast-call-graph.d.ts +0 -4
  243. package/dist/server/tools/clast-call-graph.d.ts.map +0 -1
  244. package/dist/server/tools/clast-call-graph.js +0 -70
  245. package/dist/server/tools/clast-call-graph.js.map +0 -1
  246. package/dist/server/tools/clast-comment.d.ts +0 -5
  247. package/dist/server/tools/clast-comment.d.ts.map +0 -1
  248. package/dist/server/tools/clast-comment.js +0 -64
  249. package/dist/server/tools/clast-comment.js.map +0 -1
  250. package/dist/server/tools/clast-file-summary.d.ts +0 -4
  251. package/dist/server/tools/clast-file-summary.d.ts.map +0 -1
  252. package/dist/server/tools/clast-file-summary.js +0 -42
  253. package/dist/server/tools/clast-file-summary.js.map +0 -1
  254. package/dist/server/tools/clast-get-context.d.ts +0 -4
  255. package/dist/server/tools/clast-get-context.d.ts.map +0 -1
  256. package/dist/server/tools/clast-get-context.js +0 -80
  257. package/dist/server/tools/clast-get-context.js.map +0 -1
  258. package/dist/server/tools/clast-reindex.d.ts +0 -5
  259. package/dist/server/tools/clast-reindex.d.ts.map +0 -1
  260. package/dist/server/tools/clast-reindex.js +0 -36
  261. package/dist/server/tools/clast-reindex.js.map +0 -1
  262. package/dist/server/tools/clast-search.d.ts +0 -4
  263. package/dist/server/tools/clast-search.d.ts.map +0 -1
  264. package/dist/server/tools/clast-search.js +0 -29
  265. package/dist/server/tools/clast-search.js.map +0 -1
  266. package/dist/server/tools/clast-status.d.ts +0 -5
  267. package/dist/server/tools/clast-status.d.ts.map +0 -1
  268. package/dist/server/tools/clast-status.js +0 -23
  269. package/dist/server/tools/clast-status.js.map +0 -1
  270. package/dist/types.d.ts.map +0 -1
  271. package/dist/watcher/gitignore.d.ts +0 -9
  272. package/dist/watcher/gitignore.d.ts.map +0 -1
  273. package/dist/watcher/gitignore.js +0 -38
  274. package/dist/watcher/gitignore.js.map +0 -1
  275. package/dist/watcher/index.d.ts.map +0 -1
  276. package/skills/clast-explore/SKILL.md +0 -41
  277. package/src/db/queries.ts +0 -332
  278. package/src/db/schema.ts +0 -106
  279. package/src/db/types.ts +0 -47
  280. package/src/parser/call-resolver.ts +0 -17
  281. package/src/parser/languages.ts +0 -86
  282. package/src/server/register-tools.ts +0 -30
  283. package/src/server/tools/clast-call-graph.ts +0 -82
  284. package/src/server/tools/clast-comment.ts +0 -76
  285. package/src/server/tools/clast-file-summary.ts +0 -59
  286. package/src/server/tools/clast-get-context.ts +0 -97
  287. package/src/server/tools/clast-reindex.ts +0 -52
  288. package/src/server/tools/clast-search.ts +0 -38
  289. package/src/server/tools/clast-status.ts +0 -37
  290. package/src/watcher/gitignore.ts +0 -42
package/README.md CHANGED
@@ -1,474 +1,533 @@
1
- # Clast
2
-
3
- AST-based code intelligence plugin for Claude Code. Indexes your repository using [tree-sitter](https://tree-sitter.github.io/), maintains a live AST database, and exposes MCP tools for fast symbol search, call graph traversal, and context retrieval.
4
-
5
- ## Features
6
-
7
- - **Multi-language AST parsing** — TypeScript, JavaScript, Python, Java, C#, Go, Rust, C/C++, Ruby, PHP
8
- - **Call graph tracking** — Trace callers and callees with configurable depth
9
- - **Live file watching** — Automatic incremental re-indexing on file changes
10
- - **Comment extraction** — Reads existing docstrings/comments; optionally generates missing ones via local LLM
11
- - **Per-project database** — Each repo gets its own `.clast/` SQLite database with file hashes for change detection
12
- - **Cross-platform** — Pure WASM (no native compilation), works on Windows, macOS, Linux
13
- - **Zero config** — Automatically indexes the current working directory on startup
1
+ # Clast v0.2
2
+
3
+ A code index for LLM coding agents, served over MCP. Clast parses your repository with
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.
7
+
8
+ What an agent gets:
9
+
10
+ - **Ranked symbol search** - exact names, partial names, abbreviations (`KProd`, `gcn`), typos, qualified
11
+ names (`Parser.parse`) and plain words (`resolve import path`) matched against names, signatures, docs
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`.
24
+
25
+ Languages: TypeScript, TSX, JavaScript, Python, Java, C#, Go, Rust, C, C++, Ruby, PHP, Swift, Dart.
14
26
 
15
27
  ---
16
28
 
17
- ## Quick Start
29
+ ## Quick start
18
30
 
19
- ### Option 1: Install from npm (once published)
31
+ Requires **Node.js 22.22 or newer** (Clast uses the built-in SQLite module).
20
32
 
21
- ```bash
22
- npm install -g clast-ai
23
- ```
33
+ ### As a Claude Code plugin (recommended)
24
34
 
25
- ### Option 2: Install from GitHub (no cloning needed)
35
+ In Claude Code:
26
36
 
27
- ```bash
28
- npm install -g github:xls/clast
29
37
  ```
30
-
31
- npm fetches the repo, installs dependencies, and builds automatically via the `prepare` script. The `clast` command will be available globally.
32
-
33
- ### Option 3: Clone and Build
34
-
35
- ```bash
36
- git clone https://github.com/xls/clast.git
37
- cd clast
38
- npm install
39
- npm run build
38
+ /plugin marketplace add xls/clast
39
+ /plugin install clast@clast
40
40
  ```
41
41
 
42
- ### Option 4: As a Claude Code Plugin
42
+ or from a shell: `claude plugin marketplace add xls/clast && claude plugin install clast@clast`.
43
43
 
44
- ```bash
45
- git clone https://github.com/xls/clast.git
46
- cd clast
47
- npm install && npm run build
48
- claude plugin add /path/to/clast
49
- ```
44
+ The plugin brings the MCP server, a short skill that teaches Claude which tool answers which question,
45
+ and four slash commands:
50
46
 
51
- The plugin bundles a model-invoked skill that automatically suggests Clast tools when Claude is exploring code.
47
+ | Command | What it does |
48
+ | --- | --- |
49
+ | `/clast:deadcode [path]` | Finds unused functions, methods and types, verifies the candidates, and removes the ones you confirm |
50
+ | `/clast:impact <symbol>` | What must change with a symbol, every caller, and the tests to run |
51
+ | `/clast:audit [path]` | Inventory for due diligence or onboarding: size, structure, endpoints, database access, dependencies, dead code |
52
+ | `/clast:status` | State of the index |
52
53
 
53
- ### Publishing to npm
54
+ The plugin starts the server from a global `clast-ai` 0.2.x when there is one, otherwise through
55
+ `npx clast-ai@0.2` (downloaded on first use). Update with `/plugin update clast@clast`.
54
56
 
55
- If you want to publish your own fork:
57
+ ### As a plain MCP server
56
58
 
57
59
  ```bash
58
- npm login
59
- npm publish
60
+ npm install -g clast-ai
61
+ claude mcp add -s user clast clast-ai
60
62
  ```
61
63
 
62
- Then anyone can `npm install -g clast-ai`.
63
-
64
- ---
64
+ `-s user` registers the server for all projects. This gives the tools without the skill and the slash
65
+ commands. Use one of the two ways, not both: with both, every tool is listed twice.
65
66
 
66
- ## Setup for CLI
67
+ Either way, restart Claude Code (or "Developer: Reload Window" in VS Code) and run `/mcp`: `clast`
68
+ should be listed with 15 tools. The first start indexes the project in the background (seconds, about
69
+ half a minute for 15k files); until that first index is ready the tools answer with the build progress
70
+ and ask to be called again. Later starts are ready immediately.
67
71
 
68
- Add to `~/.claude/settings.local.json`:
69
-
70
- ```json
71
- {
72
- "mcpServers": {
73
- "clast": {
74
- "command": "clast-ai"
75
- }
76
- }
77
- }
78
- ```
79
-
80
- Or using the CLI:
72
+ ### Other ways
81
73
 
82
74
  ```bash
83
- claude mcp add clast -- clast-ai
84
- # ^^^^^ ^^^^^^^^
85
- # alias command (npm package)
86
- ```
87
-
88
- > `clast` is the local alias (shows up in `/mcp` and settings). `clast-ai` is the actual npm binary that gets executed. The alias can be anything you want.
89
-
90
- Restart Claude Code (`Ctrl+Shift+P` → "Claude Code: Restart" in VS Code, or just relaunch the CLI). Clast will automatically index whichever project directory you open.
91
-
92
- ## Setup for VS Code (Claude Code Extension)
93
-
94
- The Claude Code extension for VS Code uses the **same MCP configuration files** as the CLI. There is no separate VS Code-specific config.
95
-
96
- **Step 1:** Open VS Code's integrated terminal and run:
75
+ # from GitHub, without npm
76
+ npm install -g github:xls/clast
97
77
 
98
- ```bash
99
- claude mcp add clast -- clast-ai
78
+ # from a clone (also how to work on clast itself)
79
+ git clone https://github.com/xls/clast.git && cd clast
80
+ npm install && npm run build
81
+ claude mcp add -s user clast node /path/to/clast/dist/cli.js
82
+ claude --plugin-dir /path/to/clast # or: try the plugin from the clone for one session
100
83
  ```
101
84
 
102
- Or manually add to `~/.claude/settings.local.json`:
85
+ Per-project registration for a team: put this in `.mcp.json` in the project root.
103
86
 
104
87
  ```json
105
- {
106
- "mcpServers": {
107
- "clast": {
108
- "command": "clast-ai"
109
- }
110
- }
111
- }
88
+ { "mcpServers": { "clast": { "command": "clast-ai" } } }
112
89
  ```
113
90
 
114
- **Step 2:** Restart the Claude Code extension (no need to restart VS Code itself):
91
+ The project root is `CLAST_PROJECT_DIR`, else `CLAUDE_PROJECT_DIR`, else the working directory.
115
92
 
116
- - Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac)
117
- - Type "Claude Code: Restart" and hit Enter
118
-
119
- **Step 3:** Verify by typing `/mcp` in the Claude Code chat panel — you should see `clast` listed with 7 tools.
120
-
121
- > **Note:** When VS Code spawns the MCP server, the working directory is your open workspace folder. Clast automatically indexes that directory — no path configuration needed.
93
+ ---
122
94
 
123
- If you built from source instead of installing globally:
95
+ ## Tools
96
+
97
+ | Tool | Answers | Typical use |
98
+ |------|---------|-------------|
99
+ | `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 |
100
+ | `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 |
101
+ | `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) |
102
+ | `clast_get_context` | Show me X. | Source of one symbol plus callers, callees, overloads, overrides, subtypes. Batch several names in one call |
103
+ | `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 |
104
+ | `clast_references` | Where is X used? | Every usage of an identifier (calls, types, imports, assignments) with the enclosing symbol of each line |
105
+ | `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 |
106
+ | `clast_grep` | Where does this text appear? | Literal or regex search over all text files; each hit is labelled with its enclosing function |
107
+ | `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 |
108
+ | `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 |
109
+ | `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 |
110
+ | `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 |
111
+ | `clast_status` | Is the index healthy? | Counts, languages, indexing progress, watcher state |
112
+ | `clast_reindex` | Rebuild | Rarely needed; incremental rescan or forced rebuild |
113
+ | `clast_comment` | Docs for X | Existing doc comment, or one generated by a local LLM (optional) |
114
+
115
+ All tools return compact plain text (not JSON): one line per fact, `path:line` locations that can be
116
+ passed straight to an editor, and a hint about the next useful call when something is not found.
117
+
118
+ ### Addressing symbols
119
+
120
+ Anywhere a tool takes a symbol name you can write:
121
+
122
+ | Form | Example |
123
+ |------|---------|
124
+ | bare name | `parseConfig` |
125
+ | member | `Parser.parse`, `Parser::parse`, `Parser#parse` |
126
+ | inherited member | `Engine.GET` (defined on an embedded / base type) |
127
+ | package or module path | `django.shortcuts.render`, `page.Clear`, `Illuminate\Pipeline\Pipeline` |
128
+ | file and name | `src/db/index.ts:open` |
129
+ | file and line | `src/db/index.ts:120` (the symbol containing that line) |
130
+
131
+ When a bare name has many unrelated definitions the tools list them instead of guessing.
132
+
133
+ ### Example
124
134
 
125
- ```json
126
- {
127
- "mcpServers": {
128
- "clast": {
129
- "command": "node",
130
- "args": ["/path/to/clast/dist/server/index.js"]
131
- }
132
- }
133
- }
134
135
  ```
135
-
136
- ## Per-Project Config (Optional)
137
-
138
- To only enable Clast for a specific project instead of globally, create `.mcp.json` in the project root:
139
-
140
- ```json
141
- {
142
- "mcpServers": {
143
- "clast": {
144
- "command": "clast-ai"
145
- }
146
- }
147
- }
136
+ > clast_get_context { "name": "cylinderVolume" }
137
+
138
+ function cylinderVolume src/math/volume.ts:4-6 [exported]
139
+ 3| /** Volume of a cylinder. */
140
+ 4| export function cylinderVolume(radius: number, height: number): number {
141
+ 5| return circleArea(radius) * height;
142
+ 6| }
143
+ callers (1 resolved):
144
+ main src/app.ts:10
145
+ calls (1):
146
+ circleArea src/math/area.ts:2-4
148
147
  ```
149
148
 
150
- This way Clast only activates when you open that project.
149
+ ```
150
+ > clast_call_graph { "name": "MemTable.Add", "direction": "callers" }
151
+
152
+ method leveldb.MemTable.Add db/memtable.cc:76-100
153
+ void MemTable::Add(SequenceNumber s, ValueType type, const Slice& key, const Slice& value)
154
+ callers (3; caller call-site):
155
+ leveldb.MemTableInserter.Put db/write_batch.cc:122
156
+ leveldb.MemTableInserter.Delete db/write_batch.cc:126
157
+ leveldb.MemTableConstructor.FinishImpl table/table_test.cc:315
158
+ ```
151
159
 
152
160
  ---
153
161
 
154
- ## Usage
155
-
156
- ### What happens when you start Claude Code?
157
-
158
- 1. Claude Code spawns the Clast MCP server as a background process
159
- 2. Clast automatically scans and indexes your entire project (typically under a second)
160
- 3. The file watcher starts monitoring for changes
161
- 4. All 7 `clast_*` tools become available to Claude
162
-
163
- **You don't need to do anything** — the AST database is created automatically. There's no manual "index" step.
164
-
165
- ### How does Claude know to use Clast?
162
+ ## How Claude knows to use it
166
163
 
167
- **If installed as a plugin** (`claude plugin add`): The bundled skill automatically tells Claude to prefer Clast tools over `Grep`/`Read` when exploring code.
168
-
169
- **If installed as an MCP server** (`claude mcp add`): Claude sees the tools and their descriptions, which instruct it to prefer Clast for symbol lookups, call graphs, and file structure. You can also explicitly ask Claude to use them:
170
-
171
- - *"Use clast to find the Database class"*
172
- - *"What calls parseAndStore? Check the call graph"*
173
- - *"Show me the structure of queries.ts using clast"*
174
-
175
- Or you can tell Claude once at the start of a session:
176
-
177
- > *"Use the clast tools to navigate code instead of reading files directly"*
178
-
179
- ### What can you ask Claude?
180
-
181
- | What you want | What to ask | Tool Claude uses |
182
- |---------------|-------------|------------------|
183
- | Find a function | *"Find the extractFromTree function"* | `clast_search` |
184
- | Understand a function before editing | *"What does parseAndStore do? Show me its context"* | `clast_get_context` |
185
- | Check what calls a function | *"What calls resolveCallEdges?"* | `clast_call_graph` |
186
- | Understand a file's structure | *"What's in src/db/queries.ts?"* | `clast_file_summary` |
187
- | Check if the index is working | *"Show clast status"* | `clast_status` |
188
- | Force re-index after big changes | *"Reindex the project"* | `clast_reindex` |
189
- | Generate a missing docstring | *"Generate a comment for the insertNodes function"* | `clast_comment` |
190
-
191
- ### Does the index stay up to date?
192
-
193
- Yes. The file watcher detects changes in real-time:
194
-
195
- - **File saved** → re-parsed, AST database updated, call edges re-resolved
196
- - **File deleted** → removed from database
197
- - **New file created** → parsed and added
198
-
199
- Only changed files are re-parsed (SHA-256 hash comparison), so incremental updates are near-instant.
164
+ The server sends usage instructions to the client when it connects, and every tool description says when
165
+ to prefer it over grep or reading files. Installed as a plugin, the bundled `explore` skill adds a
166
+ short workflow guide. You can also say it once: "use the clast tools to navigate the code".
200
167
 
201
168
  ---
202
169
 
203
- ## Testing with MCP Inspector
170
+ ## Command line
204
171
 
205
- To interactively test the tools without Claude Code:
172
+ The same queries are available from a shell, which is handy for scripts, hooks and debugging:
206
173
 
207
174
  ```bash
208
- npx @modelcontextprotocol/inspector node /path/to/clast/dist/server/index.js
175
+ clast-ai overview # repository map
176
+ clast-ai outline src/db # directory or file outline
177
+ clast-ai search "resolve import path" # ranked symbol search
178
+ clast-ai symbol Indexer.indexAll # source + callers + callees
179
+ clast-ai callers resolveImports --depth 2 --no-tests
180
+ clast-ai hierarchy Shape
181
+ clast-ai refs DEFAULT_CONFIG
182
+ clast-ai grep "TODO" --path src
183
+ clast-ai changes main # symbols changed vs main and their impact
184
+ clast-ai impact Shape.area # what must change with it, callers, tests to run
185
+ clast-ai unused src/billing # dead code under a directory (or the whole project)
186
+ clast-ai index --force # rebuild the index
187
+ clast-ai parse file.go # debug: what the extractor sees
209
188
  ```
210
189
 
211
- Opens a web UI where you can call each tool and see responses.
212
-
213
- ---
214
-
215
- ## How It Detects Your Project
216
-
217
- Clast resolves the project root in this order:
218
-
219
- 1. `CLAST_PROJECT_DIR` environment variable (explicit override)
220
- 2. `CLAUDE_PROJECT_DIR` (set automatically by Claude Code for plugins)
221
- 3. `process.cwd()` — the current working directory
222
-
223
- **In most cases you don't need to set anything.** Claude Code (both CLI and VS Code) sets the working directory to your project folder when spawning MCP servers.
224
-
225
- Override example (for indexing a different directory):
226
-
227
- ```json
228
- {
229
- "mcpServers": {
230
- "clast": {
231
- "command": "clast-ai",
232
- "env": {
233
- "CLAST_PROJECT_DIR": "/path/to/other/project"
234
- }
235
- }
236
- }
237
- }
238
- ```
239
-
240
- ---
241
-
242
- ## MCP Tools
243
-
244
- | Tool | Description | Read-only |
245
- |------|-------------|-----------|
246
- | `clast_search` | Search symbols by name, type, or pattern | Yes |
247
- | `clast_call_graph` | Trace callers/callees with configurable depth (1-5 levels) | Yes |
248
- | `clast_file_summary` | All symbols in a file, grouped by type (imports, classes, functions, etc.) | Yes |
249
- | `clast_get_context` | Full symbol context: definition, comments, callers, callees, parent class | Yes |
250
- | `clast_status` | Index statistics: files, nodes, edges, watcher state | Yes |
251
- | `clast_reindex` | Force re-index a specific file or the entire repo | No |
252
- | `clast_comment` | Get existing or generate new documentation comments via LLM | No |
253
-
254
- ### Example Usage in Claude Code
255
-
256
- - *"Search for the ParserManager class"* → Claude uses `clast_search`
257
- - *"What calls the `parseAndStore` function?"* → Claude uses `clast_call_graph`
258
- - *"Show me the structure of `src/db/queries.ts`"* → Claude uses `clast_file_summary`
259
- - *"I need to refactor `extractFromTree` — what's the blast radius?"* → Claude uses `clast_get_context` + `clast_call_graph`
190
+ Add `--root <dir>` to point at another project. `clast-ai` with no arguments runs the MCP server.
260
191
 
261
192
  ---
262
193
 
263
194
  ## Configuration
264
195
 
265
- Configuration is optional. Clast works with sensible defaults out of the box.
266
-
267
- To customize, create `clast.config.json` in your project root (or `.claude/clast.config.json`):
196
+ Optional. Create `clast.config.json` (or `.claude/clast.config.json`) in the project root:
268
197
 
269
198
  ```json
270
199
  {
271
- "languages": ["typescript", "javascript", "python", "java", "go", "rust"],
272
- "ignoredPaths": ["node_modules", ".git", "dist", "build"],
273
- "dbPath": ".clast/clast.db",
274
- "maxBodySize": 2000,
275
- "llm": {
276
- "endpoint": "http://localhost:11434/v1",
277
- "model": "qwen2.5-coder:7b",
278
- "apiKey": "",
279
- "alwaysGenerate": false,
280
- "maxConcurrent": 3
281
- },
282
- "watch": {
283
- "debounceMs": 300,
284
- "enabled": true
285
- }
200
+ "languages": ["typescript", "python"],
201
+ "ignoredPaths": ["node_modules", "dist", "*.min.js"],
202
+ "respectGitignore": true,
203
+ "maxFileSize": 1000000,
204
+ "workers": 0,
205
+ "watch": { "enabled": true, "debounceMs": 200 },
206
+ "llm": { "endpoint": "http://localhost:11434/v1", "model": "" }
286
207
  }
287
208
  ```
288
209
 
289
- | Setting | Default | Description |
290
- |---------|---------|-------------|
291
- | `languages` | All supported (see below) | Which languages to parse |
292
- | `ignoredPaths` | `node_modules`, `.git`, `dist`, etc. | Directories to skip |
293
- | `dbPath` | `.clast/clast.db` | SQLite database location (relative to project root) |
294
- | `maxBodySize` | `2000` | Max characters of function body stored per node |
295
- | `llm.endpoint` | `http://localhost:11434/v1` | OpenAI-compatible API endpoint |
296
- | `llm.model` | `""` (disabled) | LLM model name — empty string disables comment generation |
297
- | `llm.apiKey` | `""` | API key if the endpoint requires one |
298
- | `llm.alwaysGenerate` | `false` | Generate LLM comments even when original comments exist |
299
- | `llm.maxConcurrent` | `3` | Max parallel LLM requests during batch generation |
300
- | `watch.enabled` | `true` | Watch filesystem for changes |
301
- | `watch.debounceMs` | `300` | Debounce delay for file change events |
302
-
303
- ### Supported Languages
304
-
305
- | Language | Config name | File extensions |
306
- |----------|-------------|-----------------|
307
- | TypeScript | `typescript` | `.ts` |
308
- | TSX | `tsx` | `.tsx` |
210
+ | Setting | Default | Meaning |
211
+ |---------|---------|---------|
212
+ | `languages` | all | Language ids to index (see below) |
213
+ | `ignoredPaths` | common build / dependency dirs | gitignore-style patterns to skip, in addition to `.gitignore` |
214
+ | `respectGitignore` | `true` | Honour `.gitignore` files, including nested ones |
215
+ | `dbPath` | `.clast/index.db` | Index location (the directory gets its own `.gitignore`) |
216
+ | `maxFileSize` | `1000000` | Skip files larger than this many bytes |
217
+ | `workers` | `0` (auto) | Parser threads for bulk indexing; `1` disables workers |
218
+ | `watch.enabled` | `true` | Re-index files as they change |
219
+ | `watch.debounceMs` | `200` | Debounce for file change events |
220
+ | `llm.*` | disabled | OpenAI-compatible endpoint for `clast_comment` generation |
221
+
222
+ Environment: `CLAST_PROJECT_DIR` (project root), `CLAST_DB_PATH` (index location override),
223
+ `CLAST_GRAMMARS_DIR` (alternative grammar directory), `CLAST_DEBUG=1` (timings and spec warnings).
224
+
225
+ | Language | id | Extensions |
226
+ |----------|----|------------|
227
+ | TypeScript | `typescript` (also enables `tsx`) | `.ts`, `.mts`, `.cts`, `.tsx` |
309
228
  | JavaScript | `javascript` | `.js`, `.jsx`, `.mjs`, `.cjs` |
310
- | Python | `python` | `.py`, `.pyw` |
229
+ | Python | `python` | `.py`, `.pyi`, `.pyw` |
311
230
  | Java | `java` | `.java` |
312
231
  | C# | `csharp` | `.cs` |
313
232
  | Go | `go` | `.go` |
314
233
  | Rust | `rust` | `.rs` |
315
- | C | `c` | `.c`, `.h` |
316
- | C++ | `cpp` | `.cpp`, `.hpp`, `.cc`, `.hh`, `.cxx`, `.hxx` |
317
- | Ruby | `ruby` | `.rb` |
234
+ | C | `c` | `.c`, `.h` (headers that look like C++ are parsed as C++) |
235
+ | C++ | `cpp` | `.cpp`, `.hpp`, `.cc`, `.hh`, `.cxx`, `.hxx`, `.inl`, `.ipp` |
236
+ | Ruby | `ruby` | `.rb`, `.rake`, `.gemspec` |
318
237
  | PHP | `php` | `.php` |
238
+ | Swift | `swift` | `.swift` |
239
+ | Dart | `dart` | `.dart` |
319
240
 
320
- Use the **Config name** values in the `languages` array to enable/disable specific languages:
241
+ ### Optional: generated doc comments
321
242
 
322
- ```json
323
- {
324
- "languages": ["typescript", "javascript", "python", "go"]
325
- }
326
- ```
243
+ `clast_comment` can ask a local or hosted OpenAI-compatible model (Ollama, LM Studio, vLLM, OpenAI,
244
+ OpenRouter) to write a doc comment for an undocumented symbol. Set `llm.model` to enable it, for example
245
+ `{"llm": {"endpoint": "http://localhost:11434/v1", "model": "qwen2.5-coder:7b"}}` for Ollama. Generated
246
+ text is stored by file path and qualified name, so it survives re-indexing.
327
247
 
328
248
  ---
329
249
 
330
- ## LLM Comment Generation
331
-
332
- Clast can generate documentation comments for functions and classes that lack them. This is **optional and disabled by default** — you need to configure an OpenAI-compatible API endpoint.
333
-
334
- ### Comment Priority Logic
335
-
336
- 1. If a function has an existing comment/docstring → use it (no LLM call)
337
- 2. If no comment exists and LLM is configured → generate on demand via `clast_comment`
338
- 3. Set `llm.alwaysGenerate: true` to generate LLM descriptions even when original comments exist
339
-
340
- ### Setup with Ollama (Free, Local)
341
-
342
- [Ollama](https://ollama.com/) runs models locally on your machine. No API key needed.
343
-
344
- ```bash
345
- # Install Ollama (https://ollama.com/download)
346
- # Then pull a code model:
347
- ollama pull qwen2.5-coder:7b
348
- ```
349
-
350
- Add to your `clast.config.json`:
351
-
352
- ```json
353
- {
354
- "llm": {
355
- "endpoint": "http://localhost:11434/v1",
356
- "model": "qwen2.5-coder:7b"
357
- }
358
- }
359
- ```
360
-
361
- Ollama's default port (11434) and Clast's default endpoint already match — so you only need to set the model name.
362
-
363
- **Recommended Ollama models for code:**
364
-
365
- | Model | Size | VRAM | Quality |
366
- |-------|------|------|---------|
367
- | `qwen2.5-coder:7b` | 4.7 GB | ~6 GB | Good |
368
- | `qwen2.5-coder:14b` | 9 GB | ~12 GB | Better |
369
- | `qwen2.5-coder:32b` | 18 GB | ~24 GB | Best |
370
- | `codellama:7b` | 3.8 GB | ~6 GB | Good |
371
- | `deepseek-coder-v2:16b` | 8.9 GB | ~12 GB | Very good |
372
-
373
- ### Setup with LM Studio (Free, Local)
374
-
375
- [LM Studio](https://lmstudio.ai/) provides a GUI for running local models.
376
-
377
- 1. Download and install LM Studio
378
- 2. Download a code model (search for "Qwen 2.5 Coder" or "DeepSeek Coder")
379
- 3. Start the local server (LM Studio → Local Server → Start)
380
- 4. LM Studio serves on `http://localhost:1234/v1` by default
381
-
382
- ```json
383
- {
384
- "llm": {
385
- "endpoint": "http://localhost:1234/v1",
386
- "model": "qwen2.5-coder-7b-instruct"
387
- }
388
- }
389
- ```
390
-
391
- > **Note:** The model name in `clast.config.json` must match exactly what LM Studio shows in its server panel.
392
-
393
- ### Setup with OpenAI / OpenRouter / Any Cloud API
394
-
395
- Any service that exposes an OpenAI-compatible `/v1/chat/completions` endpoint works:
396
-
397
- ```json
398
- {
399
- "llm": {
400
- "endpoint": "https://api.openai.com/v1",
401
- "model": "gpt-4o-mini",
402
- "apiKey": "sk-..."
403
- }
404
- }
405
- ```
406
-
407
- **OpenRouter** (access many models via one API):
408
-
409
- ```json
410
- {
411
- "llm": {
412
- "endpoint": "https://openrouter.ai/api/v1",
413
- "model": "qwen/qwen-2.5-coder-32b-instruct",
414
- "apiKey": "sk-or-..."
415
- }
416
- }
417
- ```
418
-
419
- ### Setup with vLLM (Self-Hosted)
420
-
421
- If you run [vLLM](https://docs.vllm.ai/) on a server or cloud GPU:
422
-
423
- ```json
424
- {
425
- "llm": {
426
- "endpoint": "http://your-server:8000/v1",
427
- "model": "Qwen/Qwen2.5-Coder-7B-Instruct"
428
- }
429
- }
430
- ```
250
+ ## How it works
251
+
252
+ 1. **Scan** - walk the tree honouring `.gitignore` (root and nested) and `ignoredPaths`; compare size and
253
+ mtime with the index so an unchanged file costs one `stat`.
254
+ 2. **Parse** - changed files are read, hashed and parsed by a pool of worker threads (tree-sitter WASM).
255
+ A per-language spec turns the syntax tree into symbols (with qualified names, signatures, doc comments,
256
+ line ranges), calls (with receiver text and declared receiver type), imports, inheritance and
257
+ identifier occurrences.
258
+ 3. **Store** - the main thread writes to SQLite (`node:sqlite`, WAL). A cold index uses a bulk-load mode
259
+ that builds secondary indexes at the end. Symbol names, signatures and docs also go into an FTS5 index.
260
+ 4. **Link at query time** - call edges are stored as "symbol A calls name N on receiver R of type T" and
261
+ resolved when queried, so nothing goes stale when another file changes. Resolution order: declared
262
+ receiver type, then imports and same-file definitions, then `this`/`self` and inheritance, then
263
+ evidence scoring for untyped receivers. Argument counts separate overloads and rule out same-named
264
+ methods of another shape. A call through an interface or base type counts as a caller of the
265
+ implementations it can reach (Go's structural interfaces as "possible"); a call bound to a subclass
266
+ override does not count for the base method, and `super.m()` binds to the inherited method. Types
267
+ are told apart by more than their name where the language allows it: Go packages (`pkg.Type`
268
+ against the file's imports), imports and packages in TS / Python / Java / PHP, C# generic arity and
269
+ partial classes, Ruby lexical constant lookup, Rust module paths.
270
+ 5. **Stay fresh** - a watcher re-indexes saved files; in addition every tool re-checks the files it is
271
+ about to quote and re-parses them on the spot if they changed.
272
+
273
+ The index lives in `.clast/index.db` and is a pure cache: delete it at any time.
274
+
275
+ ### Performance
276
+
277
+ Measured with `npm run bench` (Node 24, Windows 11, Ryzen 9950X3D, up to 12 parser workers). Cold index is a first run on an
278
+ empty index; warm rescan is a restart with nothing changed.
279
+
280
+ <!-- BENCH:START -->
281
+ | repo | files | symbols | cold index | warm rescan | db size | search (name) | search (words) | symbol + callers/callees | callers depth 2 | references |
282
+ | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
283
+ | vscode | 15726 | 461365 | 26.1 s | 1895 ms | 357 MB | 168 ms | 42 ms | 28 ms | 38 ms | 11 ms |
284
+ | aspnetcore | 11470 | 145271 | 10.2 s | 1382 ms | 135 MB | 9.3 ms | 86 ms | 33 ms | 38 ms | 6.6 ms |
285
+ | kafka | 6481 | 126393 | 9.5 s | 863 ms | 144 MB | 26 ms | 33 ms | 25 ms | 962 ms | 5.6 ms |
286
+ | rails | 3580 | 61549 | 3.8 s | 352 ms | 52 MB | 8.7 ms | 15 ms | 22 ms | 35 ms | 3.0 ms |
287
+ | framework | 3180 | 44124 | 3.0 s | 337 ms | 38 MB | 1.4 ms | 46 ms | 23 ms | 23 ms | 2.2 ms |
288
+ | django | 2987 | 60131 | 3.1 s | 522 ms | 42 MB | 29 ms | 41 ms | 21 ms | 48 ms | 2.5 ms |
289
+ | fastapi | 1224 | 10049 | 0.8 s | 161 ms | 7 MB | 5.0 ms | 17 ms | 5.7 ms | 48 ms | 2.4 ms |
290
+ | hugo | 939 | 16896 | 1.1 s | 127 ms | 11 MB | 7.2 ms | 38 ms | 18 ms | 18 ms | 2.4 ms |
291
+ | redis | 919 | 35752 | 1.9 s | 103 ms | 23 MB | 5.7 ms | 16 ms | 7.0 ms | 26 ms | 2.2 ms |
292
+ | tokio | 816 | 12341 | 1.1 s | 85 ms | 11 MB | 3.9 ms | 17 ms | 2.0 ms | 4.2 ms | 1.4 ms |
293
+ | 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 |
294
+ | 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 |
295
+ | leveldb | 133 | 3456 | 0.6 s | 14 ms | 2 MB | 2.2 ms | 15 ms | 3.3 ms | 1.8 ms | 2.5 ms |
296
+ | Alamofire | 107 | 4085 | 1.5 s | 23 ms | 4 MB | 2.4 ms | 7.1 ms | 1.5 ms | 530 ms | 1.3 ms |
297
+ | ripgrep | 103 | 4496 | 0.6 s | 13 ms | 3 MB | 1.3 ms | 13 ms | 1.3 ms | 1.4 ms | 0.5 ms |
298
+ | 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 |
299
+ | 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 |
300
+
301
+ Node v24.7.0, win32 x64. Query times are medians of 5 in-process runs on the most-called function of each repo.
302
+ <!-- BENCH:END -->
303
+
304
+ Worst case measured: `callers IDisposable.dispose` on vscode (10.7k call sites, 4.7k definitions of
305
+ `dispose`) takes 5-6 s. Scoped with `within` it is fast.
431
306
 
432
307
  ---
433
308
 
434
- ## How It Works
309
+ ## Clast vs grep + read
310
+
311
+ Measured with real headless Claude Code sessions (`bench/ab.mjs`): every question runs in its own
312
+ fresh session, once with clast connected and once with only Read / Grep / Glob. Two large repositories:
313
+ vscode (TypeScript, 15.7k files) and OnAir (C++, 7.5k files, 1.6M lines). The index was fully built
314
+ before the first session. Answers are scored against facts verified in the source beforehand; an
315
+ implementation task is scored on its diff.
316
+
317
+ "Cost" is the API-price equivalent that Claude Code reports for the session. It weights fresh input and
318
+ output above cached prompt tokens, so it is the closest single number to "tokens that matter".
319
+
320
+ | Kind of work | Repo | Sessions per arm | Tool calls (clast / grep) | Wall time | Cost per question | Correct |
321
+ | --- | --- | ---: | ---: | ---: | ---: | ---: |
322
+ | 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 |
323
+ | Multi-hop questions | OnAir | 18 | 1.2 / 5.9 | 12.5 s / 25.8 s | $0.056 / $0.212 | 18 / 18 |
324
+ | 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 |
325
+ | Simple lookups | OnAir | 30 | 1.7 / 1.7 | 12.7 s / 14.3 s | $0.063 / $0.096 | 30 / 30 |
326
+ | Counting ("how many receiver classes under core/") | OnAir | 3 | 1.0 / 3.3 | 11 s / 34 s | about $0.02 / $0.29 | 3 / 2 |
327
+ | 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 |
328
+ | Small implementation tasks | OnAir | 6 | 11.5 / 12.8 | 39 s / 40 s | $0.58 / $0.56 | 6 / 6 |
329
+ | 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 |
330
+ | Cross-cutting refactors | OnAir | 4 | 9.8 / 7.8 | 26 s / 25 s | $0.25 / $0.24 | 4 / 4 |
331
+ | 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 |
332
+ | Before a change | OnAir | 9 | 1.7 / 3.6 | 14.3 s / 17.4 s | $0.126 / $0.173 | 9 / 9 |
333
+ | 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 |
334
+ | 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 |
335
+ | Questions that describe behaviour | vscode | 12 | 1.0 / 1.0 | 11.7 s / 11.8 s | $0.048 / $0.044 | 12 / 12 |
336
+
337
+ What this says:
338
+
339
+ - **Multi-hop questions are where clast pays off**: half the cost and 20% less time on vscode, a
340
+ quarter of the cost and half the time on OnAir. One call returns a call chain or a full inheritance
341
+ tree that grep needs three to twelve rounds for, and the agent reads 70-85% less tool output.
342
+ - **Simple lookups are a tie.** A current model with ripgrep answers them in one or two calls; clast
343
+ does not make that faster, it only returns a little less text.
344
+ - **Counting is faster and exact** with `clast_stats`. With grep the agent tallied matches by hand and
345
+ was off by one in one of three runs.
346
+ - **Finding dead code is the largest gap measured**: one `clast_unused` call and a confirming grep
347
+ against 11-21 greps, a sixth of the time and a thirteenth of the cost. Every function either arm
348
+ named was verified unused, and all ten the grep sessions found are in clast's list.
349
+ - **Describing behaviour instead of naming it** works on a repository the model has never seen
350
+ (OnAir): search over names, comments and what function bodies mention finds the place in one or two
351
+ calls where grep needs four rounds of guessing words. On vscode it is a tie at one call, because the
352
+ model already knows where things are in vscode; that row measures the model's memory, not the tools.
353
+ - **Questions before a change are cheaper, not always faster.** With `clast_impact` and `clast_unused`
354
+ the sessions cost 27-36% less because the agent reads less tool output. The clear win is "which
355
+ tests reach this function when none names it": 1 call and 11 s against 5.7 calls and 22 s on OnAir.
356
+ "Which files do I edit for a signature change" is one grep and stays a tie.
357
+ - **Implementation work showed no benefit**, neither small features nor cross-cutting refactors. Both
358
+ arms made the same edits in the same files in the same time (refactors were also checked for
359
+ completeness: nothing of the old name or call shape left). Finding every occurrence of one name is
360
+ a single grep, and before editing the agent reads the neighbouring code either way. Clast costs a
361
+ little more here because its tool definitions are in the prompt.
362
+ - **Small samples.** Two repositories, three runs per question, questions written by the tool's
363
+ author. The figures show the direction, not a precise ratio.
364
+
365
+ One result changed the server itself. Claude Code defers MCP tools behind its tool search by default,
366
+ and in that state the agent never called clast: 0 of 60 sessions, every question answered with Grep.
367
+ The navigation tools now ask the client to keep them loaded, after which the agent used clast in every
368
+ question session where it helped. The cost is about 5k tokens of tool definitions and instructions in
369
+ the cached prompt (measured with 13 tools: 23.7k against 19.3k prompt tokens for a session that calls
370
+ no tool; `clast_impact` and `clast_unused` add about 0.6k).
371
+
372
+ Reproduce with `node bench/ab.mjs <repo> bench/ab/<set>.json --runs 3`. The question sets are in
373
+ `bench/ab/`.
374
+
375
+ ### Answer quality
376
+
377
+ Reviewer agents asked deliberately hard questions on 17 real repositories and checked every answer
378
+ against the source. Over the last five rounds the share of first answers judged correct rose from
379
+ roughly 45-60% to 65-78%, with new questions each round.
380
+
381
+ Two further audits read the cited source lines for call edges that clast shows as certain (no `?`):
382
+
383
+ | Repo | Language | Wrong | Checked |
384
+ | --- | --- | ---: | ---: |
385
+ | kafka | Java | 0 | 57 |
386
+ | tokio | Rust | 0 | 17 |
387
+ | laravel | PHP | 0 | 19 |
388
+ | vscode | TypeScript | 1 | 45 |
389
+ | django | Python | 1 | 39 |
390
+ | aspnetcore | C# | 1 | 58 |
391
+ | hugo | Go | 1 | 19 |
392
+ | rails | Ruby | 4 | 18 |
393
+
394
+ The vscode and aspnetcore errors were fixed afterwards; the django, hugo and rails ones are open (see
395
+ Known limitations).
435
396
 
436
- 1. **Startup** — Clast scans your project and parses all supported files using tree-sitter (WASM)
437
- 2. **Storage** — Extracted symbols (functions, classes, methods, imports, etc.) are stored in a SQLite database at `.clast/clast.db`, along with SHA-256 file hashes
438
- 3. **Call graph** — Call relationships are extracted from function bodies and resolved across files using name matching
439
- 4. **Watching** — A file watcher detects changes and incrementally re-indexes only modified files (hash comparison skips unchanged files)
440
- 5. **Querying** — Claude Code queries the AST database through MCP tools instead of reading raw files, getting structured context with file paths and line numbers
441
-
442
- ### What Gets Indexed
443
-
444
- For each supported file, Clast extracts:
445
- - **Functions and methods** — name, signature, body, line numbers
446
- - **Classes, interfaces, structs, traits** — with their methods as children
447
- - **Imports and exports** — module relationships
448
- - **Enums and type aliases**
449
- - **Call edges** — which function calls which (resolved across files)
450
- - **Comments and docstrings** — linked to their parent symbol
451
-
452
- ### Database Location
397
+ ---
453
398
 
454
- Each project gets its own database at `{project_root}/.clast/clast.db`. Add `.clast/` to your `.gitignore` — the database is regenerated on startup from source files.
399
+ ## Changelog
400
+
401
+ ### 0.2.0
402
+
403
+ New tools (7 to 13):
404
+
405
+ - `clast_references`: every use of a symbol, not just calls.
406
+ - `clast_hierarchy`: supertypes, subtypes, implementers and overrides, including Go structural
407
+ interfaces and Ruby mixins.
408
+ - `clast_overview`: map of a repository or directory.
409
+ - `clast_grep`: text search over all text files with the enclosing symbol on every hit.
410
+ - `clast_changes`: symbols touched by a git diff, plus their callers.
411
+ - `clast_impact`: for one symbol, what has to change with it (declarations, overrides, implementations,
412
+ overloads), every direct caller by file and line, the reach through indirect callers, and the test
413
+ files to run, including tests that only reach it through its callers.
414
+ - `clast_unused`: dead code. A name counts as unused when it is never referenced outside its own
415
+ definitions, or only from other unused code (so a deleted feature's helpers are found with it).
416
+ Reported separately: code used only by tests, names that still appear in a string, config or
417
+ template, and (Java, C#, C, C++, Rust) methods whose every call binds to another definition. Left
418
+ out, with counts: entry points, decorated or attribute-marked symbols, subclasses of library types
419
+ and their methods, object-literal members, exported Go methods, C# extension classes.
420
+ - `clast_stats`: exact counts grouped by directory, file, kind or name: inventory, symbols by name
421
+ pattern, direct and indirect subtypes, call sites by name and receiver, text matches, external
422
+ dependencies, and pattern-based inventories of HTTP / RPC endpoints and database access.
423
+
424
+ Call graph:
425
+
426
+ - Calls are resolved at query time using declared receiver types, imports and aliases, field types
427
+ across files, return types of call chains, inheritance and `super`.
428
+ - Edges are shown as certain, uncertain, or `?` (matched by name only).
429
+ - Argument counts separate overloads. A bare name means all overloads; `path:line` selects one.
430
+ - A call bound to a subclass override no longer counts as a caller of the base method.
431
+ - Language rules: Go packages and structural interfaces, Rust `impl Trait for Arc<T>` and std types,
432
+ C# generic arity / partial / static classes, Ruby mixins and bare calls, Java static imports, Python
433
+ submodule calls and imported instances, TypeScript renamed re-exports, C macro bodies, C++ namespace
434
+ functions.
435
+
436
+ Search:
437
+
438
+ - Ranked by exact name, prefix, abbreviation, typo and plain words.
439
+ - Descriptive queries also match what function bodies mention (marked `[body]`).
440
+ - Overloads fold into one hit.
441
+
442
+ Performance and storage:
443
+
444
+ - `node:sqlite` on disk (WAL, FTS5) replaces in-memory sql.js.
445
+ - Parsing runs in worker threads, with an inline fallback if a worker fails.
446
+ - The server connects first and indexes afterwards; on a first build, tools wait for the index.
447
+ - A watcher re-indexes changed files, and every tool re-checks the files it quotes.
448
+
449
+ Other:
450
+
451
+ - Swift and Dart added (14 languages).
452
+ - All dependencies updated (MCP SDK 1.32, web-tree-sitter 0.27).
453
+ - 283 tests, plus `bench/bench.mjs`, `bench/resolution.mjs` and the A/B harness `bench/ab.mjs`.
454
+ - Installable as a Claude Code plugin from this repository (`/plugin marketplace add xls/clast`,
455
+ `/plugin install clast@clast`): the MCP server, a usage skill, and the slash commands
456
+ `/clast:deadcode`, `/clast:impact`, `/clast:audit` and `/clast:status`. The old VS Code extension
457
+ scaffold that wrote the server into `.claude/settings.local.json` is removed.
458
+ - The navigation tools ask Claude Code to keep them loaded instead of deferring them behind tool
459
+ search; deferred, the agent never called them.
460
+ - Ruby classes and their ancestors are compared by qualified name (`Rails::Generators::Base` is not
461
+ `ActiveRecord::Base`; `Thor::Group` is not a project class called `Group`).
462
+ - C++ functions declared through a head-building macro (`FN(jstring, nativeStart)(args)`) are indexed
463
+ under their real name.
464
+ - C / C++ function pointers whose calling convention is a macro (`err_t (MY_CALL* find)(args)`) are
465
+ indexed as a field called `find`, not as a method called `err_t`.
466
+
467
+ ### Upgrading from 0.1
468
+
469
+ - Node.js 22.22+ is required (was 18).
470
+ - The index moved from `.clast/clast.db` (sql.js) to `.clast/index.db` (`node:sqlite`). Nothing is
471
+ migrated: the index is a cache and is rebuilt on first start. The old file is ignored and can be
472
+ deleted.
473
+ - A later schema change deletes and rebuilds the index automatically (the schema is at version 26).
474
+ - Tool output is compact text instead of JSON.
475
+ - `clast_call_graph` defaults to depth 1 and gained `within` and `includeTests`.
476
+ - The binary entry point is `dist/cli.js`; `dist/server/index.js` still starts the server for old configs.
455
477
 
456
478
  ---
457
479
 
458
- ## VS Code Extension (Optional)
480
+ ## Known limitations
481
+
482
+ - **Untyped receivers**: `obj.save()` in dynamic code cannot be attributed. Such calls are listed as
483
+ "receiver type unknown", not guessed.
484
+ - **Same-named types**: classes with the same short name in different PHP namespaces or Java packages
485
+ can be confused when the calling file gives no hint.
486
+ - **Ruby**: the least exercised language. The rails audit found 4 wrong edges of 18 with one cause
487
+ (classes called `Base` in different namespaces treated as one family); classes are now compared by
488
+ qualified name, which fixes that case, but Ruby has not been re-audited since. Methods defined by
489
+ metaprogramming are invisible.
490
+ - **Overloads**: those that differ only in parameter types, not count, are one target.
491
+ - **Generics and inference**: no generic substitution; TypeScript return types are known only when
492
+ annotated.
493
+ - **Framework conventions**: Django managers, Laravel facades and container lookups, and services
494
+ injected by string are not modelled.
495
+ - **Python**: a local variable that holds a class is not followed.
496
+ - **Go**: a call through a chain of fields can be attributed to the wrong type; the module path is
497
+ inferred from imports, not from go.mod.
498
+ - **Swift**: the grammar fails on some files depending on parse order, which can drop a class while
499
+ keeping its members. Swift and Dart are new and less exercised.
500
+ - **Dead code is judged by name**: a member whose name is also used by other symbols (`get`, `run`,
501
+ `close`) is never listed, so the list is incomplete for common method names; calls built from
502
+ strings at run time are only caught when the prefix is a literal (`getattr(x, "as_" + vendor)`).
503
+ In a spot check of 8-12 random findings per repository (kafka, aspnetcore, vscode, django, tokio,
504
+ hugo, OnAir) against a word grep, the remaining wrong ones were convention-based discovery (Django
505
+ `AppConfig` classes inside Django itself). Treat the list as candidates and let the compiler or the
506
+ tests confirm a deletion.
507
+ - **Search is lexical**: a description that shares no word with the code finds the neighbourhood, not
508
+ always the function.
509
+ - **Not indexed**: Kotlin, Scala and other languages without a bundled grammar. `clast_grep` still
510
+ searches those files as text.
511
+ - **Size and cold start**: the index takes about 12-25 MB per 1,000 source files; a cold index of
512
+ vscode takes about 26 s.
513
+
514
+ The full list with examples is in [docs/v0.2-summary.md](docs/v0.2-summary.md).
459
515
 
460
- The `vscode/` directory contains a VS Code extension scaffold that:
461
- - Auto-detects workspace folders
462
- - Configures Claude Code's MCP settings automatically
463
- - Provides commands: `Clast: Reindex`, `Clast: Status`, `Clast: Configure`
516
+ ---
464
517
 
465
- This is optional — most users just need the MCP server config described in [Setup for VS Code](#setup-for-vs-code).
518
+ ## Development
466
519
 
467
- ## Requirements
520
+ ```bash
521
+ npm install
522
+ npm run build # tsc
523
+ npm test # node:test, 260+ tests incl. an end-to-end MCP session
524
+ npm run bench -- /path/to/dir-with-repos
525
+ ```
468
526
 
469
- - Node.js >= 18
470
- - For LLM comment generation: an OpenAI-compatible API endpoint (optional)
471
- - No native compilation required — all dependencies are pure JS/WASM
527
+ Adding a language: create `src/parser/langs/<name>.ts` exporting a `LangSpec` (see `typescript.ts` and
528
+ `python.ts` for the two reference implementations), register it in `src/parser/registry.ts`, and add a
529
+ test under `test/langs/`. `clast-ai ast <file>` dumps the syntax tree and `clast-ai parse <file>` shows what
530
+ your spec extracts. The grammar must be available in `@repomix/tree-sitter-wasms`.
472
531
 
473
532
  ## License
474
533