autosnippet 3.0.0 → 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 +759 -243
  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
@@ -3,26 +3,45 @@
3
3
  *
4
4
  * 三层验证:
5
5
  * 1. 结构验证 — 必填字段、内容长度、交付字段非空
6
- * 2. 去重验证 — 标题不重复
6
+ * 2. 去重验证 — 标题不重复 + 代码模式不重复
7
7
  * 3. 质量启发式 — 包含代码引用、项目特定内容
8
8
  *
9
9
  * @module CandidateGuardrail
10
10
  */
11
11
 
12
+ /**
13
+ * 生成代码模式指纹 — 去除空白/注释后取前 200 字符的小写形式
14
+ * @param {string} code
15
+ * @returns {string}
16
+ */
17
+ function codeFingerprint(code) {
18
+ return (code || '')
19
+ .replace(/\/\/[^\n]*/g, '') // 移除单行注释
20
+ .replace(/\/\*[\s\S]*?\*\//g, '') // 移除多行注释
21
+ .replace(/[\s]+/g, '') // 移除所有空白
22
+ .toLowerCase()
23
+ .slice(0, 200);
24
+ }
25
+
12
26
  export class CandidateGuardrail {
13
27
  /** @type {Set<string>} 已提交标题 (小写) */
14
28
  #globalTitles;
15
29
 
30
+ /** @type {Set<string>} 已提交代码模式指纹 */
31
+ #globalPatternFingerprints;
32
+
16
33
  /** @type {object} 维度配置 */
17
34
  #dimensionConfig;
18
35
 
19
36
  /**
20
37
  * @param {Set<string>} globalTitles — 全局已提交标题集合 (小写)
21
38
  * @param {object} dimensionConfig — { allowedKnowledgeTypes, id, outputType }
39
+ * @param {Set<string>} [globalPatternFingerprints] — 全局已提交代码指纹集合
22
40
  */
23
- constructor(globalTitles, dimensionConfig) {
41
+ constructor(globalTitles, dimensionConfig, globalPatternFingerprints) {
24
42
  this.#globalTitles = globalTitles;
25
43
  this.#dimensionConfig = dimensionConfig;
44
+ this.#globalPatternFingerprints = globalPatternFingerprints || new Set();
26
45
  }
27
46
 
28
47
  /**
@@ -40,9 +59,28 @@ export class CandidateGuardrail {
40
59
  return { valid: false, error: '缺少必填字段: content.markdown' };
41
60
  }
42
61
 
62
+ // ── 自动修复: pattern 为空时从 markdown 提取代码块 ──
63
+ if (candidate.kind === 'pattern' && candidate.content) {
64
+ const pattern = (candidate.content.pattern || '').trim();
65
+ if (!pattern) {
66
+ const extracted = CandidateGuardrail.#extractCodeFromMarkdown(markdown);
67
+ if (extracted) {
68
+ candidate.content.pattern = extracted;
69
+ }
70
+ }
71
+ }
72
+
43
73
  // 内容长度检查 — 「项目特写」需要足够的代码和描述
44
74
  if (markdown.length < 200) {
45
- return { valid: false, error: `内容过短 (${markdown.length} 字符, 最少 200)。请包含代码片段和项目上下文描述,而非一句话概括。` };
75
+ return {
76
+ valid: false,
77
+ error: `内容过短 (${markdown.length} 字符, 最少 200)。请包含代码片段和项目上下文描述,而非一句话概括。`,
78
+ };
79
+ }
80
+
81
+ // content.rationale 必填
82
+ if (!candidate.content?.rationale || String(candidate.content.rationale).trim().length === 0) {
83
+ return { valid: false, error: '缺少必填字段: content.rationale — 需要设计原理说明' };
46
84
  }
47
85
 
48
86
  // Cursor 交付字段必填检查
@@ -56,6 +94,16 @@ export class CandidateGuardrail {
56
94
  return { valid: false, error: '缺少必填字段: doClause — 需要英文祈使句正向指令' };
57
95
  }
58
96
 
97
+ // description 必填
98
+ if (!candidate.description || String(candidate.description).trim().length === 0) {
99
+ return { valid: false, error: '缺少必填字段: description — 需要中文简述 ≤80 字' };
100
+ }
101
+
102
+ // headers 必填(数组,空项目也需传 [])
103
+ if (!Array.isArray(candidate.headers)) {
104
+ return { valid: false, error: '缺少必填字段: headers — 需为 import 语句数组,无 import 时传 []' };
105
+ }
106
+
59
107
  // knowledgeType 约束 — 自动修正为允许列表中第一个类型
60
108
  const allowed = this.#dimensionConfig.allowedKnowledgeTypes;
61
109
  if (allowed?.length > 0 && candidate.knowledgeType) {
@@ -64,6 +112,17 @@ export class CandidateGuardrail {
64
112
  }
65
113
  }
66
114
 
115
+ // reasoning 必填
116
+ if (!candidate.reasoning || typeof candidate.reasoning !== 'object') {
117
+ return { valid: false, error: '缺少必填字段: reasoning — 需包含 whyStandard + sources + confidence' };
118
+ }
119
+ if (!candidate.reasoning.whyStandard?.trim()) {
120
+ return { valid: false, error: '缺少必填字段: reasoning.whyStandard' };
121
+ }
122
+ if (!Array.isArray(candidate.reasoning.sources) || candidate.reasoning.sources.length === 0) {
123
+ return { valid: false, error: '缺少必填字段: reasoning.sources — 至少包含一项来源' };
124
+ }
125
+
67
126
  return { valid: true };
68
127
  }
69
128
 
@@ -77,6 +136,19 @@ export class CandidateGuardrail {
77
136
  if (this.#globalTitles.has(normalizedTitle)) {
78
137
  return { valid: false, error: `标题重复: "${candidate.title}"` };
79
138
  }
139
+
140
+ // 代码模式指纹去重 — 相同核心代码不同维度只保留首次
141
+ const pattern = (candidate.content?.pattern || '').trim();
142
+ if (pattern.length >= 30) {
143
+ const fp = codeFingerprint(pattern);
144
+ if (fp.length >= 20 && this.#globalPatternFingerprints.has(fp)) {
145
+ return {
146
+ valid: false,
147
+ error: `代码模式重复 — 已存在相同核心代码的候选。请提交不同的代码片段,或换一个角度分析。`,
148
+ };
149
+ }
150
+ }
151
+
80
152
  return { valid: true };
81
153
  }
82
154
 
@@ -88,26 +160,46 @@ export class CandidateGuardrail {
88
160
  validateQuality(candidate) {
89
161
  const content = candidate.content?.markdown || '';
90
162
 
163
+ // 检查 content.pattern 是否语法完整(不以闭合括号开头)
164
+ const pattern = (candidate.content?.pattern || '').trim();
165
+ if (pattern) {
166
+ const firstChar = pattern[0];
167
+ if (firstChar === '}' || firstChar === ')' || firstChar === ']') {
168
+ return {
169
+ valid: false,
170
+ error: `content.pattern 以 "${firstChar}" 开头,代码片段不完整 — 请包含完整的函数/方法/表达式,确保括号配对`,
171
+ };
172
+ }
173
+ }
174
+
91
175
  // 检查是否包含代码引用或文件路径
92
- const hasCodeBlock = /```[\s\S]*?```/.test(content) || /\.(m|h|swift|js|ts)(:\d+)?/.test(content);
93
- const hasSourceRef = /来源[::]|[Ss]ource[::]|\(\w+\.\w+:\d+\)/.test(content) || /[A-Z]\w+\.(m|h|swift|java|kt|js|ts)/.test(content);
176
+ const hasCodeBlock =
177
+ /```[\s\S]*?```/.test(content) || /\.\w{1,10}(:\d+)?/.test(content);
178
+ const hasSourceRef =
179
+ /来源[::]|[Ss]ource[::]|\(\w+\.\w+:\d+\)/.test(content) ||
180
+ /[A-Z]\w+\.(?:m|h|swift|java|kt|js|ts|go|py|rs|rb|cs|cpp|c)/.test(content);
94
181
 
95
182
  if (!hasCodeBlock && !hasSourceRef) {
96
- return { valid: false, error: '内容缺少代码片段或文件引用 — 请用 read_project_file 获取代码后再提交,「项目特写」必须包含真实代码' };
183
+ return {
184
+ valid: false,
185
+ error:
186
+ '内容缺少代码片段或文件引用 — 请用 read_project_file 获取代码后再提交,「项目特写」必须包含真实代码',
187
+ };
97
188
  }
98
189
 
99
190
  // 检查是否是 Skill 摘要式内容(一行式描述、无代码、无结构)
100
- const lines = content.split('\n').filter(l => l.trim().length > 0);
191
+ const lines = content.split('\n').filter((l) => l.trim().length > 0);
101
192
  if (lines.length <= 2 && !hasCodeBlock) {
102
- return { valid: false, error: `内容过于简单 (仅 ${lines.length} 行) — 请包含代码片段、设计意图和项目上下文,不要只写一句话概括` };
193
+ return {
194
+ valid: false,
195
+ error: `内容过于简单 (仅 ${lines.length} 行) — 请包含代码片段、设计意图和项目上下文,不要只写一句话概括`,
196
+ };
103
197
  }
104
198
 
105
199
  // 检查是否是通用知识而非项目特定
106
- const genericPatterns = [
107
- /^(Singleton|Factory|Observer|MVC|MVVM) (pattern|模式)$/i,
108
- ];
200
+ const genericPatterns = [/^(Singleton|Factory|Observer|MVC|MVVM) (pattern|模式)$/i];
109
201
  const title = candidate.title || '';
110
- if (genericPatterns.some(p => p.test(title.trim()))) {
202
+ if (genericPatterns.some((p) => p.test(title.trim()))) {
111
203
  return { valid: false, error: `标题过于通用: "${title}" — 请加上项目特定的上下文` };
112
204
  }
113
205
 
@@ -121,24 +213,58 @@ export class CandidateGuardrail {
121
213
  */
122
214
  validate(candidate) {
123
215
  const structureResult = this.validateStructure(candidate);
124
- if (!structureResult.valid) return structureResult;
216
+ if (!structureResult.valid) {
217
+ return structureResult;
218
+ }
125
219
 
126
220
  const uniqueResult = this.validateUniqueness(candidate);
127
- if (!uniqueResult.valid) return uniqueResult;
221
+ if (!uniqueResult.valid) {
222
+ return uniqueResult;
223
+ }
128
224
 
129
225
  const qualityResult = this.validateQuality(candidate);
130
226
  // 质量问题返回 warning 但不阻止提交
131
- if (!qualityResult.valid) return qualityResult;
227
+ if (!qualityResult.valid) {
228
+ return qualityResult;
229
+ }
132
230
 
133
231
  return { valid: true, warning: qualityResult.warning };
134
232
  }
135
233
 
136
234
  /**
137
- * 记录已提交标题(提交成功后调用)
235
+ * 记录已提交标题和代码指纹(提交成功后调用)
138
236
  * @param {string} title
237
+ * @param {string} [pattern] — 代码模式
139
238
  */
140
- recordTitle(title) {
239
+ recordTitle(title, pattern) {
141
240
  this.#globalTitles.add((title || '').toLowerCase().trim());
241
+ if (pattern && pattern.length >= 30) {
242
+ const fp = codeFingerprint(pattern);
243
+ if (fp.length >= 20) {
244
+ this.#globalPatternFingerprints.add(fp);
245
+ }
246
+ }
247
+ }
248
+
249
+ /**
250
+ * 从 markdown 中提取最长的 fenced code block 作为 pattern
251
+ * @param {string} markdown
252
+ * @returns {string|null} 提取的代码,或 null
253
+ */
254
+ static #extractCodeFromMarkdown(markdown) {
255
+ const codeBlockRe = /```(?:\w+)?\n([\s\S]*?)```/g;
256
+ let best = null;
257
+ let bestLen = 0;
258
+ let match;
259
+ while ((match = codeBlockRe.exec(markdown)) !== null) {
260
+ const code = match[1].trim();
261
+ if (code.length > bestLen) {
262
+ best = code;
263
+ bestLen = code.length;
264
+ }
265
+ }
266
+ // 只接受有实质内容的代码块 (>=30字符)
267
+ return best && bestLen >= 30 ? best : null;
142
268
  }
143
269
  }
144
270