autosnippet 3.0.13 → 3.1.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 (100) hide show
  1. package/bin/api-server.js +2 -0
  2. package/bin/cli.js +24 -19
  3. package/config/default.json +1 -1
  4. package/lib/bootstrap.js +4 -4
  5. package/lib/cli/SetupService.js +29 -29
  6. package/lib/cli/UpgradeService.js +3 -2
  7. package/lib/core/AstAnalyzer.js +1 -1
  8. package/lib/core/ast/ensure-grammars.js +1 -1
  9. package/lib/core/ast/index.js +62 -11
  10. package/lib/core/ast/lang-dart.js +27 -21
  11. package/lib/core/ast/lang-go.js +6 -20
  12. package/lib/core/ast/lang-rust.js +53 -28
  13. package/lib/core/ast/parser-init.js +9 -5
  14. package/lib/core/discovery/DartDiscoverer.js +4 -10
  15. package/lib/core/discovery/GoDiscoverer.js +45 -25
  16. package/lib/core/discovery/NodeDiscoverer.js +1 -3
  17. package/lib/core/discovery/PythonDiscoverer.js +7 -1
  18. package/lib/core/discovery/RustDiscoverer.js +111 -38
  19. package/lib/core/discovery/index.js +2 -2
  20. package/lib/core/enhancement/django-enhancement.js +10 -4
  21. package/lib/core/enhancement/fastapi-enhancement.js +16 -9
  22. package/lib/core/enhancement/go-grpc-enhancement.js +2 -1
  23. package/lib/core/enhancement/go-web-enhancement.js +3 -6
  24. package/lib/core/enhancement/ml-enhancement.js +6 -3
  25. package/lib/core/enhancement/nextjs-enhancement.js +17 -7
  26. package/lib/core/enhancement/node-server-enhancement.js +4 -2
  27. package/lib/core/enhancement/react-enhancement.js +6 -3
  28. package/lib/core/enhancement/rust-tokio-enhancement.js +6 -2
  29. package/lib/core/enhancement/rust-web-enhancement.js +13 -7
  30. package/lib/core/enhancement/vue-enhancement.js +10 -5
  31. package/lib/external/ai/AiFactory.js +3 -1
  32. package/lib/external/ai/AiProvider.js +3 -1
  33. package/lib/external/mcp/McpServer.js +2 -0
  34. package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +1 -2
  35. package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +7 -1
  36. package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +55 -26
  37. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +8 -8
  38. package/lib/external/mcp/handlers/bootstrap/refine.js +3 -1
  39. package/lib/external/mcp/handlers/bootstrap.js +4 -10
  40. package/lib/external/mcp/handlers/browse.js +6 -2
  41. package/lib/external/mcp/handlers/guard.js +6 -2
  42. package/lib/external/mcp/handlers/skill.js +6 -2
  43. package/lib/http/HttpServer.js +1 -1
  44. package/lib/http/routes/candidates.js +3 -1
  45. package/lib/http/routes/extract.js +4 -5
  46. package/lib/http/routes/guardRules.js +1 -1
  47. package/lib/http/routes/modules.js +9 -3
  48. package/lib/http/routes/skills.js +54 -6
  49. package/lib/http/routes/violations.js +4 -3
  50. package/lib/infrastructure/external/ClipboardManager.js +24 -7
  51. package/lib/infrastructure/external/NativeUi.js +3 -1
  52. package/lib/infrastructure/external/OpenBrowser.js +1 -0
  53. package/lib/infrastructure/external/XcodeAutomation.js +5 -5
  54. package/lib/infrastructure/vector/IndexingPipeline.js +14 -5
  55. package/lib/injection/ServiceContainer.js +34 -11
  56. package/lib/platform/ios/index.js +20 -25
  57. package/lib/platform/ios/routes/spm.js +6 -3
  58. package/lib/platform/ios/snippet/PlaceholderConverter.js +6 -2
  59. package/lib/platform/ios/snippet/XcodeCodec.js +4 -2
  60. package/lib/platform/ios/spm/SpmDiscoverer.js +1 -1
  61. package/lib/platform/ios/spm/SpmService.js +3 -1
  62. package/lib/platform/ios/xcode/XcodeIntegration.js +10 -12
  63. package/lib/platform/ios/xcode/XcodeWriteUtils.js +6 -1
  64. package/lib/service/automation/FileWatcher.js +1 -3
  65. package/lib/service/automation/handlers/CreateHandler.js +3 -5
  66. package/lib/service/automation/handlers/GuardHandler.js +11 -32
  67. package/lib/service/automation/handlers/SearchHandler.js +9 -9
  68. package/lib/service/chat/CandidateGuardrail.js +11 -6
  69. package/lib/service/chat/ChatAgent.js +31 -22
  70. package/lib/service/chat/HandoffProtocol.js +5 -2
  71. package/lib/service/chat/tools/composite.js +3 -2
  72. package/lib/service/chat/tools/index.js +60 -71
  73. package/lib/service/chat/tools/infrastructure.js +9 -4
  74. package/lib/service/chat/tools/lifecycle.js +22 -5
  75. package/lib/service/chat/tools/project-access.js +5 -9
  76. package/lib/service/chat/tools.js +1 -2
  77. package/lib/service/cursor/AgentInstructionsGenerator.js +33 -15
  78. package/lib/service/cursor/CursorDeliveryPipeline.js +2 -1
  79. package/lib/service/cursor/KnowledgeCompressor.js +16 -7
  80. package/lib/service/guard/ComplianceReporter.js +5 -2
  81. package/lib/service/guard/GuardCheckEngine.js +53 -26
  82. package/lib/service/guard/GuardCodeChecks.js +217 -188
  83. package/lib/service/guard/GuardCrossFileChecks.js +203 -184
  84. package/lib/service/guard/GuardPatternUtils.js +17 -10
  85. package/lib/service/module/ModuleService.js +180 -56
  86. package/lib/service/recipe/RecipeCandidateValidator.js +11 -8
  87. package/lib/service/snippet/SnippetFactory.js +3 -3
  88. package/lib/service/snippet/SnippetInstaller.js +35 -11
  89. package/lib/service/snippet/codecs/VSCodeCodec.js +2 -2
  90. package/lib/service/wiki/WikiGenerator.js +67 -40
  91. package/lib/service/wiki/WikiRenderers.js +105 -80
  92. package/lib/service/wiki/WikiUtils.js +217 -80
  93. package/lib/shared/LanguageService.js +111 -53
  94. package/lib/shared/PathGuard.js +0 -8
  95. package/package.json +3 -9
  96. package/scripts/bench-real-projects.mjs +29 -29
  97. package/scripts/generate-recipe-drafts.js +17 -27
  98. package/scripts/init-snippets.js +43 -24
  99. package/scripts/install-vscode-copilot.js +3 -19
  100. package/scripts/setup-mcp-config.js +0 -4
@@ -85,12 +85,13 @@ function _walkNode(node, ctx) {
85
85
  function _parseUseDecl(node, ctx) {
86
86
  // 提取 use 路径文本
87
87
  const argNode = node.namedChildren.find(
88
- (c) => c.type === 'use_wildcard' ||
89
- c.type === 'use_list' ||
90
- c.type === 'use_as_clause' ||
91
- c.type === 'scoped_identifier' ||
92
- c.type === 'identifier' ||
93
- c.type === 'scoped_use_list'
88
+ (c) =>
89
+ c.type === 'use_wildcard' ||
90
+ c.type === 'use_list' ||
91
+ c.type === 'use_as_clause' ||
92
+ c.type === 'scoped_identifier' ||
93
+ c.type === 'identifier' ||
94
+ c.type === 'scoped_use_list'
94
95
  );
95
96
  if (argNode) {
96
97
  ctx.imports.push(argNode.text);
@@ -129,7 +130,9 @@ function _parseStruct(node, ctx) {
129
130
  if (fieldList) {
130
131
  for (let i = 0; i < fieldList.namedChildCount; i++) {
131
132
  const field = fieldList.namedChild(i);
132
- if (field.type !== 'field_declaration') continue;
133
+ if (field.type !== 'field_declaration') {
134
+ continue;
135
+ }
133
136
  const fieldId = field.namedChildren.find((c) => c.type === 'field_identifier');
134
137
  if (fieldId) {
135
138
  const isPublic = _hasPubVisibility(field);
@@ -151,10 +154,17 @@ function _parseStruct(node, ctx) {
151
154
  for (let i = 0; i < orderedFields.namedChildCount; i++) {
152
155
  const child = orderedFields.namedChild(i);
153
156
  // Skip visibility markers
154
- if (child.type === 'visibility_modifier') continue;
155
- if (child.type.includes('type') || child.type === 'primitive_type' ||
156
- child.type === 'scoped_type_identifier' || child.type === 'type_identifier' ||
157
- child.type === 'generic_type' || child.type === 'reference_type') {
157
+ if (child.type === 'visibility_modifier') {
158
+ continue;
159
+ }
160
+ if (
161
+ child.type.includes('type') ||
162
+ child.type === 'primitive_type' ||
163
+ child.type === 'scoped_type_identifier' ||
164
+ child.type === 'type_identifier' ||
165
+ child.type === 'generic_type' ||
166
+ child.type === 'reference_type'
167
+ ) {
158
168
  fields.push(`${idx}`);
159
169
  idx++;
160
170
  }
@@ -221,8 +231,11 @@ function _parseTrait(node, ctx) {
221
231
  if (bounds) {
222
232
  for (let i = 0; i < bounds.namedChildCount; i++) {
223
233
  const bound = bounds.namedChild(i);
224
- if (bound.type === 'type_identifier' || bound.type === 'scoped_type_identifier' ||
225
- bound.type === 'generic_type') {
234
+ if (
235
+ bound.type === 'type_identifier' ||
236
+ bound.type === 'scoped_type_identifier' ||
237
+ bound.type === 'generic_type'
238
+ ) {
226
239
  superTraits.push(bound.text);
227
240
  }
228
241
  }
@@ -259,8 +272,10 @@ function _parseImpl(node, ctx) {
259
272
  let traitName = null;
260
273
 
261
274
  const typeIdNodes = node.namedChildren.filter(
262
- (c) => c.type === 'type_identifier' || c.type === 'scoped_type_identifier' ||
263
- c.type === 'generic_type'
275
+ (c) =>
276
+ c.type === 'type_identifier' ||
277
+ c.type === 'scoped_type_identifier' ||
278
+ c.type === 'generic_type'
264
279
  );
265
280
 
266
281
  // 检查是否有 "for" — trait impl
@@ -274,7 +289,9 @@ function _parseImpl(node, ctx) {
274
289
  }
275
290
 
276
291
  const body = node.namedChildren.find((c) => c.type === 'declaration_list');
277
- if (!body || !selfType) return;
292
+ if (!body || !selfType) {
293
+ return;
294
+ }
278
295
 
279
296
  for (let i = 0; i < body.namedChildCount; i++) {
280
297
  const item = body.namedChild(i);
@@ -290,10 +307,14 @@ function _parseImpl(node, ctx) {
290
307
  function _parseImplMethod(node, selfType, traitName) {
291
308
  const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
292
309
  const name = nameNode?.text;
293
- if (!name) return null;
310
+ if (!name) {
311
+ return null;
312
+ }
294
313
 
295
314
  const params = node.namedChildren.find((c) => c.type === 'parameters');
296
- const { paramCount, hasSelfParam } = params ? _countRustParams(params) : { paramCount: 0, hasSelfParam: false };
315
+ const { paramCount, hasSelfParam } = params
316
+ ? _countRustParams(params)
317
+ : { paramCount: 0, hasSelfParam: false };
297
318
 
298
319
  const isPublic = _hasPubVisibility(node);
299
320
  const isAsync = node.children?.some((c) => c.text === 'async') || false;
@@ -324,7 +345,9 @@ function _parseImplMethod(node, selfType, traitName) {
324
345
  function _parseFunctionItem(node) {
325
346
  const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
326
347
  const name = nameNode?.text;
327
- if (!name) return null;
348
+ if (!name) {
349
+ return null;
350
+ }
328
351
 
329
352
  const params = node.namedChildren.find((c) => c.type === 'parameters');
330
353
  const { paramCount } = params ? _countRustParams(params) : { paramCount: 0 };
@@ -418,9 +441,7 @@ function detectRustPatterns(root, lang, methods, properties, classes) {
418
441
  // Builder pattern: struct 有 builder() 或一系列链式 with_*/set_* 方法
419
442
  for (const [typeName, methodList] of Object.entries(typeMethodMap)) {
420
443
  const hasBuilder = methodList.some((m) => m.name === 'builder' || m.name === 'build');
421
- const chainMethods = methodList.filter((m) =>
422
- /^(?:with_|set_|add_)/.test(m.name)
423
- );
444
+ const chainMethods = methodList.filter((m) => /^(?:with_|set_|add_)/.test(m.name));
424
445
  if (hasBuilder || chainMethods.length >= 3) {
425
446
  patterns.push({
426
447
  type: 'builder',
@@ -524,7 +545,9 @@ function _detectAsync(root, patterns) {
524
545
  function walk(node) {
525
546
  if (node.type === 'function_item' || node.type === 'function_signature_item') {
526
547
  const isAsync = node.children?.some((c) => c.text === 'async');
527
- if (isAsync) asyncFnCount++;
548
+ if (isAsync) {
549
+ asyncFnCount++;
550
+ }
528
551
  }
529
552
  if (node.type === 'await_expression') {
530
553
  awaitCount++;
@@ -575,13 +598,17 @@ function _extractDerives(node) {
575
598
  const siblings = [];
576
599
  for (let i = 0; i < node.parent.namedChildCount; i++) {
577
600
  const sib = node.parent.namedChild(i);
578
- if (sib === node) break;
601
+ if (sib === node) {
602
+ break;
603
+ }
579
604
  siblings.push(sib);
580
605
  }
581
606
  // Collect attribute_item nodes immediately before this node
582
607
  for (let i = siblings.length - 1; i >= 0; i--) {
583
608
  const sib = siblings[i];
584
- if (sib.type !== 'attribute_item') break;
609
+ if (sib.type !== 'attribute_item') {
610
+ break;
611
+ }
585
612
  const text = sib.text;
586
613
  const deriveMatch = text.match(/#\[derive\(([^)]+)\)\]/);
587
614
  if (deriveMatch) {
@@ -596,9 +623,7 @@ function _extractDerives(node) {
596
623
  // ── Helper: Visibility ──────────────────────────────────────
597
624
 
598
625
  function _hasPubVisibility(node) {
599
- return node.children?.some(
600
- (c) => c.type === 'visibility_modifier' || c.text === 'pub'
601
- ) || false;
626
+ return node.children?.some((c) => c.type === 'visibility_modifier' || c.text === 'pub') || false;
602
627
  }
603
628
 
604
629
  // ── Helper: Count Parameters ────────────────────────────────
@@ -11,9 +11,9 @@
11
11
  * 下游 analyzeFile / findCallExpressions 等保持同步调用。
12
12
  */
13
13
 
14
- import { fileURLToPath } from 'node:url';
15
- import path from 'node:path';
16
14
  import { readFile } from 'node:fs/promises';
15
+ import path from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
17
 
18
18
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
19
 
@@ -30,7 +30,9 @@ let _initialized = false;
30
30
  * 幂等 — 多次调用只执行一次
31
31
  */
32
32
  export async function initParser() {
33
- if (_initialized) return;
33
+ if (_initialized) {
34
+ return;
35
+ }
34
36
 
35
37
  try {
36
38
  // web-tree-sitter ESM: 导出 { Parser, Language, ... } 命名空间
@@ -40,7 +42,7 @@ export async function initParser() {
40
42
  Parser = typeof _namespace === 'function' ? _namespace : _namespace.Parser;
41
43
  await Parser.init();
42
44
  _initialized = true;
43
- } catch (err) {
45
+ } catch {
44
46
  // web-tree-sitter 不可用时优雅降级
45
47
  Parser = null;
46
48
  _initialized = false;
@@ -68,7 +70,9 @@ export function isParserReady() {
68
70
  * @returns {Promise<object|null>} Language 对象,失败返回 null
69
71
  */
70
72
  export async function loadLanguageWasm(wasmFileName) {
71
- if (!_initialized || !_namespace) return null;
73
+ if (!_initialized || !_namespace) {
74
+ return null;
75
+ }
72
76
 
73
77
  const wasmPath = path.join(GRAMMARS_DIR, wasmFileName);
74
78
  try {
@@ -308,7 +308,7 @@ export class DartDiscoverer extends ProjectDiscoverer {
308
308
 
309
309
  try {
310
310
  const content = readFileSync(melosPath, 'utf8');
311
- const melos = this.#parseSimpleYaml(content);
311
+ const _melos = this.#parseSimpleYaml(content);
312
312
 
313
313
  // Melos packages 字段(简化处理: 扫描 packages/ 目录)
314
314
  const packagesDir = join(projectRoot, 'packages');
@@ -370,9 +370,7 @@ export class DartDiscoverer extends ProjectDiscoverer {
370
370
  return;
371
371
  }
372
372
 
373
- const nodeSet = new Set(
374
- this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id))
375
- );
373
+ const nodeSet = new Set(this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id)));
376
374
  const rootNode = this.#packageName;
377
375
 
378
376
  const addDep = (name, isDev) => {
@@ -420,9 +418,7 @@ export class DartDiscoverer extends ProjectDiscoverer {
420
418
  return;
421
419
  }
422
420
 
423
- const nodeSet = new Set(
424
- this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id))
425
- );
421
+ const nodeSet = new Set(this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id)));
426
422
  const edgeSet = new Set();
427
423
 
428
424
  // 收集 lib/ 下的子目录作为内部模块
@@ -457,9 +453,7 @@ export class DartDiscoverer extends ProjectDiscoverer {
457
453
  const fromModule = relDir ? `lib/${relDir.split('/')[0]}` : this.#packageName;
458
454
 
459
455
  // 匹配 import 'package:xxx/yyy.dart'
460
- const imports = content.matchAll(
461
- /import\s+['"]package:(\w+)\/([^'"]+)['"]/g
462
- );
456
+ const imports = content.matchAll(/import\s+['"]package:(\w+)\/([^'"]+)['"]/g);
463
457
  for (const m of imports) {
464
458
  const pkg = m[1];
465
459
  const filePath = m[2];
@@ -191,9 +191,7 @@ export class GoDiscoverer extends ProjectDiscoverer {
191
191
  if (existsSync(dirPath)) {
192
192
  try {
193
193
  const entries = readdirSync(dirPath, { withFileTypes: true });
194
- const hasGoFiles = entries.some(
195
- (e) => e.isFile() && e.name.endsWith('.go')
196
- );
194
+ const hasGoFiles = entries.some((e) => e.isFile() && e.name.endsWith('.go'));
197
195
  const hasGoSubDirs = entries.some(
198
196
  (e) => e.isDirectory() && !e.name.startsWith('.') && !EXCLUDE_DIRS.has(e.name)
199
197
  );
@@ -313,30 +311,38 @@ export class GoDiscoverer extends ProjectDiscoverer {
313
311
  * 发现内部子包——目录中包含 .go 文件即为一个 Go package
314
312
  */
315
313
  #discoverInternalPackages(projectRoot) {
316
- const nodeSet = new Set(this.#depGraph.nodes.map(n => (typeof n === 'string' ? n : n.id)));
314
+ const nodeSet = new Set(this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id)));
317
315
 
318
316
  const walk = (dir, relPath, depth) => {
319
- if (depth > 6) return;
317
+ if (depth > 6) {
318
+ return;
319
+ }
320
320
  try {
321
321
  const entries = readdirSync(dir, { withFileTypes: true });
322
322
  for (const entry of entries) {
323
- if (!entry.isDirectory() || entry.name.startsWith('.') || EXCLUDE_DIRS.has(entry.name)) continue;
323
+ if (!entry.isDirectory() || entry.name.startsWith('.') || EXCLUDE_DIRS.has(entry.name)) {
324
+ continue;
325
+ }
324
326
  const subDir = join(dir, entry.name);
325
327
  const subRel = relPath ? `${relPath}/${entry.name}` : entry.name;
326
328
 
327
329
  // 检查目录中是否包含 .go 文件
328
330
  try {
329
331
  const subEntries = readdirSync(subDir);
330
- const hasGoFiles = subEntries.some(e => e.endsWith('.go'));
332
+ const hasGoFiles = subEntries.some((e) => e.endsWith('.go'));
331
333
  if (hasGoFiles && !nodeSet.has(subRel)) {
332
334
  this.#depGraph.nodes.push({ id: subRel, label: subRel, type: 'internal' });
333
335
  nodeSet.add(subRel);
334
336
  }
335
- } catch { /* skip */ }
337
+ } catch {
338
+ /* skip */
339
+ }
336
340
 
337
341
  walk(subDir, subRel, depth + 1);
338
342
  }
339
- } catch { /* skip */ }
343
+ } catch {
344
+ /* skip */
345
+ }
340
346
  };
341
347
 
342
348
  walk(projectRoot, '', 0);
@@ -351,10 +357,11 @@ export class GoDiscoverer extends ProjectDiscoverer {
351
357
  return;
352
358
  }
353
359
 
354
- const nodeSet = new Set(this.#depGraph.nodes.map(n => (typeof n === 'string' ? n : n.id)));
355
- const rootNode = (typeof this.#depGraph.nodes[0] === 'string')
356
- ? this.#depGraph.nodes[0]
357
- : this.#depGraph.nodes[0]?.id || 'root';
360
+ const nodeSet = new Set(this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id)));
361
+ const rootNode =
362
+ typeof this.#depGraph.nodes[0] === 'string'
363
+ ? this.#depGraph.nodes[0]
364
+ : this.#depGraph.nodes[0]?.id || 'root';
358
365
 
359
366
  const addExtDep = (fullPath, indirect) => {
360
367
  const shortName = fullPath.split('/').pop();
@@ -411,18 +418,21 @@ export class GoDiscoverer extends ProjectDiscoverer {
411
418
  * 解析内部 Go import 语句,构建子包间依赖关系
412
419
  */
413
420
  #parseInternalImports(projectRoot) {
414
- if (!this.#modulePath) return;
421
+ if (!this.#modulePath) {
422
+ return;
423
+ }
415
424
 
416
425
  const internalNodes = new Set(
417
426
  this.#depGraph.nodes
418
- .filter(n => (typeof n === 'object' && n.type === 'internal'))
419
- .map(n => n.id)
427
+ .filter((n) => typeof n === 'object' && n.type === 'internal')
428
+ .map((n) => n.id)
420
429
  );
421
430
 
422
431
  // 也包含根包
423
- const rootNodeId = typeof this.#depGraph.nodes[0] === 'string'
424
- ? this.#depGraph.nodes[0]
425
- : this.#depGraph.nodes[0]?.id;
432
+ const rootNodeId =
433
+ typeof this.#depGraph.nodes[0] === 'string'
434
+ ? this.#depGraph.nodes[0]
435
+ : this.#depGraph.nodes[0]?.id;
426
436
 
427
437
  const edgeSet = new Set();
428
438
 
@@ -430,7 +440,9 @@ export class GoDiscoverer extends ProjectDiscoverer {
430
440
  try {
431
441
  const entries = readdirSync(dir);
432
442
  for (const entry of entries) {
433
- if (!entry.endsWith('.go')) continue;
443
+ if (!entry.endsWith('.go')) {
444
+ continue;
445
+ }
434
446
  try {
435
447
  const content = readFileSync(join(dir, entry), 'utf8');
436
448
  // 匹配 import 块和单行 import
@@ -445,9 +457,13 @@ export class GoDiscoverer extends ProjectDiscoverer {
445
457
  for (const m of singleImports) {
446
458
  this.#matchInternalImport(`"${m[1]}"`, pkgId, rootNodeId, internalNodes, edgeSet);
447
459
  }
448
- } catch { /* skip */ }
460
+ } catch {
461
+ /* skip */
462
+ }
449
463
  }
450
- } catch { /* skip */ }
464
+ } catch {
465
+ /* skip */
466
+ }
451
467
  };
452
468
 
453
469
  // 扫描根包
@@ -464,10 +480,14 @@ export class GoDiscoverer extends ProjectDiscoverer {
464
480
  */
465
481
  #matchInternalImport(line, fromPkgId, rootNodeId, internalNodes, edgeSet) {
466
482
  const match = line.match(/"([^"]+)"/);
467
- if (!match) return;
483
+ if (!match) {
484
+ return;
485
+ }
468
486
 
469
487
  const importPath = match[1];
470
- if (!importPath.startsWith(this.#modulePath + '/')) return;
488
+ if (!importPath.startsWith(`${this.#modulePath}/`)) {
489
+ return;
490
+ }
471
491
 
472
492
  // 去掉 module path 前缀得到相对路径
473
493
  const relImport = importPath.slice(this.#modulePath.length + 1);
@@ -479,7 +499,7 @@ export class GoDiscoverer extends ProjectDiscoverer {
479
499
  } else {
480
500
  // 可能是子路径,匹配最近的已知包
481
501
  for (const nodeId of internalNodes) {
482
- if (relImport.startsWith(nodeId + '/') || relImport === nodeId) {
502
+ if (relImport.startsWith(`${nodeId}/`) || relImport === nodeId) {
483
503
  targetId = nodeId;
484
504
  break;
485
505
  }
@@ -92,9 +92,7 @@ export class NodeDiscoverer extends ProjectDiscoverer {
92
92
  );
93
93
  } else {
94
94
  confidence *= 0.05;
95
- reasons.push(
96
- `${marker.lang} marker found — confidence heavily reduced`
97
- );
95
+ reasons.push(`${marker.lang} marker found — confidence heavily reduced`);
98
96
  }
99
97
  break;
100
98
  }
@@ -214,7 +214,13 @@ 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')) {
217
+ if (
218
+ deps.has('langchain') ||
219
+ deps.has('langchain-core') ||
220
+ deps.has('langgraph') ||
221
+ deps.has('llama-index') ||
222
+ deps.has('llama_index')
223
+ ) {
218
224
  return 'langchain';
219
225
  }
220
226
  if (deps.has('torch') || deps.has('tensorflow')) {