autosnippet 3.0.1 → 3.0.2

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 (290) hide show
  1. package/README.md +230 -324
  2. package/bin/api-server.js +1 -1
  3. package/bin/cli.js +204 -244
  4. package/bin/mcp-server.js +5 -3
  5. package/config/knowledge-base.config.js +132 -132
  6. package/dashboard/dist/assets/{icons-CEfgGaZi.js → icons-Cdq22n2i.js} +95 -100
  7. package/dashboard/dist/assets/index-ClkyPkDX.js +133 -0
  8. package/dashboard/dist/assets/index-t4QrJwv1.css +1 -0
  9. package/dashboard/dist/index.html +3 -3
  10. package/lib/bootstrap.js +8 -8
  11. package/lib/cli/AiScanService.js +86 -40
  12. package/lib/cli/KnowledgeSyncService.js +113 -74
  13. package/lib/cli/SetupService.js +439 -277
  14. package/lib/cli/UpgradeService.js +63 -100
  15. package/lib/core/AstAnalyzer.js +276 -597
  16. package/lib/core/ast/ProjectGraph.js +101 -40
  17. package/lib/core/ast/ensure-grammars.js +232 -0
  18. package/lib/core/ast/index.js +115 -0
  19. package/lib/core/ast/lang-dart.js +661 -0
  20. package/lib/core/ast/lang-go.js +530 -0
  21. package/lib/core/ast/lang-java.js +435 -0
  22. package/lib/core/ast/lang-javascript.js +272 -0
  23. package/lib/core/ast/lang-kotlin.js +423 -0
  24. package/lib/core/ast/lang-objc.js +388 -0
  25. package/lib/core/ast/lang-python.js +371 -0
  26. package/lib/core/ast/lang-swift.js +337 -0
  27. package/lib/core/ast/lang-typescript.js +503 -0
  28. package/lib/core/capability/CapabilityProbe.js +18 -9
  29. package/lib/core/constitution/Constitution.js +2 -3
  30. package/lib/core/constitution/ConstitutionValidator.js +65 -24
  31. package/lib/core/discovery/DartDiscoverer.js +534 -0
  32. package/lib/core/discovery/DiscovererRegistry.js +83 -0
  33. package/lib/core/discovery/GenericDiscoverer.js +225 -0
  34. package/lib/core/discovery/GoDiscoverer.js +541 -0
  35. package/lib/core/discovery/JvmDiscoverer.js +506 -0
  36. package/lib/core/discovery/NodeDiscoverer.js +466 -0
  37. package/lib/core/discovery/ProjectDiscoverer.js +93 -0
  38. package/lib/core/discovery/PythonDiscoverer.js +338 -0
  39. package/lib/core/discovery/SpmDiscoverer.js +5 -0
  40. package/lib/core/discovery/index.js +53 -0
  41. package/lib/core/enhancement/EnhancementPack.js +71 -0
  42. package/lib/core/enhancement/EnhancementRegistry.js +47 -0
  43. package/lib/core/enhancement/android-enhancement.js +102 -0
  44. package/lib/core/enhancement/django-enhancement.js +70 -0
  45. package/lib/core/enhancement/fastapi-enhancement.js +63 -0
  46. package/lib/core/enhancement/go-grpc-enhancement.js +152 -0
  47. package/lib/core/enhancement/go-web-enhancement.js +201 -0
  48. package/lib/core/enhancement/index.js +65 -0
  49. package/lib/core/enhancement/node-server-enhancement.js +88 -0
  50. package/lib/core/enhancement/react-enhancement.js +86 -0
  51. package/lib/core/enhancement/spring-enhancement.js +112 -0
  52. package/lib/core/enhancement/vue-enhancement.js +96 -0
  53. package/lib/core/gateway/Gateway.js +8 -9
  54. package/lib/core/gateway/GatewayActionRegistry.js +1 -1
  55. package/lib/core/permission/PermissionManager.js +12 -8
  56. package/lib/domain/index.js +13 -9
  57. package/lib/domain/knowledge/KnowledgeEntry.js +111 -101
  58. package/lib/domain/knowledge/KnowledgeRepository.js +0 -1
  59. package/lib/domain/knowledge/Lifecycle.js +22 -22
  60. package/lib/domain/knowledge/index.js +9 -12
  61. package/lib/domain/knowledge/values/Constraints.js +31 -21
  62. package/lib/domain/knowledge/values/Content.js +21 -13
  63. package/lib/domain/knowledge/values/Quality.js +31 -18
  64. package/lib/domain/knowledge/values/Reasoning.js +20 -12
  65. package/lib/domain/knowledge/values/Relations.js +37 -25
  66. package/lib/domain/knowledge/values/Stats.js +18 -12
  67. package/lib/domain/knowledge/values/index.js +4 -3
  68. package/lib/domain/snippet/Snippet.js +35 -10
  69. package/lib/external/ai/AiFactory.js +48 -16
  70. package/lib/external/ai/AiProvider.js +184 -90
  71. package/lib/external/ai/providers/ClaudeProvider.js +25 -12
  72. package/lib/external/ai/providers/GoogleGeminiProvider.js +59 -30
  73. package/lib/external/ai/providers/MockProvider.js +9 -3
  74. package/lib/external/ai/providers/OpenAiProvider.js +51 -29
  75. package/lib/external/mcp/McpServer.js +66 -36
  76. package/lib/external/mcp/errorHandler.js +23 -11
  77. package/lib/external/mcp/handlers/LanguageExtensions.js +138 -53
  78. package/lib/external/mcp/handlers/TargetClassifier.js +52 -16
  79. package/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +81 -20
  80. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +71 -42
  81. package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +9 -17
  82. package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +14 -9
  83. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +15 -7
  84. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +352 -153
  85. package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +52 -12
  86. package/lib/external/mcp/handlers/bootstrap/skills.js +143 -39
  87. package/lib/external/mcp/handlers/bootstrap.js +691 -168
  88. package/lib/external/mcp/handlers/browse.js +66 -22
  89. package/lib/external/mcp/handlers/candidate.js +118 -35
  90. package/lib/external/mcp/handlers/consolidated.js +49 -17
  91. package/lib/external/mcp/handlers/guard.js +104 -39
  92. package/lib/external/mcp/handlers/knowledge.js +60 -36
  93. package/lib/external/mcp/handlers/search.js +43 -14
  94. package/lib/external/mcp/handlers/skill.js +120 -45
  95. package/lib/external/mcp/handlers/structure.js +240 -86
  96. package/lib/external/mcp/handlers/system.js +42 -12
  97. package/lib/external/mcp/handlers/wiki.js +58 -33
  98. package/lib/external/mcp/tools.js +306 -123
  99. package/lib/http/HttpServer.js +72 -47
  100. package/lib/http/middleware/RateLimiter.js +5 -3
  101. package/lib/http/middleware/errorHandler.js +6 -1
  102. package/lib/http/middleware/requestLogger.js +14 -3
  103. package/lib/http/middleware/roleResolver.js +30 -23
  104. package/lib/http/routes/ai.js +387 -265
  105. package/lib/http/routes/auth.js +81 -61
  106. package/lib/http/routes/candidates.js +430 -320
  107. package/lib/http/routes/commands.js +289 -189
  108. package/lib/http/routes/extract.js +158 -125
  109. package/lib/http/routes/guardRules.js +309 -217
  110. package/lib/http/routes/knowledge.js +213 -154
  111. package/lib/http/routes/modules.js +578 -0
  112. package/lib/http/routes/monitoring.js +6 -6
  113. package/lib/http/routes/recipes.js +104 -93
  114. package/lib/http/routes/search.js +361 -305
  115. package/lib/http/routes/skills.js +145 -98
  116. package/lib/http/routes/snippets.js +42 -30
  117. package/lib/http/routes/spm.js +3 -405
  118. package/lib/http/routes/violations.js +113 -93
  119. package/lib/http/routes/wiki.js +211 -170
  120. package/lib/http/utils/routeHelpers.js +3 -1
  121. package/lib/http/utils/sse-sessions.js +16 -6
  122. package/lib/http/utils/sse.js +15 -5
  123. package/lib/infrastructure/audit/AuditLogger.js +5 -2
  124. package/lib/infrastructure/audit/AuditStore.js +10 -7
  125. package/lib/infrastructure/cache/CacheService.js +3 -1
  126. package/lib/infrastructure/cache/GraphCache.js +8 -4
  127. package/lib/infrastructure/cache/UnifiedCacheAdapter.js +1 -1
  128. package/lib/infrastructure/config/ConfigLoader.js +9 -5
  129. package/lib/infrastructure/config/Defaults.js +30 -10
  130. package/lib/infrastructure/config/Paths.js +28 -8
  131. package/lib/infrastructure/config/TriggerSymbol.js +22 -10
  132. package/lib/infrastructure/database/DatabaseConnection.js +15 -10
  133. package/lib/infrastructure/database/migrations/001_initial_schema.js +0 -1
  134. package/lib/infrastructure/external/ClipboardManager.js +6 -2
  135. package/lib/infrastructure/external/NativeUi.js +50 -43
  136. package/lib/infrastructure/external/OpenBrowser.js +14 -17
  137. package/lib/infrastructure/external/XcodeAutomation.js +14 -258
  138. package/lib/infrastructure/logging/Logger.js +46 -30
  139. package/lib/infrastructure/monitoring/ErrorTracker.js +7 -5
  140. package/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -4
  141. package/lib/infrastructure/paths/HeaderResolver.js +25 -9
  142. package/lib/infrastructure/paths/PathFinder.js +34 -12
  143. package/lib/infrastructure/plugin/PluginManager.js +26 -8
  144. package/lib/infrastructure/realtime/RealtimeService.js +2 -2
  145. package/lib/infrastructure/vector/Chunker.js +22 -7
  146. package/lib/infrastructure/vector/IndexingPipeline.js +46 -22
  147. package/lib/infrastructure/vector/JsonVectorAdapter.js +90 -53
  148. package/lib/infrastructure/vector/VectorStore.js +28 -10
  149. package/lib/injection/ServiceContainer.js +247 -93
  150. package/lib/platform/ios/index.js +63 -0
  151. package/lib/platform/ios/routes/spm.js +437 -0
  152. package/lib/platform/ios/snippet/PlaceholderConverter.js +55 -0
  153. package/lib/platform/ios/snippet/XcodeCodec.js +112 -0
  154. package/lib/{service → platform/ios}/spm/DependencyGraph.js +41 -17
  155. package/lib/{service → platform/ios}/spm/PackageSwiftParser.js +41 -14
  156. package/lib/{service → platform/ios}/spm/PolicyEngine.js +9 -4
  157. package/lib/platform/ios/spm/SpmDiscoverer.js +122 -0
  158. package/lib/{service → platform/ios}/spm/SpmService.js +385 -127
  159. package/lib/{service/automation → platform/ios/xcode}/SaveEventFilter.js +8 -7
  160. package/lib/platform/ios/xcode/XcodeAutomation.js +350 -0
  161. package/lib/{service/automation → platform/ios/xcode}/XcodeIntegration.js +325 -145
  162. package/lib/repository/base/BaseRepository.js +7 -9
  163. package/lib/repository/knowledge/KnowledgeRepository.impl.js +98 -75
  164. package/lib/repository/token/TokenUsageStore.js +4 -2
  165. package/lib/service/automation/ActionPipeline.js +1 -1
  166. package/lib/service/automation/AutomationOrchestrator.js +8 -4
  167. package/lib/service/automation/ContextCollector.js +7 -5
  168. package/lib/service/automation/DirectiveDetector.js +23 -16
  169. package/lib/service/automation/FileWatcher.js +112 -56
  170. package/lib/service/automation/TriggerResolver.js +6 -4
  171. package/lib/service/automation/handlers/AlinkHandler.js +24 -12
  172. package/lib/service/automation/handlers/CreateHandler.js +19 -20
  173. package/lib/service/automation/handlers/DraftHandler.js +14 -8
  174. package/lib/service/automation/handlers/GuardHandler.js +93 -63
  175. package/lib/service/automation/handlers/HeaderHandler.js +1 -6
  176. package/lib/service/automation/handlers/SearchHandler.js +155 -88
  177. package/lib/service/bootstrap/BootstrapTaskManager.js +77 -35
  178. package/lib/service/candidate/SimilarityService.js +25 -9
  179. package/lib/service/chat/AnalystAgent.js +50 -24
  180. package/lib/service/chat/CandidateGuardrail.js +143 -17
  181. package/lib/service/chat/ChatAgent.js +655 -260
  182. package/lib/service/chat/ContextWindow.js +116 -71
  183. package/lib/service/chat/ConversationStore.js +77 -36
  184. package/lib/service/chat/EpisodicConsolidator.js +47 -23
  185. package/lib/service/chat/HandoffProtocol.js +98 -22
  186. package/lib/service/chat/Memory.js +34 -14
  187. package/lib/service/chat/ProducerAgent.js +40 -20
  188. package/lib/service/chat/ProjectSemanticMemory.js +109 -78
  189. package/lib/service/chat/ReasoningLayer.js +148 -70
  190. package/lib/service/chat/ReasoningTrace.js +44 -32
  191. package/lib/service/chat/TaskPipeline.js +39 -19
  192. package/lib/service/chat/ToolRegistry.js +48 -29
  193. package/lib/service/chat/WorkingMemory.js +44 -18
  194. package/lib/service/chat/tools.js +1096 -494
  195. package/lib/service/context/RecipeExtractor.js +132 -51
  196. package/lib/service/cursor/CursorDeliveryPipeline.js +82 -37
  197. package/lib/service/cursor/KnowledgeCompressor.js +25 -22
  198. package/lib/service/cursor/RulesGenerator.js +13 -7
  199. package/lib/service/cursor/SkillsSyncer.js +77 -27
  200. package/lib/service/cursor/TokenBudget.js +2 -2
  201. package/lib/service/cursor/TopicClassifier.js +54 -20
  202. package/lib/service/guard/ComplianceReporter.js +55 -43
  203. package/lib/service/guard/ExclusionManager.js +67 -29
  204. package/lib/service/guard/GuardCheckEngine.js +381 -86
  205. package/lib/service/guard/GuardFeedbackLoop.js +22 -10
  206. package/lib/service/guard/GuardService.js +29 -19
  207. package/lib/service/guard/RuleLearner.js +55 -23
  208. package/lib/service/guard/SourceFileCollector.js +27 -20
  209. package/lib/service/guard/ViolationsStore.js +43 -38
  210. package/lib/service/knowledge/CodeEntityGraph.js +147 -82
  211. package/lib/service/knowledge/ConfidenceRouter.js +12 -10
  212. package/lib/service/knowledge/KnowledgeFileWriter.js +147 -56
  213. package/lib/service/knowledge/KnowledgeGraphService.js +81 -34
  214. package/lib/service/knowledge/KnowledgeService.js +222 -112
  215. package/lib/service/module/ModuleService.js +969 -0
  216. package/lib/service/quality/FeedbackCollector.js +27 -15
  217. package/lib/service/quality/QualityScorer.js +78 -24
  218. package/lib/service/recipe/RecipeCandidateValidator.js +110 -44
  219. package/lib/service/recipe/RecipeParser.js +78 -45
  220. package/lib/service/search/CoarseRanker.js +43 -28
  221. package/lib/service/search/CrossEncoderReranker.js +32 -21
  222. package/lib/service/search/InvertedIndex.js +21 -7
  223. package/lib/service/search/MultiSignalRanker.js +90 -28
  224. package/lib/service/search/RetrievalFunnel.js +45 -24
  225. package/lib/service/search/SearchEngine.js +255 -103
  226. package/lib/service/skills/EventAggregator.js +32 -15
  227. package/lib/service/skills/SignalCollector.js +140 -64
  228. package/lib/service/skills/SkillAdvisor.js +79 -42
  229. package/lib/service/skills/SkillHooks.js +16 -14
  230. package/lib/service/snippet/PlaceholderConverter.js +5 -0
  231. package/lib/service/snippet/SnippetFactory.js +116 -99
  232. package/lib/service/snippet/SnippetInstaller.js +234 -62
  233. package/lib/service/snippet/codecs/SnippetCodec.js +67 -0
  234. package/lib/service/snippet/codecs/VSCodeCodec.js +102 -0
  235. package/lib/service/snippet/codecs/XcodeCodec.js +5 -0
  236. package/lib/service/wiki/WikiGenerator.js +637 -263
  237. package/lib/shared/DimensionCopyRegistry.js +472 -0
  238. package/lib/shared/LanguageService.js +399 -0
  239. package/lib/shared/PathGuard.js +45 -28
  240. package/lib/shared/RecipeReadinessChecker.js +72 -12
  241. package/lib/shared/constants.js +41 -41
  242. package/lib/shared/errors/BaseError.js +2 -2
  243. package/lib/shared/errors/index.js +4 -4
  244. package/lib/shared/similarity.js +25 -8
  245. package/lib/shared/token-utils.js +6 -2
  246. package/lib/shared/utils/common.js +12 -4
  247. package/package.json +49 -13
  248. package/scripts/bench-real-projects.mjs +256 -0
  249. package/scripts/build-native-ui.js +30 -30
  250. package/scripts/clear-old-vector-index.js +5 -35
  251. package/scripts/clear-vector-cache.js +7 -37
  252. package/scripts/collect-test-project-stats.mjs +160 -0
  253. package/scripts/diagnose-mcp.js +41 -32
  254. package/scripts/ensure-parse-package.js +6 -9
  255. package/scripts/generate-recipe-drafts.js +116 -77
  256. package/scripts/init-db.js +3 -20
  257. package/scripts/init-snippets.js +305 -0
  258. package/scripts/init-vector-db.js +173 -170
  259. package/scripts/install-cursor-skill.js +148 -104
  260. package/scripts/install-full.js +8 -21
  261. package/scripts/install-vscode-copilot.js +146 -145
  262. package/scripts/migrate-md-to-knowledge.mjs +139 -151
  263. package/scripts/postinstall-safe.js +5 -17
  264. package/scripts/recipe-audit.js +106 -82
  265. package/scripts/release.js +283 -323
  266. package/scripts/setup-mcp-config.js +60 -52
  267. package/scripts/verify-context-api.js +20 -20
  268. package/skills/autosnippet-analysis/SKILL.md +10 -6
  269. package/skills/autosnippet-candidates/SKILL.md +27 -26
  270. package/skills/autosnippet-coldstart/SKILL.md +555 -38
  271. package/skills/autosnippet-concepts/SKILL.md +349 -337
  272. package/skills/autosnippet-create/SKILL.md +5 -5
  273. package/skills/autosnippet-reference-dart/SKILL.md +543 -0
  274. package/skills/autosnippet-reference-go/SKILL.md +539 -0
  275. package/skills/autosnippet-reference-java/SKILL.md +534 -0
  276. package/skills/autosnippet-reference-jsts/SKILL.md +41 -9
  277. package/skills/autosnippet-reference-kotlin/SKILL.md +526 -0
  278. package/skills/autosnippet-reference-objc/SKILL.md +29 -6
  279. package/skills/autosnippet-reference-python/SKILL.md +800 -0
  280. package/skills/autosnippet-reference-swift/SKILL.md +70 -14
  281. package/skills/autosnippet-structure/SKILL.md +4 -4
  282. package/templates/cursor-rules/autosnippet-conventions.mdc +2 -2
  283. package/templates/recipes-setup/README.md +2 -2
  284. package/templates/recipes-setup/_template.md +1 -1
  285. package/dashboard/dist/assets/index-Bun3ld_J.css +0 -1
  286. package/dashboard/dist/assets/index-_Sk_Dmg3.js +0 -143
  287. package/resources/asd-entry/main.swift +0 -159
  288. package/scripts/build-asd-entry.js +0 -51
  289. package/scripts/init-xcode-snippets.js +0 -311
  290. package/template.json +0 -39
@@ -0,0 +1,399 @@
1
+ /**
2
+ * @module LanguageService
3
+ * @description 统一语言服务 — 项目中唯一的语言映射与检测来源
4
+ *
5
+ * 所有文件扩展名→语言映射、扩展名→显示名、主语言推断都必须通过此服务。
6
+ * 禁止在业务代码中自建 langMap / _inferLang。
7
+ *
8
+ * ---
9
+ * 使用方式:
10
+ * import { LanguageService } from '../shared/LanguageService.js';
11
+ * const lang = LanguageService.inferLang('App.swift'); // 'swift'
12
+ * const display = LanguageService.displayName('swift'); // 'Swift'
13
+ * const primary = LanguageService.detectPrimary(langStats); // 'typescript'
14
+ */
15
+
16
+ // ═══════════════════════════════════════════════════════════
17
+ // 1) 文件扩展名 → 规范化语言 ID
18
+ // ═══════════════════════════════════════════════════════════
19
+
20
+ /** @type {Readonly<Record<string, string>>} */
21
+ const EXT_TO_LANG = Object.freeze({
22
+ // Apple
23
+ '.swift': 'swift',
24
+ '.m': 'objectivec',
25
+ '.mm': 'objectivec',
26
+ '.h': 'objectivec', // C/ObjC 头文件默认归 objectivec
27
+
28
+ // C/C++
29
+ '.c': 'c',
30
+ '.cpp': 'cpp',
31
+ '.cc': 'cpp',
32
+ '.cxx': 'cpp',
33
+ '.hpp': 'cpp',
34
+
35
+ // JavaScript/TypeScript
36
+ '.js': 'javascript',
37
+ '.mjs': 'javascript',
38
+ '.cjs': 'javascript',
39
+ '.jsx': 'javascript',
40
+ '.ts': 'typescript',
41
+ '.tsx': 'typescript',
42
+ '.vue': 'javascript',
43
+ '.svelte': 'javascript',
44
+
45
+ // Python
46
+ '.py': 'python',
47
+
48
+ // JVM
49
+ '.java': 'java',
50
+ '.kt': 'kotlin',
51
+ '.kts': 'kotlin',
52
+
53
+ // Go / Rust / Ruby
54
+ '.go': 'go',
55
+ '.rs': 'rust',
56
+ '.rb': 'ruby',
57
+
58
+ // Dart / C#
59
+ '.dart': 'dart',
60
+ '.cs': 'csharp',
61
+
62
+ // Markup / Data (常用)
63
+ '.md': 'markdown',
64
+ '.json': 'json',
65
+ '.yaml': 'yaml',
66
+ '.yml': 'yaml',
67
+ '.toml': 'toml',
68
+ '.xml': 'xml',
69
+ '.plist': 'plist',
70
+ });
71
+
72
+ // ═══════════════════════════════════════════════════════════
73
+ // 2) 裸扩展名(不带 dot)→ 规范化语言 ID
74
+ // 用于 langStats(bootstrap 按 extname('.').replace('.','') 做 key)
75
+ // ═══════════════════════════════════════════════════════════
76
+
77
+ /** @type {Readonly<Record<string, string>>} */
78
+ const BARE_EXT_TO_LANG = Object.freeze({
79
+ swift: 'swift',
80
+ m: 'objectivec',
81
+ mm: 'objectivec',
82
+ h: 'objectivec',
83
+ c: 'c',
84
+ cpp: 'cpp',
85
+ cc: 'cpp',
86
+ cxx: 'cpp',
87
+ hpp: 'cpp',
88
+ js: 'javascript',
89
+ mjs: 'javascript',
90
+ cjs: 'javascript',
91
+ jsx: 'javascript',
92
+ ts: 'typescript',
93
+ tsx: 'typescript',
94
+ vue: 'javascript',
95
+ svelte: 'javascript',
96
+ py: 'python',
97
+ java: 'java',
98
+ kt: 'kotlin',
99
+ kts: 'kotlin',
100
+ go: 'go',
101
+ rs: 'rust',
102
+ rb: 'ruby',
103
+ dart: 'dart',
104
+ cs: 'csharp',
105
+ });
106
+
107
+ // ═══════════════════════════════════════════════════════════
108
+ // 3) 语言 ID → 人类可读显示名
109
+ // ═══════════════════════════════════════════════════════════
110
+
111
+ /** @type {Readonly<Record<string, string>>} */
112
+ const LANG_DISPLAY_NAMES = Object.freeze({
113
+ swift: 'Swift',
114
+ objectivec: 'Objective-C',
115
+ c: 'C',
116
+ cpp: 'C++',
117
+ javascript: 'JavaScript',
118
+ typescript: 'TypeScript',
119
+ python: 'Python',
120
+ java: 'Java',
121
+ kotlin: 'Kotlin',
122
+ go: 'Go',
123
+ rust: 'Rust',
124
+ ruby: 'Ruby',
125
+ dart: 'Dart',
126
+ csharp: 'C#',
127
+ markdown: 'Markdown',
128
+ json: 'JSON',
129
+ yaml: 'YAML',
130
+ toml: 'TOML',
131
+ xml: 'XML',
132
+ plist: 'Property List',
133
+ unknown: 'Unknown',
134
+ });
135
+
136
+ // ═══════════════════════════════════════════════════════════
137
+ // 4) 已知可分析的编程语言集合
138
+ // ═══════════════════════════════════════════════════════════
139
+
140
+ /** @type {ReadonlySet<string>} */
141
+ const KNOWN_PROGRAMMING_LANGS = Object.freeze(
142
+ new Set([
143
+ 'swift',
144
+ 'objectivec',
145
+ 'c',
146
+ 'cpp',
147
+ 'javascript',
148
+ 'typescript',
149
+ 'python',
150
+ 'java',
151
+ 'kotlin',
152
+ 'go',
153
+ 'rust',
154
+ 'ruby',
155
+ 'dart',
156
+ 'csharp',
157
+ ])
158
+ );
159
+
160
+ // ═══════════════════════════════════════════════════════════
161
+ // 5) 源代码扩展名(Guard / 文件收集时使用)
162
+ // ═══════════════════════════════════════════════════════════
163
+
164
+ /** @type {ReadonlySet<string>} */
165
+ const SOURCE_CODE_EXTS = Object.freeze(
166
+ new Set([
167
+ '.m',
168
+ '.mm',
169
+ '.h',
170
+ '.swift',
171
+ '.c',
172
+ '.cpp',
173
+ '.cc',
174
+ '.cxx',
175
+ '.hpp',
176
+ '.js',
177
+ '.mjs',
178
+ '.cjs',
179
+ '.jsx',
180
+ '.ts',
181
+ '.tsx',
182
+ '.vue',
183
+ '.svelte',
184
+ '.py',
185
+ '.java',
186
+ '.kt',
187
+ '.kts',
188
+ '.go',
189
+ '.rs',
190
+ '.rb',
191
+ '.dart',
192
+ '.cs',
193
+ ])
194
+ );
195
+
196
+ // ═══════════════════════════════════════════════════════════
197
+ // LanguageService — 静态单例
198
+ // ═══════════════════════════════════════════════════════════
199
+
200
+ export class LanguageService {
201
+ // ─── 文件名 → 语言 ────────────────────────────
202
+
203
+ /**
204
+ * 从文件名(或路径)推断规范化语言 ID
205
+ * @param {string} filename
206
+ * @returns {string} 语言 ID,如 'swift', 'typescript', 'python', 'unknown'
207
+ */
208
+ static inferLang(filename) {
209
+ const dot = filename.lastIndexOf('.');
210
+ if (dot === -1) {
211
+ return 'unknown';
212
+ }
213
+ const ext = filename.slice(dot).toLowerCase();
214
+ return EXT_TO_LANG[ext] || 'unknown';
215
+ }
216
+
217
+ /**
218
+ * 从文件扩展名(带 dot)推断语言
219
+ * @param {string} ext - 如 '.ts', '.py'
220
+ * @returns {string}
221
+ */
222
+ static langFromExt(ext) {
223
+ return EXT_TO_LANG[ext.toLowerCase()] || 'unknown';
224
+ }
225
+
226
+ // ─── 显示名 ────────────────────────────────────
227
+
228
+ /**
229
+ * 语言 ID → 人类可读名称
230
+ * @param {string} langId
231
+ * @returns {string}
232
+ */
233
+ static displayName(langId) {
234
+ return LANG_DISPLAY_NAMES[langId] || langId;
235
+ }
236
+
237
+ /**
238
+ * 文件扩展名(带 dot)→ 人类可读语言名
239
+ * @param {string} ext - 如 '.swift', '.ts'
240
+ * @returns {string}
241
+ */
242
+ static displayNameFromExt(ext) {
243
+ const lang = EXT_TO_LANG[ext.toLowerCase()];
244
+ return lang ? LANG_DISPLAY_NAMES[lang] || lang : ext;
245
+ }
246
+
247
+ // ─── 主语言检测 ────────────────────────────────
248
+
249
+ /**
250
+ * 从文件扩展名统计推断主语言
251
+ * @param {Record<string, number>} langStats - key = 裸扩展名 (如 'ts', 'm', 'py'),value = 文件数
252
+ * @returns {string} 主语言 ID
253
+ */
254
+ static detectPrimary(langStats) {
255
+ if (!langStats || typeof langStats !== 'object') {
256
+ return 'unknown';
257
+ }
258
+ // 按规范化语言聚合计数(避免 ObjC 的 .h/.m/.mm 分散)
259
+ const aggregated = {};
260
+ for (const [ext, count] of Object.entries(langStats)) {
261
+ const lang = BARE_EXT_TO_LANG[ext] || ext;
262
+ aggregated[lang] = (aggregated[lang] || 0) + count;
263
+ }
264
+ let best = 'unknown',
265
+ bestCount = 0;
266
+ for (const [lang, count] of Object.entries(aggregated)) {
267
+ if (count > bestCount && KNOWN_PROGRAMMING_LANGS.has(lang)) {
268
+ best = lang;
269
+ bestCount = count;
270
+ }
271
+ }
272
+ return best;
273
+ }
274
+
275
+ /**
276
+ * 从文件扩展名统计返回所有检测到的编程语言(按文件数降序)
277
+ * @param {Record<string, number>} langStats
278
+ * @returns {Array<{ lang: string, count: number }>}
279
+ */
280
+ static detectAll(langStats) {
281
+ if (!langStats || typeof langStats !== 'object') {
282
+ return [];
283
+ }
284
+ const aggregated = {};
285
+ for (const [ext, count] of Object.entries(langStats)) {
286
+ const lang = BARE_EXT_TO_LANG[ext] || ext;
287
+ aggregated[lang] = (aggregated[lang] || 0) + count;
288
+ }
289
+ return Object.entries(aggregated)
290
+ .filter(([lang]) => KNOWN_PROGRAMMING_LANGS.has(lang))
291
+ .sort((a, b) => b[1] - a[1])
292
+ .map(([lang, count]) => ({ lang, count }));
293
+ }
294
+
295
+ /**
296
+ * 多语言项目画像 — 返回主语言 + 次要语言 + 完整排序列表
297
+ *
298
+ * 与 detectPrimary 的区别:
299
+ * - detectPrimary 只给出一个语言,适用于需要单值场景
300
+ * - detectProfile 给出完整画像,适用于维度文案、AI prompt 等需要
301
+ * 感知多语言的场景
302
+ *
303
+ * @param {Record<string, number>} langStats - key=裸扩展名, value=文件数
304
+ * @param {object} [opts]
305
+ * @param {number} [opts.secondaryThreshold=0.1] 次要语言文件占比阈值(≥此比例才算次要语言)
306
+ * @returns {{ primary: string, secondary: string[], all: Array<{lang:string, count:number, ratio:number}>, totalFiles: number, isMultiLang: boolean }}
307
+ */
308
+ static detectProfile(langStats, opts = {}) {
309
+ const threshold = opts.secondaryThreshold ?? 0.1;
310
+ const all = LanguageService.detectAll(langStats);
311
+ if (all.length === 0) {
312
+ return { primary: 'unknown', secondary: [], all: [], totalFiles: 0, isMultiLang: false };
313
+ }
314
+
315
+ const totalFiles = all.reduce((s, e) => s + e.count, 0);
316
+ const enriched = all.map((e) => ({ ...e, ratio: e.count / totalFiles }));
317
+ const primary = enriched[0].lang;
318
+ const secondary = enriched
319
+ .slice(1)
320
+ .filter((e) => e.ratio >= threshold)
321
+ .map((e) => e.lang);
322
+
323
+ return {
324
+ primary,
325
+ secondary,
326
+ all: enriched,
327
+ totalFiles,
328
+ isMultiLang: secondary.length > 0,
329
+ };
330
+ }
331
+
332
+ // ─── 查询方法 ─────────────────────────────────
333
+
334
+ /**
335
+ * 该语言 ID 是否是已知编程语言
336
+ * @param {string} langId
337
+ * @returns {boolean}
338
+ */
339
+ static isKnownLang(langId) {
340
+ return KNOWN_PROGRAMMING_LANGS.has(langId);
341
+ }
342
+
343
+ /**
344
+ * 该扩展名是否为源代码文件
345
+ * @param {string} ext - 带 dot,如 '.ts'
346
+ * @returns {boolean}
347
+ */
348
+ static isSourceExt(ext) {
349
+ return SOURCE_CODE_EXTS.has(ext.toLowerCase());
350
+ }
351
+
352
+ /**
353
+ * 获取所有源代码扩展名(不可变)
354
+ * @returns {ReadonlySet<string>}
355
+ */
356
+ static get sourceExts() {
357
+ return SOURCE_CODE_EXTS;
358
+ }
359
+
360
+ /**
361
+ * 获取所有已知编程语言 ID(不可变)
362
+ * @returns {ReadonlySet<string>}
363
+ */
364
+ static get knownLangs() {
365
+ return KNOWN_PROGRAMMING_LANGS;
366
+ }
367
+
368
+ /**
369
+ * 获取完整的 ext→lang 映射(不可变)
370
+ * @returns {Readonly<Record<string, string>>}
371
+ */
372
+ static get extToLangMap() {
373
+ return EXT_TO_LANG;
374
+ }
375
+
376
+ /**
377
+ * 获取完整的 bareExt→lang 映射(不可变)
378
+ * @returns {Readonly<Record<string, string>>}
379
+ */
380
+ static get bareExtToLangMap() {
381
+ return BARE_EXT_TO_LANG;
382
+ }
383
+
384
+ /**
385
+ * 根据语言 ID 返回主扩展名(带 dot)
386
+ * @param {string} langId - 如 'go', 'swift', 'python'
387
+ * @returns {string|null} - 如 '.go', '.swift', '.py';未知返回 null
388
+ */
389
+ static extForLang(langId) {
390
+ if (!langId) return null;
391
+ const lower = langId.toLowerCase();
392
+ for (const [ext, lang] of Object.entries(EXT_TO_LANG)) {
393
+ if (lang === lower) return ext;
394
+ }
395
+ return null;
396
+ }
397
+ }
398
+
399
+ export default LanguageService;
@@ -26,8 +26,8 @@
26
26
  * - 错误不静默:越界写操作抛出 PathGuardError
27
27
  */
28
28
 
29
- import path from 'node:path';
30
29
  import fs from 'node:fs';
30
+ import path from 'node:path';
31
31
 
32
32
  export class PathGuardError extends Error {
33
33
  /**
@@ -41,8 +41,8 @@ export class PathGuardError extends Error {
41
41
  : `[PathGuard] 写入路径越界: "${targetPath}" 不在允许范围内。`;
42
42
  super(
43
43
  msg +
44
- `\n projectRoot: ${projectRoot}` +
45
- `\n 提示: 检查 process.cwd() 或 projectRoot 配置是否正确`
44
+ `\n projectRoot: ${projectRoot}` +
45
+ `\n 提示: 检查 process.cwd() 或 projectRoot 配置是否正确`
46
46
  );
47
47
  this.name = 'PathGuardError';
48
48
  this.targetPath = targetPath;
@@ -55,19 +55,16 @@ export class PathGuardError extends Error {
55
55
  * 注意:这是相对于 projectRoot 的前缀列表
56
56
  */
57
57
  const PROJECT_WRITE_SCOPE_PREFIXES = [
58
- '.autosnippet', // 运行时 DB、记忆、对话、信号快照
59
- '.cursor', // Cursor IDE 集成
60
- '.vscode', // VSCode 集成
61
- '.github', // Copilot instructions
58
+ '.autosnippet', // 运行时 DB、记忆、对话、信号快照
59
+ '.cursor', // Cursor IDE 集成
60
+ '.vscode', // VSCode 集成
61
+ '.github', // Copilot instructions
62
62
  ];
63
63
 
64
64
  /**
65
65
  * 项目根目录下允许直接写入的文件(非目录前缀匹配)
66
66
  */
67
- const PROJECT_ROOT_WRITABLE_FILES = [
68
- '.gitignore',
69
- '.env',
70
- ];
67
+ const PROJECT_ROOT_WRITABLE_FILES = ['.gitignore', '.env'];
71
68
 
72
69
  class PathGuard {
73
70
  /** @type {string|null} 项目根目录(绝对路径) */
@@ -85,8 +82,6 @@ class PathGuard {
85
82
  /** @type {boolean} 是否已配置 */
86
83
  #configured = false;
87
84
 
88
- constructor() {}
89
-
90
85
  /**
91
86
  * 配置 PathGuard(每个进程执行一次)
92
87
  * @param {object} opts
@@ -102,7 +97,7 @@ class PathGuard {
102
97
 
103
98
  this.#projectRoot = path.resolve(projectRoot);
104
99
  this.#packageRoot = packageRoot ? path.resolve(packageRoot) : null;
105
- this.#knowledgeBaseDir = knowledgeBaseDir || null; // 延迟解析
100
+ this.#knowledgeBaseDir = knowledgeBaseDir || null; // 延迟解析
106
101
 
107
102
  // 默认白名单:Xcode snippets 目录、全局缓存(cache 子目录,不含整个 ~/.autosnippet)
108
103
  const HOME = process.env.HOME || process.env.USERPROFILE || '';
@@ -148,7 +143,9 @@ class PathGuard {
148
143
  * @throws {PathGuardError}
149
144
  */
150
145
  assertSafe(targetPath) {
151
- if (!this.#configured) return;
146
+ if (!this.#configured) {
147
+ return;
148
+ }
152
149
 
153
150
  if (!targetPath || typeof targetPath !== 'string') {
154
151
  throw new PathGuardError(String(targetPath), this.#projectRoot);
@@ -157,14 +154,20 @@ class PathGuard {
157
154
  const resolved = path.resolve(targetPath);
158
155
 
159
156
  // 1. 项目目录内 — 允许
160
- if (this.#isUnder(resolved, this.#projectRoot)) return;
157
+ if (this.#isUnder(resolved, this.#projectRoot)) {
158
+ return;
159
+ }
161
160
 
162
161
  // 2. AutoSnippet 包自身目录内(logs/ 等)— 允许
163
- if (this.#packageRoot && this.#isUnder(resolved, this.#packageRoot)) return;
162
+ if (this.#packageRoot && this.#isUnder(resolved, this.#packageRoot)) {
163
+ return;
164
+ }
164
165
 
165
166
  // 3. 白名单目录 — 允许
166
167
  for (const allowed of this.#allowList) {
167
- if (this.#isUnder(resolved, allowed)) return;
168
+ if (this.#isUnder(resolved, allowed)) {
169
+ return;
170
+ }
168
171
  }
169
172
 
170
173
  // 越界
@@ -179,7 +182,9 @@ class PathGuard {
179
182
  * @throws {PathGuardError}
180
183
  */
181
184
  assertProjectWriteSafe(targetPath) {
182
- if (!this.#configured) return;
185
+ if (!this.#configured) {
186
+ return;
187
+ }
183
188
 
184
189
  // 先做边界检查
185
190
  this.assertSafe(targetPath);
@@ -187,7 +192,9 @@ class PathGuard {
187
192
  const resolved = path.resolve(targetPath);
188
193
 
189
194
  // 如果不在 projectRoot 内(在白名单/packageRoot 中),跳过项目内检查
190
- if (!this.#isUnder(resolved, this.#projectRoot)) return;
195
+ if (!this.#isUnder(resolved, this.#projectRoot)) {
196
+ return;
197
+ }
191
198
 
192
199
  // 计算相对于 projectRoot 的路径
193
200
  const relative = path.relative(this.#projectRoot, resolved);
@@ -195,21 +202,27 @@ class PathGuard {
195
202
 
196
203
  // 检查是否在允许的前缀中
197
204
  for (const prefix of PROJECT_WRITE_SCOPE_PREFIXES) {
198
- if (firstSegment === prefix) return;
205
+ if (firstSegment === prefix) {
206
+ return;
207
+ }
199
208
  }
200
209
 
201
210
  // 检查知识库目录(动态解析)
202
211
  const kbDir = this.#resolveKnowledgeBaseDir();
203
- if (kbDir && firstSegment === kbDir) return;
212
+ if (kbDir && firstSegment === kbDir) {
213
+ return;
214
+ }
204
215
 
205
216
  // 检查根目录可写文件(如 .gitignore)
206
- if (PROJECT_ROOT_WRITABLE_FILES.includes(relative)) return;
217
+ if (PROJECT_ROOT_WRITABLE_FILES.includes(relative)) {
218
+ return;
219
+ }
207
220
 
208
221
  // 不在允许的写入范围内
209
222
  throw new PathGuardError(
210
223
  resolved,
211
224
  this.#projectRoot,
212
- `项目内写入范围受限: "${relative}" 不在允许的目录中(允许: ${[...PROJECT_WRITE_SCOPE_PREFIXES, kbDir || 'AutoSnippet'].join(', ')})`,
225
+ `项目内写入范围受限: "${relative}" 不在允许的目录中(允许: ${[...PROJECT_WRITE_SCOPE_PREFIXES, kbDir || 'AutoSnippet'].join(', ')})`
213
226
  );
214
227
  }
215
228
 
@@ -280,7 +293,9 @@ class PathGuard {
280
293
  * 优先使用 configure 阶段传入的值,否则尝试运行时探测
281
294
  */
282
295
  #resolveKnowledgeBaseDir() {
283
- if (this.#knowledgeBaseDir) return this.#knowledgeBaseDir;
296
+ if (this.#knowledgeBaseDir) {
297
+ return this.#knowledgeBaseDir;
298
+ }
284
299
 
285
300
  // 运行时探测: 查找包含 AutoSnippet.boxspec.json 的子目录
286
301
  try {
@@ -293,7 +308,9 @@ class PathGuard {
293
308
  }
294
309
  }
295
310
  }
296
- } catch { /* ignore */ }
311
+ } catch {
312
+ /* ignore */
313
+ }
297
314
 
298
315
  // 默认
299
316
  return 'AutoSnippet';
@@ -303,8 +320,8 @@ class PathGuard {
303
320
  /**
304
321
  * 延迟加载 fs(避免循环依赖)
305
322
  */
306
- function await_fs() {
307
- // eslint-disable-next-line no-eval
323
+ function _await_fs() {
324
+ // biome-ignore lint/security/noGlobalEval: intentional CJS require fallback for avoiding circular deps
308
325
  return eval("require('fs')");
309
326
  }
310
327
 
@@ -7,7 +7,18 @@
7
7
  * @returns {{ ready: boolean, missing: string[], suggestions: string[] }}
8
8
  */
9
9
 
10
- const STANDARD_CATEGORIES = ['View', 'Service', 'Tool', 'Model', 'Network', 'Storage', 'UI', 'Utility'];
10
+ import { LanguageService } from './LanguageService.js';
11
+
12
+ const STANDARD_CATEGORIES = [
13
+ 'View',
14
+ 'Service',
15
+ 'Tool',
16
+ 'Model',
17
+ 'Network',
18
+ 'Storage',
19
+ 'UI',
20
+ 'Utility',
21
+ ];
11
22
 
12
23
  /**
13
24
  * Bootstrap 等特殊来源使用的 category 白名单 —— 这些 category
@@ -28,12 +39,49 @@ export function checkRecipeReadiness(item) {
28
39
  const missing = [];
29
40
  const suggestions = [];
30
41
 
31
- // ── 必填 ──
42
+ // ── 核心必填 ──
43
+ if (!item.title || !String(item.title).trim()) {
44
+ missing.push('title');
45
+ suggestions.push('title 必须非空(中文简短标题 ≤20 字)');
46
+ }
47
+
48
+ // content 有效性(pattern/markdown/rationale 至少有实质内容)
49
+ const hasCode = !!(item.code || item.content?.pattern || item.content?.markdown);
50
+ if (!hasCode) {
51
+ missing.push('content');
52
+ suggestions.push('content 需包含 pattern 或 markdown(代码片段或正文)');
53
+ }
54
+
55
+ if (!item.content?.rationale && !item.rationale) {
56
+ missing.push('rationale');
57
+ suggestions.push('content.rationale 必须提供设计原理说明');
58
+ }
59
+
60
+ if (!item.language) {
61
+ missing.push('language');
62
+ suggestions.push('language 必须指定(如 swift/typescript/python/java/go 等)');
63
+ }
64
+
65
+ if (!item.kind) {
66
+ missing.push('kind');
67
+ suggestions.push('kind 必须为 rule/pattern/fact');
68
+ }
69
+
70
+ if (!item.doClause) {
71
+ missing.push('doClause');
72
+ suggestions.push('doClause 需为英文祈使句正向指令(≤60 tokens)');
73
+ }
74
+
32
75
  if (!item.category) {
33
76
  missing.push('category');
34
- suggestions.push('category 必须为: ' + STANDARD_CATEGORIES.join('/'));
35
- } else if (!STANDARD_CATEGORIES.includes(item.category) && !WHITELISTED_CATEGORIES.includes(item.category)) {
36
- suggestions.push(`category "${item.category}" 非标准值,应为: ${STANDARD_CATEGORIES.join('/')}(bootstrap/knowledge 等特殊来源可忽略此建议)`);
77
+ suggestions.push(`category 必须为: ${STANDARD_CATEGORIES.join('/')}`);
78
+ } else if (
79
+ !STANDARD_CATEGORIES.includes(item.category) &&
80
+ !WHITELISTED_CATEGORIES.includes(item.category)
81
+ ) {
82
+ suggestions.push(
83
+ `category "${item.category}" 非标准值,应为: ${STANDARD_CATEGORIES.join('/')}(bootstrap/knowledge 等特殊来源可忽略此建议)`
84
+ );
37
85
  }
38
86
 
39
87
  if (!item.trigger) {
@@ -49,12 +97,11 @@ export function checkRecipeReadiness(item) {
49
97
  suggestions.push('请提供描述(≤100字)');
50
98
  }
51
99
 
52
- if (!Array.isArray(item.headers) || item.headers.length === 0) {
100
+ if (!Array.isArray(item.headers)) {
53
101
  missing.push('headers');
54
- suggestions.push('请提供完整 import 语句数组,如 ["#import <Module/Header.h>"]');
102
+ suggestions.push('请提供完整 import 语句数组,无 import 时传 []');
55
103
  }
56
104
 
57
- // ── 建议 ──
58
105
  if (!item.usageGuide) {
59
106
  missing.push('usageGuide');
60
107
  suggestions.push('请提供使用指南(Markdown ### 章节格式)');
@@ -62,15 +109,28 @@ export function checkRecipeReadiness(item) {
62
109
 
63
110
  if (!item.knowledgeType) {
64
111
  missing.push('knowledgeType');
112
+ suggestions.push('knowledgeType 必须指定(如 code-pattern/architecture/best-practice)');
65
113
  }
66
114
 
67
- if (!item.rationale) {
68
- missing.push('rationale');
115
+ // reasoning 检查
116
+ if (!item.reasoning || typeof item.reasoning !== 'object') {
117
+ missing.push('reasoning');
118
+ suggestions.push('reasoning 必须包含 whyStandard + sources + confidence');
119
+ } else {
120
+ if (!item.reasoning.whyStandard?.trim()) {
121
+ missing.push('reasoning.whyStandard');
122
+ }
123
+ if (!Array.isArray(item.reasoning.sources) || item.reasoning.sources.length === 0) {
124
+ missing.push('reasoning.sources');
125
+ }
69
126
  }
70
127
 
71
128
  const lang = item.language?.toLowerCase();
72
- if (lang && lang !== 'swift' && lang !== 'objectivec' && lang !== 'objc' && lang !== 'markdown') {
73
- suggestions.push(`language "${item.language}" Recipe 一般为 swift/objectivec/markdown`);
129
+ // 使用 LanguageService 统一语言集,额外接受 'objc' 别名和 'markdown'
130
+ if (lang && !LanguageService.isKnownLang(lang) && lang !== 'objc' && lang !== 'markdown') {
131
+ suggestions.push(
132
+ `language "${item.language}" — 请使用标准语言标识 (swift/typescript/python/java/kotlin 等)`
133
+ );
74
134
  }
75
135
 
76
136
  return { ready: missing.length === 0, missing, suggestions };