autosnippet 3.0.3 → 3.0.7

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 (45) hide show
  1. package/README.md +85 -240
  2. package/dashboard/dist/assets/{icons-Cdq22n2i.js → icons-eQ_rWCus.js} +97 -102
  3. package/dashboard/dist/assets/index-B3Nnkdxi.js +133 -0
  4. package/dashboard/dist/assets/index-BFNDAqh3.css +1 -0
  5. package/dashboard/dist/index.html +3 -3
  6. package/lib/core/AstAnalyzer.js +2 -2
  7. package/lib/core/ast/ensure-grammars.js +2 -0
  8. package/lib/core/ast/index.js +8 -0
  9. package/lib/core/ast/lang-rust.js +695 -0
  10. package/lib/core/discovery/PythonDiscoverer.js +3 -0
  11. package/lib/core/discovery/RustDiscoverer.js +467 -0
  12. package/lib/core/discovery/index.js +3 -0
  13. package/lib/core/enhancement/django-enhancement.js +169 -3
  14. package/lib/core/enhancement/fastapi-enhancement.js +149 -3
  15. package/lib/core/enhancement/go-grpc-enhancement.js +4 -0
  16. package/lib/core/enhancement/go-web-enhancement.js +6 -0
  17. package/lib/core/enhancement/index.js +5 -0
  18. package/lib/core/enhancement/langchain-enhancement.js +233 -0
  19. package/lib/core/enhancement/ml-enhancement.js +265 -0
  20. package/lib/core/enhancement/nextjs-enhancement.js +219 -0
  21. package/lib/core/enhancement/node-server-enhancement.js +178 -4
  22. package/lib/core/enhancement/react-enhancement.js +165 -4
  23. package/lib/core/enhancement/rust-tokio-enhancement.js +231 -0
  24. package/lib/core/enhancement/rust-web-enhancement.js +256 -0
  25. package/lib/core/enhancement/spring-enhancement.js +2 -0
  26. package/lib/core/enhancement/vue-enhancement.js +143 -2
  27. package/lib/external/ai/AiProvider.js +45 -6
  28. package/lib/external/mcp/handlers/bootstrap/skills.js +2 -0
  29. package/lib/external/mcp/handlers/bootstrap.js +33 -9
  30. package/lib/external/mcp/handlers/guard.js +42 -0
  31. package/lib/http/routes/candidates.js +7 -1
  32. package/lib/service/chat/ChatAgent.js +1 -0
  33. package/lib/service/chat/tools.js +5 -1
  34. package/lib/service/guard/ComplianceReporter.js +20 -7
  35. package/lib/service/guard/GuardCheckEngine.js +156 -5
  36. package/lib/service/guard/SourceFileCollector.js +15 -0
  37. package/package.json +28 -6
  38. package/skills/autosnippet-coldstart/SKILL.md +4 -2
  39. package/skills/autosnippet-concepts/SKILL.md +5 -3
  40. package/skills/autosnippet-reference-rust/SKILL.md +401 -0
  41. package/skills/autosnippet-structure/SKILL.md +1 -1
  42. package/templates/recipes-setup/README.md +2 -2
  43. package/templates/recipes-setup/_template.md +1 -1
  44. package/dashboard/dist/assets/index-ClkyPkDX.js +0 -133
  45. package/dashboard/dist/assets/index-t4QrJwv1.css +0 -1
@@ -0,0 +1,695 @@
1
+ /**
2
+ * @module lang-rust
3
+ * @description Rust AST Walker 插件
4
+ *
5
+ * 提取: struct, enum, trait, impl, function, method, mod, use, const/static
6
+ * 模式: Builder, Newtype, Factory (new/from), Error Handling (Result/Option/?),
7
+ * Async (tokio/async-std), Unsafe block, Derive macro
8
+ */
9
+
10
+ import { createRequire } from 'node:module';
11
+
12
+ const require = createRequire(import.meta.url);
13
+
14
+ function walkRust(root, ctx) {
15
+ for (let i = 0; i < root.namedChildCount; i++) {
16
+ const child = root.namedChild(i);
17
+ _walkNode(child, ctx);
18
+ }
19
+ }
20
+
21
+ function _walkNode(node, ctx) {
22
+ switch (node.type) {
23
+ case 'use_declaration': {
24
+ _parseUseDecl(node, ctx);
25
+ break;
26
+ }
27
+
28
+ case 'mod_item': {
29
+ _parseModItem(node, ctx);
30
+ break;
31
+ }
32
+
33
+ case 'struct_item': {
34
+ _parseStruct(node, ctx);
35
+ break;
36
+ }
37
+
38
+ case 'enum_item': {
39
+ _parseEnum(node, ctx);
40
+ break;
41
+ }
42
+
43
+ case 'trait_item': {
44
+ _parseTrait(node, ctx);
45
+ break;
46
+ }
47
+
48
+ case 'impl_item': {
49
+ _parseImpl(node, ctx);
50
+ break;
51
+ }
52
+
53
+ case 'function_item': {
54
+ const funcInfo = _parseFunctionItem(node);
55
+ if (funcInfo) {
56
+ ctx.methods.push(funcInfo);
57
+ }
58
+ break;
59
+ }
60
+
61
+ case 'const_item':
62
+ case 'static_item': {
63
+ _parseConstStatic(node, ctx);
64
+ break;
65
+ }
66
+
67
+ case 'type_item': {
68
+ _parseTypeAlias(node, ctx);
69
+ break;
70
+ }
71
+
72
+ case 'macro_definition': {
73
+ _parseMacroDef(node, ctx);
74
+ break;
75
+ }
76
+
77
+ // 带 attribute 的顶层项
78
+ case 'attribute_item':
79
+ case 'inner_attribute_item':
80
+ break;
81
+
82
+ default:
83
+ break;
84
+ }
85
+ }
86
+
87
+ // ── Use Declaration ──────────────────────────────────────────
88
+
89
+ function _parseUseDecl(node, ctx) {
90
+ // 提取 use 路径文本
91
+ const argNode = node.namedChildren.find(
92
+ (c) => c.type === 'use_wildcard' ||
93
+ c.type === 'use_list' ||
94
+ c.type === 'use_as_clause' ||
95
+ c.type === 'scoped_identifier' ||
96
+ c.type === 'identifier' ||
97
+ c.type === 'scoped_use_list'
98
+ );
99
+ if (argNode) {
100
+ ctx.imports.push(argNode.text);
101
+ }
102
+ }
103
+
104
+ // ── Mod Item ─────────────────────────────────────────────────
105
+
106
+ function _parseModItem(node, ctx) {
107
+ const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
108
+ if (nameNode) {
109
+ ctx.metadata = ctx.metadata || {};
110
+ ctx.metadata.modules = ctx.metadata.modules || [];
111
+ ctx.metadata.modules.push(nameNode.text);
112
+ }
113
+ // 如果是 inline mod { ... },递归内部声明
114
+ const body = node.namedChildren.find((c) => c.type === 'declaration_list');
115
+ if (body) {
116
+ for (let i = 0; i < body.namedChildCount; i++) {
117
+ _walkNode(body.namedChild(i), ctx);
118
+ }
119
+ }
120
+ }
121
+
122
+ // ── Struct ───────────────────────────────────────────────────
123
+
124
+ function _parseStruct(node, ctx) {
125
+ const nameNode = node.namedChildren.find((c) => c.type === 'type_identifier');
126
+ const name = nameNode?.text || 'Unknown';
127
+
128
+ const fields = [];
129
+ const derives = _extractDerives(node);
130
+
131
+ // Named fields (struct Foo { field: Type })
132
+ const fieldList = node.namedChildren.find((c) => c.type === 'field_declaration_list');
133
+ if (fieldList) {
134
+ for (let i = 0; i < fieldList.namedChildCount; i++) {
135
+ const field = fieldList.namedChild(i);
136
+ if (field.type !== 'field_declaration') continue;
137
+ const fieldId = field.namedChildren.find((c) => c.type === 'field_identifier');
138
+ if (fieldId) {
139
+ const isPublic = _hasPubVisibility(field);
140
+ ctx.properties.push({
141
+ name: fieldId.text,
142
+ className: name,
143
+ isExported: isPublic,
144
+ line: field.startPosition.row + 1,
145
+ });
146
+ fields.push(fieldId.text);
147
+ }
148
+ }
149
+ }
150
+
151
+ // Tuple struct fields (struct Foo(Type1, Type2))
152
+ const orderedFields = node.namedChildren.find((c) => c.type === 'ordered_field_declaration_list');
153
+ if (orderedFields) {
154
+ let idx = 0;
155
+ for (let i = 0; i < orderedFields.namedChildCount; i++) {
156
+ const child = orderedFields.namedChild(i);
157
+ // Skip visibility markers
158
+ if (child.type === 'visibility_modifier') continue;
159
+ if (child.type.includes('type') || child.type === 'primitive_type' ||
160
+ child.type === 'scoped_type_identifier' || child.type === 'type_identifier' ||
161
+ child.type === 'generic_type' || child.type === 'reference_type') {
162
+ fields.push(`${idx}`);
163
+ idx++;
164
+ }
165
+ }
166
+ }
167
+
168
+ ctx.classes.push({
169
+ name,
170
+ kind: 'struct',
171
+ superclass: null,
172
+ protocols: [],
173
+ derives,
174
+ fieldCount: fields.length,
175
+ line: node.startPosition.row + 1,
176
+ endLine: node.endPosition.row + 1,
177
+ });
178
+ }
179
+
180
+ // ── Enum ─────────────────────────────────────────────────────
181
+
182
+ function _parseEnum(node, ctx) {
183
+ const nameNode = node.namedChildren.find((c) => c.type === 'type_identifier');
184
+ const name = nameNode?.text || 'Unknown';
185
+ const derives = _extractDerives(node);
186
+
187
+ const variants = [];
188
+ const body = node.namedChildren.find((c) => c.type === 'enum_variant_list');
189
+ if (body) {
190
+ for (let i = 0; i < body.namedChildCount; i++) {
191
+ const variant = body.namedChild(i);
192
+ if (variant.type === 'enum_variant') {
193
+ const variantName = variant.namedChildren.find((c) => c.type === 'identifier');
194
+ if (variantName) {
195
+ variants.push(variantName.text);
196
+ }
197
+ }
198
+ }
199
+ }
200
+
201
+ ctx.classes.push({
202
+ name,
203
+ kind: 'enum',
204
+ superclass: null,
205
+ protocols: [],
206
+ derives,
207
+ variants,
208
+ variantCount: variants.length,
209
+ line: node.startPosition.row + 1,
210
+ endLine: node.endPosition.row + 1,
211
+ });
212
+ }
213
+
214
+ // ── Trait ─────────────────────────────────────────────────────
215
+
216
+ function _parseTrait(node, ctx) {
217
+ const nameNode = node.namedChildren.find((c) => c.type === 'type_identifier');
218
+ const name = nameNode?.text || 'Unknown';
219
+
220
+ const methods = [];
221
+ const superTraits = [];
222
+
223
+ // Trait bounds (trait Foo: Bar + Baz)
224
+ const bounds = node.namedChildren.find((c) => c.type === 'trait_bounds');
225
+ if (bounds) {
226
+ for (let i = 0; i < bounds.namedChildCount; i++) {
227
+ const bound = bounds.namedChild(i);
228
+ if (bound.type === 'type_identifier' || bound.type === 'scoped_type_identifier' ||
229
+ bound.type === 'generic_type') {
230
+ superTraits.push(bound.text);
231
+ }
232
+ }
233
+ }
234
+
235
+ // Trait body — collect method signatures
236
+ const body = node.namedChildren.find((c) => c.type === 'declaration_list');
237
+ if (body) {
238
+ for (let i = 0; i < body.namedChildCount; i++) {
239
+ const item = body.namedChild(i);
240
+ if (item.type === 'function_signature_item' || item.type === 'function_item') {
241
+ const methodName = item.namedChildren.find((c) => c.type === 'identifier');
242
+ if (methodName) {
243
+ methods.push(methodName.text);
244
+ }
245
+ }
246
+ }
247
+ }
248
+
249
+ ctx.protocols.push({
250
+ name,
251
+ inherits: superTraits,
252
+ methods,
253
+ line: node.startPosition.row + 1,
254
+ endLine: node.endPosition.row + 1,
255
+ });
256
+ }
257
+
258
+ // ── Impl Block ───────────────────────────────────────────────
259
+
260
+ function _parseImpl(node, ctx) {
261
+ // impl Type { ... } 或 impl Trait for Type { ... }
262
+ let selfType = null;
263
+ let traitName = null;
264
+
265
+ const typeIdNodes = node.namedChildren.filter(
266
+ (c) => c.type === 'type_identifier' || c.type === 'scoped_type_identifier' ||
267
+ c.type === 'generic_type'
268
+ );
269
+
270
+ // 检查是否有 "for" — trait impl
271
+ const hasFor = node.children?.some((c) => c.type === 'for');
272
+
273
+ if (hasFor && typeIdNodes.length >= 2) {
274
+ traitName = typeIdNodes[0]?.text;
275
+ selfType = typeIdNodes[1]?.text;
276
+ } else if (typeIdNodes.length >= 1) {
277
+ selfType = typeIdNodes[0]?.text;
278
+ }
279
+
280
+ const body = node.namedChildren.find((c) => c.type === 'declaration_list');
281
+ if (!body || !selfType) return;
282
+
283
+ for (let i = 0; i < body.namedChildCount; i++) {
284
+ const item = body.namedChild(i);
285
+ if (item.type === 'function_item') {
286
+ const methodInfo = _parseImplMethod(item, selfType, traitName);
287
+ if (methodInfo) {
288
+ ctx.methods.push(methodInfo);
289
+ }
290
+ }
291
+ }
292
+ }
293
+
294
+ function _parseImplMethod(node, selfType, traitName) {
295
+ const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
296
+ const name = nameNode?.text;
297
+ if (!name) return null;
298
+
299
+ const params = node.namedChildren.find((c) => c.type === 'parameters');
300
+ const { paramCount, hasSelfParam } = params ? _countRustParams(params) : { paramCount: 0, hasSelfParam: false };
301
+
302
+ const isPublic = _hasPubVisibility(node);
303
+ const isAsync = node.children?.some((c) => c.text === 'async') || false;
304
+
305
+ const body = node.namedChildren.find((c) => c.type === 'block');
306
+ const bodyLines = body ? body.endPosition.row - body.startPosition.row + 1 : 0;
307
+ const complexity = body ? _estimateComplexity(body) : 1;
308
+ const nestingDepth = body ? _maxNesting(body, 0) : 0;
309
+
310
+ return {
311
+ name,
312
+ className: selfType,
313
+ isClassMethod: !hasSelfParam, // 无 self → associated function (static)
314
+ isExported: isPublic,
315
+ isAsync,
316
+ traitImpl: traitName || null,
317
+ paramCount,
318
+ bodyLines,
319
+ complexity,
320
+ nestingDepth,
321
+ line: node.startPosition.row + 1,
322
+ kind: 'definition',
323
+ };
324
+ }
325
+
326
+ // ── Function Item (free fn) ──────────────────────────────────
327
+
328
+ function _parseFunctionItem(node) {
329
+ const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
330
+ const name = nameNode?.text;
331
+ if (!name) return null;
332
+
333
+ const params = node.namedChildren.find((c) => c.type === 'parameters');
334
+ const { paramCount } = params ? _countRustParams(params) : { paramCount: 0 };
335
+
336
+ const isPublic = _hasPubVisibility(node);
337
+ const isAsync = node.children?.some((c) => c.text === 'async') || false;
338
+ const isUnsafe = node.children?.some((c) => c.text === 'unsafe') || false;
339
+
340
+ const body = node.namedChildren.find((c) => c.type === 'block');
341
+ const bodyLines = body ? body.endPosition.row - body.startPosition.row + 1 : 0;
342
+ const complexity = body ? _estimateComplexity(body) : 1;
343
+ const nestingDepth = body ? _maxNesting(body, 0) : 0;
344
+
345
+ return {
346
+ name,
347
+ className: null,
348
+ isClassMethod: true, // free function → "static" equivalent
349
+ isExported: isPublic,
350
+ isAsync,
351
+ isUnsafe,
352
+ paramCount,
353
+ bodyLines,
354
+ complexity,
355
+ nestingDepth,
356
+ line: node.startPosition.row + 1,
357
+ kind: 'definition',
358
+ };
359
+ }
360
+
361
+ // ── Const / Static ───────────────────────────────────────────
362
+
363
+ function _parseConstStatic(node, ctx) {
364
+ const isConst = node.type === 'const_item';
365
+ const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
366
+ if (nameNode) {
367
+ ctx.properties.push({
368
+ name: nameNode.text,
369
+ className: null,
370
+ isExported: _hasPubVisibility(node),
371
+ isConst,
372
+ isStatic: !isConst,
373
+ line: node.startPosition.row + 1,
374
+ });
375
+ }
376
+ }
377
+
378
+ // ── Type Alias ───────────────────────────────────────────────
379
+
380
+ function _parseTypeAlias(node, ctx) {
381
+ const nameNode = node.namedChildren.find((c) => c.type === 'type_identifier');
382
+ if (nameNode) {
383
+ ctx.classes.push({
384
+ name: nameNode.text,
385
+ kind: 'type-alias',
386
+ line: node.startPosition.row + 1,
387
+ endLine: node.endPosition.row + 1,
388
+ });
389
+ }
390
+ }
391
+
392
+ // ── Macro Definition ─────────────────────────────────────────
393
+
394
+ function _parseMacroDef(node, ctx) {
395
+ const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
396
+ if (nameNode) {
397
+ ctx.metadata = ctx.metadata || {};
398
+ ctx.metadata.macros = ctx.metadata.macros || [];
399
+ ctx.metadata.macros.push({
400
+ name: nameNode.text,
401
+ line: node.startPosition.row + 1,
402
+ });
403
+ }
404
+ }
405
+
406
+ // ── Rust Pattern Detection ───────────────────────────────────
407
+
408
+ function detectRustPatterns(root, lang, methods, properties, classes) {
409
+ const patterns = [];
410
+
411
+ // 构建 type → methods 索引
412
+ const typeMethodMap = {};
413
+ for (const m of methods) {
414
+ if (m.className) {
415
+ if (!typeMethodMap[m.className]) {
416
+ typeMethodMap[m.className] = [];
417
+ }
418
+ typeMethodMap[m.className].push(m);
419
+ }
420
+ }
421
+
422
+ // Builder pattern: struct 有 builder() 或一系列链式 with_*/set_* 方法
423
+ for (const [typeName, methodList] of Object.entries(typeMethodMap)) {
424
+ const hasBuilder = methodList.some((m) => m.name === 'builder' || m.name === 'build');
425
+ const chainMethods = methodList.filter((m) =>
426
+ /^(?:with_|set_|add_)/.test(m.name)
427
+ );
428
+ if (hasBuilder || chainMethods.length >= 3) {
429
+ patterns.push({
430
+ type: 'builder',
431
+ className: typeName,
432
+ confidence: hasBuilder ? 0.9 : 0.7,
433
+ });
434
+ }
435
+ }
436
+
437
+ // Factory: new() / from() / create() associated functions
438
+ for (const m of methods) {
439
+ if (
440
+ m.className &&
441
+ m.isClassMethod && // associated function (no self)
442
+ /^(?:new|from|create|open|connect|with_capacity|default)$/.test(m.name)
443
+ ) {
444
+ patterns.push({
445
+ type: 'factory',
446
+ className: m.className,
447
+ methodName: m.name,
448
+ line: m.line,
449
+ confidence: 0.85,
450
+ });
451
+ }
452
+ }
453
+
454
+ // Newtype pattern: struct with single field
455
+ for (const cls of classes) {
456
+ if (cls.kind === 'struct' && cls.fieldCount === 1) {
457
+ patterns.push({
458
+ type: 'newtype',
459
+ className: cls.name,
460
+ line: cls.line,
461
+ confidence: 0.75,
462
+ });
463
+ }
464
+ }
465
+
466
+ // Error enum pattern: enum with Error/Err suffix or derives thiserror
467
+ for (const cls of classes) {
468
+ if (cls.kind === 'enum' && /(?:Error|Err)$/.test(cls.name)) {
469
+ patterns.push({
470
+ type: 'error-enum',
471
+ className: cls.name,
472
+ variantCount: cls.variantCount || 0,
473
+ line: cls.line,
474
+ confidence: 0.9,
475
+ });
476
+ }
477
+ }
478
+
479
+ // Trait impl richness: types with many methods suggest impl-heavy design
480
+ for (const [typeName, methodList] of Object.entries(typeMethodMap)) {
481
+ if (methodList.length >= 3) {
482
+ const traitImpls = new Set(methodList.filter((m) => m.traitImpl).map((m) => m.traitImpl));
483
+ patterns.push({
484
+ type: 'impl-rich',
485
+ className: typeName,
486
+ methodCount: methodList.length,
487
+ traitImplCount: traitImpls.size,
488
+ confidence: 0.7,
489
+ });
490
+ }
491
+ }
492
+
493
+ // Unsafe usage
494
+ _detectUnsafe(root, patterns);
495
+
496
+ // Async usage
497
+ _detectAsync(root, patterns);
498
+
499
+ // Derive macro analysis
500
+ _detectDerives(classes, patterns);
501
+
502
+ return patterns;
503
+ }
504
+
505
+ function _detectUnsafe(root, patterns) {
506
+ let count = 0;
507
+ function walk(node) {
508
+ if (node.type === 'unsafe_block') {
509
+ count++;
510
+ }
511
+ for (let i = 0; i < node.namedChildCount; i++) {
512
+ walk(node.namedChild(i));
513
+ }
514
+ }
515
+ walk(root);
516
+ if (count > 0) {
517
+ patterns.push({
518
+ type: 'unsafe',
519
+ count,
520
+ confidence: 0.95,
521
+ });
522
+ }
523
+ }
524
+
525
+ function _detectAsync(root, patterns) {
526
+ let asyncFnCount = 0;
527
+ let awaitCount = 0;
528
+ function walk(node) {
529
+ if (node.type === 'function_item' || node.type === 'function_signature_item') {
530
+ const isAsync = node.children?.some((c) => c.text === 'async');
531
+ if (isAsync) asyncFnCount++;
532
+ }
533
+ if (node.type === 'await_expression') {
534
+ awaitCount++;
535
+ }
536
+ for (let i = 0; i < node.namedChildCount; i++) {
537
+ walk(node.namedChild(i));
538
+ }
539
+ }
540
+ walk(root);
541
+ if (asyncFnCount > 0 || awaitCount > 0) {
542
+ patterns.push({
543
+ type: 'async',
544
+ asyncFunctions: asyncFnCount,
545
+ awaitExpressions: awaitCount,
546
+ confidence: 0.9,
547
+ });
548
+ }
549
+ }
550
+
551
+ function _detectDerives(classes, patterns) {
552
+ const deriveCounts = {};
553
+ for (const cls of classes) {
554
+ if (cls.derives) {
555
+ for (const d of cls.derives) {
556
+ deriveCounts[d] = (deriveCounts[d] || 0) + 1;
557
+ }
558
+ }
559
+ }
560
+ const commonDerives = Object.entries(deriveCounts)
561
+ .filter(([, count]) => count >= 2)
562
+ .map(([name, count]) => ({ name, count }));
563
+
564
+ if (commonDerives.length > 0) {
565
+ patterns.push({
566
+ type: 'derive-usage',
567
+ derives: commonDerives,
568
+ confidence: 0.8,
569
+ });
570
+ }
571
+ }
572
+
573
+ // ── Helper: Extract #[derive(...)] ──────────────────────────
574
+
575
+ function _extractDerives(node) {
576
+ const derives = [];
577
+ // Look at preceding siblings (attribute_item nodes)
578
+ if (node.parent) {
579
+ const siblings = [];
580
+ for (let i = 0; i < node.parent.namedChildCount; i++) {
581
+ const sib = node.parent.namedChild(i);
582
+ if (sib === node) break;
583
+ siblings.push(sib);
584
+ }
585
+ // Collect attribute_item nodes immediately before this node
586
+ for (let i = siblings.length - 1; i >= 0; i--) {
587
+ const sib = siblings[i];
588
+ if (sib.type !== 'attribute_item') break;
589
+ const text = sib.text;
590
+ const deriveMatch = text.match(/#\[derive\(([^)]+)\)\]/);
591
+ if (deriveMatch) {
592
+ const items = deriveMatch[1].split(',').map((s) => s.trim());
593
+ derives.push(...items);
594
+ }
595
+ }
596
+ }
597
+ return derives;
598
+ }
599
+
600
+ // ── Helper: Visibility ──────────────────────────────────────
601
+
602
+ function _hasPubVisibility(node) {
603
+ return node.children?.some(
604
+ (c) => c.type === 'visibility_modifier' || c.text === 'pub'
605
+ ) || false;
606
+ }
607
+
608
+ // ── Helper: Count Parameters ────────────────────────────────
609
+
610
+ function _countRustParams(paramList) {
611
+ let paramCount = 0;
612
+ let hasSelfParam = false;
613
+
614
+ for (let i = 0; i < paramList.namedChildCount; i++) {
615
+ const child = paramList.namedChild(i);
616
+ if (child.type === 'self_parameter') {
617
+ hasSelfParam = true;
618
+ // Don't count self in paramCount
619
+ } else if (child.type === 'parameter') {
620
+ paramCount++;
621
+ }
622
+ }
623
+ return { paramCount, hasSelfParam };
624
+ }
625
+
626
+ // ── Utility: Complexity ─────────────────────────────────────
627
+
628
+ function _estimateComplexity(node) {
629
+ let complexity = 1;
630
+ const BRANCH_TYPES = new Set([
631
+ 'if_expression',
632
+ 'if_let_expression',
633
+ 'for_expression',
634
+ 'while_expression',
635
+ 'while_let_expression',
636
+ 'loop_expression',
637
+ 'match_expression',
638
+ 'match_arm',
639
+ ]);
640
+ function walk(n) {
641
+ if (BRANCH_TYPES.has(n.type)) {
642
+ complexity++;
643
+ }
644
+ if (n.type === 'binary_expression') {
645
+ const op = n.children?.find((c) => c.text === '&&' || c.text === '||');
646
+ if (op) {
647
+ complexity++;
648
+ }
649
+ }
650
+ for (let i = 0; i < n.namedChildCount; i++) {
651
+ walk(n.namedChild(i));
652
+ }
653
+ }
654
+ walk(node);
655
+ return complexity;
656
+ }
657
+
658
+ function _maxNesting(node, depth) {
659
+ const NESTING_TYPES = new Set([
660
+ 'if_expression',
661
+ 'if_let_expression',
662
+ 'for_expression',
663
+ 'while_expression',
664
+ 'while_let_expression',
665
+ 'loop_expression',
666
+ 'match_expression',
667
+ 'block',
668
+ ]);
669
+ let max = depth;
670
+ const nextDepth = NESTING_TYPES.has(node.type) ? depth + 1 : depth;
671
+ for (let i = 0; i < node.namedChildCount; i++) {
672
+ const childMax = _maxNesting(node.namedChild(i), nextDepth);
673
+ if (childMax > max) {
674
+ max = childMax;
675
+ }
676
+ }
677
+ return max;
678
+ }
679
+
680
+ // ── Plugin Export ────────────────────────────────────────────
681
+
682
+ let _grammar = null;
683
+ function getGrammar() {
684
+ if (!_grammar) {
685
+ _grammar = require('tree-sitter-rust');
686
+ }
687
+ return _grammar;
688
+ }
689
+
690
+ export const plugin = {
691
+ getGrammar,
692
+ walk: walkRust,
693
+ detectPatterns: detectRustPatterns,
694
+ extensions: ['.rs'],
695
+ };
@@ -214,6 +214,9 @@ export class PythonDiscoverer extends ProjectDiscoverer {
214
214
  if (deps.has('fastapi')) {
215
215
  return 'fastapi';
216
216
  }
217
+ if (deps.has('langchain') || deps.has('langchain-core') || deps.has('langgraph') || deps.has('llama-index') || deps.has('llama_index')) {
218
+ return 'langchain';
219
+ }
217
220
  if (deps.has('torch') || deps.has('tensorflow')) {
218
221
  return 'ml';
219
222
  }