autosnippet 3.0.13 → 3.1.0

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 +54 -36
  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
@@ -238,15 +238,15 @@ const LANG_ALIASES = Object.freeze({
238
238
 
239
239
  /** @type {Readonly<Record<string, readonly string[]>>} */
240
240
  const ECO_TO_LANGS = Object.freeze({
241
- spm: Object.freeze(['swift', 'objectivec']),
242
- node: Object.freeze(['javascript', 'typescript']),
243
- go: Object.freeze(['go']),
244
- jvm: Object.freeze(['java', 'kotlin']),
245
- python: Object.freeze(['python']),
246
- dart: Object.freeze(['dart']),
247
- rust: Object.freeze(['rust']),
248
- dotnet: Object.freeze(['csharp']),
249
- ruby: Object.freeze(['ruby']),
241
+ spm: Object.freeze(['swift', 'objectivec']),
242
+ node: Object.freeze(['javascript', 'typescript']),
243
+ go: Object.freeze(['go']),
244
+ jvm: Object.freeze(['java', 'kotlin']),
245
+ python: Object.freeze(['python']),
246
+ dart: Object.freeze(['dart']),
247
+ rust: Object.freeze(['rust']),
248
+ dotnet: Object.freeze(['csharp']),
249
+ ruby: Object.freeze(['ruby']),
250
250
  generic: Object.freeze([]),
251
251
  });
252
252
 
@@ -260,41 +260,59 @@ const ECO_TO_LANGS = Object.freeze({
260
260
  */
261
261
  const BUILD_SYSTEM_MARKERS = Object.freeze([
262
262
  // Apple / iOS
263
- { file: 'Package.swift', eco: 'spm', buildTool: 'SPM' },
264
- { file: 'Podfile', eco: 'spm', buildTool: 'CocoaPods' },
263
+ { file: 'Package.swift', eco: 'spm', buildTool: 'SPM' },
264
+ { file: 'Podfile', eco: 'spm', buildTool: 'CocoaPods' },
265
265
  // JS / TS (lock files before package.json to detect specific tool)
266
- { file: 'yarn.lock', eco: 'node', buildTool: 'Yarn' },
267
- { file: 'pnpm-lock.yaml', eco: 'node', buildTool: 'pnpm' },
268
- { file: 'package.json', eco: 'node', buildTool: 'npm' },
266
+ { file: 'yarn.lock', eco: 'node', buildTool: 'Yarn' },
267
+ { file: 'pnpm-lock.yaml', eco: 'node', buildTool: 'pnpm' },
268
+ { file: 'package.json', eco: 'node', buildTool: 'npm' },
269
269
  // Python
270
- { file: 'Pipfile', eco: 'python', buildTool: 'Pipenv' },
271
- { file: 'pyproject.toml', eco: 'python', buildTool: 'Poetry' },
272
- { file: 'setup.py', eco: 'python', buildTool: 'setuptools' },
273
- { file: 'requirements.txt', eco: 'python', buildTool: 'pip' },
270
+ { file: 'Pipfile', eco: 'python', buildTool: 'Pipenv' },
271
+ { file: 'pyproject.toml', eco: 'python', buildTool: 'Poetry' },
272
+ { file: 'setup.py', eco: 'python', buildTool: 'setuptools' },
273
+ { file: 'requirements.txt', eco: 'python', buildTool: 'pip' },
274
274
  // Go
275
- { file: 'go.mod', eco: 'go', buildTool: 'Go Modules' },
275
+ { file: 'go.mod', eco: 'go', buildTool: 'Go Modules' },
276
276
  // Rust
277
- { file: 'Cargo.toml', eco: 'rust', buildTool: 'Cargo' },
277
+ { file: 'Cargo.toml', eco: 'rust', buildTool: 'Cargo' },
278
278
  // JVM
279
- { file: 'pom.xml', eco: 'jvm', buildTool: 'Maven' },
280
- { file: 'build.gradle', eco: 'jvm', buildTool: 'Gradle' },
281
- { file: 'build.gradle.kts', eco: 'jvm', buildTool: 'Gradle (Kotlin)' },
279
+ { file: 'pom.xml', eco: 'jvm', buildTool: 'Maven' },
280
+ { file: 'build.gradle', eco: 'jvm', buildTool: 'Gradle' },
281
+ { file: 'build.gradle.kts', eco: 'jvm', buildTool: 'Gradle (Kotlin)' },
282
282
  // Dart / Flutter
283
- { file: 'pubspec.yaml', eco: 'dart', buildTool: 'Flutter' },
284
- { file: 'melos.yaml', eco: 'dart', buildTool: 'Melos' },
283
+ { file: 'pubspec.yaml', eco: 'dart', buildTool: 'Flutter' },
284
+ { file: 'melos.yaml', eco: 'dart', buildTool: 'Melos' },
285
285
  // C# / .NET
286
- { file: '*.csproj', eco: 'dotnet', buildTool: '.NET' },
287
- { file: '*.sln', eco: 'dotnet', buildTool: '.NET' },
286
+ { file: '*.csproj', eco: 'dotnet', buildTool: '.NET' },
287
+ { file: '*.sln', eco: 'dotnet', buildTool: '.NET' },
288
288
  // Ruby
289
- { file: 'Gemfile', eco: 'ruby', buildTool: 'Bundler' },
289
+ { file: 'Gemfile', eco: 'ruby', buildTool: 'Bundler' },
290
290
  ]);
291
291
 
292
292
  /** 扫描目录时跳过的标准目录(性能优化) */
293
- const SCAN_SKIP_DIRS = Object.freeze(new Set([
294
- '.git', 'node_modules', '.build', 'build', 'dist', 'target', 'out',
295
- 'vendor', '.cache', 'Pods', 'DerivedData', '__pycache__', '.venv', 'venv',
296
- '.gradle', 'Carthage', '.fvm', '.dart_tool', '.cargo',
297
- ]));
293
+ const SCAN_SKIP_DIRS = Object.freeze(
294
+ new Set([
295
+ '.git',
296
+ 'node_modules',
297
+ '.build',
298
+ 'build',
299
+ 'dist',
300
+ 'target',
301
+ 'out',
302
+ 'vendor',
303
+ '.cache',
304
+ 'Pods',
305
+ 'DerivedData',
306
+ '__pycache__',
307
+ '.venv',
308
+ 'venv',
309
+ '.gradle',
310
+ 'Carthage',
311
+ '.fvm',
312
+ '.dart_tool',
313
+ '.cargo',
314
+ ])
315
+ );
298
316
 
299
317
  // ═══════════════════════════════════════════════════════════
300
318
  // LanguageService — 静态单例
@@ -309,7 +327,9 @@ export class LanguageService {
309
327
  * @returns {string} 语言 ID,如 'swift', 'typescript', 'python', 'unknown'
310
328
  */
311
329
  static inferLang(filename) {
312
- if (!filename || typeof filename !== 'string') return 'unknown';
330
+ if (!filename || typeof filename !== 'string') {
331
+ return 'unknown';
332
+ }
313
333
  const dot = filename.lastIndexOf('.');
314
334
  if (dot === -1) {
315
335
  return 'unknown';
@@ -324,7 +344,9 @@ export class LanguageService {
324
344
  * @returns {string}
325
345
  */
326
346
  static langFromExt(ext) {
327
- if (!ext || typeof ext !== 'string') return 'unknown';
347
+ if (!ext || typeof ext !== 'string') {
348
+ return 'unknown';
349
+ }
328
350
  return EXT_TO_LANG[ext.toLowerCase()] || 'unknown';
329
351
  }
330
352
 
@@ -344,9 +366,13 @@ export class LanguageService {
344
366
  * @returns {string} 规范化语言 ID
345
367
  */
346
368
  static normalize(langId) {
347
- if (!langId || typeof langId !== 'string') return 'unknown';
369
+ if (!langId || typeof langId !== 'string') {
370
+ return 'unknown';
371
+ }
348
372
  const lower = langId.toLowerCase().trim();
349
- if (KNOWN_PROGRAMMING_LANGS.has(lower)) return lower;
373
+ if (KNOWN_PROGRAMMING_LANGS.has(lower)) {
374
+ return lower;
375
+ }
350
376
  return LANG_ALIASES[lower] || lower;
351
377
  }
352
378
 
@@ -527,10 +553,14 @@ export class LanguageService {
527
553
  * @returns {string|null} - 如 '.go', '.swift', '.py';未知返回 null
528
554
  */
529
555
  static extForLang(langId) {
530
- if (!langId) return null;
556
+ if (!langId) {
557
+ return null;
558
+ }
531
559
  const lower = langId.toLowerCase();
532
560
  for (const [ext, lang] of Object.entries(EXT_TO_LANG)) {
533
- if (lang === lower) return ext;
561
+ if (lang === lower) {
562
+ return ext;
563
+ }
534
564
  }
535
565
  return null;
536
566
  }
@@ -585,13 +615,17 @@ export class LanguageService {
585
615
  * @returns {Array<{ eco: string, buildTool: string }>}
586
616
  */
587
617
  static matchBuildMarkers(entryNames) {
588
- if (!Array.isArray(entryNames) || entryNames.length === 0) return [];
618
+ if (!Array.isArray(entryNames) || entryNames.length === 0) {
619
+ return [];
620
+ }
589
621
  const nameSet = new Set(entryNames);
590
622
  const results = [];
591
623
  const seenEco = new Set();
592
624
 
593
625
  for (const marker of BUILD_SYSTEM_MARKERS) {
594
- if (seenEco.has(marker.eco)) continue;
626
+ if (seenEco.has(marker.eco)) {
627
+ continue;
628
+ }
595
629
  const isGlob = marker.file.startsWith('*');
596
630
  const matched = isGlob
597
631
  ? entryNames.some((n) => n.endsWith(marker.file.slice(1)))
@@ -618,7 +652,9 @@ export class LanguageService {
618
652
  * @returns {string[]} 规范化语言 ID 数组(如 ['rust', 'dart'])
619
653
  */
620
654
  static detectProjectLanguages(projectRoot, opts = {}) {
621
- if (!projectRoot || typeof projectRoot !== 'string') return [];
655
+ if (!projectRoot || typeof projectRoot !== 'string') {
656
+ return [];
657
+ }
622
658
  const { discovererIds, maxDepth = 2 } = opts;
623
659
 
624
660
  // ── Path 1: 从 Discoverer ID 映射 ──
@@ -627,9 +663,13 @@ export class LanguageService {
627
663
  if (nonGeneric.length > 0) {
628
664
  const langSet = new Set();
629
665
  for (const did of nonGeneric) {
630
- for (const lang of ECO_TO_LANGS[did] || []) langSet.add(lang);
666
+ for (const lang of ECO_TO_LANGS[did] || []) {
667
+ langSet.add(lang);
668
+ }
669
+ }
670
+ if (langSet.size > 0) {
671
+ return [...langSet];
631
672
  }
632
- if (langSet.size > 0) return [...langSet];
633
673
  }
634
674
  }
635
675
 
@@ -639,20 +679,28 @@ export class LanguageService {
639
679
  const scanDir = (dir) => {
640
680
  try {
641
681
  for (const marker of BUILD_SYSTEM_MARKERS) {
642
- if (seenEco.has(marker.eco)) continue;
682
+ if (seenEco.has(marker.eco)) {
683
+ continue;
684
+ }
643
685
  const isGlob = marker.file.startsWith('*');
644
686
  let matched = false;
645
687
  if (isGlob) {
646
688
  try {
647
689
  const suffix = marker.file.slice(1);
648
690
  matched = readdirSync(dir).some((n) => n.endsWith(suffix));
649
- } catch { /* skip */ }
691
+ } catch {
692
+ /* skip */
693
+ }
650
694
  } else {
651
695
  matched = existsSync(join(dir, marker.file));
652
696
  }
653
- if (matched) seenEco.add(marker.eco);
697
+ if (matched) {
698
+ seenEco.add(marker.eco);
699
+ }
654
700
  }
655
- } catch { /* skip unreadable dir */ }
701
+ } catch {
702
+ /* skip unreadable dir */
703
+ }
656
704
  };
657
705
 
658
706
  // Level 0: 项目根目录
@@ -664,22 +712,32 @@ export class LanguageService {
664
712
  const queue = [[projectRoot, 0]];
665
713
  while (queue.length > 0) {
666
714
  const [dir, depth] = queue.shift();
667
- if (depth >= maxDepth) continue;
715
+ if (depth >= maxDepth) {
716
+ continue;
717
+ }
668
718
  try {
669
719
  for (const ent of readdirSync(dir, { withFileTypes: true })) {
670
- if (!ent.isDirectory() || ent.name.startsWith('.') || SCAN_SKIP_DIRS.has(ent.name)) continue;
720
+ if (!ent.isDirectory() || ent.name.startsWith('.') || SCAN_SKIP_DIRS.has(ent.name)) {
721
+ continue;
722
+ }
671
723
  const sub = join(dir, ent.name);
672
724
  scanDir(sub);
673
- if (depth + 1 < maxDepth) queue.push([sub, depth + 1]);
725
+ if (depth + 1 < maxDepth) {
726
+ queue.push([sub, depth + 1]);
727
+ }
674
728
  }
675
- } catch { /* skip */ }
729
+ } catch {
730
+ /* skip */
731
+ }
676
732
  }
677
733
  }
678
734
 
679
735
  // ── 将生态 ID 转为语言 ID ──
680
736
  const langSet = new Set();
681
737
  for (const eco of seenEco) {
682
- for (const lang of ECO_TO_LANGS[eco] || []) langSet.add(lang);
738
+ for (const lang of ECO_TO_LANGS[eco] || []) {
739
+ langSet.add(lang);
740
+ }
683
741
  }
684
742
 
685
743
  // ── 启发式: node 与其他生态共存时,JS/TS 通常只是构建工具,去掉 ──
@@ -320,14 +320,6 @@ class PathGuard {
320
320
  }
321
321
  }
322
322
 
323
- /**
324
- * 延迟加载 fs(避免循环依赖)
325
- */
326
- function _await_fs() {
327
- // biome-ignore lint/security/noGlobalEval: intentional CJS require fallback for avoiding circular deps
328
- return eval("require('fs')");
329
- }
330
-
331
323
  // 单例 — 整个进程共享
332
324
  const pathGuard = new PathGuard();
333
325
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autosnippet",
3
- "version": "3.0.13",
3
+ "version": "3.1.0",
4
4
  "description": "Extract code patterns into a knowledge base for AI coding assistants",
5
5
  "type": "module",
6
6
  "main": "lib/bootstrap.js",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "postinstall": "node scripts/postinstall-safe.js",
12
- "prepublishOnly": "node scripts/build-native-ui.js",
12
+ "prepublishOnly": "npm run build:dashboard && node scripts/build-native-ui.js",
13
13
  "build:native-ui": "swiftc resources/native-ui/main.swift resources/native-ui/combined-window.swift -o resources/native-ui/native-ui -framework AppKit",
14
14
  "install:cursor-skill": "node scripts/install-cursor-skill.js",
15
15
  "install:cursor-skill:mcp": "node scripts/install-cursor-skill.js --mcp",
@@ -71,18 +71,14 @@
71
71
  "url": "git+https://github.com/GxFn/AutoSnippet.git"
72
72
  },
73
73
  "dependencies": {
74
- "@google/generative-ai": "^0.21.0",
75
74
  "@modelcontextprotocol/sdk": "^1.25.3",
76
- "axios": "^1.6.7",
77
75
  "better-sqlite3": "^9.2.2",
78
- "chalk": "^5.3.0",
79
76
  "chokidar": "^3.6.0",
80
77
  "commander": "^11.1.0",
81
78
  "cors": "^2.8.5",
82
79
  "dotenv": "^16.3.1",
83
80
  "express": "^4.18.2",
84
81
  "helmet": "^7.1.0",
85
- "inquirer": "^9.2.12",
86
82
  "js-yaml": "^4.1.0",
87
83
  "minimist": "^1.2.5",
88
84
  "open": "^8.0.4",
@@ -91,8 +87,7 @@
91
87
  "undici": "^6.23.0",
92
88
  "uuid": "^9.0.1",
93
89
  "web-tree-sitter": "^0.25.0",
94
- "winston": "^3.11.0",
95
- "zod": "^4.3.6"
90
+ "winston": "^3.11.0"
96
91
  },
97
92
  "bugs": {
98
93
  "url": "https://github.com/GxFn/AutoSnippet/issues"
@@ -122,7 +117,6 @@
122
117
  "@biomejs/biome": "2.0.0",
123
118
  "@types/node": "^20.10.6",
124
119
  "jest": "^30.2.0",
125
- "mocha": "^11.7.5",
126
120
  "nodemon": "^3.1.13",
127
121
  "prettier": "^3.1.1",
128
122
  "supertest": "^6.3.3"
@@ -177,48 +177,48 @@ async function benchmark() {
177
177
  results.push(row);
178
178
  process.stdout.write(
179
179
  `✓ ${name.padEnd(20)} ${row.files.toString().padStart(5)} files | ` +
180
- `detect=${row.detectMs}ms load=${row.loadMs}ms collect=${row.fileCollectMs}ms ` +
181
- `ast=${row.astMs}ms total=${row.totalMs}ms\n`
180
+ `detect=${row.detectMs}ms load=${row.loadMs}ms collect=${row.fileCollectMs}ms ` +
181
+ `ast=${row.astMs}ms total=${row.totalMs}ms\n`
182
182
  );
183
183
  }
184
184
 
185
185
  // ── 汇总表 ──────────────────────────────────────────────────────
186
- process.stdout.write('\n' + '═'.repeat(120) + '\n');
186
+ process.stdout.write(`\n${'═'.repeat(120)}\n`);
187
187
  process.stdout.write(
188
188
  'Project'.padEnd(22) +
189
- 'Files'.padStart(6) +
190
- 'Disc'.padStart(10) +
191
- 'Detect'.padStart(8) +
192
- 'Load'.padStart(8) +
193
- 'Collect'.padStart(9) +
194
- 'Lang'.padStart(10) +
195
- 'AST'.padStart(8) +
196
- 'Cls'.padStart(5) +
197
- 'Enh'.padStart(8) +
198
- 'DimC'.padStart(6) +
199
- 'Total'.padStart(8) +
200
- ' Packs\n'
189
+ 'Files'.padStart(6) +
190
+ 'Disc'.padStart(10) +
191
+ 'Detect'.padStart(8) +
192
+ 'Load'.padStart(8) +
193
+ 'Collect'.padStart(9) +
194
+ 'Lang'.padStart(10) +
195
+ 'AST'.padStart(8) +
196
+ 'Cls'.padStart(5) +
197
+ 'Enh'.padStart(8) +
198
+ 'DimC'.padStart(6) +
199
+ 'Total'.padStart(8) +
200
+ ' Packs\n'
201
201
  );
202
- process.stdout.write('─'.repeat(120) + '\n');
202
+ process.stdout.write(`${'─'.repeat(120)}\n`);
203
203
 
204
204
  for (const r of results) {
205
205
  process.stdout.write(
206
206
  r.project.padEnd(22) +
207
- r.files.toString().padStart(6) +
208
- r.discoverer.padStart(10) +
209
- `${r.detectMs}ms`.padStart(8) +
210
- `${r.loadMs}ms`.padStart(8) +
211
- `${r.fileCollectMs}ms`.padStart(9) +
212
- (r.primaryLang || '?').padStart(10) +
213
- `${r.astMs}ms`.padStart(8) +
214
- r.astClasses.toString().padStart(5) +
215
- `${r.enhancementMs}ms`.padStart(8) +
216
- `${r.dimCopyMs}ms`.padStart(6) +
217
- `${r.totalMs}ms`.padStart(8) +
218
- ` ${r.enhPacks}\n`
207
+ r.files.toString().padStart(6) +
208
+ r.discoverer.padStart(10) +
209
+ `${r.detectMs}ms`.padStart(8) +
210
+ `${r.loadMs}ms`.padStart(8) +
211
+ `${r.fileCollectMs}ms`.padStart(9) +
212
+ (r.primaryLang || '?').padStart(10) +
213
+ `${r.astMs}ms`.padStart(8) +
214
+ r.astClasses.toString().padStart(5) +
215
+ `${r.enhancementMs}ms`.padStart(8) +
216
+ `${r.dimCopyMs}ms`.padStart(6) +
217
+ `${r.totalMs}ms`.padStart(8) +
218
+ ` ${r.enhPacks}\n`
219
219
  );
220
220
  }
221
- process.stdout.write('═'.repeat(120) + '\n');
221
+ process.stdout.write(`${'═'.repeat(120)}\n`);
222
222
 
223
223
  // 性能阈值检查
224
224
  let warnings = 0;
@@ -72,13 +72,20 @@ function readSnippet(filePath, maxChars) {
72
72
  function detectLanguageByExt(ext) {
73
73
  const map = {
74
74
  '.swift': 'swift',
75
- '.m': 'objectivec', '.h': 'objectivec', '.mm': 'objectivec',
76
- '.js': 'javascript', '.mjs': 'javascript', '.jsx': 'javascript',
77
- '.ts': 'typescript', '.tsx': 'typescript',
75
+ '.m': 'objectivec',
76
+ '.h': 'objectivec',
77
+ '.mm': 'objectivec',
78
+ '.js': 'javascript',
79
+ '.mjs': 'javascript',
80
+ '.jsx': 'javascript',
81
+ '.ts': 'typescript',
82
+ '.tsx': 'typescript',
78
83
  '.py': 'python',
79
- '.java': 'java', '.kt': 'kotlin',
84
+ '.java': 'java',
85
+ '.kt': 'kotlin',
80
86
  '.go': 'go',
81
- '.rs': 'rust', '.rb': 'ruby',
87
+ '.rs': 'rust',
88
+ '.rb': 'ruby',
82
89
  };
83
90
  return map[ext] || 'text';
84
91
  }
@@ -109,18 +116,7 @@ function buildRecipe({ title, trigger, language, sourceFile, snippet }) {
109
116
 
110
117
  const DEFAULT_EXTS = '.swift,.m,.h,.mm,.js,.mjs,.jsx,.ts,.tsx,.py,.java,.kt,.go';
111
118
 
112
- function printUsage() {
113
- console.log(`Usage: generate-recipe-drafts [options]
114
-
115
- Options:
116
- --projectRoot <path> 项目根目录 (默认: 当前目录)
117
- --targetDir <path> 要扫描的源码目录 (默认: projectRoot)
118
- --outDir <path> 输出目录 (默认: <projectRoot>/autosnippet-drafts/recipes)
119
- --exts <list> 扫描的扩展名,逗号分隔 (默认: ${DEFAULT_EXTS})
120
- --maxChars <n> 单文件最大截取字符数 (默认: 4000)
121
- --help 显示帮助
122
- `);
123
- }
119
+ function printUsage() {}
124
120
 
125
121
  function main() {
126
122
  const args = parseArgs(process.argv);
@@ -130,13 +126,9 @@ function main() {
130
126
  process.exit(0);
131
127
  }
132
128
 
133
- const projectRoot = args.projectRoot
134
- ? path.resolve(args.projectRoot)
135
- : process.cwd();
129
+ const projectRoot = args.projectRoot ? path.resolve(args.projectRoot) : process.cwd();
136
130
 
137
- const targetDir = args.targetDir
138
- ? path.resolve(args.targetDir)
139
- : projectRoot;
131
+ const targetDir = args.targetDir ? path.resolve(args.targetDir) : projectRoot;
140
132
 
141
133
  const outDir = args.outDir
142
134
  ? path.resolve(args.outDir)
@@ -159,7 +151,7 @@ function main() {
159
151
  const files = [];
160
152
  walk(targetDir, exts, files);
161
153
 
162
- let count = 0;
154
+ let _count = 0;
163
155
  for (const filePath of files) {
164
156
  const ext = path.extname(filePath);
165
157
  const base = path.basename(filePath, ext);
@@ -180,10 +172,8 @@ function main() {
180
172
  const safeName = `${base}${ext}`.replace(/\./g, '_');
181
173
  const outFile = path.join(outDir, `${safeName}.md`);
182
174
  fs.writeFileSync(outFile, recipe, 'utf8');
183
- count++;
175
+ _count++;
184
176
  }
185
-
186
- console.log(`✅ 生成 ${count} 个 recipe 草稿 → ${outDir}`);
187
177
  }
188
178
 
189
179
  main();