clast-ai 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/.claude-plugin/plugin.json +11 -9
  3. package/README.md +447 -401
  4. package/bench/ab/onair-concept.json +22 -0
  5. package/bench/ab/onair-deadcode.json +7 -0
  6. package/bench/ab/onair-hard.json +32 -0
  7. package/bench/ab/onair-impact.json +17 -0
  8. package/bench/ab/onair-refactors.json +18 -0
  9. package/bench/ab/onair-survey.json +17 -0
  10. package/bench/ab/onair-tasks.json +20 -0
  11. package/bench/ab/onair.json +52 -0
  12. package/bench/ab/vscode-concept.json +22 -0
  13. package/bench/ab/vscode-hard.json +32 -0
  14. package/bench/ab/vscode-impact.json +17 -0
  15. package/bench/ab/vscode-refactors.json +18 -0
  16. package/bench/ab/vscode-tasks.json +20 -0
  17. package/bench/ab/vscode.json +52 -0
  18. package/bench/ab.mjs +213 -0
  19. package/bench/bench.mjs +98 -0
  20. package/bench/resolution.mjs +75 -0
  21. package/dist/cli.d.ts +2 -0
  22. package/dist/cli.js +257 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/config/defaults.d.ts +12 -2
  25. package/dist/config/defaults.js +38 -31
  26. package/dist/config/defaults.js.map +1 -1
  27. package/dist/config/index.d.ts +1 -1
  28. package/dist/config/index.js +26 -14
  29. package/dist/config/index.js.map +1 -1
  30. package/dist/db/index.d.ts +73 -13
  31. package/dist/db/index.js +481 -39
  32. package/dist/db/index.js.map +1 -1
  33. package/dist/engine.d.ts +15 -0
  34. package/dist/engine.js +28 -0
  35. package/dist/engine.js.map +1 -0
  36. package/dist/indexer/imports.d.ts +6 -0
  37. package/dist/indexer/imports.js +254 -0
  38. package/dist/indexer/imports.js.map +1 -0
  39. package/dist/indexer/index.d.ts +63 -0
  40. package/dist/indexer/index.js +360 -0
  41. package/dist/indexer/index.js.map +1 -0
  42. package/dist/indexer/process.d.ts +22 -0
  43. package/dist/indexer/process.js +42 -0
  44. package/dist/indexer/process.js.map +1 -0
  45. package/dist/indexer/scan.d.ts +43 -0
  46. package/dist/indexer/scan.js +223 -0
  47. package/dist/indexer/scan.js.map +1 -0
  48. package/dist/indexer/worker.d.ts +1 -0
  49. package/dist/indexer/worker.js +23 -0
  50. package/dist/indexer/worker.js.map +1 -0
  51. package/dist/llm/comment-generator.d.ts +17 -19
  52. package/dist/llm/comment-generator.js +46 -80
  53. package/dist/llm/comment-generator.js.map +1 -1
  54. package/dist/llm/index.d.ts +0 -1
  55. package/dist/llm/index.js.map +1 -1
  56. package/dist/llm/prompts.d.ts +0 -1
  57. package/dist/llm/prompts.js +16 -16
  58. package/dist/parser/extractor.d.ts +21 -16
  59. package/dist/parser/extractor.js +700 -228
  60. package/dist/parser/extractor.js.map +1 -1
  61. package/dist/parser/index.d.ts +17 -21
  62. package/dist/parser/index.js +115 -171
  63. package/dist/parser/index.js.map +1 -1
  64. package/dist/parser/langs/c.d.ts +3 -0
  65. package/dist/parser/langs/c.js +879 -0
  66. package/dist/parser/langs/c.js.map +1 -0
  67. package/dist/parser/langs/csharp.d.ts +2 -0
  68. package/dist/parser/langs/csharp.js +548 -0
  69. package/dist/parser/langs/csharp.js.map +1 -0
  70. package/dist/parser/langs/dart.d.ts +2 -0
  71. package/dist/parser/langs/dart.js +893 -0
  72. package/dist/parser/langs/dart.js.map +1 -0
  73. package/dist/parser/langs/go.d.ts +2 -0
  74. package/dist/parser/langs/go.js +455 -0
  75. package/dist/parser/langs/go.js.map +1 -0
  76. package/dist/parser/langs/java.d.ts +2 -0
  77. package/dist/parser/langs/java.js +390 -0
  78. package/dist/parser/langs/java.js.map +1 -0
  79. package/dist/parser/langs/php.d.ts +2 -0
  80. package/dist/parser/langs/php.js +486 -0
  81. package/dist/parser/langs/php.js.map +1 -0
  82. package/dist/parser/langs/python.d.ts +2 -0
  83. package/dist/parser/langs/python.js +216 -0
  84. package/dist/parser/langs/python.js.map +1 -0
  85. package/dist/parser/langs/ruby.d.ts +2 -0
  86. package/dist/parser/langs/ruby.js +602 -0
  87. package/dist/parser/langs/ruby.js.map +1 -0
  88. package/dist/parser/langs/rust.d.ts +2 -0
  89. package/dist/parser/langs/rust.js +931 -0
  90. package/dist/parser/langs/rust.js.map +1 -0
  91. package/dist/parser/langs/swift.d.ts +2 -0
  92. package/dist/parser/langs/swift.js +616 -0
  93. package/dist/parser/langs/swift.js.map +1 -0
  94. package/dist/parser/langs/typescript.d.ts +4 -0
  95. package/dist/parser/langs/typescript.js +535 -0
  96. package/dist/parser/langs/typescript.js.map +1 -0
  97. package/dist/parser/registry.d.ts +16 -0
  98. package/dist/parser/registry.js +87 -0
  99. package/dist/parser/registry.js.map +1 -0
  100. package/dist/parser/returns.d.ts +9 -0
  101. package/dist/parser/returns.js +152 -0
  102. package/dist/parser/returns.js.map +1 -0
  103. package/dist/parser/spec.d.ts +165 -0
  104. package/dist/parser/spec.js +188 -0
  105. package/dist/parser/spec.js.map +1 -0
  106. package/dist/query/arity.d.ts +27 -0
  107. package/dist/query/arity.js +187 -0
  108. package/dist/query/arity.js.map +1 -0
  109. package/dist/query/callgraph.d.ts +113 -0
  110. package/dist/query/callgraph.js +332 -0
  111. package/dist/query/callgraph.js.map +1 -0
  112. package/dist/query/format.d.ts +36 -0
  113. package/dist/query/format.js +136 -0
  114. package/dist/query/format.js.map +1 -0
  115. package/dist/query/hierarchy.d.ts +44 -0
  116. package/dist/query/hierarchy.js +441 -0
  117. package/dist/query/hierarchy.js.map +1 -0
  118. package/dist/query/resolve.d.ts +265 -0
  119. package/dist/query/resolve.js +1957 -0
  120. package/dist/query/resolve.js.map +1 -0
  121. package/dist/query/search.d.ts +35 -0
  122. package/dist/query/search.js +379 -0
  123. package/dist/query/search.js.map +1 -0
  124. package/dist/query/words.d.ts +30 -0
  125. package/dist/query/words.js +201 -0
  126. package/dist/query/words.js.map +1 -0
  127. package/dist/server/index.d.ts +1 -3
  128. package/dist/server/index.js +313 -73
  129. package/dist/server/index.js.map +1 -1
  130. package/dist/tools/context.d.ts +35 -0
  131. package/dist/tools/context.js +79 -0
  132. package/dist/tools/context.js.map +1 -0
  133. package/dist/tools/explore.d.ts +51 -0
  134. package/dist/tools/explore.js +779 -0
  135. package/dist/tools/explore.js.map +1 -0
  136. package/dist/tools/impact.d.ts +21 -0
  137. package/dist/tools/impact.js +743 -0
  138. package/dist/tools/impact.js.map +1 -0
  139. package/dist/tools/search.d.ts +59 -0
  140. package/dist/tools/search.js +843 -0
  141. package/dist/tools/search.js.map +1 -0
  142. package/dist/tools/stats.d.ts +26 -0
  143. package/dist/tools/stats.js +604 -0
  144. package/dist/tools/stats.js.map +1 -0
  145. package/dist/types.d.ts +95 -40
  146. package/dist/types.js +9 -1
  147. package/dist/types.js.map +1 -1
  148. package/dist/version.d.ts +2 -0
  149. package/dist/version.js +3 -0
  150. package/dist/version.js.map +1 -0
  151. package/dist/watcher/index.d.ts +18 -16
  152. package/dist/watcher/index.js +94 -77
  153. package/dist/watcher/index.js.map +1 -1
  154. package/package.json +21 -24
  155. package/plugin/server.mjs +50 -0
  156. package/skills/audit/SKILL.md +37 -0
  157. package/skills/deadcode/SKILL.md +33 -0
  158. package/skills/explore/SKILL.md +54 -0
  159. package/skills/impact/SKILL.md +24 -0
  160. package/skills/status/SKILL.md +11 -0
  161. package/src/cli.ts +256 -0
  162. package/src/config/defaults.ts +50 -32
  163. package/src/config/index.ts +23 -14
  164. package/src/db/index.ts +540 -43
  165. package/src/engine.ts +30 -0
  166. package/src/indexer/imports.ts +241 -0
  167. package/src/indexer/index.ts +369 -0
  168. package/src/indexer/process.ts +63 -0
  169. package/src/indexer/scan.ts +226 -0
  170. package/src/indexer/worker.ts +24 -0
  171. package/src/llm/comment-generator.ts +52 -103
  172. package/src/llm/index.ts +80 -80
  173. package/src/llm/prompts.ts +95 -95
  174. package/src/parser/extractor.ts +643 -256
  175. package/src/parser/index.ts +105 -205
  176. package/src/parser/langs/c.ts +860 -0
  177. package/src/parser/langs/csharp.ts +519 -0
  178. package/src/parser/langs/dart.ts +820 -0
  179. package/src/parser/langs/go.ts +437 -0
  180. package/src/parser/langs/java.ts +361 -0
  181. package/src/parser/langs/php.ts +450 -0
  182. package/src/parser/langs/python.ts +206 -0
  183. package/src/parser/langs/ruby.ts +554 -0
  184. package/src/parser/langs/rust.ts +904 -0
  185. package/src/parser/langs/swift.ts +590 -0
  186. package/src/parser/langs/typescript.ts +499 -0
  187. package/src/parser/registry.ts +88 -0
  188. package/src/parser/returns.ts +137 -0
  189. package/src/parser/spec.ts +309 -0
  190. package/src/query/arity.ts +173 -0
  191. package/src/query/callgraph.ts +473 -0
  192. package/src/query/format.ts +136 -0
  193. package/src/query/hierarchy.ts +447 -0
  194. package/src/query/resolve.ts +1972 -0
  195. package/src/query/search.ts +430 -0
  196. package/src/query/words.ts +183 -0
  197. package/src/server/index.ts +390 -79
  198. package/src/tools/context.ts +82 -0
  199. package/src/tools/explore.ts +860 -0
  200. package/src/tools/impact.ts +748 -0
  201. package/src/tools/search.ts +886 -0
  202. package/src/tools/stats.ts +633 -0
  203. package/src/types.ts +113 -45
  204. package/src/version.ts +2 -0
  205. package/src/watcher/index.ts +82 -88
  206. package/tsconfig.json +7 -4
  207. package/.mcp.json +0 -8
  208. package/commands/clast-status.md +0 -9
  209. package/dist/config/defaults.d.ts.map +0 -1
  210. package/dist/config/index.d.ts.map +0 -1
  211. package/dist/db/index.d.ts.map +0 -1
  212. package/dist/db/queries.d.ts +0 -44
  213. package/dist/db/queries.d.ts.map +0 -1
  214. package/dist/db/queries.js +0 -251
  215. package/dist/db/queries.js.map +0 -1
  216. package/dist/db/schema.d.ts +0 -3
  217. package/dist/db/schema.d.ts.map +0 -1
  218. package/dist/db/schema.js +0 -94
  219. package/dist/db/schema.js.map +0 -1
  220. package/dist/db/types.d.ts +0 -44
  221. package/dist/db/types.d.ts.map +0 -1
  222. package/dist/db/types.js +0 -2
  223. package/dist/db/types.js.map +0 -1
  224. package/dist/llm/comment-generator.d.ts.map +0 -1
  225. package/dist/llm/index.d.ts.map +0 -1
  226. package/dist/llm/prompts.d.ts.map +0 -1
  227. package/dist/parser/call-resolver.d.ts +0 -15
  228. package/dist/parser/call-resolver.d.ts.map +0 -1
  229. package/dist/parser/call-resolver.js +0 -16
  230. package/dist/parser/call-resolver.js.map +0 -1
  231. package/dist/parser/extractor.d.ts.map +0 -1
  232. package/dist/parser/index.d.ts.map +0 -1
  233. package/dist/parser/languages.d.ts +0 -11
  234. package/dist/parser/languages.d.ts.map +0 -1
  235. package/dist/parser/languages.js +0 -74
  236. package/dist/parser/languages.js.map +0 -1
  237. package/dist/server/index.d.ts.map +0 -1
  238. package/dist/server/register-tools.d.ts +0 -7
  239. package/dist/server/register-tools.d.ts.map +0 -1
  240. package/dist/server/register-tools.js +0 -17
  241. package/dist/server/register-tools.js.map +0 -1
  242. package/dist/server/tools/clast-call-graph.d.ts +0 -4
  243. package/dist/server/tools/clast-call-graph.d.ts.map +0 -1
  244. package/dist/server/tools/clast-call-graph.js +0 -70
  245. package/dist/server/tools/clast-call-graph.js.map +0 -1
  246. package/dist/server/tools/clast-comment.d.ts +0 -5
  247. package/dist/server/tools/clast-comment.d.ts.map +0 -1
  248. package/dist/server/tools/clast-comment.js +0 -64
  249. package/dist/server/tools/clast-comment.js.map +0 -1
  250. package/dist/server/tools/clast-file-summary.d.ts +0 -4
  251. package/dist/server/tools/clast-file-summary.d.ts.map +0 -1
  252. package/dist/server/tools/clast-file-summary.js +0 -42
  253. package/dist/server/tools/clast-file-summary.js.map +0 -1
  254. package/dist/server/tools/clast-get-context.d.ts +0 -4
  255. package/dist/server/tools/clast-get-context.d.ts.map +0 -1
  256. package/dist/server/tools/clast-get-context.js +0 -80
  257. package/dist/server/tools/clast-get-context.js.map +0 -1
  258. package/dist/server/tools/clast-reindex.d.ts +0 -5
  259. package/dist/server/tools/clast-reindex.d.ts.map +0 -1
  260. package/dist/server/tools/clast-reindex.js +0 -36
  261. package/dist/server/tools/clast-reindex.js.map +0 -1
  262. package/dist/server/tools/clast-search.d.ts +0 -4
  263. package/dist/server/tools/clast-search.d.ts.map +0 -1
  264. package/dist/server/tools/clast-search.js +0 -29
  265. package/dist/server/tools/clast-search.js.map +0 -1
  266. package/dist/server/tools/clast-status.d.ts +0 -5
  267. package/dist/server/tools/clast-status.d.ts.map +0 -1
  268. package/dist/server/tools/clast-status.js +0 -23
  269. package/dist/server/tools/clast-status.js.map +0 -1
  270. package/dist/types.d.ts.map +0 -1
  271. package/dist/watcher/gitignore.d.ts +0 -9
  272. package/dist/watcher/gitignore.d.ts.map +0 -1
  273. package/dist/watcher/gitignore.js +0 -38
  274. package/dist/watcher/gitignore.js.map +0 -1
  275. package/dist/watcher/index.d.ts.map +0 -1
  276. package/skills/clast-explore/SKILL.md +0 -41
  277. package/src/db/queries.ts +0 -332
  278. package/src/db/schema.ts +0 -106
  279. package/src/db/types.ts +0 -47
  280. package/src/parser/call-resolver.ts +0 -17
  281. package/src/parser/languages.ts +0 -86
  282. package/src/server/register-tools.ts +0 -30
  283. package/src/server/tools/clast-call-graph.ts +0 -82
  284. package/src/server/tools/clast-comment.ts +0 -76
  285. package/src/server/tools/clast-file-summary.ts +0 -59
  286. package/src/server/tools/clast-get-context.ts +0 -97
  287. package/src/server/tools/clast-reindex.ts +0 -52
  288. package/src/server/tools/clast-search.ts +0 -38
  289. package/src/server/tools/clast-status.ts +0 -37
  290. package/src/watcher/gitignore.ts +0 -42
package/src/db/queries.ts DELETED
@@ -1,332 +0,0 @@
1
- import type { Database } from 'sql.js';
2
- import type { FileRow, AstNodeRow, CallEdgeRow, CommentRow, StatusRow } from './types.js';
3
-
4
- export class Queries {
5
- private db: Database;
6
-
7
- constructor(db: Database) {
8
- this.db = db;
9
- }
10
-
11
- private run(sql: string, params: unknown[] = []): void {
12
- this.db.run(sql, params as (string | number | null | Uint8Array)[]);
13
- }
14
-
15
- private get<T>(sql: string, params: unknown[] = []): T | undefined {
16
- const result = this.db.exec(sql, params as (string | number | null | Uint8Array)[]);
17
- if (result.length === 0 || result[0]!.values.length === 0) return undefined;
18
- const columns = result[0]!.columns;
19
- const values = result[0]!.values[0]!;
20
- const obj: Record<string, unknown> = {};
21
- for (let i = 0; i < columns.length; i++) {
22
- obj[columns[i]!] = values[i];
23
- }
24
- return obj as T;
25
- }
26
-
27
- private all<T>(sql: string, params: unknown[] = []): T[] {
28
- const result = this.db.exec(sql, params as (string | number | null | Uint8Array)[]);
29
- if (result.length === 0) return [];
30
- const columns = result[0]!.columns;
31
- return result[0]!.values.map(values => {
32
- const obj: Record<string, unknown> = {};
33
- for (let i = 0; i < columns.length; i++) {
34
- obj[columns[i]!] = values[i];
35
- }
36
- return obj as T;
37
- });
38
- }
39
-
40
- private getLastInsertId(): number {
41
- const result = this.db.exec('SELECT last_insert_rowid() as id');
42
- if (result.length > 0 && result[0]!.values.length > 0) {
43
- return Number(result[0]!.values[0]![0]);
44
- }
45
- return 0;
46
- }
47
-
48
- // --- File operations ---
49
-
50
- upsertFile(filePath: string, hash: string, language: string, nodeCount: number): void {
51
- this.run(
52
- `INSERT OR REPLACE INTO files (file_path, hash, language, last_indexed, node_count)
53
- VALUES (?, ?, ?, ?, ?)`,
54
- [filePath, hash, language, Date.now(), nodeCount]
55
- );
56
- }
57
-
58
- deleteFile(filePath: string): void {
59
- // Manually cascade since sql.js foreign key cascade can be unreliable
60
- this.run(`DELETE FROM comments WHERE node_id IN (SELECT id FROM ast_nodes WHERE file_path = ?)`, [filePath]);
61
- this.run(`DELETE FROM call_edges WHERE caller_id IN (SELECT id FROM ast_nodes WHERE file_path = ?)`, [filePath]);
62
- this.run(`DELETE FROM call_edges WHERE callee_id IN (SELECT id FROM ast_nodes WHERE file_path = ?)`, [filePath]);
63
- this.run(`DELETE FROM ast_nodes WHERE file_path = ?`, [filePath]);
64
- this.run(`DELETE FROM files WHERE file_path = ?`, [filePath]);
65
- }
66
-
67
- getFileHash(filePath: string): string | null {
68
- const row = this.get<{ hash: string }>(`SELECT hash FROM files WHERE file_path = ?`, [filePath]);
69
- return row?.hash ?? null;
70
- }
71
-
72
- getAllFiles(): FileRow[] {
73
- return this.all<FileRow>(`SELECT * FROM files`);
74
- }
75
-
76
- // --- Node operations ---
77
-
78
- insertNode(node: Omit<AstNodeRow, 'id'>): number {
79
- this.run(
80
- `INSERT INTO ast_nodes (file_path, name, type, signature, start_line, end_line, start_col, end_col, parent_id, language, body_text)
81
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
82
- [
83
- node.file_path, node.name, node.type, node.signature,
84
- node.start_line, node.end_line, node.start_col, node.end_col,
85
- node.parent_id, node.language, node.body_text,
86
- ]
87
- );
88
- return this.getLastInsertId();
89
- }
90
-
91
- insertNodes(nodes: Omit<AstNodeRow, 'id'>[]): number[] {
92
- const ids: number[] = [];
93
- this.run('BEGIN TRANSACTION');
94
- try {
95
- for (const node of nodes) {
96
- ids.push(this.insertNode(node));
97
- }
98
- this.run('COMMIT');
99
- } catch (err) {
100
- this.run('ROLLBACK');
101
- throw err;
102
- }
103
- return ids;
104
- }
105
-
106
- getNodeById(id: number): AstNodeRow | undefined {
107
- return this.get<AstNodeRow>(`SELECT * FROM ast_nodes WHERE id = ?`, [id]);
108
- }
109
-
110
- getNodeByName(name: string): AstNodeRow[] {
111
- return this.all<AstNodeRow>(
112
- `SELECT * FROM ast_nodes WHERE name = ? ORDER BY file_path, start_line`,
113
- [name]
114
- );
115
- }
116
-
117
- getNodesByFile(filePath: string): AstNodeRow[] {
118
- return this.all<AstNodeRow>(
119
- `SELECT * FROM ast_nodes WHERE file_path = ? ORDER BY start_line`,
120
- [filePath]
121
- );
122
- }
123
-
124
- searchNodes(query: string, type?: string, limit: number = 20): AstNodeRow[] {
125
- const pattern = `%${query}%`;
126
- if (type) {
127
- return this.all<AstNodeRow>(
128
- `SELECT * FROM ast_nodes WHERE name LIKE ? AND type = ? LIMIT ?`,
129
- [pattern, type, limit]
130
- );
131
- }
132
- return this.all<AstNodeRow>(
133
- `SELECT * FROM ast_nodes WHERE name LIKE ? LIMIT ?`,
134
- [pattern, limit]
135
- );
136
- }
137
-
138
- // --- Edge operations ---
139
-
140
- insertEdge(edge: Omit<CallEdgeRow, 'id'>): number {
141
- this.run(
142
- `INSERT INTO call_edges (caller_id, callee_id, callee_name, file_path, line)
143
- VALUES (?, ?, ?, ?, ?)`,
144
- [edge.caller_id, edge.callee_id, edge.callee_name, edge.file_path, edge.line]
145
- );
146
- return this.getLastInsertId();
147
- }
148
-
149
- insertEdges(edges: Omit<CallEdgeRow, 'id'>[]): void {
150
- this.run('BEGIN TRANSACTION');
151
- try {
152
- for (const edge of edges) {
153
- this.insertEdge(edge);
154
- }
155
- this.run('COMMIT');
156
- } catch (err) {
157
- this.run('ROLLBACK');
158
- throw err;
159
- }
160
- }
161
-
162
- getCallersOf(nameOrId: string | number, depth: number = 2): AstNodeRow[] {
163
- const whereClause = typeof nameOrId === 'number'
164
- ? 'callee_id = ?'
165
- : 'callee_name = ?';
166
-
167
- const sql = `
168
- WITH RECURSIVE caller_chain(id, depth) AS (
169
- SELECT caller_id, 1
170
- FROM call_edges
171
- WHERE ${whereClause}
172
- UNION
173
- SELECT ce.caller_id, cc.depth + 1
174
- FROM call_edges ce
175
- JOIN caller_chain cc ON ce.callee_id = cc.id
176
- WHERE cc.depth < ?
177
- )
178
- SELECT DISTINCT an.* FROM ast_nodes an
179
- JOIN caller_chain cc ON an.id = cc.id
180
- ORDER BY an.file_path, an.start_line
181
- `;
182
- return this.all<AstNodeRow>(sql, [nameOrId, depth]);
183
- }
184
-
185
- getCalleesOf(nameOrId: string | number, depth: number = 2): AstNodeRow[] {
186
- const whereClause = typeof nameOrId === 'number'
187
- ? 'an.id = ?'
188
- : 'an.name = ?';
189
-
190
- const sql = `
191
- WITH RECURSIVE callee_chain(id, name, depth) AS (
192
- SELECT ce.callee_id, ce.callee_name, 1
193
- FROM call_edges ce
194
- JOIN ast_nodes an ON ce.caller_id = an.id
195
- WHERE ${whereClause}
196
- UNION
197
- SELECT ce2.callee_id, ce2.callee_name, cc.depth + 1
198
- FROM call_edges ce2
199
- JOIN callee_chain cc ON ce2.caller_id = cc.id
200
- WHERE cc.depth < ? AND cc.id IS NOT NULL
201
- )
202
- SELECT DISTINCT an.* FROM ast_nodes an
203
- JOIN callee_chain cc ON an.id = cc.id
204
- WHERE cc.id IS NOT NULL
205
- ORDER BY an.file_path, an.start_line
206
- `;
207
- return this.all<AstNodeRow>(sql, [nameOrId, depth]);
208
- }
209
-
210
- getCallEdgesForNode(nodeId: number): { outgoing: CallEdgeRow[]; incoming: CallEdgeRow[] } {
211
- const outgoing = this.all<CallEdgeRow>(
212
- `SELECT * FROM call_edges WHERE caller_id = ?`,
213
- [nodeId]
214
- );
215
- const incoming = this.all<CallEdgeRow>(
216
- `SELECT * FROM call_edges WHERE callee_id = ?`,
217
- [nodeId]
218
- );
219
- return { outgoing, incoming };
220
- }
221
-
222
- // --- Comment operations ---
223
-
224
- insertComment(nodeId: number, text: string, source: 'original' | 'generated'): number {
225
- this.run(
226
- `INSERT INTO comments (node_id, text, source, generated_at)
227
- VALUES (?, ?, ?, ?)`,
228
- [nodeId, text, source, source === 'generated' ? Date.now() : null]
229
- );
230
- return this.getLastInsertId();
231
- }
232
-
233
- getCommentsForNode(nodeId: number): CommentRow[] {
234
- return this.all<CommentRow>(`SELECT * FROM comments WHERE node_id = ?`, [nodeId]);
235
- }
236
-
237
- deleteCommentsBySource(nodeId: number, source: 'original' | 'generated'): void {
238
- this.run(`DELETE FROM comments WHERE node_id = ? AND source = ?`, [nodeId, source]);
239
- }
240
-
241
- // --- Context (combined) ---
242
-
243
- getNodeWithContext(name: string): {
244
- nodes: AstNodeRow[];
245
- comments: CommentRow[];
246
- callers: AstNodeRow[];
247
- callees: AstNodeRow[];
248
- } {
249
- const nodes = this.getNodeByName(name);
250
- if (nodes.length === 0) {
251
- return { nodes: [], comments: [], callers: [], callees: [] };
252
- }
253
-
254
- const allComments: CommentRow[] = [];
255
- for (const node of nodes) {
256
- allComments.push(...this.getCommentsForNode(node.id));
257
- }
258
-
259
- const callers = this.getCallersOf(name, 1);
260
- const callees = this.getCalleesOf(name, 1);
261
-
262
- return { nodes, comments: allComments, callers, callees };
263
- }
264
-
265
- // --- Status ---
266
-
267
- getStatus(): StatusRow {
268
- const result = this.get<StatusRow>(`
269
- SELECT
270
- (SELECT COUNT(*) FROM files) as files_indexed,
271
- (SELECT COUNT(*) FROM ast_nodes) as total_nodes,
272
- (SELECT COUNT(*) FROM call_edges) as total_edges,
273
- (SELECT COUNT(*) FROM comments) as total_comments,
274
- (SELECT MAX(last_indexed) FROM files) as last_indexed
275
- `);
276
- return result ?? {
277
- files_indexed: 0,
278
- total_nodes: 0,
279
- total_edges: 0,
280
- total_comments: 0,
281
- last_indexed: null,
282
- };
283
- }
284
-
285
- // --- Bulk operations ---
286
-
287
- deleteNodesForFile(filePath: string): void {
288
- this.run(`DELETE FROM comments WHERE node_id IN (SELECT id FROM ast_nodes WHERE file_path = ?)`, [filePath]);
289
- this.run(`DELETE FROM call_edges WHERE caller_id IN (SELECT id FROM ast_nodes WHERE file_path = ?)`, [filePath]);
290
- this.run(`DELETE FROM ast_nodes WHERE file_path = ?`, [filePath]);
291
- }
292
-
293
- resolveEdges(): number {
294
- // Count unresolved before
295
- const before = this.get<{ cnt: number }>(
296
- `SELECT COUNT(*) as cnt FROM call_edges WHERE callee_id IS NULL`
297
- );
298
- const beforeCount = before?.cnt ?? 0;
299
-
300
- this.run(`
301
- UPDATE call_edges
302
- SET callee_id = (
303
- SELECT an.id FROM ast_nodes an
304
- WHERE an.name = call_edges.callee_name
305
- LIMIT 1
306
- )
307
- WHERE callee_id IS NULL
308
- `);
309
-
310
- const after = this.get<{ cnt: number }>(
311
- `SELECT COUNT(*) as cnt FROM call_edges WHERE callee_id IS NULL`
312
- );
313
- const afterCount = after?.cnt ?? 0;
314
-
315
- return beforeCount - afterCount;
316
- }
317
-
318
- getNodesWithoutComments(limit: number = 100): AstNodeRow[] {
319
- return this.all<AstNodeRow>(`
320
- SELECT an.* FROM ast_nodes an
321
- LEFT JOIN comments c ON an.id = c.node_id
322
- WHERE c.id IS NULL
323
- AND an.type IN ('function', 'method', 'class', 'interface', 'struct', 'trait')
324
- LIMIT ?
325
- `, [limit]);
326
- }
327
-
328
- /** Flush the in-memory database to a Uint8Array for saving to disk. */
329
- export(): Uint8Array {
330
- return this.db.export();
331
- }
332
- }
package/src/db/schema.ts DELETED
@@ -1,106 +0,0 @@
1
- import type { Database } from 'sql.js';
2
-
3
- const SCHEMA_VERSION = 1;
4
-
5
- const CREATE_TABLES = `
6
- CREATE TABLE IF NOT EXISTS _clast_meta (
7
- key TEXT PRIMARY KEY,
8
- value TEXT NOT NULL
9
- );
10
-
11
- CREATE TABLE IF NOT EXISTS files (
12
- file_path TEXT PRIMARY KEY,
13
- hash TEXT NOT NULL,
14
- language TEXT NOT NULL,
15
- last_indexed INTEGER NOT NULL,
16
- node_count INTEGER NOT NULL DEFAULT 0
17
- );
18
-
19
- CREATE TABLE IF NOT EXISTS ast_nodes (
20
- id INTEGER PRIMARY KEY AUTOINCREMENT,
21
- file_path TEXT NOT NULL,
22
- name TEXT NOT NULL,
23
- type TEXT NOT NULL,
24
- signature TEXT NOT NULL DEFAULT '',
25
- start_line INTEGER NOT NULL,
26
- end_line INTEGER NOT NULL,
27
- start_col INTEGER NOT NULL,
28
- end_col INTEGER NOT NULL,
29
- parent_id INTEGER,
30
- language TEXT NOT NULL,
31
- body_text TEXT NOT NULL DEFAULT '',
32
- FOREIGN KEY (file_path) REFERENCES files(file_path) ON DELETE CASCADE,
33
- FOREIGN KEY (parent_id) REFERENCES ast_nodes(id) ON DELETE CASCADE
34
- );
35
-
36
- CREATE INDEX IF NOT EXISTS idx_nodes_name ON ast_nodes(name);
37
- CREATE INDEX IF NOT EXISTS idx_nodes_type ON ast_nodes(type);
38
- CREATE INDEX IF NOT EXISTS idx_nodes_file ON ast_nodes(file_path);
39
- CREATE INDEX IF NOT EXISTS idx_nodes_name_type ON ast_nodes(name, type);
40
-
41
- CREATE TABLE IF NOT EXISTS call_edges (
42
- id INTEGER PRIMARY KEY AUTOINCREMENT,
43
- caller_id INTEGER NOT NULL,
44
- callee_id INTEGER,
45
- callee_name TEXT NOT NULL,
46
- file_path TEXT NOT NULL,
47
- line INTEGER NOT NULL,
48
- FOREIGN KEY (caller_id) REFERENCES ast_nodes(id) ON DELETE CASCADE,
49
- FOREIGN KEY (callee_id) REFERENCES ast_nodes(id) ON DELETE SET NULL
50
- );
51
-
52
- CREATE INDEX IF NOT EXISTS idx_edges_caller ON call_edges(caller_id);
53
- CREATE INDEX IF NOT EXISTS idx_edges_callee ON call_edges(callee_id);
54
- CREATE INDEX IF NOT EXISTS idx_edges_callee_name ON call_edges(callee_name);
55
-
56
- CREATE TABLE IF NOT EXISTS comments (
57
- id INTEGER PRIMARY KEY AUTOINCREMENT,
58
- node_id INTEGER NOT NULL,
59
- text TEXT NOT NULL,
60
- source TEXT NOT NULL CHECK(source IN ('original', 'generated')),
61
- generated_at INTEGER,
62
- FOREIGN KEY (node_id) REFERENCES ast_nodes(id) ON DELETE CASCADE
63
- );
64
-
65
- CREATE INDEX IF NOT EXISTS idx_comments_node ON comments(node_id);
66
- `;
67
-
68
- export function initializeSchema(db: Database): void {
69
- db.run('PRAGMA foreign_keys = ON');
70
-
71
- const currentVersion = getSchemaVersion(db);
72
-
73
- if (currentVersion < SCHEMA_VERSION) {
74
- // sql.js doesn't support multi-statement exec with CREATE TABLE IF NOT EXISTS well,
75
- // so we split and execute each statement
76
- const statements = CREATE_TABLES.split(';')
77
- .map(s => s.trim())
78
- .filter(s => s.length > 0);
79
-
80
- for (const stmt of statements) {
81
- db.run(stmt);
82
- }
83
- setSchemaVersion(db, SCHEMA_VERSION);
84
- }
85
- }
86
-
87
- function getSchemaVersion(db: Database): number {
88
- try {
89
- const result = db.exec(
90
- `SELECT value FROM _clast_meta WHERE key = 'schema_version'`
91
- );
92
- if (result.length > 0 && result[0]!.values.length > 0) {
93
- return parseInt(String(result[0]!.values[0]![0]), 10);
94
- }
95
- return 0;
96
- } catch {
97
- return 0;
98
- }
99
- }
100
-
101
- function setSchemaVersion(db: Database, version: number): void {
102
- db.run(
103
- `INSERT OR REPLACE INTO _clast_meta (key, value) VALUES ('schema_version', ?)`,
104
- [String(version)]
105
- );
106
- }
package/src/db/types.ts DELETED
@@ -1,47 +0,0 @@
1
- export interface FileRow {
2
- file_path: string;
3
- hash: string;
4
- language: string;
5
- last_indexed: number;
6
- node_count: number;
7
- }
8
-
9
- export interface AstNodeRow {
10
- id: number;
11
- file_path: string;
12
- name: string;
13
- type: string;
14
- signature: string;
15
- start_line: number;
16
- end_line: number;
17
- start_col: number;
18
- end_col: number;
19
- parent_id: number | null;
20
- language: string;
21
- body_text: string;
22
- }
23
-
24
- export interface CallEdgeRow {
25
- id: number;
26
- caller_id: number;
27
- callee_id: number | null;
28
- callee_name: string;
29
- file_path: string;
30
- line: number;
31
- }
32
-
33
- export interface CommentRow {
34
- id: number;
35
- node_id: number;
36
- text: string;
37
- source: 'original' | 'generated';
38
- generated_at: number | null;
39
- }
40
-
41
- export interface StatusRow {
42
- files_indexed: number;
43
- total_nodes: number;
44
- total_edges: number;
45
- total_comments: number;
46
- last_indexed: number | null;
47
- }
@@ -1,17 +0,0 @@
1
- import type { Queries } from '../db/queries.js';
2
-
3
- /**
4
- * Resolves unresolved call edges in the database.
5
- *
6
- * After all files are parsed and nodes inserted, call edges may have
7
- * callee_id = null because the callee wasn't inserted yet. This resolver
8
- * attempts to match callee_name to ast_nodes.name.
9
- *
10
- * Resolution strategy (best-effort):
11
- * 1. Exact name match within the same file
12
- * 2. Exact name match across all files
13
- * 3. Leave unresolved if no match
14
- */
15
- export function resolveCallEdges(queries: Queries): number {
16
- return queries.resolveEdges();
17
- }
@@ -1,86 +0,0 @@
1
- import { join, dirname } from 'node:path';
2
- import { existsSync } from 'node:fs';
3
- import { fileURLToPath } from 'node:url';
4
- import { createRequire } from 'node:module';
5
-
6
- interface LanguageEntry {
7
- name: string;
8
- extensions: string[];
9
- wasmFile: string;
10
- }
11
-
12
- const LANGUAGES: LanguageEntry[] = [
13
- { name: 'typescript', extensions: ['.ts'], wasmFile: 'tree-sitter-typescript.wasm' },
14
- { name: 'tsx', extensions: ['.tsx'], wasmFile: 'tree-sitter-tsx.wasm' },
15
- { name: 'javascript', extensions: ['.js', '.jsx', '.mjs', '.cjs'], wasmFile: 'tree-sitter-javascript.wasm' },
16
- { name: 'python', extensions: ['.py', '.pyw'], wasmFile: 'tree-sitter-python.wasm' },
17
- { name: 'java', extensions: ['.java'], wasmFile: 'tree-sitter-java.wasm' },
18
- { name: 'csharp', extensions: ['.cs'], wasmFile: 'tree-sitter-c_sharp.wasm' },
19
- { name: 'go', extensions: ['.go'], wasmFile: 'tree-sitter-go.wasm' },
20
- { name: 'rust', extensions: ['.rs'], wasmFile: 'tree-sitter-rust.wasm' },
21
- { name: 'c', extensions: ['.c', '.h'], wasmFile: 'tree-sitter-c.wasm' },
22
- { name: 'cpp', extensions: ['.cpp', '.hpp', '.cc', '.hh', '.cxx', '.hxx'], wasmFile: 'tree-sitter-cpp.wasm' },
23
- { name: 'ruby', extensions: ['.rb'], wasmFile: 'tree-sitter-ruby.wasm' },
24
- { name: 'php', extensions: ['.php'], wasmFile: 'tree-sitter-php.wasm' },
25
- ];
26
-
27
- // Extension → language entry mapping
28
- const EXT_MAP = new Map<string, LanguageEntry>();
29
- for (const lang of LANGUAGES) {
30
- for (const ext of lang.extensions) {
31
- EXT_MAP.set(ext, lang);
32
- }
33
- }
34
-
35
- export function getLanguageForExtension(ext: string): LanguageEntry | undefined {
36
- return EXT_MAP.get(ext.toLowerCase());
37
- }
38
-
39
- export function getGrammarWasmPath(langName: string): string | null {
40
- const entry = LANGUAGES.find(l => l.name === langName);
41
- if (!entry) return null;
42
-
43
- // Try to resolve from tree-sitter-wasms npm package first
44
- const require = createRequire(import.meta.url);
45
- try {
46
- const wasmsDir = dirname(require.resolve('tree-sitter-wasms/package.json'));
47
- const npmPath = join(wasmsDir, 'out', entry.wasmFile);
48
- if (existsSync(npmPath)) return npmPath;
49
- } catch {
50
- // tree-sitter-wasms not installed, try fallbacks
51
- }
52
-
53
- const __dirname = dirname(fileURLToPath(import.meta.url));
54
-
55
- // Check grammars/ directory relative to project
56
- const candidates = [
57
- // Custom grammars dir (env override)
58
- process.env.CLAST_GRAMMARS_DIR ? join(process.env.CLAST_GRAMMARS_DIR, entry.wasmFile) : null,
59
- // Plugin root
60
- process.env.CLAUDE_PLUGIN_ROOT ? join(process.env.CLAUDE_PLUGIN_ROOT, 'grammars', entry.wasmFile) : null,
61
- // Relative to dist/
62
- join(__dirname, '..', '..', 'grammars', entry.wasmFile),
63
- // CWD
64
- join(process.cwd(), 'grammars', entry.wasmFile),
65
- ].filter(Boolean) as string[];
66
-
67
- for (const candidate of candidates) {
68
- if (existsSync(candidate)) return candidate;
69
- }
70
-
71
- return null;
72
- }
73
-
74
- export function getSupportedExtensions(enabledLanguages?: string[]): string[] {
75
- const result: string[] = [];
76
- for (const lang of LANGUAGES) {
77
- if (!enabledLanguages || enabledLanguages.includes(lang.name)) {
78
- result.push(...lang.extensions);
79
- }
80
- }
81
- return result;
82
- }
83
-
84
- export function getAllLanguageNames(): string[] {
85
- return LANGUAGES.map(l => l.name);
86
- }
@@ -1,30 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { ClastDatabase } from '../db/index.js';
3
- import type { ParserManager } from '../parser/index.js';
4
- import type { FileWatcher } from '../watcher/index.js';
5
- import type { CommentGenerator } from '../llm/comment-generator.js';
6
-
7
- import { registerSearchTool } from './tools/clast-search.js';
8
- import { registerCallGraphTool } from './tools/clast-call-graph.js';
9
- import { registerFileSummaryTool } from './tools/clast-file-summary.js';
10
- import { registerGetContextTool } from './tools/clast-get-context.js';
11
- import { registerStatusTool } from './tools/clast-status.js';
12
- import { registerReindexTool } from './tools/clast-reindex.js';
13
- import { registerCommentTool } from './tools/clast-comment.js';
14
-
15
- export function registerAllTools(
16
- server: McpServer,
17
- db: ClastDatabase,
18
- parser: ParserManager,
19
- watcher: FileWatcher,
20
- commentGen: CommentGenerator,
21
- projectRoot: string
22
- ): void {
23
- registerSearchTool(server, db);
24
- registerCallGraphTool(server, db);
25
- registerFileSummaryTool(server, db);
26
- registerGetContextTool(server, db);
27
- registerStatusTool(server, db, watcher);
28
- registerReindexTool(server, db, parser, projectRoot);
29
- registerCommentTool(server, db, commentGen);
30
- }
@@ -1,82 +0,0 @@
1
- import { z } from 'zod';
2
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import type { ClastDatabase } from '../../db/index.js';
4
-
5
- export function registerCallGraphTool(server: McpServer, db: ClastDatabase): void {
6
- server.tool(
7
- 'clast_call_graph',
8
- 'Get the call graph for a function or method: what it calls (callees) and what calls it (callers). Use this BEFORE refactoring to understand impact — shows the full dependency chain with file paths and line numbers. Supports depth 1-5.',
9
- {
10
- name: z.string().describe('Function or method name to trace'),
11
- depth: z.number().int().min(1).max(5).default(2).describe('How many levels deep to traverse'),
12
- direction: z.enum(['callers', 'callees', 'both']).default('both').describe('Direction to traverse'),
13
- },
14
- async ({ name, depth, direction }) => {
15
- const node = db.queries.getNodeByName(name);
16
- if (node.length === 0) {
17
- return {
18
- content: [{
19
- type: 'text' as const,
20
- text: JSON.stringify({ error: `No symbol found with name "${name}"` }),
21
- }],
22
- };
23
- }
24
-
25
- const result: Record<string, unknown> = {
26
- symbol: {
27
- name: node[0]!.name,
28
- type: node[0]!.type,
29
- filePath: node[0]!.file_path,
30
- startLine: node[0]!.start_line,
31
- endLine: node[0]!.end_line,
32
- signature: node[0]!.signature,
33
- },
34
- };
35
-
36
- if (direction === 'callers' || direction === 'both') {
37
- const callers = db.queries.getCallersOf(name, depth);
38
- result.callers = callers.map(n => ({
39
- name: n.name,
40
- type: n.type,
41
- filePath: n.file_path,
42
- startLine: n.start_line,
43
- signature: n.signature,
44
- }));
45
- }
46
-
47
- if (direction === 'callees' || direction === 'both') {
48
- const callees = db.queries.getCalleesOf(name, depth);
49
- result.callees = callees.map(n => ({
50
- name: n.name,
51
- type: n.type,
52
- filePath: n.file_path,
53
- startLine: n.start_line,
54
- signature: n.signature,
55
- }));
56
- }
57
-
58
- // Also include direct call edge details for the primary node
59
- if (node[0]) {
60
- const edges = db.queries.getCallEdgesForNode(node[0].id);
61
- result.directCalls = {
62
- outgoing: edges.outgoing.map(e => ({
63
- calleeName: e.callee_name,
64
- line: e.line,
65
- resolved: e.callee_id !== null,
66
- })),
67
- incoming: edges.incoming.map(e => ({
68
- callerId: e.caller_id,
69
- line: e.line,
70
- })),
71
- };
72
- }
73
-
74
- return {
75
- content: [{
76
- type: 'text' as const,
77
- text: JSON.stringify(result, null, 2),
78
- }],
79
- };
80
- }
81
- );
82
- }