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
@@ -30,16 +30,20 @@ export class GuardFeedbackLoop {
30
30
  * @returns {Array<{ ruleId: string, filePath: string, fixRecipeId: string }>} 已修复且有 Recipe 关联的列表
31
31
  */
32
32
  detectFixedViolations(currentResult, filePath) {
33
- if (!this.violationsStore) return [];
33
+ if (!this.violationsStore) {
34
+ return [];
35
+ }
34
36
 
35
37
  try {
36
38
  const previousRuns = this.violationsStore.getRunsByFile(filePath);
37
- if (previousRuns.length === 0) return [];
39
+ if (previousRuns.length === 0) {
40
+ return [];
41
+ }
38
42
 
39
43
  // 取最近一次运行结果
40
44
  const lastRun = previousRuns[previousRuns.length - 1];
41
- const lastRuleIds = new Set((lastRun.violations || []).map(v => v.ruleId));
42
- const currentRuleIds = new Set((currentResult.violations || []).map(v => v.ruleId));
45
+ const lastRuleIds = new Set((lastRun.violations || []).map((v) => v.ruleId));
46
+ const currentRuleIds = new Set((currentResult.violations || []).map((v) => v.ruleId));
43
47
 
44
48
  const fixed = [];
45
49
  for (const ruleId of lastRuleIds) {
@@ -64,7 +68,9 @@ export class GuardFeedbackLoop {
64
68
  * @param {Array<{ ruleId: string, filePath: string, fixRecipeId: string }>} fixedList
65
69
  */
66
70
  autoConfirmUsage(fixedList) {
67
- if (!this.feedbackCollector || !fixedList?.length) return;
71
+ if (!this.feedbackCollector || !fixedList?.length) {
72
+ return;
73
+ }
68
74
 
69
75
  for (const { ruleId, fixRecipeId, filePath } of fixedList) {
70
76
  try {
@@ -74,7 +80,9 @@ export class GuardFeedbackLoop {
74
80
  ruleId,
75
81
  filePath,
76
82
  });
77
- this.logger.info(`[GuardFeedbackLoop] Auto-confirmed usage: recipe=${fixRecipeId} from fixing rule=${ruleId}`);
83
+ this.logger.info(
84
+ `[GuardFeedbackLoop] Auto-confirmed usage: recipe=${fixRecipeId} from fixing rule=${ruleId}`
85
+ );
78
86
  } catch (err) {
79
87
  this.logger.debug(`[GuardFeedbackLoop] autoConfirmUsage error: ${err.message}`);
80
88
  }
@@ -91,7 +99,9 @@ export class GuardFeedbackLoop {
91
99
  const fixed = this.detectFixedViolations(currentResult, filePath);
92
100
  if (fixed.length > 0) {
93
101
  this.autoConfirmUsage(fixed);
94
- this.logger.info(`[GuardFeedbackLoop] Detected ${fixed.length} fixed violations in ${filePath}`);
102
+ this.logger.info(
103
+ `[GuardFeedbackLoop] Detected ${fixed.length} fixed violations in ${filePath}`
104
+ );
95
105
  }
96
106
  return fixed;
97
107
  }
@@ -101,7 +111,7 @@ export class GuardFeedbackLoop {
101
111
  */
102
112
  _findFixRecipe(ruleId, violations) {
103
113
  // 先从 violation 本身的 fixSuggestion 查找
104
- for (const v of (violations || [])) {
114
+ for (const v of violations || []) {
105
115
  if (v.ruleId === ruleId && v.fixSuggestion) {
106
116
  return v.fixSuggestion.replace(/^recipe:/, '');
107
117
  }
@@ -111,11 +121,13 @@ export class GuardFeedbackLoop {
111
121
  if (this.guardCheckEngine) {
112
122
  try {
113
123
  const rules = this.guardCheckEngine.getRules();
114
- const rule = rules.find(r => r.id === ruleId);
124
+ const rule = rules.find((r) => r.id === ruleId);
115
125
  if (rule?.fixSuggestion) {
116
126
  return rule.fixSuggestion.replace(/^recipe:/, '');
117
127
  }
118
- } catch { /* ignore */ }
128
+ } catch {
129
+ /* ignore */
130
+ }
119
131
  }
120
132
 
121
133
  return null;
@@ -1,7 +1,7 @@
1
+ import { v4 as uuidv4 } from 'uuid';
1
2
  import Logger from '../../infrastructure/logging/Logger.js';
2
- import { ValidationError, ConflictError, NotFoundError } from '../../shared/errors/index.js';
3
+ import { ConflictError, NotFoundError, ValidationError } from '../../shared/errors/index.js';
3
4
  import { unixNow } from '../../shared/utils/common.js';
4
- import { v4 as uuidv4 } from 'uuid';
5
5
 
6
6
  /**
7
7
  * GuardService
@@ -44,14 +44,16 @@ export class GuardService {
44
44
  boundaries: [],
45
45
  preconditions: [],
46
46
  sideEffects: [],
47
- guards: [{
48
- ...(data.pattern ? { pattern: data.pattern } : {}),
49
- severity: data.severity || 'warning',
50
- message: data.description || '',
51
- type: data.type || 'regex',
52
- ...(data.astQuery ? { astQuery: data.astQuery } : {}),
53
- ...(data.fixSuggestion ? { fixSuggestion: data.fixSuggestion } : {}),
54
- }],
47
+ guards: [
48
+ {
49
+ ...(data.pattern ? { pattern: data.pattern } : {}),
50
+ severity: data.severity || 'warning',
51
+ message: data.description || '',
52
+ type: data.type || 'regex',
53
+ ...(data.astQuery ? { astQuery: data.astQuery } : {}),
54
+ ...(data.fixSuggestion ? { fixSuggestion: data.fixSuggestion } : {}),
55
+ },
56
+ ],
55
57
  },
56
58
  tags: data.languages || [],
57
59
  lifecycle: 'active',
@@ -82,7 +84,9 @@ export class GuardService {
82
84
  async enableRule(ruleId, context) {
83
85
  try {
84
86
  const entry = await this.knowledgeRepository.findById(ruleId);
85
- if (!entry) throw new NotFoundError('Guard rule not found', 'knowledge_entry', ruleId);
87
+ if (!entry) {
88
+ throw new NotFoundError('Guard rule not found', 'knowledge_entry', ruleId);
89
+ }
86
90
  if (entry.lifecycle === 'active') {
87
91
  throw new ConflictError('Rule is already enabled', 'Cannot enable an already enabled rule');
88
92
  }
@@ -111,9 +115,14 @@ export class GuardService {
111
115
  async disableRule(ruleId, reason, context) {
112
116
  try {
113
117
  const entry = await this.knowledgeRepository.findById(ruleId);
114
- if (!entry) throw new NotFoundError('Guard rule not found', 'knowledge_entry', ruleId);
118
+ if (!entry) {
119
+ throw new NotFoundError('Guard rule not found', 'knowledge_entry', ruleId);
120
+ }
115
121
  if (entry.lifecycle === 'deprecated') {
116
- throw new ConflictError('Rule is already disabled', 'Cannot disable an already disabled rule');
122
+ throw new ConflictError(
123
+ 'Rule is already disabled',
124
+ 'Cannot disable an already disabled rule'
125
+ );
117
126
  }
118
127
 
119
128
  if (!reason || reason.trim().length === 0) {
@@ -158,9 +167,7 @@ export class GuardService {
158
167
 
159
168
  // 按语言过滤
160
169
  if (language) {
161
- guardEntries = guardEntries.filter(
162
- e => !e.language || e.language === language
163
- );
170
+ guardEntries = guardEntries.filter((e) => !e.language || e.language === language);
164
171
  }
165
172
 
166
173
  const matches = [];
@@ -176,7 +183,7 @@ export class GuardService {
176
183
  ruleName: entry.title,
177
184
  severity: guard.severity || 'warning',
178
185
  message: guard.message || '',
179
- matches: codeMatches.map(m => ({
186
+ matches: codeMatches.map((m) => ({
180
187
  match: m[0],
181
188
  index: m.index,
182
189
  line: code.substring(0, m.index).split('\\n').length,
@@ -185,7 +192,10 @@ export class GuardService {
185
192
  });
186
193
  }
187
194
  } catch (e) {
188
- this.logger.warn('Error matching guard pattern', { entryId: entry.id, error: e.message });
195
+ this.logger.warn('Error matching guard pattern', {
196
+ entryId: entry.id,
197
+ error: e.message,
198
+ });
189
199
  }
190
200
  }
191
201
  }
@@ -221,7 +231,7 @@ export class GuardService {
221
231
  const { page = 1, pageSize = 20 } = pagination;
222
232
  const result = await this.knowledgeRepository.search(keyword, { page, pageSize });
223
233
  result.data = (result.data || []).filter(
224
- r => r.kind === 'rule' && r.knowledgeType === 'boundary-constraint'
234
+ (r) => r.kind === 'rule' && r.knowledgeType === 'boundary-constraint'
225
235
  );
226
236
  result.total = result.data.length;
227
237
  return result;
@@ -4,11 +4,11 @@
4
4
  * 持久化到 AutoSnippet/guard-learner.json(Git 友好)
5
5
  */
6
6
 
7
- import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from 'node:fs';
8
- import { join, dirname } from 'node:path';
7
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
8
+ import { dirname, join } from 'node:path';
9
9
  import Logger from '../../infrastructure/logging/Logger.js';
10
- import pathGuard from '../../shared/PathGuard.js';
11
10
  import { RULE_LEARNER } from '../../shared/constants.js';
11
+ import pathGuard from '../../shared/PathGuard.js';
12
12
 
13
13
  const PROBLEMATIC_THRESHOLD = {
14
14
  falsePositiveRate: RULE_LEARNER.PROBLEMATIC_FALSE_POSITIVE_RATE,
@@ -17,7 +17,7 @@ const PROBLEMATIC_THRESHOLD = {
17
17
 
18
18
  export class RuleLearner {
19
19
  #learnerPath;
20
- #data; // { ruleStats: { [ruleId]: { triggers, correct, falsePositive, falseNegative } } }
20
+ #data; // { ruleStats: { [ruleId]: { triggers, correct, falsePositive, falseNegative } } }
21
21
 
22
22
  constructor(projectRoot, options = {}) {
23
23
  const kbDir = options.knowledgeBaseDir || 'AutoSnippet';
@@ -37,7 +37,9 @@ export class RuleLearner {
37
37
  stat.triggers++;
38
38
  const now = new Date().toISOString();
39
39
  stat.lastTriggered = now;
40
- if (!stat.firstTriggered) stat.firstTriggered = now;
40
+ if (!stat.firstTriggered) {
41
+ stat.firstTriggered = now;
42
+ }
41
43
  this.#save();
42
44
  }
43
45
 
@@ -48,9 +50,13 @@ export class RuleLearner {
48
50
  */
49
51
  recordFeedback(ruleId, feedbackType) {
50
52
  const stat = this.#ensureStat(ruleId);
51
- if (feedbackType === 'correct') stat.correct++;
52
- else if (feedbackType === 'falsePositive') stat.falsePositive++;
53
- else if (feedbackType === 'falseNegative') stat.falseNegative++;
53
+ if (feedbackType === 'correct') {
54
+ stat.correct++;
55
+ } else if (feedbackType === 'falsePositive') {
56
+ stat.falsePositive++;
57
+ } else if (feedbackType === 'falseNegative') {
58
+ stat.falseNegative++;
59
+ }
54
60
  stat.lastFeedback = new Date().toISOString();
55
61
  this.#save();
56
62
  }
@@ -70,9 +76,9 @@ export class RuleLearner {
70
76
  const fp = stat.falsePositive;
71
77
  const fn = stat.falseNegative;
72
78
 
73
- const precision = (tp + fp) > 0 ? tp / (tp + fp) : 1;
74
- const recall = (tp + fn) > 0 ? tp / (tp + fn) : 1;
75
- const f1 = (precision + recall) > 0 ? (2 * precision * recall) / (precision + recall) : 0;
79
+ const precision = tp + fp > 0 ? tp / (tp + fp) : 1;
80
+ const recall = tp + fn > 0 ? tp / (tp + fn) : 1;
81
+ const f1 = precision + recall > 0 ? (2 * precision * recall) / (precision + recall) : 0;
76
82
  const falsePositiveRate = stat.triggers > 0 ? fp / stat.triggers : 0;
77
83
 
78
84
  return { precision, recall, f1, triggers: stat.triggers, falsePositiveRate };
@@ -85,7 +91,9 @@ export class RuleLearner {
85
91
  getProblematicRules() {
86
92
  const results = [];
87
93
  for (const [ruleId, stat] of Object.entries(this.#data.ruleStats)) {
88
- if (stat.triggers < PROBLEMATIC_THRESHOLD.minTriggers) continue;
94
+ if (stat.triggers < PROBLEMATIC_THRESHOLD.minTriggers) {
95
+ continue;
96
+ }
89
97
 
90
98
  const metrics = this.getMetrics(ruleId);
91
99
  if (metrics.falsePositiveRate >= PROBLEMATIC_THRESHOLD.falsePositiveRate) {
@@ -163,7 +171,10 @@ export class RuleLearner {
163
171
  // 策略 2: 高触发 + 高精度内置规则 → 建议创建项目定制版
164
172
  const allStats = this.getAllStats();
165
173
  for (const [ruleId, stat] of Object.entries(allStats)) {
166
- if (stat.triggers > RULE_LEARNER.HIGH_TRIGGER_COUNT && (stat.metrics?.precision ?? 1) > RULE_LEARNER.HIGH_PRECISION) {
174
+ if (
175
+ stat.triggers > RULE_LEARNER.HIGH_TRIGGER_COUNT &&
176
+ (stat.metrics?.precision ?? 1) > RULE_LEARNER.HIGH_PRECISION
177
+ ) {
167
178
  suggestions.push({
168
179
  type: 'specialize',
169
180
  ruleId,
@@ -177,14 +188,18 @@ export class RuleLearner {
177
188
  // 策略 3: 长期无触发的规则 → 可能不适用
178
189
  for (const [ruleId, stat] of Object.entries(allStats)) {
179
190
  if (stat.triggers === 0 && stat.lastTriggered) {
180
- const daysSinceLastTrigger = (Date.now() - new Date(stat.lastTriggered).getTime()) / 86400000;
191
+ const daysSinceLastTrigger =
192
+ (Date.now() - new Date(stat.lastTriggered).getTime()) / 86400000;
181
193
  if (daysSinceLastTrigger > RULE_LEARNER.UNUSED_DAYS_THRESHOLD) {
182
194
  suggestions.push({
183
195
  type: 'review_unused',
184
196
  ruleId,
185
197
  message: `规则 ${ruleId} 超过 ${RULE_LEARNER.UNUSED_DAYS_THRESHOLD} 天未触发,建议审查是否仍需保留`,
186
198
  confidence: RULE_LEARNER.CONFIDENCE_REVIEW,
187
- evidence: { daysSinceLastTrigger: Math.round(daysSinceLastTrigger), triggers: stat.triggers },
199
+ evidence: {
200
+ daysSinceLastTrigger: Math.round(daysSinceLastTrigger),
201
+ triggers: stat.triggers,
202
+ },
188
203
  });
189
204
  }
190
205
  }
@@ -201,10 +216,14 @@ export class RuleLearner {
201
216
  */
202
217
  trackRuleEffectiveness(ruleId) {
203
218
  const stat = this.#data.ruleStats[ruleId];
204
- if (!stat) return { status: 'no_data', triggers: 0, precision: 1, recommendation: 'monitor' };
219
+ if (!stat) {
220
+ return { status: 'no_data', triggers: 0, precision: 1, recommendation: 'monitor' };
221
+ }
205
222
 
206
223
  const firstTriggered = stat.firstTriggered || stat.lastTriggered;
207
- if (!firstTriggered) return { status: 'no_triggers', triggers: 0, precision: 1, recommendation: 'monitor' };
224
+ if (!firstTriggered) {
225
+ return { status: 'no_triggers', triggers: 0, precision: 1, recommendation: 'monitor' };
226
+ }
208
227
 
209
228
  const daysSinceFirstTrigger = (Date.now() - new Date(firstTriggered).getTime()) / 86400000;
210
229
 
@@ -222,7 +241,10 @@ export class RuleLearner {
222
241
  const metrics = this.getMetrics(ruleId);
223
242
 
224
243
  // 14 天后判定
225
- if (metrics.precision < RULE_LEARNER.LOW_PRECISION && stat.triggers >= PROBLEMATIC_THRESHOLD.minTriggers) {
244
+ if (
245
+ metrics.precision < RULE_LEARNER.LOW_PRECISION &&
246
+ stat.triggers >= PROBLEMATIC_THRESHOLD.minTriggers
247
+ ) {
226
248
  return {
227
249
  status: 'ineffective',
228
250
  triggers: stat.triggers,
@@ -263,17 +285,23 @@ export class RuleLearner {
263
285
  if (existsSync(this.#learnerPath)) {
264
286
  return JSON.parse(readFileSync(this.#learnerPath, 'utf-8'));
265
287
  }
266
- } catch { /* silent */ }
288
+ } catch {
289
+ /* silent */
290
+ }
267
291
  return { ruleStats: {} };
268
292
  }
269
293
 
270
294
  #save() {
271
295
  try {
272
296
  const dir = dirname(this.#learnerPath);
273
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
297
+ if (!existsSync(dir)) {
298
+ mkdirSync(dir, { recursive: true });
299
+ }
274
300
  writeFileSync(this.#learnerPath, JSON.stringify(this.#data, null, 2));
275
301
  } catch (err) {
276
- Logger.getInstance().warn('RuleLearner: failed to persist learner data', { error: err.message });
302
+ Logger.getInstance().warn('RuleLearner: failed to persist learner data', {
303
+ error: err.message,
304
+ });
277
305
  }
278
306
  }
279
307
 
@@ -283,10 +311,14 @@ export class RuleLearner {
283
311
  if (existsSync(oldPath) && !existsSync(this.#learnerPath)) {
284
312
  const content = readFileSync(oldPath, 'utf-8');
285
313
  const dir = dirname(this.#learnerPath);
286
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
314
+ if (!existsSync(dir)) {
315
+ mkdirSync(dir, { recursive: true });
316
+ }
287
317
  writeFileSync(this.#learnerPath, content);
288
318
  unlinkSync(oldPath);
289
319
  }
290
- } catch { /* 迁移失败不阻断启动 */ }
320
+ } catch {
321
+ /* 迁移失败不阻断启动 */
322
+ }
291
323
  }
292
324
  }
@@ -1,25 +1,30 @@
1
1
  /**
2
2
  * SourceFileCollector — 递归收集项目源文件
3
- *
3
+ *
4
4
  * 从 GuardHandler 提取的公共工具,供 ComplianceReporter / guard:ci / guard:staged 复用
5
5
  */
6
6
 
7
- import { join, extname } from 'node:path';
8
7
  import { readdir, readFile } from 'node:fs/promises';
8
+ import { extname, join } from 'node:path';
9
+ import { LanguageService } from '../../shared/LanguageService.js';
9
10
 
10
- /** 支持审计的源文件扩展名 */
11
- export const SOURCE_EXTS = new Set([
12
- '.m', '.mm', '.h', '.swift',
13
- '.c', '.cpp', '.cc', '.cxx', '.hpp',
14
- '.js', '.ts', '.jsx', '.tsx',
15
- '.java', '.kt', '.py', '.rb', '.go', '.rs',
16
- ]);
11
+ /** 支持审计的源文件扩展名 — 委托给 LanguageService */
12
+ export const SOURCE_EXTS = LanguageService.sourceExts;
17
13
 
18
14
  /** 跳过的目录 */
19
15
  const SKIP_DIRS = new Set([
20
- 'node_modules', '.git', 'build', 'DerivedData',
21
- 'Pods', '.build', 'vendor', 'dist', '.next',
22
- 'Carthage', 'xcuserdata', '__pycache__',
16
+ 'node_modules',
17
+ '.git',
18
+ 'build',
19
+ 'DerivedData',
20
+ 'Pods',
21
+ '.build',
22
+ 'vendor',
23
+ 'dist',
24
+ '.next',
25
+ 'Carthage',
26
+ 'xcuserdata',
27
+ '__pycache__',
23
28
  ]);
24
29
 
25
30
  /**
@@ -32,16 +37,14 @@ const SKIP_DIRS = new Set([
32
37
  * @returns {Promise<string[]>} 文件路径列表
33
38
  */
34
39
  export async function collectSourceFiles(dir, options = {}) {
35
- const {
36
- extensions = SOURCE_EXTS,
37
- skipDirs = SKIP_DIRS,
38
- maxFiles = Infinity,
39
- } = options;
40
+ const { extensions = SOURCE_EXTS, skipDirs = SKIP_DIRS, maxFiles = Infinity } = options;
40
41
 
41
42
  const files = [];
42
43
 
43
44
  async function walk(currentDir) {
44
- if (files.length >= maxFiles) return;
45
+ if (files.length >= maxFiles) {
46
+ return;
47
+ }
45
48
 
46
49
  let entries;
47
50
  try {
@@ -51,8 +54,12 @@ export async function collectSourceFiles(dir, options = {}) {
51
54
  }
52
55
 
53
56
  for (const entry of entries) {
54
- if (files.length >= maxFiles) return;
55
- if (entry.name.startsWith('.') && entry.name !== '.') continue;
57
+ if (files.length >= maxFiles) {
58
+ return;
59
+ }
60
+ if (entry.name.startsWith('.') && entry.name !== '.') {
61
+ continue;
62
+ }
56
63
 
57
64
  const fullPath = join(currentDir, entry.name);
58
65
  if (entry.isDirectory()) {
@@ -27,25 +27,29 @@ export class ViolationsStore {
27
27
  const id = `run_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
28
28
  const now = Math.floor(Date.now() / 1000);
29
29
 
30
- this.#db.prepare(`
30
+ this.#db
31
+ .prepare(`
31
32
  INSERT INTO guard_violations (id, file_path, triggered_at, violation_count, summary, violations_json, created_at)
32
33
  VALUES (?, ?, ?, ?, ?, ?, ?)
33
- `).run(
34
- id,
35
- run.filePath || '',
36
- new Date().toISOString(),
37
- (run.violations || []).length,
38
- run.summary || '',
39
- JSON.stringify(run.violations || []),
40
- now,
41
- );
34
+ `)
35
+ .run(
36
+ id,
37
+ run.filePath || '',
38
+ new Date().toISOString(),
39
+ (run.violations || []).length,
40
+ run.summary || '',
41
+ JSON.stringify(run.violations || []),
42
+ now
43
+ );
42
44
 
43
45
  // 超限截断:保留最新 MAX_RUNS 条
44
- this.#db.prepare(`
46
+ this.#db
47
+ .prepare(`
45
48
  DELETE FROM guard_violations WHERE id NOT IN (
46
49
  SELECT id FROM guard_violations ORDER BY created_at DESC LIMIT ?
47
50
  )
48
- `).run(MAX_RUNS);
51
+ `)
52
+ .run(MAX_RUNS);
49
53
 
50
54
  return id;
51
55
  }
@@ -56,50 +60,49 @@ export class ViolationsStore {
56
60
  * 获取所有运行记录(最新在后)
57
61
  */
58
62
  getRuns() {
59
- const rows = this.#db.prepare(
60
- 'SELECT * FROM guard_violations ORDER BY created_at ASC'
61
- ).all();
62
- return rows.map(r => this.#rowToRun(r));
63
+ const rows = this.#db.prepare('SELECT * FROM guard_violations ORDER BY created_at ASC').all();
64
+ return rows.map((r) => this.#rowToRun(r));
63
65
  }
64
66
 
65
67
  /**
66
68
  * 按文件路径查询历史
67
69
  */
68
70
  getRunsByFile(filePath) {
69
- const rows = this.#db.prepare(
70
- 'SELECT * FROM guard_violations WHERE file_path = ? ORDER BY created_at ASC'
71
- ).all(filePath);
72
- return rows.map(r => this.#rowToRun(r));
71
+ const rows = this.#db
72
+ .prepare('SELECT * FROM guard_violations WHERE file_path = ? ORDER BY created_at ASC')
73
+ .all(filePath);
74
+ return rows.map((r) => this.#rowToRun(r));
73
75
  }
74
76
 
75
77
  /**
76
78
  * 获取最近 N 条记录
77
79
  */
78
80
  getRecentRuns(n = 20) {
79
- const rows = this.#db.prepare(
80
- 'SELECT * FROM guard_violations ORDER BY created_at DESC, rowid DESC LIMIT ?'
81
- ).all(n);
82
- return rows.reverse().map(r => this.#rowToRun(r));
81
+ const rows = this.#db
82
+ .prepare('SELECT * FROM guard_violations ORDER BY created_at DESC, rowid DESC LIMIT ?')
83
+ .all(n);
84
+ return rows.reverse().map((r) => this.#rowToRun(r));
83
85
  }
84
86
 
85
87
  /**
86
88
  * 获取统计汇总
87
89
  */
88
90
  getStats() {
89
- const row = this.#db.prepare(`
91
+ const row = this.#db
92
+ .prepare(`
90
93
  SELECT
91
94
  COUNT(*) AS totalRuns,
92
95
  COALESCE(SUM(violation_count), 0) AS totalViolations,
93
96
  MAX(triggered_at) AS lastRunAt
94
97
  FROM guard_violations
95
- `).get();
98
+ `)
99
+ .get();
96
100
 
97
101
  return {
98
102
  totalRuns: row.totalRuns,
99
103
  totalViolations: row.totalViolations,
100
- averageViolationsPerRun: row.totalRuns > 0
101
- ? (row.totalViolations / row.totalRuns).toFixed(2)
102
- : 0,
104
+ averageViolationsPerRun:
105
+ row.totalRuns > 0 ? (row.totalViolations / row.totalRuns).toFixed(2) : 0,
103
106
  lastRunAt: row.lastRunAt || null,
104
107
  };
105
108
  }
@@ -111,7 +114,8 @@ export class ViolationsStore {
111
114
  */
112
115
  getStatsByRule() {
113
116
  try {
114
- return this.#db.prepare(`
117
+ return this.#db
118
+ .prepare(`
115
119
  SELECT
116
120
  json_extract(j.value, '$.ruleId') AS ruleId,
117
121
  json_extract(j.value, '$.severity') AS severity,
@@ -120,7 +124,8 @@ export class ViolationsStore {
120
124
  WHERE json_extract(j.value, '$.ruleId') IS NOT NULL
121
125
  GROUP BY ruleId, severity
122
126
  ORDER BY count DESC
123
- `).all();
127
+ `)
128
+ .all();
124
129
  } catch {
125
130
  return [];
126
131
  }
@@ -137,8 +142,8 @@ export class ViolationsStore {
137
142
  return {
138
143
  errorsChange: 0,
139
144
  warningsChange: 0,
140
- latestErrors: latest.filter(v => v.severity === 'error').length,
141
- latestWarnings: latest.filter(v => v.severity === 'warning').length,
145
+ latestErrors: latest.filter((v) => v.severity === 'error').length,
146
+ latestWarnings: latest.filter((v) => v.severity === 'warning').length,
142
147
  previousErrors: 0,
143
148
  previousWarnings: 0,
144
149
  hasHistory: false,
@@ -146,10 +151,10 @@ export class ViolationsStore {
146
151
  }
147
152
 
148
153
  const [prev, latest] = recent;
149
- const latestErrors = latest.violations.filter(v => v.severity === 'error').length;
150
- const latestWarnings = latest.violations.filter(v => v.severity === 'warning').length;
151
- const previousErrors = prev.violations.filter(v => v.severity === 'error').length;
152
- const previousWarnings = prev.violations.filter(v => v.severity === 'warning').length;
154
+ const latestErrors = latest.violations.filter((v) => v.severity === 'error').length;
155
+ const latestWarnings = latest.violations.filter((v) => v.severity === 'warning').length;
156
+ const previousErrors = prev.violations.filter((v) => v.severity === 'error').length;
157
+ const previousWarnings = prev.violations.filter((v) => v.severity === 'warning').length;
153
158
 
154
159
  return {
155
160
  errorsChange: latestErrors - previousErrors,
@@ -211,7 +216,7 @@ export class ViolationsStore {
211
216
  const total = this.#db.prepare(countSql).get(...countParams).c;
212
217
 
213
218
  return {
214
- data: rows.map(r => this.#rowToRun(r)),
219
+ data: rows.map((r) => this.#rowToRun(r)),
215
220
  pagination: { page, limit, total, pages: Math.ceil(total / limit) },
216
221
  };
217
222
  }