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
package/bin/api-server.js CHANGED
@@ -44,6 +44,8 @@ async function main() {
44
44
  auditLogger: components.auditLogger,
45
45
  gateway: components.gateway,
46
46
  constitution: components.constitution,
47
+ config: components.config,
48
+ skillHooks: components.skillHooks,
47
49
  projectRoot,
48
50
  });
49
51
  logger.info('Service container initialized successfully');
package/bin/cli.js CHANGED
@@ -17,7 +17,7 @@
17
17
  * asd mirror - 镜像 .cursor/ → .qoder/ .trae/
18
18
  */
19
19
 
20
- import { existsSync, readFileSync, readdirSync, copyFileSync, mkdirSync } from 'node:fs';
20
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync } from 'node:fs';
21
21
  import { dirname, join, resolve } from 'node:path';
22
22
  import { fileURLToPath } from 'node:url';
23
23
  import { Command } from 'commander';
@@ -648,25 +648,29 @@ program
648
648
  // ── Level 1: 项目配置文件(确定性高)──
649
649
  const hasAppleConfig = entries.some(
650
650
  (e) =>
651
- e.name === 'Package.swift' || // SPM
652
- e.name === 'Podfile' || // CocoaPods
653
- e.name === 'Cartfile' || // Carthage
654
- e.name === 'project.yml' || // XcodeGen
655
- e.name.endsWith('.xcodeproj') || // Xcode project
656
- e.name.endsWith('.xcworkspace') // Xcode workspace
651
+ e.name === 'Package.swift' || // SPM
652
+ e.name === 'Podfile' || // CocoaPods
653
+ e.name === 'Cartfile' || // Carthage
654
+ e.name === 'project.yml' || // XcodeGen
655
+ e.name.endsWith('.xcodeproj') || // Xcode project
656
+ e.name.endsWith('.xcworkspace') // Xcode workspace
657
657
  );
658
- if (hasAppleConfig) return true;
658
+ if (hasAppleConfig) {
659
+ return true;
660
+ }
659
661
 
660
662
  // ── Level 2: 目录结构特征 ──
661
663
  const hasAppleDir = entries.some(
662
664
  (e) =>
663
665
  e.isDirectory() &&
664
- (e.name === 'Tuist' || // Tuist 项目
665
- e.name === 'Pods' || // CocoaPods 产物
666
- e.name === 'Carthage' || // Carthage 产物
667
- e.name === 'DerivedData') // Xcode 构建产物
666
+ (e.name === 'Tuist' || // Tuist 项目
667
+ e.name === 'Pods' || // CocoaPods 产物
668
+ e.name === 'Carthage' || // Carthage 产物
669
+ e.name === 'DerivedData') // Xcode 构建产物
668
670
  );
669
- if (hasAppleDir) return true;
671
+ if (hasAppleDir) {
672
+ return true;
673
+ }
670
674
 
671
675
  // ── Level 3: 向下扫一层(处理 monorepo 或 Sources/ 下有 .swift 的情况)──
672
676
  const APPLE_EXTS = new Set(['.swift', '.m', '.mm', '.h']);
@@ -683,7 +687,9 @@ program
683
687
  if (subEntries.some((f) => APPLE_EXTS.has(f.slice(f.lastIndexOf('.'))))) {
684
688
  return true;
685
689
  }
686
- } catch { /* 读取失败忽略 */ }
690
+ } catch {
691
+ /* 读取失败忽略 */
692
+ }
687
693
  }
688
694
  }
689
695
 
@@ -710,7 +716,8 @@ program
710
716
  ? `http://127.0.0.1:${port}`
711
717
  : `http://localhost:5173`;
712
718
  } else {
713
- process.env.ASD_DASHBOARD_URL = process.env.ASD_DASHBOARD_URL || `http://${host}:${port}`;
719
+ process.env.ASD_DASHBOARD_URL =
720
+ process.env.ASD_DASHBOARD_URL || `http://${host}:${port}`;
714
721
  }
715
722
 
716
723
  const { FileWatcher } = await import('../lib/service/automation/FileWatcher.js');
@@ -725,7 +732,6 @@ program
725
732
  }
726
733
  }
727
734
  } else if (process.env.ASD_DEBUG === '1') {
728
- console.log('ℹ️ Non-Apple project — file watcher skipped (use VSCode extension instead)');
729
735
  }
730
736
  } catch (err) {
731
737
  console.error(`❌ API server failed to start: ${err.message}`);
@@ -885,7 +891,6 @@ program
885
891
  const destName = file.endsWith('.mdc') ? file.replace(/\.mdc$/, '.md') : file;
886
892
  copyFileSync(join(cursorRulesDir, file), join(targetRulesDir, destName));
887
893
  }
888
- console.log(` ✅ ${target}/rules/ — ${files.length} rules mirrored`);
889
894
  }
890
895
 
891
896
  const cursorSkillsDir = join(cursorDir, 'skills');
@@ -897,10 +902,8 @@ program
897
902
  for (const dir of skillDirs) {
898
903
  _copyDirRecursive(join(cursorSkillsDir, dir.name), join(targetSkillsDir, dir.name));
899
904
  }
900
- console.log(` ✅ ${target}/skills/ — ${skillDirs.length} skills mirrored`);
901
905
  }
902
906
  }
903
- console.log('\n 💡 完成!如有新增交付物料,可随时重新运行 asd mirror');
904
907
  });
905
908
 
906
909
  /** @private 递归复制目录(mirror 命令用) */
@@ -1004,6 +1007,8 @@ async function initContainer(opts = {}) {
1004
1007
  auditLogger: bootstrap.components.auditLogger,
1005
1008
  gateway: bootstrap.components.gateway,
1006
1009
  constitution: bootstrap.components.constitution,
1010
+ config: bootstrap.components.config,
1011
+ skillHooks: bootstrap.components.skillHooks,
1007
1012
  projectRoot,
1008
1013
  });
1009
1014
  return { bootstrap, container };
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "vector": {
47
47
  "enabled": true,
48
- "dimensions": 1536,
48
+ "dimensions": 768,
49
49
  "indexPath": "./data/vector-index"
50
50
  },
51
51
  "qualityGate": {
package/lib/bootstrap.js CHANGED
@@ -57,7 +57,7 @@ export class Bootstrap {
57
57
  // 2. 初始化日志系统
58
58
  await this.initializeLogger();
59
59
 
60
- this.components.logger.info('AutoSnippet 2.0 - Starting initialization...');
60
+ this.components.logger.info('AutoSnippet - Starting initialization...');
61
61
 
62
62
  // 3. 连接数据库
63
63
  await this.initializeDatabase();
@@ -75,7 +75,7 @@ export class Bootstrap {
75
75
  // await this.registerRoutes();
76
76
 
77
77
  const duration = Date.now() - startTime;
78
- this.components.logger.info(`AutoSnippet 2.0 initialized successfully (${duration}ms)`);
78
+ this.components.logger.info(`AutoSnippet initialized successfully (${duration}ms)`);
79
79
 
80
80
  return this.components;
81
81
  } catch (error) {
@@ -200,14 +200,14 @@ export class Bootstrap {
200
200
  * 关闭应用程序
201
201
  */
202
202
  async shutdown() {
203
- this.components.logger?.info('AutoSnippet 2.0 - Shutting down...');
203
+ this.components.logger?.info('AutoSnippet - Shutting down...');
204
204
 
205
205
  // 关闭数据库连接
206
206
  if (this.components.db) {
207
207
  await this.components.db.close();
208
208
  }
209
209
 
210
- this.components.logger?.info('AutoSnippet 2.0 - Shutdown complete');
210
+ this.components.logger?.info('AutoSnippet - Shutdown complete');
211
211
  }
212
212
 
213
213
  /**
@@ -53,7 +53,7 @@ import {
53
53
  } from 'node:fs';
54
54
  import { dirname, join, resolve } from 'node:path';
55
55
  import { fileURLToPath } from 'node:url';
56
- import { checkWriteSafety, safeCopyFile, safeWriteFile } from '../service/cursor/FileProtection.js';
56
+ import { checkWriteSafety } from '../service/cursor/FileProtection.js';
57
57
 
58
58
  const __filename = fileURLToPath(import.meta.url);
59
59
  const __dirname = dirname(__filename);
@@ -103,21 +103,15 @@ export class SetupService {
103
103
  const results = [];
104
104
  const total = steps.length;
105
105
 
106
- console.log('');
107
- console.log(` ⚙ AutoSnippet Setup — ${this.projectName}`);
108
- console.log(` ${'─'.repeat(44)}`);
109
-
110
106
  for (let i = 0; i < total; i++) {
111
107
  const { label, fn } = steps[i];
112
108
  const tag = `[${i + 1}/${total}]`;
113
109
  process.stdout.write(` ${tag} ${label}...`);
114
110
  try {
115
111
  const r = await fn();
116
- const detail = this._formatStepDetail(r);
117
- console.log(` ✅${detail}`);
112
+ const _detail = this._formatStepDetail(r);
118
113
  results.push({ step: i + 1, label, ok: true, ...(r || {}) });
119
114
  } catch (err) {
120
- console.log(` ❌`);
121
115
  console.error(` ${err.message}`);
122
116
  results.push({ step: i + 1, label, ok: false, error: err.message });
123
117
  }
@@ -129,7 +123,9 @@ export class SetupService {
129
123
 
130
124
  /** @private 格式化步骤结果的简要信息 */
131
125
  _formatStepDetail(r) {
132
- if (!r) return '';
126
+ if (!r) {
127
+ return '';
128
+ }
133
129
  const parts = [];
134
130
  if (r.configured) {
135
131
  parts.push(r.configured.join(', '));
@@ -145,17 +141,8 @@ export class SetupService {
145
141
 
146
142
  printSummary() {
147
143
  const results = this._results || [];
148
- const ok = results.filter((r) => r.ok).length;
149
- const fail = results.filter((r) => !r.ok).length;
150
-
151
- console.log(` ${'─'.repeat(44)}`);
152
- console.log(` ✨ Setup 完成: ${ok} 成功${fail > 0 ? `, ${fail} 失败` : ''}`);
153
- console.log('');
154
- console.log(' 后续操作:');
155
- console.log(' asd coldstart 扫描项目、AI 生成知识库');
156
- console.log(' asd ui 启动 Dashboard + API Server');
157
- console.log(' asd watch 启动 Xcode 文件监听');
158
- console.log('');
144
+ const _ok = results.filter((r) => r.ok).length;
145
+ const _fail = results.filter((r) => !r.ok).length;
159
146
  }
160
147
 
161
148
  /* ═══ Step 1: 运行时目录与配置 ═══════════════════════ */
@@ -577,7 +564,12 @@ export class SetupService {
577
564
  const whichCmd = process.platform === 'win32' ? 'where' : 'which';
578
565
  for (const cmd of ['code', 'cursor', 'codex', 'code-insiders']) {
579
566
  try {
580
- const p = execSync(`${whichCmd} ${cmd}`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim().split(/\r?\n/)[0];
567
+ const p = execSync(`${whichCmd} ${cmd}`, {
568
+ encoding: 'utf8',
569
+ stdio: ['pipe', 'pipe', 'ignore'],
570
+ })
571
+ .trim()
572
+ .split(/\r?\n/)[0];
581
573
  if (p) {
582
574
  candidates.push({ name: cmd, cli: p });
583
575
  }
@@ -591,7 +583,11 @@ export class SetupService {
591
583
  // macOS: /Applications/xxx.app/Contents/Resources/app/bin/
592
584
  const appPaths = [
593
585
  { name: 'vscode', app: '/Applications/Visual Studio Code.app', bin: 'code' },
594
- { name: 'vscode-insiders', app: '/Applications/Visual Studio Code - Insiders.app', bin: 'code-insiders' },
586
+ {
587
+ name: 'vscode-insiders',
588
+ app: '/Applications/Visual Studio Code - Insiders.app',
589
+ bin: 'code-insiders',
590
+ },
595
591
  { name: 'cursor', app: '/Applications/Cursor.app', bin: 'cursor' },
596
592
  { name: 'codex', app: '/Applications/Codex.app', bin: 'codex' },
597
593
  ];
@@ -603,10 +599,15 @@ export class SetupService {
603
599
  }
604
600
  } else if (process.platform === 'win32') {
605
601
  // Windows: %LOCALAPPDATA%\Programs\xxx
606
- const localAppData = process.env.LOCALAPPDATA || join(process.env.USERPROFILE || '', 'AppData', 'Local');
602
+ const localAppData =
603
+ process.env.LOCALAPPDATA || join(process.env.USERPROFILE || '', 'AppData', 'Local');
607
604
  const winPaths = [
608
605
  { name: 'vscode', dir: 'Microsoft VS Code', bin: 'bin/code.cmd' },
609
- { name: 'vscode-insiders', dir: 'Microsoft VS Code Insiders', bin: 'bin/code-insiders.cmd' },
606
+ {
607
+ name: 'vscode-insiders',
608
+ dir: 'Microsoft VS Code Insiders',
609
+ bin: 'bin/code-insiders.cmd',
610
+ },
610
611
  { name: 'cursor', dir: 'cursor', bin: 'cursor.exe' },
611
612
  ];
612
613
  for (const { name, dir, bin } of winPaths) {
@@ -639,7 +640,9 @@ export class SetupService {
639
640
  } catch {
640
641
  /* use as-is */
641
642
  }
642
- if (seen.has(realPath)) return false;
643
+ if (seen.has(realPath)) {
644
+ return false;
645
+ }
643
646
  seen.add(realPath);
644
647
  return true;
645
648
  });
@@ -720,9 +723,8 @@ export class SetupService {
720
723
  }
721
724
 
722
725
  // 即使 --force,也不覆盖用户原有的非 AutoSnippet 文件
723
- const { canWrite, reason } = checkWriteSafety(dest);
726
+ const { canWrite } = checkWriteSafety(dest);
724
727
  if (!canWrite) {
725
- console.log(` ⏭️ copilot-instructions.md — 跳过(${reason},非 AutoSnippet 生成)`);
726
728
  return;
727
729
  }
728
730
 
@@ -772,7 +774,6 @@ export class SetupService {
772
774
  ].join('\n');
773
775
  writeFileSync(agentsPath, agentsContent);
774
776
  } else {
775
- console.log(' ⏭️ AGENTS.md — 跳过(用户文件,非 AutoSnippet 生成)');
776
777
  }
777
778
  }
778
779
 
@@ -805,7 +806,6 @@ export class SetupService {
805
806
  ].join('\n');
806
807
  writeFileSync(claudePath, claudeContent);
807
808
  } else {
808
- console.log(' ⏭️ CLAUDE.md — 跳过(用户文件,非 AutoSnippet 生成)');
809
809
  }
810
810
  }
811
811
  }
@@ -181,7 +181,9 @@ export class UpgradeService {
181
181
  pipeline
182
182
  .deliver()
183
183
  .then((_result) => {})
184
- .catch((_err) => {});
184
+ .catch((_err) => {
185
+ /* fire-and-forget: delivery failure is non-critical during upgrade */
186
+ });
185
187
  }
186
188
  })
187
189
  .catch(() => {
@@ -266,7 +268,6 @@ export class UpgradeService {
266
268
  mkdirSync(destDir, { recursive: true });
267
269
  const { written } = safeCopyFile(src, dest);
268
270
  if (!written) {
269
- console.log(' ⏭️ copilot-instructions.md — 跳过(用户文件,非 AutoSnippet 生成)');
270
271
  }
271
272
  }
272
273
 
@@ -128,7 +128,7 @@ function analyzeProject(files, lang, options) {
128
128
 
129
129
  // SFC 预处理: .vue / .svelte 等文件 → 提取 <script> 块再交给 AST
130
130
  if (preprocessFile) {
131
- const ext = file.name ? ('.' + file.name.split('.').pop()) : '';
131
+ const ext = file.name ? `.${file.name.split('.').pop()}` : '';
132
132
  const result = preprocessFile(content, ext);
133
133
  if (result) {
134
134
  content = result.content;
@@ -88,7 +88,7 @@ export async function ensureGrammars(detectedLanguages, options = {}) {
88
88
  if (result.failed.length > 0) {
89
89
  logger?.warn?.(
90
90
  `[ensure-grammars] ${result.failed.length} grammar(s) missing. ` +
91
- `Expected in: ${GRAMMARS_DIR}`
91
+ `Expected in: ${GRAMMARS_DIR}`
92
92
  );
93
93
  } else {
94
94
  logger?.info?.('[ensure-grammars] All required grammar .wasm files available');
@@ -20,7 +20,7 @@
20
20
  */
21
21
 
22
22
  import { registerLanguage } from '../AstAnalyzer.js';
23
- import { initParser, loadLanguageWasm, isParserReady } from './parser-init.js';
23
+ import { initParser, isParserReady, loadLanguageWasm } from './parser-init.js';
24
24
 
25
25
  let _loaded = false;
26
26
 
@@ -36,17 +36,68 @@ export function _resetForReload() {
36
36
  * 语言注册表 — langId → { wasmFile, module, setGrammarFn, langId, tsxWasmFile?, setTsxGrammarFn? }
37
37
  */
38
38
  const LANG_REGISTRY = [
39
- { langId: 'objectivec', wasmFile: 'tree-sitter-objc.wasm', module: './lang-objc.js', setFn: 'setGrammar' },
40
- { langId: 'swift', wasmFile: 'tree-sitter-swift.wasm', module: './lang-swift.js', setFn: 'setGrammar' },
41
- { langId: 'typescript', wasmFile: 'tree-sitter-typescript.wasm', module: './lang-typescript.js', setFn: 'setGrammar' },
42
- { langId: 'tsx', wasmFile: 'tree-sitter-tsx.wasm', module: './lang-typescript.js', setFn: 'setTsxGrammar', pluginKey: 'tsxPlugin' },
43
- { langId: 'javascript', wasmFile: 'tree-sitter-javascript.wasm', module: './lang-javascript.js', setFn: 'setGrammar' },
44
- { langId: 'python', wasmFile: 'tree-sitter-python.wasm', module: './lang-python.js', setFn: 'setGrammar' },
45
- { langId: 'java', wasmFile: 'tree-sitter-java.wasm', module: './lang-java.js', setFn: 'setGrammar' },
46
- { langId: 'kotlin', wasmFile: 'tree-sitter-kotlin.wasm', module: './lang-kotlin.js', setFn: 'setGrammar' },
39
+ {
40
+ langId: 'objectivec',
41
+ wasmFile: 'tree-sitter-objc.wasm',
42
+ module: './lang-objc.js',
43
+ setFn: 'setGrammar',
44
+ },
45
+ {
46
+ langId: 'swift',
47
+ wasmFile: 'tree-sitter-swift.wasm',
48
+ module: './lang-swift.js',
49
+ setFn: 'setGrammar',
50
+ },
51
+ {
52
+ langId: 'typescript',
53
+ wasmFile: 'tree-sitter-typescript.wasm',
54
+ module: './lang-typescript.js',
55
+ setFn: 'setGrammar',
56
+ },
57
+ {
58
+ langId: 'tsx',
59
+ wasmFile: 'tree-sitter-tsx.wasm',
60
+ module: './lang-typescript.js',
61
+ setFn: 'setTsxGrammar',
62
+ pluginKey: 'tsxPlugin',
63
+ },
64
+ {
65
+ langId: 'javascript',
66
+ wasmFile: 'tree-sitter-javascript.wasm',
67
+ module: './lang-javascript.js',
68
+ setFn: 'setGrammar',
69
+ },
70
+ {
71
+ langId: 'python',
72
+ wasmFile: 'tree-sitter-python.wasm',
73
+ module: './lang-python.js',
74
+ setFn: 'setGrammar',
75
+ },
76
+ {
77
+ langId: 'java',
78
+ wasmFile: 'tree-sitter-java.wasm',
79
+ module: './lang-java.js',
80
+ setFn: 'setGrammar',
81
+ },
82
+ {
83
+ langId: 'kotlin',
84
+ wasmFile: 'tree-sitter-kotlin.wasm',
85
+ module: './lang-kotlin.js',
86
+ setFn: 'setGrammar',
87
+ },
47
88
  { langId: 'go', wasmFile: 'tree-sitter-go.wasm', module: './lang-go.js', setFn: 'setGrammar' },
48
- { langId: 'dart', wasmFile: 'tree-sitter-dart.wasm', module: './lang-dart.js', setFn: 'setGrammar' },
49
- { langId: 'rust', wasmFile: 'tree-sitter-rust.wasm', module: './lang-rust.js', setFn: 'setGrammar' },
89
+ {
90
+ langId: 'dart',
91
+ wasmFile: 'tree-sitter-dart.wasm',
92
+ module: './lang-dart.js',
93
+ setFn: 'setGrammar',
94
+ },
95
+ {
96
+ langId: 'rust',
97
+ wasmFile: 'tree-sitter-rust.wasm',
98
+ module: './lang-rust.js',
99
+ setFn: 'setGrammar',
100
+ },
50
101
  ];
51
102
 
52
103
  /**
@@ -146,8 +146,12 @@ function _parseClassDef(node, ctx) {
146
146
  }
147
147
 
148
148
  let kind = 'class';
149
- if (isAbstract) kind = 'abstract-class';
150
- if (isSealed) kind = 'sealed-class';
149
+ if (isAbstract) {
150
+ kind = 'abstract-class';
151
+ }
152
+ if (isSealed) {
153
+ kind = 'sealed-class';
154
+ }
151
155
 
152
156
  ctx.classes.push({
153
157
  name,
@@ -227,7 +231,9 @@ function _parseMixinDecl(node, ctx) {
227
231
  );
228
232
  const name = nameNode?.text || 'Unknown';
229
233
 
230
- const onClause = node.namedChildren.find((c) => c.type === 'on_clause' || c.type === 'superclass');
234
+ const onClause = node.namedChildren.find(
235
+ (c) => c.type === 'on_clause' || c.type === 'superclass'
236
+ );
231
237
  const constraints = [];
232
238
  if (onClause) {
233
239
  for (let i = 0; i < onClause.namedChildCount; i++) {
@@ -262,9 +268,7 @@ function _parseExtensionDecl(node, ctx) {
262
268
  const name = nameNode?.text || 'anonymous_extension';
263
269
 
264
270
  // on Type
265
- const onType = node.namedChildren.find(
266
- (c) => c.type === 'type_identifier' && c !== nameNode
267
- );
271
+ const onType = node.namedChildren.find((c) => c.type === 'type_identifier' && c !== nameNode);
268
272
 
269
273
  ctx.categories.push({
270
274
  name,
@@ -273,7 +277,9 @@ function _parseExtensionDecl(node, ctx) {
273
277
  endLine: node.endPosition.row + 1,
274
278
  });
275
279
 
276
- const body = node.namedChildren.find((c) => c.type === 'class_body' || c.type === 'extension_body');
280
+ const body = node.namedChildren.find(
281
+ (c) => c.type === 'class_body' || c.type === 'extension_body'
282
+ );
277
283
  if (body) {
278
284
  _walkClassBody(body, ctx, name);
279
285
  }
@@ -310,9 +316,7 @@ function _parseFunctionDef(node, className) {
310
316
  const isAsync = node.text.includes('async') || node.text.includes('async*');
311
317
  const isOverride = node.text.includes('@override');
312
318
 
313
- const body = node.namedChildren.find(
314
- (c) => c.type === 'function_body' || c.type === 'block'
315
- );
319
+ const body = node.namedChildren.find((c) => c.type === 'function_body' || c.type === 'block');
316
320
  const bodyLines = body ? body.endPosition.row - body.startPosition.row + 1 : 0;
317
321
  const complexity = body ? _estimateComplexity(body) : 1;
318
322
  const nestingDepth = body ? _maxNesting(body, 0) : 0;
@@ -372,13 +376,17 @@ function detectDartPatterns(root, lang, methods, properties, classes) {
372
376
  const classPropMap = {};
373
377
  for (const m of methods) {
374
378
  if (m.className) {
375
- if (!classMethodMap[m.className]) classMethodMap[m.className] = [];
379
+ if (!classMethodMap[m.className]) {
380
+ classMethodMap[m.className] = [];
381
+ }
376
382
  classMethodMap[m.className].push(m);
377
383
  }
378
384
  }
379
385
  for (const p of properties) {
380
386
  if (p.className) {
381
- if (!classPropMap[p.className]) classPropMap[p.className] = [];
387
+ if (!classPropMap[p.className]) {
388
+ classPropMap[p.className] = [];
389
+ }
382
390
  classPropMap[p.className].push(p);
383
391
  }
384
392
  }
@@ -430,10 +438,7 @@ function detectDartPatterns(root, lang, methods, properties, classes) {
430
438
  }
431
439
 
432
440
  // ChangeNotifier (Provider pattern)
433
- if (
434
- cls.superclass === 'ChangeNotifier' ||
435
- (cls.mixins && cls.mixins.includes('ChangeNotifier'))
436
- ) {
441
+ if (cls.superclass === 'ChangeNotifier' || cls.mixins?.includes('ChangeNotifier')) {
437
442
  patterns.push({
438
443
  type: 'change-notifier',
439
444
  className: cls.name,
@@ -448,9 +453,7 @@ function detectDartPatterns(root, lang, methods, properties, classes) {
448
453
  const hasPrivateConstructor = classMethods.some(
449
454
  (m) => m.kind === 'constructor' && m.name.startsWith('_')
450
455
  );
451
- const hasStaticInstance = classProps.some(
452
- (p) => p.isStatic && (p.isFinal || p.isConst)
453
- );
456
+ const hasStaticInstance = classProps.some((p) => p.isStatic && (p.isFinal || p.isConst));
454
457
  const hasFactoryConstructor = classMethods.some((m) => m.kind === 'factory');
455
458
 
456
459
  if (hasPrivateConstructor && (hasStaticInstance || hasFactoryConstructor)) {
@@ -493,7 +496,7 @@ function detectDartPatterns(root, lang, methods, properties, classes) {
493
496
  }
494
497
 
495
498
  // Freezed pattern — @freezed/@Freezed annotation + with _$ClassName mixin
496
- if (cls.mixins && cls.mixins.some((m) => m.startsWith('_$'))) {
499
+ if (cls.mixins?.some((m) => m.startsWith('_$'))) {
497
500
  patterns.push({
498
501
  type: 'freezed',
499
502
  className: cls.name,
@@ -548,7 +551,10 @@ function _detectStreamUsage(root, patterns) {
548
551
  if (node.type === 'type_identifier' && node.text === 'Stream') {
549
552
  streamCount++;
550
553
  }
551
- if (node.type === 'identifier' && (node.text === 'StreamController' || node.text === 'StreamSubscription')) {
554
+ if (
555
+ node.type === 'identifier' &&
556
+ (node.text === 'StreamController' || node.text === 'StreamSubscription')
557
+ ) {
552
558
  streamCount++;
553
559
  }
554
560
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -39,9 +39,7 @@ function walkGo(root, ctx) {
39
39
  // 单行 import
40
40
  const spec = child.namedChildren.find((c) => c.type === 'import_spec');
41
41
  if (spec) {
42
- const strLit = spec.namedChildren.find(
43
- (c) => c.type === 'interpreted_string_literal'
44
- );
42
+ const strLit = spec.namedChildren.find((c) => c.type === 'interpreted_string_literal');
45
43
  if (strLit) {
46
44
  ctx.imports.push(strLit.text.replace(/"/g, ''));
47
45
  }
@@ -117,7 +115,7 @@ function _walkTypeDeclaration(node, ctx) {
117
115
 
118
116
  function _parseStruct(name, structNode, specNode, ctx) {
119
117
  const fields = [];
120
- let embeddedTypes = [];
118
+ const embeddedTypes = [];
121
119
 
122
120
  const fieldList = structNode.namedChildren.find((c) => c.type === 'field_declaration_list');
123
121
  if (fieldList) {
@@ -326,14 +324,9 @@ function detectGoPatterns(root, lang, methods, properties, classes) {
326
324
  continue;
327
325
  }
328
326
  // 检查是否有 package-level var 指向此 struct
329
- const hasPackageVar = properties.some(
330
- (p) => !p.className && !p.isConst && !p.isExported
331
- );
327
+ const hasPackageVar = properties.some((p) => !p.className && !p.isConst && !p.isExported);
332
328
  const hasNewFunc = methods.some(
333
- (m) =>
334
- !m.className &&
335
- /^(?:New|Get|Default)/.test(m.name) &&
336
- m.isExported
329
+ (m) => !m.className && /^(?:New|Get|Default)/.test(m.name) && m.isExported
337
330
  );
338
331
  if (hasPackageVar && hasNewFunc) {
339
332
  patterns.push({
@@ -346,11 +339,7 @@ function detectGoPatterns(root, lang, methods, properties, classes) {
346
339
 
347
340
  // Factory: New* / Create* package-level functions
348
341
  for (const m of methods) {
349
- if (
350
- !m.className &&
351
- m.isExported &&
352
- /^(?:New|Create|Make|Build|Open|Connect)/.test(m.name)
353
- ) {
342
+ if (!m.className && m.isExported && /^(?:New|Create|Make|Build|Open|Connect)/.test(m.name)) {
354
343
  patterns.push({
355
344
  type: 'factory',
356
345
  methodName: m.name,
@@ -446,10 +435,7 @@ function _countParams(paramList) {
446
435
  let count = 0;
447
436
  for (let i = 0; i < paramList.namedChildCount; i++) {
448
437
  const child = paramList.namedChild(i);
449
- if (
450
- child.type === 'parameter_declaration' ||
451
- child.type === 'variadic_parameter_declaration'
452
- ) {
438
+ if (child.type === 'parameter_declaration' || child.type === 'variadic_parameter_declaration') {
453
439
  // Each identifier in the same declaration is a parameter
454
440
  const ids = child.namedChildren.filter((c) => c.type === 'identifier');
455
441
  count += Math.max(ids.length, 1);