clast-ai 0.1.2 → 0.2.1

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