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/src/types.ts CHANGED
@@ -1,69 +1,137 @@
1
- export type AstNodeType =
1
+ /** Kinds of symbols stored in the index. */
2
+ export type SymbolKind =
2
3
  | 'function'
3
4
  | 'method'
4
5
  | 'class'
5
6
  | 'interface'
6
- | 'enum'
7
- | 'import'
8
- | 'export'
9
- | 'variable'
10
- | 'type_alias'
11
7
  | 'struct'
12
8
  | 'trait'
13
- | 'module';
9
+ | 'enum'
10
+ | 'type'
11
+ | 'namespace'
12
+ | 'constant'
13
+ | 'variable'
14
+ | 'field'
15
+ | 'property'
16
+ | 'macro'
17
+ | 'declaration';
14
18
 
15
- export interface AstNode {
16
- id?: number;
17
- filePath: string;
19
+ export const SYMBOL_KINDS: SymbolKind[] = [
20
+ 'function', 'method', 'class', 'interface', 'struct', 'trait', 'enum', 'type',
21
+ 'namespace', 'constant', 'variable', 'field', 'property', 'macro', 'declaration',
22
+ ];
23
+
24
+ /** Kinds that can contain members (used for outlines and qualified names). */
25
+ export const CONTAINER_KINDS = new Set<SymbolKind>([
26
+ 'class', 'interface', 'struct', 'trait', 'enum', 'namespace',
27
+ ]);
28
+
29
+ export const CALLABLE_KINDS = new Set<SymbolKind>(['function', 'method', 'macro', 'declaration']);
30
+
31
+ /** A symbol as produced by the extractor (before it gets a database id). */
32
+ export interface ExSymbol {
18
33
  name: string;
19
- type: AstNodeType;
34
+ /** Dotted qualified name, e.g. "ParserManager.parseAndStore". */
35
+ qname: string;
36
+ kind: SymbolKind;
37
+ /** Declared return type (bare class name) of a function / method, when it could be read off the signature. */
38
+ ret: string | null;
20
39
  signature: string;
40
+ doc: string;
41
+ /** 1-based line of the definition. */
21
42
  startLine: number;
22
43
  endLine: number;
23
44
  startCol: number;
24
- endCol: number;
25
- parentId: number | null;
26
- language: string;
27
- bodyText: string;
45
+ /** 1-based first line of the leading doc comment / decorators, or 0. */
46
+ docLine: number;
47
+ /** Index of the parent symbol in the same extraction, or -1. */
48
+ parent: number;
49
+ exported: boolean;
50
+ }
51
+
52
+ export interface ExCall {
53
+ /** Index of the calling symbol in the same extraction, or -1 for file level. */
54
+ caller: number;
55
+ name: string;
56
+ line: number;
57
+ /** Receiver text for member calls ("this", "self", "Foo", "db"), if simple. */
58
+ receiver: string | null;
59
+ /** Declared type of the receiver when a type hint was in scope (`Foo` for `x: Foo; x.bar()`). */
60
+ rtype: string | null;
61
+ /** Number of arguments at the call site; -1 when unknown (spread arguments, macros, no argument list). */
62
+ argc: number;
63
+ count: number;
64
+ /**
65
+ * For calls on an expression receiver that is itself a call
66
+ * (`a.b().c()`): index of that receiver-producing call in the same
67
+ * extraction, else -1. The receiver type is then that call's return type.
68
+ */
69
+ via: number;
28
70
  }
29
71
 
30
- export interface CallEdge {
31
- id?: number;
32
- callerId: number;
33
- calleeId: number | null;
34
- calleeName: string;
35
- filePath: string;
72
+ export interface ExImport {
73
+ source: string;
74
+ names: string[];
36
75
  line: number;
37
76
  }
38
77
 
39
- export interface FileInfo {
40
- filePath: string;
41
- hash: string;
42
- language: string;
43
- lastIndexed: number;
44
- nodeCount: number;
78
+ export interface ExHeritage {
79
+ symbol: number;
80
+ name: string;
81
+ /** Module / namespace the supertype was written with (`forms` in `forms.CharField`), if any. */
82
+ qualifier: string | null;
83
+ kind: 'extends' | 'implements';
84
+ /** Number of type arguments the supertype was written with (`Base<T>` -> 1); tells `Foo` from `Foo<T>` in C#. */
85
+ arity: number;
45
86
  }
46
87
 
47
- export interface CommentInfo {
48
- id?: number;
49
- nodeId: number;
50
- text: string;
51
- source: 'original' | 'generated';
52
- generatedAt: number | null;
88
+ /** Everything extracted from a single source file. */
89
+ export interface Extraction {
90
+ lang: string;
91
+ lineCount: number;
92
+ symbols: ExSymbol[];
93
+ calls: ExCall[];
94
+ imports: ExImport[];
95
+ heritage: ExHeritage[];
96
+ /** Name of the symbol exported as the module default (`module.exports = X`), if any. */
97
+ defaultExport: string | null;
98
+ /** Declared field types of class-like symbols: [container, field, type]. */
99
+ fieldTypes: Array<[string, string, string]>;
100
+ /** Identifier occurrences: [name, "12,40,41"] (1-based lines, capped per file). */
101
+ occ: Array<[string, string]>;
102
+ hadError: boolean;
53
103
  }
54
104
 
55
- export interface ParseResult {
56
- nodes: AstNode[];
57
- edges: CallEdge[];
58
- comments: CommentInfo[];
105
+ export interface SymbolRow {
106
+ id: number;
107
+ file_id: number;
108
+ name: string;
109
+ name_lc: string;
110
+ qname: string;
111
+ kind: SymbolKind;
112
+ signature: string;
113
+ doc: string;
114
+ start_line: number;
115
+ end_line: number;
116
+ start_col: number;
117
+ doc_line: number;
118
+ parent_id: number | null;
119
+ exported: number;
120
+ /** Declared return type name, if known. */
121
+ ret: string | null;
122
+ /** Joined from files. */
123
+ path: string;
124
+ lang: string;
59
125
  }
60
126
 
61
- export interface IndexStatus {
62
- filesIndexed: number;
63
- totalNodes: number;
64
- totalEdges: number;
65
- totalComments: number;
66
- lastIndexed: number | null;
67
- watcherActive: boolean;
68
- pendingFiles: number;
127
+ export interface FileRow {
128
+ id: number;
129
+ path: string;
130
+ hash: string;
131
+ mtime: number;
132
+ size: number;
133
+ lang: string;
134
+ line_count: number;
135
+ symbol_count: number;
136
+ indexed_at: number;
69
137
  }
package/src/version.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** Package version (keep in sync with package.json; checked by a test). */
2
+ export const VERSION = '0.2.1';
@@ -1,34 +1,25 @@
1
1
  import { watch, type FSWatcher } from 'chokidar';
2
- import { extname } from 'node:path';
2
+ import { basename, relative, sep } from 'node:path';
3
3
  import { EventEmitter } from 'node:events';
4
- import { getSupportedExtensions } from '../parser/languages.js';
5
- import { buildIgnorePatterns } from './gitignore.js';
6
- import type { ParserManager } from '../parser/index.js';
7
- import type { ClastDatabase } from '../db/index.js';
8
- import type { ClastConfig } from '../config/index.js';
4
+ import type { Indexer } from '../indexer/index.js';
9
5
 
6
+ /**
7
+ * Watches the project and keeps the index current. Events are debounced per
8
+ * file and processed strictly one at a time (the parser and database are not
9
+ * re-entrant).
10
+ */
10
11
  export class FileWatcher extends EventEmitter {
11
12
  private watcher: FSWatcher | null = null;
12
- private projectRoot: string;
13
- private config: ClastConfig;
14
- private db: ClastDatabase;
15
- private parser: ParserManager;
16
- private debounceTimers = new Map<string, ReturnType<typeof setTimeout>>();
17
- private _pendingCount = 0;
18
- private supportedExts: Set<string>;
13
+ private timers = new Map<string, ReturnType<typeof setTimeout>>();
14
+ private queue: Array<{ rel: string; kind: 'change' | 'unlink' }> = [];
15
+ private draining = false;
16
+ private rescanTimer: ReturnType<typeof setTimeout> | null = null;
19
17
 
20
18
  constructor(
21
- projectRoot: string,
22
- config: ClastConfig,
23
- db: ClastDatabase,
24
- parser: ParserManager
19
+ private readonly indexer: Indexer,
20
+ private readonly debounceMs: number
25
21
  ) {
26
22
  super();
27
- this.projectRoot = projectRoot;
28
- this.config = config;
29
- this.db = db;
30
- this.parser = parser;
31
- this.supportedExts = new Set(getSupportedExtensions(config.languages));
32
23
  }
33
24
 
34
25
  get isActive(): boolean {
@@ -36,96 +27,99 @@ export class FileWatcher extends EventEmitter {
36
27
  }
37
28
 
38
29
  get pendingCount(): number {
39
- return this._pendingCount;
30
+ return this.timers.size + this.queue.length;
40
31
  }
41
32
 
42
33
  start(): void {
43
34
  if (this.watcher) return;
44
- if (!this.config.watch.enabled) return;
35
+ const root = this.indexer.root;
36
+ const rules = this.indexer.rules;
45
37
 
46
- const ignoredPatterns = buildIgnorePatterns(this.projectRoot, this.config.ignoredPaths);
47
-
48
- this.watcher = watch(this.projectRoot, {
49
- ignored: [
50
- /(^|[/\\])\./,
51
- ...ignoredPatterns.map(p => {
52
- if (!p.includes('*') && !p.includes('/')) {
53
- return `**/${p}/**`;
54
- }
55
- return p;
56
- }),
57
- ],
38
+ this.watcher = watch(root, {
58
39
  persistent: true,
59
40
  ignoreInitial: true,
41
+ followSymlinks: false,
42
+ awaitWriteFinish: false,
43
+ // Prune ignored directories early so huge trees (node_modules, build
44
+ // output) are never even watched.
45
+ ignored: (path: string, stats?: { isDirectory(): boolean; isFile(): boolean }) => {
46
+ const rel = relative(root, path).split(sep).join('/');
47
+ if (!rel || rel.startsWith('..')) return false;
48
+ const name = basename(path);
49
+ if (name === '.gitignore') return false;
50
+ if (stats?.isFile()) return !rules.isSupported(rel) || rules.isIgnored(rel);
51
+ if (stats?.isDirectory()) return name.startsWith('.') || rules.isIgnored(rel + '/x');
52
+ return false;
53
+ },
60
54
  });
61
55
 
62
- this.watcher.on('add', path => this.handleFileChange(path));
63
- this.watcher.on('change', path => this.handleFileChange(path));
64
- this.watcher.on('unlink', path => this.handleFileDelete(path));
56
+ const toRel = (p: string): string => relative(root, p).split(sep).join('/');
57
+ this.watcher.on('add', p => this.schedule(toRel(p), 'change'));
58
+ this.watcher.on('change', p => this.schedule(toRel(p), 'change'));
59
+ this.watcher.on('unlink', p => this.schedule(toRel(p), 'unlink'));
60
+ this.watcher.on('unlinkDir', () => this.scheduleRescan());
65
61
  this.watcher.on('error', err => {
66
- console.error('[clast] Watcher error:', err);
62
+ this.emit('error', { filePath: '', error: err as Error });
67
63
  });
68
64
  }
69
65
 
70
- stop(): void {
71
- if (!this.watcher) return;
72
-
73
- for (const timer of this.debounceTimers.values()) {
74
- clearTimeout(timer);
75
- }
76
- this.debounceTimers.clear();
77
-
78
- this.watcher.close();
66
+ async stop(): Promise<void> {
67
+ for (const t of this.timers.values()) clearTimeout(t);
68
+ this.timers.clear();
69
+ if (this.rescanTimer) clearTimeout(this.rescanTimer);
70
+ this.queue = [];
71
+ const w = this.watcher;
79
72
  this.watcher = null;
73
+ if (w) await w.close();
80
74
  }
81
75
 
82
- private handleFileChange(filePath: string): void {
83
- const ext = extname(filePath);
84
- if (!this.supportedExts.has(ext)) return;
76
+ /** A changed .gitignore or a removed directory affects many files: rescan. */
77
+ private scheduleRescan(): void {
78
+ if (this.rescanTimer) clearTimeout(this.rescanTimer);
79
+ this.rescanTimer = setTimeout(() => {
80
+ this.rescanTimer = null;
81
+ this.indexer.indexAll().catch(err => this.emit('error', { filePath: '', error: err as Error }));
82
+ }, Math.max(this.debounceMs, 500));
83
+ }
85
84
 
86
- const existing = this.debounceTimers.get(filePath);
85
+ private schedule(rel: string, kind: 'change' | 'unlink'): void {
86
+ if (basename(rel) === '.gitignore') {
87
+ this.scheduleRescan();
88
+ return;
89
+ }
90
+ if (!this.indexer.rules.isSupported(rel)) return;
91
+ const existing = this.timers.get(rel);
87
92
  if (existing) clearTimeout(existing);
88
-
89
- this._pendingCount++;
90
- this.debounceTimers.set(
91
- filePath,
93
+ this.timers.set(
94
+ rel,
92
95
  setTimeout(() => {
93
- this.debounceTimers.delete(filePath);
94
- this._pendingCount--;
95
- this.processFile(filePath);
96
- }, this.config.watch.debounceMs)
96
+ this.timers.delete(rel);
97
+ this.queue.push({ rel, kind });
98
+ void this.drain();
99
+ }, this.debounceMs)
97
100
  );
98
101
  }
99
102
 
100
- private handleFileDelete(filePath: string): void {
101
- const ext = extname(filePath);
102
- if (!this.supportedExts.has(ext)) return;
103
-
104
- const existing = this.debounceTimers.get(filePath);
105
- if (existing) {
106
- clearTimeout(existing);
107
- this.debounceTimers.delete(filePath);
108
- this._pendingCount--;
109
- }
110
-
111
- try {
112
- this.parser.removeFile(filePath, this.projectRoot, this.db);
113
- this.emit('deleted', { filePath });
114
- } catch (err) {
115
- this.emit('error', { filePath, error: err as Error });
116
- }
117
- }
118
-
119
- private async processFile(filePath: string): Promise<void> {
103
+ private async drain(): Promise<void> {
104
+ if (this.draining) return;
105
+ this.draining = true;
120
106
  try {
121
- const parsed = await this.parser.incrementalUpdate(filePath, this.projectRoot, this.db);
122
- if (parsed) {
123
- const relPath = filePath.replace(/\\/g, '/');
124
- const nodes = this.db.queries.getNodesByFile(relPath);
125
- this.emit('indexed', { filePath, nodesFound: nodes.length });
107
+ // Let a bulk index finish first; it will pick up these files anyway.
108
+ await this.indexer.whenIdle();
109
+ while (this.queue.length) {
110
+ const { rel, kind } = this.queue.shift()!;
111
+ try {
112
+ if (kind === 'unlink') {
113
+ if (this.indexer.removeFile(rel)) this.emit('deleted', { filePath: rel });
114
+ } else if (await this.indexer.indexFile(rel)) {
115
+ this.emit('indexed', { filePath: rel });
116
+ }
117
+ } catch (err) {
118
+ this.emit('error', { filePath: rel, error: err as Error });
119
+ }
126
120
  }
127
- } catch (err) {
128
- this.emit('error', { filePath, error: err as Error });
121
+ } finally {
122
+ this.draining = false;
129
123
  }
130
124
  }
131
125
  }
package/tsconfig.json CHANGED
@@ -1,18 +1,21 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES2022",
3
+ "target": "ES2023",
4
4
  "module": "NodeNext",
5
5
  "moduleResolution": "NodeNext",
6
- "lib": ["ES2022"],
6
+ "lib": ["ES2023"],
7
+ "types": ["node"],
7
8
  "outDir": "dist",
8
9
  "rootDir": "src",
9
10
  "strict": true,
11
+ "noUncheckedIndexedAccess": true,
12
+ "noUnusedLocals": true,
13
+ "noUnusedParameters": true,
10
14
  "esModuleInterop": true,
11
15
  "skipLibCheck": true,
12
16
  "forceConsistentCasingInFileNames": true,
13
17
  "resolveJsonModule": true,
14
18
  "declaration": true,
15
- "declarationMap": true,
16
19
  "sourceMap": true
17
20
  },
18
21
  "include": ["src/**/*.ts"],
package/.mcp.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "clast": {
4
- "command": "node",
5
- "args": ["${CLAUDE_PLUGIN_ROOT}/dist/server/index.js"]
6
- }
7
- }
8
- }
@@ -1,9 +0,0 @@
1
- ---
2
- name: clast-status
3
- description: Show Clast AST indexing status for the current repository
4
- allowed-tools: [clast_status]
5
- ---
6
-
7
- Call the `clast_status` tool and present the results to the user in a clear, readable format.
8
- Include: files indexed, total symbols, total call edges, total comments, last indexed time,
9
- watcher status, and any pending files.
@@ -1 +0,0 @@
1
- {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE;QACH,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,EAAE,WA0C5B,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAwBjE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAkB3D;AAED,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAK9E"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAkB,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAIlD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,qBAAa,aAAa;IACxB,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAA+C;IACzD,OAAO,EAAE,OAAO,CAAC;IAExB,OAAO;WAUM,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAgBzD,IAAI,IAAI,IAAI;IASZ,KAAK,IAAI,IAAI;IASb,IAAI,GAAG,IAAI,QAAQ,CAElB;CACF;AAED,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
@@ -1,44 +0,0 @@
1
- import type { Database } from 'sql.js';
2
- import type { FileRow, AstNodeRow, CallEdgeRow, CommentRow, StatusRow } from './types.js';
3
- export declare class Queries {
4
- private db;
5
- constructor(db: Database);
6
- private run;
7
- private get;
8
- private all;
9
- private getLastInsertId;
10
- upsertFile(filePath: string, hash: string, language: string, nodeCount: number): void;
11
- deleteFile(filePath: string): void;
12
- getFileHash(filePath: string): string | null;
13
- getAllFiles(): FileRow[];
14
- insertNode(node: Omit<AstNodeRow, 'id'>): number;
15
- insertNodes(nodes: Omit<AstNodeRow, 'id'>[]): number[];
16
- getNodeById(id: number): AstNodeRow | undefined;
17
- getNodeByName(name: string): AstNodeRow[];
18
- getNodesByFile(filePath: string): AstNodeRow[];
19
- searchNodes(query: string, type?: string, limit?: number): AstNodeRow[];
20
- insertEdge(edge: Omit<CallEdgeRow, 'id'>): number;
21
- insertEdges(edges: Omit<CallEdgeRow, 'id'>[]): void;
22
- getCallersOf(nameOrId: string | number, depth?: number): AstNodeRow[];
23
- getCalleesOf(nameOrId: string | number, depth?: number): AstNodeRow[];
24
- getCallEdgesForNode(nodeId: number): {
25
- outgoing: CallEdgeRow[];
26
- incoming: CallEdgeRow[];
27
- };
28
- insertComment(nodeId: number, text: string, source: 'original' | 'generated'): number;
29
- getCommentsForNode(nodeId: number): CommentRow[];
30
- deleteCommentsBySource(nodeId: number, source: 'original' | 'generated'): void;
31
- getNodeWithContext(name: string): {
32
- nodes: AstNodeRow[];
33
- comments: CommentRow[];
34
- callers: AstNodeRow[];
35
- callees: AstNodeRow[];
36
- };
37
- getStatus(): StatusRow;
38
- deleteNodesForFile(filePath: string): void;
39
- resolveEdges(): number;
40
- getNodesWithoutComments(limit?: number): AstNodeRow[];
41
- /** Flush the in-memory database to a Uint8Array for saving to disk. */
42
- export(): Uint8Array;
43
- }
44
- //# sourceMappingURL=queries.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/db/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE1F,qBAAa,OAAO;IAClB,OAAO,CAAC,EAAE,CAAW;gBAET,EAAE,EAAE,QAAQ;IAIxB,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,GAAG;IAYX,OAAO,CAAC,GAAG;IAaX,OAAO,CAAC,eAAe;IAUvB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAQrF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IASlC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAK5C,WAAW,IAAI,OAAO,EAAE;IAMxB,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,MAAM;IAahD,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE;IAetD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI/C,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE;IAOzC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE;IAO9C,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,UAAU,EAAE;IAgB3E,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,MAAM;IASjD,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,GAAG,IAAI;IAanD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,GAAE,MAAU,GAAG,UAAU,EAAE;IAuBxE,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,GAAE,MAAU,GAAG,UAAU,EAAE;IAyBxE,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;QAAC,QAAQ,EAAE,WAAW,EAAE,CAAA;KAAE;IAczF,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,MAAM;IASrF,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE;IAIhD,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAM9E,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG;QAChC,KAAK,EAAE,UAAU,EAAE,CAAC;QACpB,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,OAAO,EAAE,UAAU,EAAE,CAAC;QACtB,OAAO,EAAE,UAAU,EAAE,CAAC;KACvB;IAmBD,SAAS,IAAI,SAAS;IAoBtB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAM1C,YAAY,IAAI,MAAM;IAyBtB,uBAAuB,CAAC,KAAK,GAAE,MAAY,GAAG,UAAU,EAAE;IAU1D,uEAAuE;IACvE,MAAM,IAAI,UAAU;CAGrB"}