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
@@ -8,8 +8,8 @@
8
8
 
9
9
  import fs from 'node:fs';
10
10
  import path from 'node:path';
11
- import { LanguageService } from '../../shared/LanguageService.js';
12
11
  import Logger from '../../infrastructure/logging/Logger.js';
12
+ import { LanguageService } from '../../shared/LanguageService.js';
13
13
 
14
14
  const logger = Logger.getInstance();
15
15
 
@@ -131,12 +131,10 @@ export function inferModuleFromPath(filePath) {
131
131
  // Java/Kotlin: src/main/java/{pkg}/... → 跳过域名前缀,取最后一个有意义的包目录
132
132
  // 例: src/main/java/org/springframework/samples/petclinic/vet/Vet.java → "vet"
133
133
  // 例: src/main/java/com/example/demo/DemoApp.java → "demo"
134
- const JAVA_DOMAIN_PREFIXES = new Set(['com', 'org', 'net', 'io', 'de', 'fr', 'jp', 'cn', 'uk', 'us', 'edu', 'gov']);
135
134
  for (const langDir of ['java', 'kotlin']) {
136
135
  const langIdx = parts.indexOf(langDir);
137
136
  if (langIdx >= 0 && langIdx + 1 < parts.length) {
138
137
  // 文件名所在目录(倒数第二个 part)才是 "模块"
139
- const fileName = parts[parts.length - 1];
140
138
  const pkgParts = parts.slice(langIdx + 1, parts.length - 1); // 包路径(不含文件名)
141
139
  if (pkgParts.length >= 2) {
142
140
  // 从尾部取: 最后一个包段即为功能模块
@@ -294,8 +292,7 @@ export function getInheritanceRoots(codeEntityGraph) {
294
292
  }
295
293
  try {
296
294
  // 尝试查询继承关系
297
- const entities =
298
- codeEntityGraph.queryEntities?.({ entityType: 'class', limit: 50 }) || [];
295
+ const entities = codeEntityGraph.queryEntities?.({ entityType: 'class', limit: 50 }) || [];
299
296
  const roots = [];
300
297
  for (const e of entities) {
301
298
  const _parents =
@@ -341,9 +338,7 @@ export function dedup(files, wikiDir, emit) {
341
338
  /* skip */
342
339
  }
343
340
  removed.push(file.path);
344
- logger.info(
345
- `[WikiGenerator] Dedup: removed ${file.path} (same hash as ${existing.path})`
346
- );
341
+ logger.info(`[WikiGenerator] Dedup: removed ${file.path} (same hash as ${existing.path})`);
347
342
  }
348
343
  // hash 不同 → 保留两个(不同目录允许同名)
349
344
  } else {
@@ -361,8 +356,7 @@ export function dedup(files, wikiDir, emit) {
361
356
  const firstPath = hashMap.get(file.hash);
362
357
  // 优先保留代码生成的(非 synced)
363
358
  const isFirstSynced = firstPath.startsWith('documents/') || firstPath.startsWith('skills/');
364
- const isCurrentSynced =
365
- file.path.startsWith('documents/') || file.path.startsWith('skills/');
359
+ const isCurrentSynced = file.path.startsWith('documents/') || file.path.startsWith('skills/');
366
360
 
367
361
  if (isCurrentSynced && !isFirstSynced) {
368
362
  // 当前是 synced,first 是 codegen → 删除 synced
@@ -411,19 +405,69 @@ export function dedup(files, wikiDir, emit) {
411
405
  */
412
406
  export function getLangTerms(langId) {
413
407
  const TERMS = {
414
- swift: { typeLabel: { zh: '类/结构体', en: 'Classes/Structs' }, interfaceLabel: { zh: '协议', en: 'Protocols' }, moduleMetric: { zh: 'SPM Targets', en: 'SPM Targets' } },
415
- objectivec: { typeLabel: { zh: '', en: 'Classes' }, interfaceLabel: { zh: '协议', en: 'Protocols' }, moduleMetric: { zh: 'Targets', en: 'Targets' } },
416
- typescript: { typeLabel: { zh: '类', en: 'Classes' }, interfaceLabel: { zh: '接口', en: 'Interfaces' }, moduleMetric: { zh: 'Packages', en: 'Packages' } },
417
- javascript: { typeLabel: { zh: '类/模块', en: 'Classes/Modules' }, interfaceLabel: { zh: '接口', en: 'Interfaces' }, moduleMetric: { zh: 'Packages', en: 'Packages' } },
418
- python: { typeLabel: { zh: '类', en: 'Classes' }, interfaceLabel: { zh: '抽象基类', en: 'Abstract Base' }, moduleMetric: { zh: 'Packages', en: 'Packages' } },
419
- go: { typeLabel: { zh: '结构体', en: 'Structs' }, interfaceLabel: { zh: '接口', en: 'Interfaces' }, moduleMetric: { zh: 'Go Modules', en: 'Go Modules' } },
420
- rust: { typeLabel: { zh: '结构体/枚举', en: 'Structs/Enums' }, interfaceLabel: { zh: 'Trait', en: 'Traits' }, moduleMetric: { zh: 'Crates', en: 'Crates' } },
421
- java: { typeLabel: { zh: '类', en: 'Classes' }, interfaceLabel: { zh: '接口', en: 'Interfaces' }, moduleMetric: { zh: 'Modules', en: 'Modules' } },
422
- kotlin: { typeLabel: { zh: '类', en: 'Classes' }, interfaceLabel: { zh: '接口', en: 'Interfaces' }, moduleMetric: { zh: 'Modules', en: 'Modules' } },
423
- dart: { typeLabel: { zh: '类', en: 'Classes' }, interfaceLabel: { zh: '抽象类', en: 'Abstract Classes' }, moduleMetric: { zh: 'Packages', en: 'Packages' } },
424
- csharp: { typeLabel: { zh: '类', en: 'Classes' }, interfaceLabel: { zh: '接口', en: 'Interfaces' }, moduleMetric: { zh: 'Projects', en: 'Projects' } },
408
+ swift: {
409
+ typeLabel: { zh: '类/结构体', en: 'Classes/Structs' },
410
+ interfaceLabel: { zh: '协议', en: 'Protocols' },
411
+ moduleMetric: { zh: 'SPM Targets', en: 'SPM Targets' },
412
+ },
413
+ objectivec: {
414
+ typeLabel: { zh: '', en: 'Classes' },
415
+ interfaceLabel: { zh: '协议', en: 'Protocols' },
416
+ moduleMetric: { zh: 'Targets', en: 'Targets' },
417
+ },
418
+ typescript: {
419
+ typeLabel: { zh: '类', en: 'Classes' },
420
+ interfaceLabel: { zh: '接口', en: 'Interfaces' },
421
+ moduleMetric: { zh: 'Packages', en: 'Packages' },
422
+ },
423
+ javascript: {
424
+ typeLabel: { zh: '类/模块', en: 'Classes/Modules' },
425
+ interfaceLabel: { zh: '接口', en: 'Interfaces' },
426
+ moduleMetric: { zh: 'Packages', en: 'Packages' },
427
+ },
428
+ python: {
429
+ typeLabel: { zh: '类', en: 'Classes' },
430
+ interfaceLabel: { zh: '抽象基类', en: 'Abstract Base' },
431
+ moduleMetric: { zh: 'Packages', en: 'Packages' },
432
+ },
433
+ go: {
434
+ typeLabel: { zh: '结构体', en: 'Structs' },
435
+ interfaceLabel: { zh: '接口', en: 'Interfaces' },
436
+ moduleMetric: { zh: 'Go Modules', en: 'Go Modules' },
437
+ },
438
+ rust: {
439
+ typeLabel: { zh: '结构体/枚举', en: 'Structs/Enums' },
440
+ interfaceLabel: { zh: 'Trait', en: 'Traits' },
441
+ moduleMetric: { zh: 'Crates', en: 'Crates' },
442
+ },
443
+ java: {
444
+ typeLabel: { zh: '类', en: 'Classes' },
445
+ interfaceLabel: { zh: '接口', en: 'Interfaces' },
446
+ moduleMetric: { zh: 'Modules', en: 'Modules' },
447
+ },
448
+ kotlin: {
449
+ typeLabel: { zh: '类', en: 'Classes' },
450
+ interfaceLabel: { zh: '接口', en: 'Interfaces' },
451
+ moduleMetric: { zh: 'Modules', en: 'Modules' },
452
+ },
453
+ dart: {
454
+ typeLabel: { zh: '类', en: 'Classes' },
455
+ interfaceLabel: { zh: '抽象类', en: 'Abstract Classes' },
456
+ moduleMetric: { zh: 'Packages', en: 'Packages' },
457
+ },
458
+ csharp: {
459
+ typeLabel: { zh: '类', en: 'Classes' },
460
+ interfaceLabel: { zh: '接口', en: 'Interfaces' },
461
+ moduleMetric: { zh: 'Projects', en: 'Projects' },
462
+ },
425
463
  };
426
- return TERMS[langId] || { typeLabel: { zh: '类型', en: 'Types' }, interfaceLabel: { zh: '接口', en: 'Interfaces' }, moduleMetric: { zh: 'Modules', en: 'Modules' } };
464
+ return (
465
+ TERMS[langId] || {
466
+ typeLabel: { zh: '类型', en: 'Types' },
467
+ interfaceLabel: { zh: '接口', en: 'Interfaces' },
468
+ moduleMetric: { zh: 'Modules', en: 'Modules' },
469
+ }
470
+ );
427
471
  }
428
472
 
429
473
  /**
@@ -456,11 +500,13 @@ export function detectBuildSystems(rootEntryNames, projectRoot) {
456
500
  try {
457
501
  const entries = fs.readdirSync(projectRoot, { withFileTypes: true });
458
502
  for (const dir of entries) {
459
- if (!dir.isDirectory() || dir.name.startsWith('.') || skipDirs.has(dir.name)) continue;
503
+ if (!dir.isDirectory() || dir.name.startsWith('.') || skipDirs.has(dir.name)) {
504
+ continue;
505
+ }
460
506
  try {
461
- const subEntries = fs.readdirSync(path.join(projectRoot, dir.name)).filter(
462
- (n) => !n.startsWith('.')
463
- );
507
+ const subEntries = fs
508
+ .readdirSync(path.join(projectRoot, dir.name))
509
+ .filter((n) => !n.startsWith('.'));
464
510
  const subResults = LanguageService.matchBuildMarkers(subEntries);
465
511
  for (const r of subResults) {
466
512
  if (!seenEco.has(r.eco)) {
@@ -468,9 +514,13 @@ export function detectBuildSystems(rootEntryNames, projectRoot) {
468
514
  seenEco.add(r.eco);
469
515
  }
470
516
  }
471
- } catch { /* skip unreadable subdirs */ }
517
+ } catch {
518
+ /* skip unreadable subdirs */
519
+ }
472
520
  }
473
- } catch { /* skip */ }
521
+ } catch {
522
+ /* skip */
523
+ }
474
524
  }
475
525
 
476
526
  return results;
@@ -498,12 +548,29 @@ export function detectBuildSystems(rootEntryNames, projectRoot) {
498
548
 
499
549
  /** 入口文件名模式 */
500
550
  const ENTRY_POINT_NAMES = new Set([
501
- 'index.js', 'index.ts', 'index.tsx', 'index.jsx', 'index.mjs',
502
- 'main.js', 'main.ts', 'main.go', 'main.py', 'main.rs', 'main.dart', 'main.c', 'main.cpp',
503
- 'mod.rs', 'lib.rs',
551
+ 'index.js',
552
+ 'index.ts',
553
+ 'index.tsx',
554
+ 'index.jsx',
555
+ 'index.mjs',
556
+ 'main.js',
557
+ 'main.ts',
558
+ 'main.go',
559
+ 'main.py',
560
+ 'main.rs',
561
+ 'main.dart',
562
+ 'main.c',
563
+ 'main.cpp',
564
+ 'mod.rs',
565
+ 'lib.rs',
504
566
  '__init__.py',
505
- 'app.js', 'app.ts', 'app.py', 'app.rb',
506
- 'server.js', 'server.ts', 'server.py',
567
+ 'app.js',
568
+ 'app.ts',
569
+ 'app.py',
570
+ 'app.rb',
571
+ 'server.js',
572
+ 'server.ts',
573
+ 'server.py',
507
574
  ]);
508
575
 
509
576
  /** 多语言 import/require 正则 (轻量级, 不依赖 AST) */
@@ -540,11 +607,7 @@ const IMPORT_PATTERNS = [
540
607
  * @returns {FolderProfile[]}
541
608
  */
542
609
  export function profileFolders(projectInfo, options = {}) {
543
- const {
544
- minFiles = 3,
545
- maxFolders = 20,
546
- sampleLines = 40,
547
- } = options;
610
+ const { minFiles = 3, maxFolders = 20, sampleLines = 40 } = options;
548
611
 
549
612
  const root = projectInfo.root;
550
613
  const sourceFiles = projectInfo.sourceFiles || [];
@@ -578,12 +641,18 @@ export function profileFolders(projectInfo, options = {}) {
578
641
  // ── 3. 筛选重要文件夹 ──
579
642
  const candidates = [];
580
643
  for (const [dir, files] of folderRecursive) {
581
- if (files.length < minFiles) continue;
644
+ if (files.length < minFiles) {
645
+ continue;
646
+ }
582
647
  // 排除根目录 '.'
583
- if (dir === '.') continue;
648
+ if (dir === '.') {
649
+ continue;
650
+ }
584
651
  // 排除太深的目录 (depth > 4), 这些通常是叶子目录, 信息量低
585
652
  const depth = dir.split('/').length;
586
- if (depth > 4) continue;
653
+ if (depth > 4) {
654
+ continue;
655
+ }
587
656
 
588
657
  candidates.push({ dir, files, depth });
589
658
  }
@@ -607,7 +676,9 @@ export function profileFolders(projectInfo, options = {}) {
607
676
 
608
677
  // 按 fileCount 降序 + depth 升序 排序
609
678
  profiles.sort((a, b) => {
610
- if (b.fileCount !== a.fileCount) return b.fileCount - a.fileCount;
679
+ if (b.fileCount !== a.fileCount) {
680
+ return b.fileCount - a.fileCount;
681
+ }
611
682
  return a.depth - b.depth;
612
683
  });
613
684
 
@@ -623,18 +694,20 @@ function _pruneRedundantFolders(candidates, maxFolders) {
623
694
  const removedDirs = new Set();
624
695
 
625
696
  for (const c of candidates) {
626
- if (removedDirs.has(c.dir)) continue;
697
+ if (removedDirs.has(c.dir)) {
698
+ continue;
699
+ }
627
700
 
628
701
  // 检查是否有已 kept 的父目录, 且文件比率 > 80%
629
702
  let isRedundant = false;
630
703
  for (const k of kept) {
631
- if (c.dir.startsWith(k.dir + '/')) {
704
+ if (c.dir.startsWith(`${k.dir}/`)) {
632
705
  // c 是 k 的子目录
633
706
  if (c.files.length / k.files.length > 0.8) {
634
707
  isRedundant = true;
635
708
  break;
636
709
  }
637
- } else if (k.dir.startsWith(c.dir + '/')) {
710
+ } else if (k.dir.startsWith(`${c.dir}/`)) {
638
711
  // c 是 k 的父目录, k 覆盖了 c 大部分 → 保留 c (更高层), 移除 k
639
712
  if (k.files.length / c.files.length > 0.8) {
640
713
  removedDirs.add(k.dir);
@@ -646,10 +719,12 @@ function _pruneRedundantFolders(candidates, maxFolders) {
646
719
  kept.push(c);
647
720
  }
648
721
 
649
- if (kept.length >= maxFolders) break;
722
+ if (kept.length >= maxFolders) {
723
+ break;
724
+ }
650
725
  }
651
726
 
652
- return kept.filter(c => !removedDirs.has(c.dir));
727
+ return kept.filter((c) => !removedDirs.has(c.dir));
653
728
  }
654
729
 
655
730
  /**
@@ -670,14 +745,16 @@ function _buildFolderProfile(relDir, files, depth, projectRoot, sampleLines) {
670
745
  try {
671
746
  const stat = fs.statSync(path.join(projectRoot, f));
672
747
  totalSize += stat.size;
673
- } catch { /* skip */ }
748
+ } catch {
749
+ /* skip */
750
+ }
674
751
  }
675
752
 
676
753
  // ── 文件名列表 ──
677
- const fileNames = files.map(f => path.basename(f)).sort();
754
+ const fileNames = files.map((f) => path.basename(f)).sort();
678
755
 
679
756
  // ── 入口点检测 ──
680
- const entryPoints = files.filter(f => ENTRY_POINT_NAMES.has(path.basename(f).toLowerCase()));
757
+ const entryPoints = files.filter((f) => ENTRY_POINT_NAMES.has(path.basename(f).toLowerCase()));
681
758
 
682
759
  // ── 重要文件 (大文件 top5 + 入口文件) ──
683
760
  const fileSizes = [];
@@ -685,15 +762,12 @@ function _buildFolderProfile(relDir, files, depth, projectRoot, sampleLines) {
685
762
  try {
686
763
  const stat = fs.statSync(path.join(projectRoot, f));
687
764
  fileSizes.push({ file: f, size: stat.size });
688
- } catch { /* skip */ }
765
+ } catch {
766
+ /* skip */
767
+ }
689
768
  }
690
769
  fileSizes.sort((a, b) => b.size - a.size);
691
- const keyFiles = [
692
- ...new Set([
693
- ...entryPoints,
694
- ...fileSizes.slice(0, 5).map(fs => fs.file),
695
- ]),
696
- ];
770
+ const keyFiles = [...new Set([...entryPoints, ...fileSizes.slice(0, 5).map((fs) => fs.file)])];
697
771
 
698
772
  // ── README 检测 ──
699
773
  let readme = null;
@@ -706,7 +780,9 @@ function _buildFolderProfile(relDir, files, depth, projectRoot, sampleLines) {
706
780
  readme = content.slice(0, 1000); // 只取前 1000 字符
707
781
  break;
708
782
  }
709
- } catch { /* skip */ }
783
+ } catch {
784
+ /* skip */
785
+ }
710
786
  }
711
787
 
712
788
  // ── 命名模式检测 ──
@@ -739,7 +815,7 @@ function _buildFolderProfile(relDir, files, depth, projectRoot, sampleLines) {
739
815
  readme,
740
816
  purpose: purpose ? purpose : null,
741
817
  imports,
742
- entryPoints: [...new Set(entryPoints.map(f => path.basename(f)))],
818
+ entryPoints: [...new Set(entryPoints.map((f) => path.basename(f)))],
743
819
  namingPatterns,
744
820
  headerComments,
745
821
  };
@@ -753,14 +829,20 @@ function _buildFolderProfile(relDir, files, depth, projectRoot, sampleLines) {
753
829
  */
754
830
  function _detectNamingPatterns(fileNames) {
755
831
  const patterns = [];
756
- const lower = fileNames.map(n => n.toLowerCase());
832
+ const lower = fileNames.map((n) => n.toLowerCase());
757
833
 
758
834
  // 测试文件
759
- const testFiles = lower.filter(n =>
760
- n.startsWith('test_') || n.startsWith('test.') ||
761
- n.endsWith('_test.go') || n.endsWith('.test.js') || n.endsWith('.test.ts') ||
762
- n.endsWith('.spec.js') || n.endsWith('.spec.ts') || n.endsWith('_spec.rb') ||
763
- n.startsWith('test') && n.includes('.')
835
+ const testFiles = lower.filter(
836
+ (n) =>
837
+ n.startsWith('test_') ||
838
+ n.startsWith('test.') ||
839
+ n.endsWith('_test.go') ||
840
+ n.endsWith('.test.js') ||
841
+ n.endsWith('.test.ts') ||
842
+ n.endsWith('.spec.js') ||
843
+ n.endsWith('.spec.ts') ||
844
+ n.endsWith('_spec.rb') ||
845
+ (n.startsWith('test') && n.includes('.'))
764
846
  );
765
847
  if (testFiles.length > 0) {
766
848
  patterns.push(`test files: ${testFiles.length}`);
@@ -803,16 +885,54 @@ function _extractImports(keyFiles, projectRoot, sampleLines, currentDir) {
803
885
 
804
886
  // Node.js / 常见运行时内置模块 — 不应计入项目文件夹依赖
805
887
  const BUILTIN_MODULES = new Set([
806
- 'fs', 'path', 'os', 'http', 'https', 'url', 'util', 'crypto', 'stream',
807
- 'events', 'child_process', 'cluster', 'net', 'dns', 'tls', 'zlib',
808
- 'readline', 'assert', 'buffer', 'querystring', 'string_decoder',
809
- 'timers', 'tty', 'dgram', 'vm', 'worker_threads', 'perf_hooks',
810
- 'async_hooks', 'v8', 'inspector', 'console', 'process', 'module',
888
+ 'fs',
889
+ 'path',
890
+ 'os',
891
+ 'http',
892
+ 'https',
893
+ 'url',
894
+ 'util',
895
+ 'crypto',
896
+ 'stream',
897
+ 'events',
898
+ 'child_process',
899
+ 'cluster',
900
+ 'net',
901
+ 'dns',
902
+ 'tls',
903
+ 'zlib',
904
+ 'readline',
905
+ 'assert',
906
+ 'buffer',
907
+ 'querystring',
908
+ 'string_decoder',
909
+ 'timers',
910
+ 'tty',
911
+ 'dgram',
912
+ 'vm',
913
+ 'worker_threads',
914
+ 'perf_hooks',
915
+ 'async_hooks',
916
+ 'v8',
917
+ 'inspector',
918
+ 'console',
919
+ 'process',
920
+ 'module',
811
921
  // node: prefix 会被 firstSeg 拆出 "node" — 直接排除
812
922
  'node',
813
923
  // 常见第三方包 (非项目目录)
814
- 'react', 'vue', 'express', 'lodash', 'axios', 'moment', 'dayjs',
815
- 'webpack', 'vite', 'jest', 'mocha', 'chai',
924
+ 'react',
925
+ 'vue',
926
+ 'express',
927
+ 'lodash',
928
+ 'axios',
929
+ 'moment',
930
+ 'dayjs',
931
+ 'webpack',
932
+ 'vite',
933
+ 'jest',
934
+ 'mocha',
935
+ 'chai',
816
936
  ]);
817
937
 
818
938
  for (const relFile of keyFiles) {
@@ -829,13 +949,20 @@ function _extractImports(keyFiles, projectRoot, sampleLines, currentDir) {
829
949
  const target = match[1] || match[2];
830
950
  if (target) {
831
951
  // 跳过 node: 协议前缀 (Node.js 内置模块)
832
- if (target.startsWith('node:')) continue;
952
+ if (target.startsWith('node:')) {
953
+ continue;
954
+ }
833
955
 
834
956
  // 解析相对路径 import → 文件夹名
835
957
  if (target.startsWith('.') || target.startsWith('/')) {
836
958
  const resolved = path.normalize(path.join(currentDir, target));
837
959
  const topDir = resolved.split('/')[0];
838
- if (topDir && topDir !== '.' && topDir !== '..' && topDir !== currentDir.split('/')[0]) {
960
+ if (
961
+ topDir &&
962
+ topDir !== '.' &&
963
+ topDir !== '..' &&
964
+ topDir !== currentDir.split('/')[0]
965
+ ) {
839
966
  importTargets.add(topDir);
840
967
  }
841
968
  } else {
@@ -849,7 +976,9 @@ function _extractImports(keyFiles, projectRoot, sampleLines, currentDir) {
849
976
  }
850
977
  }
851
978
  }
852
- } catch { /* skip unreadable files */ }
979
+ } catch {
980
+ /* skip unreadable files */
981
+ }
853
982
  }
854
983
 
855
984
  return [...importTargets].slice(0, 20);
@@ -870,18 +999,24 @@ function _extractHeaderComment(fullPath) {
870
999
  if (blockMatch) {
871
1000
  const comment = blockMatch[1]
872
1001
  .split('\n')
873
- .map(l => l.replace(/^\s*\*\s?/, '').trim())
874
- .filter(l => l && !l.startsWith('@'))
1002
+ .map((l) => l.replace(/^\s*\*\s?/, '').trim())
1003
+ .filter((l) => l && !l.startsWith('@'))
875
1004
  .join(' ')
876
1005
  .slice(0, 200);
877
- if (comment.length > 10) return comment;
1006
+ if (comment.length > 10) {
1007
+ return comment;
1008
+ }
878
1009
  }
879
1010
 
880
1011
  // 尝试匹配 # 或 // 开头的连续行注释
881
1012
  const lineComments = [];
882
1013
  for (const line of lines) {
883
1014
  const stripped = line.trim();
884
- if (stripped.startsWith('#') && !stripped.startsWith('#!') && !stripped.startsWith('#include')) {
1015
+ if (
1016
+ stripped.startsWith('#') &&
1017
+ !stripped.startsWith('#!') &&
1018
+ !stripped.startsWith('#include')
1019
+ ) {
885
1020
  lineComments.push(stripped.replace(/^#+\s*/, ''));
886
1021
  } else if (stripped.startsWith('//')) {
887
1022
  lineComments.push(stripped.replace(/^\/\/\s*/, ''));
@@ -897,7 +1032,9 @@ function _extractHeaderComment(fullPath) {
897
1032
  }
898
1033
  if (lineComments.length > 0) {
899
1034
  const comment = lineComments.join(' ').slice(0, 200);
900
- if (comment.length > 10) return comment;
1035
+ if (comment.length > 10) {
1036
+ return comment;
1037
+ }
901
1038
  }
902
1039
 
903
1040
  return null;