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
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ // Starts the clast MCP server for the Claude Code plugin. A plugin is installed by cloning the
3
+ // repository, which has no build output, so the server is taken from the first place that has one:
4
+ // 1. this checkout, when it has been built (a development clone),
5
+ // 2. a global `clast-ai` of the same minor version that is at least as new as the plugin,
6
+ // 3. `npx clast-ai@<plugin version>` (downloaded on first use, cached afterwards).
7
+ // Standard input and output are passed straight through: they carry the MCP protocol.
8
+ //
9
+ // After the server is up it also runs `clast-ai migrate --apply --auto` in the background. That
10
+ // removes, once per version, what an older install left behind (hand-made MCP registrations that
11
+ // would list every tool twice, the v0.1 index file), so installing or upgrading the plugin is
12
+ // all a user has to do.
13
+ import { spawn, spawnSync } from 'node:child_process';
14
+ import { existsSync, readFileSync } from 'node:fs';
15
+ import { dirname, join } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+
18
+ const root = join(dirname(fileURLToPath(import.meta.url)), '..');
19
+ const version = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).version;
20
+ const [major, minor, patch] = version.split('.').map(Number);
21
+ const windows = process.platform === 'win32';
22
+ const log = msg => process.stderr.write(`[clast plugin] ${msg}\n`);
23
+
24
+ function pick() {
25
+ const local = join(root, 'dist', 'cli.js');
26
+ if (existsSync(local) && existsSync(join(root, 'node_modules'))) {
27
+ return { command: process.execPath, args: [local], shell: false, what: 'local build' };
28
+ }
29
+ const probe = windows
30
+ ? spawnSync('clast-ai --version', { encoding: 'utf8', shell: true, windowsHide: true })
31
+ : spawnSync('clast-ai', ['--version'], { encoding: 'utf8', windowsHide: true });
32
+ const found = probe.status === 0 ? probe.stdout.trim() : '';
33
+ const [fMajor, fMinor, fPatch] = found.split('.').map(Number);
34
+ if (found && fMajor === major && fMinor === minor && fPatch >= patch) {
35
+ return { command: 'clast-ai', args: [], shell: windows, what: `global clast-ai ${found}` };
36
+ }
37
+ if (found) log(`global clast-ai is ${found}, the plugin is ${version}: using npx instead (npm install -g clast-ai updates it)`);
38
+ return { command: 'npx', args: ['-y', `clast-ai@${version}`], shell: windows, what: `npx clast-ai@${version}` };
39
+ }
40
+
41
+ /** Run the chosen clast-ai with extra arguments. Through a shell (.cmd shims on Windows) the command is one string of fixed words. */
42
+ function run(choice, extra, options) {
43
+ const args = [...choice.args, ...extra];
44
+ return choice.shell
45
+ ? spawn([choice.command, ...args].join(' '), { ...options, shell: true, windowsHide: true })
46
+ : spawn(choice.command, args, { ...options, windowsHide: true });
47
+ }
48
+
49
+ if (Number(process.versions.node.split('.')[0]) < 22) {
50
+ log(`Node.js 22.22 or newer is required (this is ${process.versions.node}).`);
51
+ process.exit(1);
52
+ }
53
+ const choice = pick();
54
+ log(`starting ${choice.what}`);
55
+ const child = run(choice, [], { stdio: 'inherit' });
56
+ child.on('error', err => {
57
+ log(`could not start the server: ${err.message}. Install it with: npm install -g clast-ai`);
58
+ process.exit(1);
59
+ });
60
+ child.on('exit', (code, signal) => process.exit(code ?? (signal ? 1 : 0)));
61
+ for (const sig of ['SIGINT', 'SIGTERM']) process.on(sig, () => child.kill(sig));
62
+
63
+ // Clean up after older installs, without holding up or disturbing the server (no shared stdio).
64
+ if (process.env.CLAST_NO_MIGRATE !== '1') {
65
+ try {
66
+ const cleanup = run(choice, ['migrate', '--apply', '--auto'], { stdio: 'ignore', detached: !windows });
67
+ cleanup.on('error', () => {});
68
+ cleanup.unref();
69
+ } catch {
70
+ // Never let the cleanup stop the server.
71
+ }
72
+ }
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: audit
3
+ description: Produce a factual inventory of a code base for due diligence or onboarding - size, languages, structure, endpoints, database access, dependencies, largest hot spots and dead code.
4
+ argument-hint: "[path]"
5
+ disable-model-invocation: true
6
+ allowed-tools: mcp__plugin_clast_clast__clast_stats, mcp__plugin_clast_clast__clast_overview, mcp__plugin_clast_clast__clast_unused, mcp__plugin_clast_clast__clast_file_summary, mcp__plugin_clast_clast__clast_grep, Grep, Read, Glob
7
+ ---
8
+
9
+ Produce an inventory of this code base. Scope: `$ARGUMENTS` (the whole project when empty; pass it as
10
+ `path` to every call below).
11
+
12
+ Gather the facts with these calls. They are independent, so issue them together:
13
+
14
+ - `clast_stats` what=inventory: files, lines, languages, production vs tests vs vendored, symbols by
15
+ kind, longest functions.
16
+ - `clast_overview`: the main directories and what lives in them.
17
+ - `clast_stats` what=endpoints: HTTP / RPC routes by framework and by directory, and outbound client
18
+ calls.
19
+ - `clast_stats` what=database: SQL, schema, ORM models, migrations, query call sites by receiver.
20
+ - `clast_stats` what=imports: external dependencies by how many files use them.
21
+ - `clast_unused` with the default limit: only the totals and the largest files matter here.
22
+
23
+ Then write the report:
24
+
25
+ 1. **Size and shape**: lines and files per language, share of tests, vendored or generated code.
26
+ 2. **Structure**: the top-level areas with one line each on what they contain.
27
+ 3. **Interfaces**: number of endpoints by framework and area; outbound calls.
28
+ 4. **Data**: tables or models, migrations, where queries are issued from, the most-used models.
29
+ 5. **Dependencies**: the most-used external packages; anything unusual (several libraries for the same
30
+ job, very old or abandoned-looking packages if the manifest shows versions).
31
+ 6. **Maintainability signals**: the longest functions and largest files, the share of code used only
32
+ by tests, the amount of unused code.
33
+ 7. **Caveats**: endpoint and database figures come from pattern matching; dead code is judged by name.
34
+ Spot-check one figure from each of sections 3 and 4 with a Grep and state whether it matched.
35
+
36
+ Use exact numbers from the tools; do not estimate or tally by hand. Keep it to what was measured: no
37
+ opinions on quality that the numbers do not support.
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: deadcode
3
+ description: Find dead code (unused functions, methods and types) in the project or under a path, verify the candidates, and offer to remove them.
4
+ argument-hint: "[path]"
5
+ disable-model-invocation: true
6
+ allowed-tools: mcp__plugin_clast_clast__clast_unused, mcp__plugin_clast_clast__clast_call_graph, mcp__plugin_clast_clast__clast_references, mcp__plugin_clast_clast__clast_grep, Grep, Read
7
+ ---
8
+
9
+ Find dead code. Scope: `$ARGUMENTS` (the whole project when empty).
10
+
11
+ 1. Call `clast_unused` with `path` set to the scope (omit it for the whole project). On a large
12
+ project start with the default limit; raise it only if the user wants the full list.
13
+ 2. The result has groups. Treat them differently:
14
+ - **unused**: the name is never referenced, or only by other unused code. Strong candidates.
15
+ - **probably unused**: the name is called, but every call binds to another definition. Check each
16
+ with `clast_call_graph` before reporting it.
17
+ - **used only by tests**: dead in production; removing it means removing the tests too. Report, do
18
+ not propose deleting without asking.
19
+ - **name appears in a string, config or template**: may be reached by reflection, routing or a
20
+ template. Open the cited location and say what it is.
21
+ - symbols marked `exported`: unused inside this project. In an application that is dead code; in a
22
+ library it may be public API. Say which case applies here (look for a published package, a
23
+ public header directory, or an API surface file).
24
+ 3. Verify before you present: for the candidates you intend to recommend (at most about 15), run one
25
+ Grep for the bare name across the repository and drop any that turn up in build files, scripts,
26
+ generated code or documentation that shows it is an entry point.
27
+ 4. Report as a short table grouped by file: symbol, lines, size, and why it is considered unused. Give
28
+ the total number of removable lines. State the limits in one sentence: the check is by name, so
29
+ members with common names (`get`, `run`) are never listed, and code reached only through reflection
30
+ can be listed wrongly.
31
+ 5. Do not delete anything yet. Ask which of the candidates to remove. When the user confirms, delete
32
+ them (and helpers that only they used), then build or run the tests the project has to confirm
33
+ nothing broke, and report the result.
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: explore
3
+ description: >
4
+ How to get the most out of the clast code index: which tool answers which question, and the habits
5
+ that save round trips. Use when navigating, reviewing or about to change code in a repository that
6
+ has the clast MCP tools, especially for multi-step questions (call chains, subclasses, impact, counts,
7
+ dead code) or code described by behaviour instead of by name.
8
+ ---
9
+
10
+ # Using the clast code index well
11
+
12
+ Clast answers structural questions in one call. It does not replace Grep for "where is this exact
13
+ name" or Read for code you are about to edit: measured against plain grep, clast wins on questions that
14
+ take several hops, on counting, on dead code and on finding code by what it does; simple lookups are a
15
+ tie. Use it where it wins.
16
+
17
+ ## Pick the tool by question
18
+
19
+ | Question | Tool |
20
+ |----------|------|
21
+ | What is in this repo / directory / file? | `clast_overview`, then `clast_file_summary` |
22
+ | Where is the code that does X (no name known)? | `clast_search` with a short description of the behaviour |
23
+ | Show me X, I am about to edit X | `clast_get_context` (that symbol's source, callers, callees) |
24
+ | Who calls X, through how many levels? | `clast_call_graph` (`depth` 2-3, `within` to scope) |
25
+ | What implements / extends / overrides X? | `clast_hierarchy` |
26
+ | What else must change with X, and which tests do I run? | `clast_impact` |
27
+ | How many X? How big? Endpoints, database access, dependencies? | `clast_stats` (never tally search results by hand) |
28
+ | What is unused? What did a refactor leave behind? | `clast_unused` |
29
+ | What did my diff touch? | `clast_changes` |
30
+ | Every use of an identifier, or a string / config key | `clast_references`, `clast_grep` |
31
+
32
+ ## Habits that save round trips
33
+
34
+ - One call, not a loop: `clast_search` and `clast_get_context` take arrays of names; `clast_call_graph`
35
+ and `clast_hierarchy` take a `depth`.
36
+ - Address symbols precisely: `Class.method`, `path/file.ext:name`, `path/file.ext:123`. A bare name
37
+ with several definitions returns the candidates; pick one, do not guess.
38
+ - Read the symbol, not the file: `clast_get_context` returns exactly the definition's lines.
39
+ - An edge marked `?` was matched by name only (the receiver's type is unknown). Verify it with a quick
40
+ Grep or Read before you rely on it; unmarked edges are resolved through types and imports.
41
+ - Callers of a method include calls through its interface or base class, not calls bound to an override
42
+ in a subclass. For every implementation's callers ask for the interface or base method.
43
+ - Before a signature change: `clast_impact` lists the overrides and declarations that change with it,
44
+ the callers by file and line, and the tests that reach it even when none names it.
45
+ - `clast_unused` lists candidates judged by name. Members with common names (`get`, `run`) are never
46
+ listed, and reflection or framework wiring can keep a listed symbol alive: confirm with the compiler
47
+ or the tests before deleting.
48
+ - Narrow with `path` (and `within` for call graphs) on large repositories; results and run time shrink.
49
+ - The index follows file changes, including your own edits. There is no need to reindex.
50
+ - If a tool says a name is not defined in the project, it is external or built at run time:
51
+ `clast_references` and `clast_grep` still find its uses.
52
+
53
+ Slash commands in this plugin: `/clast:deadcode [path]`, `/clast:impact <symbol>`, `/clast:audit [path]`,
54
+ `/clast:status`.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: impact
3
+ description: Show what a change to one function, method or type affects - what must change with it, every caller, and the tests to run.
4
+ argument-hint: "<symbol>"
5
+ disable-model-invocation: true
6
+ allowed-tools: mcp__plugin_clast_clast__clast_impact, mcp__plugin_clast_clast__clast_search, mcp__plugin_clast_clast__clast_get_context, mcp__plugin_clast_clast__clast_call_graph, mcp__plugin_clast_clast__clast_hierarchy, Grep, Read
7
+ ---
8
+
9
+ Assess the impact of changing `$ARGUMENTS`.
10
+
11
+ 1. If no symbol was given, ask which one. If the name is ambiguous, `clast_impact` lists the
12
+ candidates: pick the one the user means (ask when it is not obvious) and address it as
13
+ `Container.name` or `path/file.ext:name`.
14
+ 2. Call `clast_impact` for the symbol (default depth 3).
15
+ 3. Report, in this order and without padding:
16
+ - **Changes together**: declarations, overrides, implementations and overloads of the same member.
17
+ - **Direct callers**: by file with line numbers. Callers marked `?` are name-only matches: check
18
+ each with a quick Read of the cited line and say whether it is a real caller.
19
+ - **Reach**: how many functions in how many files depend on it indirectly, by area.
20
+ - **Tests to run**: the test files, split into those that call it directly and those that reach it
21
+ through callers. Give the project's command to run exactly those tests if you can tell what it is.
22
+ - If no test reaches it, say so plainly: that is the main risk of the change.
23
+ 4. End with a one-line verdict on the size of the change (local, moderate, wide) and what to edit
24
+ first. Do not start editing unless the user asks.
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: status
3
+ description: Show the state of the clast index for this project - files, symbols, languages, whether it is up to date.
4
+ disable-model-invocation: true
5
+ allowed-tools: mcp__plugin_clast_clast__clast_status, mcp__plugin_clast_clast__clast_reindex
6
+ ---
7
+
8
+ Call `clast_status` and report in a few lines: files and symbols indexed, languages, whether the index
9
+ is up to date or still building, and where the database is. If the index is still building, say how
10
+ far it is. If the status shows a problem (no files, a failed run), say so and offer to run
11
+ `clast_reindex`; do not reindex unasked.
package/src/cli.ts ADDED
@@ -0,0 +1,265 @@
1
+ #!/usr/bin/env node
2
+ // More I/O threads: the startup scan stats every source file, which is the
3
+ // dominant warm-start cost. Must be set before the first async fs call.
4
+ process.env.UV_THREADPOOL_SIZE ??= '16';
5
+
6
+ import { readFileSync } from 'node:fs';
7
+ import { resolve } from 'node:path';
8
+ import { Clast } from './engine.js';
9
+ import { SourceParser } from './parser/index.js';
10
+ import { ToolContext } from './tools/context.js';
11
+ import { callGraph, getContext, hierarchy, search } from './tools/search.js';
12
+ import { changes, fileSummary, grep, overview, references, reindex, status } from './tools/explore.js';
13
+ import { impact, unused } from './tools/impact.js';
14
+ import { stats, type StatsArgs } from './tools/stats.js';
15
+ import { VERSION } from './version.js';
16
+
17
+ const HELP = `clast-ai ${VERSION} - code index for LLM agents
18
+
19
+ usage: clast-ai [command] [args] [--root <dir>]
20
+
21
+ (no command), serve run the MCP server on stdio (what Claude Code launches)
22
+ index [--force] build / update the index
23
+ status index statistics
24
+ overview [path] repository map
25
+ outline <path> file or directory outline
26
+ search <query...> ranked symbol search [--kind k] [--path p] [--lang l] [--limit n]
27
+ symbol <name...> source + callers + callees of a symbol [--path p] [--max-lines n] [--no-body]
28
+ callers <name> who calls it [--depth n] [--limit n] [--within path] [--no-tests|--tests] [--uncertain]
29
+ callees <name> what it calls [--depth n] [--limit n]
30
+ hierarchy <name> supertypes / subtypes / overrides [--depth n] [--limit n] [--within path]
31
+ refs <name> all references to an identifier [--path p] [--limit n]
32
+ grep <pattern> text search with enclosing symbols [--regex] [--path p] [--limit n]
33
+ changes [base] symbols touched by the git diff and their impact
34
+ impact <name> what must change with it, who reaches it, which tests to run [--depth n] [--limit n]
35
+ unused [path] dead code [--kind k] [--lang l] [--limit n] [--no-exported]
36
+ stats [what] [name] exact counts: inventory | symbols | subtypes | calls | text | imports | endpoints | database
37
+ [--kind k] [--on receiver] [--path p] [--lang l] [--group-by g] [--depth n] [--limit n] [--tests]
38
+ migrate [--apply] find (and with --apply remove) what older installs left behind: duplicate
39
+ MCP registrations next to the plugin, the v0.1 index file
40
+ parse <file> [--json] debug: show what the extractor produces for a file
41
+ ast <file> debug: dump the tree-sitter syntax tree
42
+
43
+ Query commands refresh the index incrementally first (skip with --no-scan).`;
44
+
45
+ const CLI_NAMES: Record<string, string> = {
46
+ clast_search: 'clast-ai search',
47
+ clast_get_context: 'clast-ai symbol',
48
+ clast_call_graph: 'clast-ai callers / callees',
49
+ clast_references: 'clast-ai refs',
50
+ clast_hierarchy: 'clast-ai hierarchy',
51
+ clast_file_summary: 'clast-ai outline',
52
+ clast_overview: 'clast-ai overview',
53
+ clast_grep: 'clast-ai grep',
54
+ clast_changes: 'clast-ai changes',
55
+ clast_stats: 'clast-ai stats',
56
+ clast_impact: 'clast-ai impact',
57
+ clast_unused: 'clast-ai unused',
58
+ clast_reindex: 'clast-ai index',
59
+ clast_status: 'clast-ai status',
60
+ };
61
+
62
+ interface Parsed {
63
+ positional: string[];
64
+ flags: Map<string, string | true>;
65
+ }
66
+
67
+ const BOOLEAN_FLAGS = new Set([
68
+ 'force', 'json', 'regex', 'no-body', 'no-scan', 'no-tests', 'tests', 'uncertain', 'help', 'version', 'all', 'no-exported', 'apply', 'auto',
69
+ ]);
70
+
71
+ function parseArgs(argv: string[]): Parsed {
72
+ const positional: string[] = [];
73
+ const flags = new Map<string, string | true>();
74
+ for (let i = 0; i < argv.length; i++) {
75
+ const a = argv[i]!;
76
+ if (a.startsWith('--')) {
77
+ const eq = a.indexOf('=');
78
+ if (eq !== -1) flags.set(a.slice(2, eq), a.slice(eq + 1));
79
+ else if (BOOLEAN_FLAGS.has(a.slice(2)) || i + 1 >= argv.length) flags.set(a.slice(2), true);
80
+ else flags.set(a.slice(2), argv[++i]!);
81
+ } else if (a === '-h') flags.set('help', true);
82
+ else positional.push(a);
83
+ }
84
+ return { positional, flags };
85
+ }
86
+
87
+ async function main(): Promise<void> {
88
+ const argv = process.argv.slice(2);
89
+ const cmd = argv[0] && !argv[0].startsWith('-') ? argv[0] : undefined;
90
+ const { positional, flags } = parseArgs(cmd ? argv.slice(1) : argv);
91
+ const str = (k: string): string | undefined => {
92
+ const v = flags.get(k);
93
+ return typeof v === 'string' ? v : undefined;
94
+ };
95
+ const num = (k: string): number | undefined => {
96
+ const v = str(k);
97
+ return v !== undefined && /^\d+$/.test(v) ? Number(v) : undefined;
98
+ };
99
+
100
+ if (flags.has('version')) return void console.log(VERSION);
101
+ if (flags.has('help') || cmd === 'help') return void console.log(HELP);
102
+
103
+ if (cmd === undefined || cmd === 'serve') {
104
+ if (str('root')) process.env.CLAST_PROJECT_DIR = resolve(str('root')!);
105
+ const { startServer } = await import('./server/index.js');
106
+ await startServer();
107
+ return;
108
+ }
109
+
110
+ if (cmd === 'migrate') {
111
+ const { migrate } = await import('./migrate.js');
112
+ const out = await migrate({ apply: flags.has('apply'), auto: flags.has('auto'), force: flags.has('force'), cwd: str('root') });
113
+ if (out) console.log(out);
114
+ return;
115
+ }
116
+
117
+ if (cmd === 'parse' || cmd === 'ast') {
118
+ const file = positional[0];
119
+ if (!file) throw new Error(`usage: clast-ai ${cmd} <file>`);
120
+ const source = readFileSync(file, 'utf-8');
121
+ const parser = new SourceParser();
122
+ if (cmd === 'ast') return void console.log(await parser.dumpTree(file, source));
123
+ const ex = await parser.extract(file, source);
124
+ if (!ex) throw new Error('unsupported file type');
125
+ if (flags.has('json')) return void console.log(JSON.stringify(ex, null, 1));
126
+ for (const s of ex.symbols) {
127
+ console.log(
128
+ `${String(s.startLine).padStart(5)}-${String(s.endLine).padEnd(5)} ${s.kind.padEnd(11)} ${s.qname}` +
129
+ `${s.exported ? ' [exported]' : ''}\n${' '.repeat(12)}sig: ${s.signature}` +
130
+ (s.doc ? `\n${' '.repeat(12)}doc: ${s.doc.split('\n')[0]}` : '')
131
+ );
132
+ }
133
+ console.log(`\ncalls (${ex.calls.length}):`);
134
+ for (const c of ex.calls) {
135
+ const caller = c.caller >= 0 ? ex.symbols[c.caller]!.qname : '<file>';
136
+ console.log(` ${caller} -> ${c.receiver ? c.receiver + '.' : ''}${c.name} @${c.line}${c.count > 1 ? ' x' + c.count : ''}`);
137
+ }
138
+ console.log(`\nimports (${ex.imports.length}):`);
139
+ for (const i of ex.imports) console.log(` ${i.source} {${i.names.join(', ')}} @${i.line}`);
140
+ console.log(`\nheritage (${ex.heritage.length}):`);
141
+ for (const h of ex.heritage) console.log(` ${ex.symbols[h.symbol]!.qname} ${h.kind} ${h.name}`);
142
+ console.log(`\nidentifiers: ${ex.occ.length} distinct, parse errors: ${ex.hadError}`);
143
+ return;
144
+ }
145
+
146
+ const root = resolve(str('root') ?? process.env.CLAST_PROJECT_DIR ?? process.cwd());
147
+ const clast = new Clast(root);
148
+ const ctx = new ToolContext(clast);
149
+ try {
150
+ if (cmd === 'index') {
151
+ const target = positional[0];
152
+ if (target) {
153
+ // Convenience: `clast-ai index <dir>` indexes another directory.
154
+ clast.close();
155
+ const other = new Clast(resolve(target));
156
+ const octx = new ToolContext(other);
157
+ console.log(await reindex(octx, { force: flags.has('force') }));
158
+ other.close();
159
+ return;
160
+ }
161
+ console.log(await reindex(ctx, { force: flags.has('force') }));
162
+ return;
163
+ }
164
+
165
+ if (!flags.has('no-scan')) await clast.indexer.indexAll();
166
+ const includeTests = flags.has('no-tests') ? false : flags.has('tests') ? true : undefined;
167
+ let out: string;
168
+ switch (cmd) {
169
+ case 'status':
170
+ out = await status(ctx);
171
+ break;
172
+ case 'overview':
173
+ case 'map':
174
+ out = await overview(ctx, { path: positional[0] });
175
+ break;
176
+ case 'outline':
177
+ case 'file':
178
+ out = await fileSummary(ctx, { path: positional[0] ?? '.', members: flags.has('all') ? 'all' : 'auto' });
179
+ break;
180
+ case 'search':
181
+ out = await search(ctx, {
182
+ query: positional.join(' '), kind: str('kind'), path: str('path'), lang: str('lang'), limit: num('limit'),
183
+ });
184
+ break;
185
+ case 'symbol':
186
+ case 'context':
187
+ out = await getContext(ctx, {
188
+ name: positional, path: str('path'), maxLines: num('max-lines'), includeBody: !flags.has('no-body'),
189
+ });
190
+ break;
191
+ case 'callers':
192
+ case 'callees':
193
+ case 'graph':
194
+ out = await callGraph(ctx, {
195
+ name: positional[0] ?? '', path: str('path'),
196
+ direction: cmd === 'graph' ? 'both' : cmd,
197
+ depth: num('depth'), limit: num('limit'), includeTests, within: str('within'), uncertain: flags.has('uncertain'),
198
+ });
199
+ break;
200
+ case 'hierarchy':
201
+ case 'types':
202
+ out = await hierarchy(ctx, {
203
+ name: positional[0] ?? '', path: str('path'), depth: num('depth'), limit: num('limit'), within: str('within'),
204
+ direction: str('direction') as 'supertypes' | 'subtypes' | 'both' | undefined,
205
+ });
206
+ break;
207
+ case 'refs':
208
+ case 'references':
209
+ out = await references(ctx, { name: positional[0] ?? '', path: str('path'), limit: num('limit'), includeTests });
210
+ break;
211
+ case 'grep':
212
+ out = await grep(ctx, {
213
+ pattern: positional.join(' '), regex: flags.has('regex'), path: str('path'), lang: str('lang'),
214
+ limit: num('limit'), includeTests,
215
+ });
216
+ break;
217
+ case 'changes':
218
+ out = await changes(ctx, { base: positional[0] });
219
+ break;
220
+ case 'impact':
221
+ out = await impact(ctx, { name: positional.join(' '), path: str('path'), depth: num('depth'), limit: num('limit') });
222
+ break;
223
+ case 'unused':
224
+ case 'dead':
225
+ out = await unused(ctx, {
226
+ path: positional[0] ?? str('path'), lang: str('lang'), kind: str('kind'), limit: num('limit'),
227
+ exported: flags.has('no-exported') ? false : undefined,
228
+ });
229
+ break;
230
+ case 'stats':
231
+ case 'count': {
232
+ const WHATS = ['inventory', 'symbols', 'subtypes', 'calls', 'text', 'imports', 'endpoints', 'database'];
233
+ const hasWhat = WHATS.includes(positional[0] ?? '');
234
+ out = await stats(ctx, {
235
+ what: hasWhat ? (positional[0] as StatsArgs['what']) : undefined,
236
+ name: (hasWhat ? positional.slice(1) : positional).join(' ') || undefined,
237
+ kind: str('kind'), on: str('on'), path: str('path'), lang: str('lang'),
238
+ groupBy: str('group-by') as StatsArgs['groupBy'], depth: num('depth'), limit: num('limit'),
239
+ includeTests: includeTests === true,
240
+ });
241
+ break;
242
+ }
243
+ default:
244
+ console.error(`unknown command: ${cmd}\n\n${HELP}`);
245
+ process.exitCode = 1;
246
+ return;
247
+ }
248
+ // Tool hints name MCP tools; say the same thing in CLI terms here.
249
+ console.log(
250
+ out
251
+ .replace(/clast_[a-z_]+/g, t => CLI_NAMES[t] ?? t)
252
+ .replace(/\buncertain=true\b/g, '--uncertain')
253
+ .replace(/\bincludeTests=true\b/g, '--tests')
254
+ .replace(/\braise maxLines\b/g, 'raise --max-lines')
255
+ );
256
+ if (out.startsWith('error:')) process.exitCode = 1;
257
+ } finally {
258
+ try { clast.close(); } catch { /* closed above */ }
259
+ }
260
+ }
261
+
262
+ main().catch(err => {
263
+ console.error(err instanceof Error ? err.message : err);
264
+ process.exit(1);
265
+ });
@@ -1,7 +1,18 @@
1
1
  export interface ClastConfig {
2
+ /** Language ids to index. Empty = all supported languages. */
2
3
  languages: string[];
4
+ /** Directory / file names (or gitignore-style patterns) to skip. */
3
5
  ignoredPaths: string[];
6
+ /** Honour .gitignore files (root and nested). */
7
+ respectGitignore: boolean;
8
+ /** Index location, relative to the project root unless absolute. */
4
9
  dbPath: string;
10
+ /** Files larger than this many bytes are skipped (generated / vendored blobs). */
11
+ maxFileSize: number;
12
+ /** Safety cap on the number of source files indexed (e.g. when started in a home directory). */
13
+ maxFiles: number;
14
+ /** Parser worker threads for bulk indexing. 0 = auto, 1 = no workers. */
15
+ workers: number;
5
16
  llm: {
6
17
  endpoint: string;
7
18
  model: string;
@@ -13,39 +24,47 @@ export interface ClastConfig {
13
24
  debounceMs: number;
14
25
  enabled: boolean;
15
26
  };
16
- maxBodySize: number;
17
27
  }
18
28
 
29
+ export const DEFAULT_IGNORED = [
30
+ 'node_modules',
31
+ '.git',
32
+ '.hg',
33
+ '.svn',
34
+ 'dist',
35
+ 'build',
36
+ 'out',
37
+ '__pycache__',
38
+ '.venv',
39
+ 'venv',
40
+ '.clast',
41
+ 'vendor',
42
+ 'target',
43
+ '.next',
44
+ '.nuxt',
45
+ 'coverage',
46
+ '.idea',
47
+ '.vscode-test',
48
+ '.gradle',
49
+ '.tox',
50
+ '.mypy_cache',
51
+ '.pytest_cache',
52
+ 'bower_components',
53
+ 'Pods',
54
+ '*.min.js',
55
+ '*.min.css',
56
+ '*.bundle.js',
57
+ '*.d.ts.map',
58
+ ];
59
+
19
60
  export const DEFAULT_CONFIG: ClastConfig = {
20
- languages: [
21
- 'typescript',
22
- 'javascript',
23
- 'python',
24
- 'java',
25
- 'csharp',
26
- 'go',
27
- 'rust',
28
- 'c',
29
- 'cpp',
30
- 'ruby',
31
- 'php',
32
- ],
33
- ignoredPaths: [
34
- 'node_modules',
35
- '.git',
36
- 'dist',
37
- 'build',
38
- 'out',
39
- '__pycache__',
40
- '.venv',
41
- 'venv',
42
- '.clast',
43
- 'vendor',
44
- 'target',
45
- '.next',
46
- 'coverage',
47
- ],
48
- dbPath: '.clast/clast.db',
61
+ languages: [],
62
+ ignoredPaths: DEFAULT_IGNORED,
63
+ respectGitignore: true,
64
+ dbPath: '.clast/index.db',
65
+ maxFileSize: 1_000_000,
66
+ maxFiles: 150_000,
67
+ workers: 0,
49
68
  llm: {
50
69
  endpoint: 'http://localhost:11434/v1',
51
70
  model: '',
@@ -54,8 +73,7 @@ export const DEFAULT_CONFIG: ClastConfig = {
54
73
  alwaysGenerate: false,
55
74
  },
56
75
  watch: {
57
- debounceMs: 300,
76
+ debounceMs: 200,
58
77
  enabled: true,
59
78
  },
60
- maxBodySize: 2000,
61
79
  };