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
@@ -18,10 +18,10 @@
18
18
  * 落盘目录:isCandidate() → candidates/ | isActive()/deprecated → recipes/
19
19
  */
20
20
 
21
+ import { createHash } from 'node:crypto';
21
22
  import fs from 'node:fs';
22
23
  import path from 'node:path';
23
- import { createHash } from 'node:crypto';
24
- import { RECIPES_DIR, CANDIDATES_DIR } from '../../infrastructure/config/Defaults.js';
24
+ import { CANDIDATES_DIR, RECIPES_DIR } from '../../infrastructure/config/Defaults.js';
25
25
  import Logger from '../../infrastructure/logging/Logger.js';
26
26
  import pathGuard from '../../shared/PathGuard.js';
27
27
 
@@ -30,13 +30,35 @@ import pathGuard from '../../shared/PathGuard.js';
30
30
  * ═══════════════════════════════════════════════════════════ */
31
31
 
32
32
  const SCALAR_FIELDS = [
33
- 'id', 'title', 'trigger', 'lifecycle', 'language', 'category',
34
- 'kind', 'knowledgeType', 'complexity', 'scope', 'difficulty',
35
- 'description', 'source', 'moduleName',
36
- 'topicHint', 'whenClause', 'doClause', 'dontClause', 'coreCode',
37
- 'createdBy', 'createdAt', 'updatedAt',
38
- 'publishedAt', 'publishedBy', 'reviewedBy', 'reviewedAt',
39
- 'rejectionReason', 'sourceFile', 'sourceCandidateId',
33
+ 'id',
34
+ 'title',
35
+ 'trigger',
36
+ 'lifecycle',
37
+ 'language',
38
+ 'category',
39
+ 'kind',
40
+ 'knowledgeType',
41
+ 'complexity',
42
+ 'scope',
43
+ 'difficulty',
44
+ 'description',
45
+ 'source',
46
+ 'moduleName',
47
+ 'topicHint',
48
+ 'whenClause',
49
+ 'doClause',
50
+ 'dontClause',
51
+ 'coreCode',
52
+ 'createdBy',
53
+ 'createdAt',
54
+ 'updatedAt',
55
+ 'publishedAt',
56
+ 'publishedBy',
57
+ 'reviewedBy',
58
+ 'reviewedAt',
59
+ 'rejectionReason',
60
+ 'sourceFile',
61
+ 'sourceCandidateId',
40
62
  ];
41
63
 
42
64
  /* ═══════════════════════════════════════════════════════════
@@ -48,10 +70,10 @@ export class KnowledgeFileWriter {
48
70
  * @param {string} projectRoot 项目根目录
49
71
  */
50
72
  constructor(projectRoot) {
51
- this.projectRoot = projectRoot;
52
- this.recipesDir = path.join(projectRoot, RECIPES_DIR);
73
+ this.projectRoot = projectRoot;
74
+ this.recipesDir = path.join(projectRoot, RECIPES_DIR);
53
75
  this.candidatesDir = path.join(projectRoot, CANDIDATES_DIR);
54
- this.logger = Logger.getInstance();
76
+ this.logger = Logger.getInstance();
55
77
  }
56
78
 
57
79
  /* ═══ 序列化 ═══════════════════════════════════════════ */
@@ -74,20 +96,30 @@ export class KnowledgeFileWriter {
74
96
  }
75
97
 
76
98
  // ── 简单数组字段(行内 JSON)──
77
- if (json.tags?.length) lines.push(`tags: ${JSON.stringify(json.tags)}`);
78
- if (json.headers?.length) lines.push(`headers: ${JSON.stringify(json.headers)}`);
79
- if (json.headerPaths?.length) lines.push(`headerPaths: ${JSON.stringify(json.headerPaths)}`);
80
- if (json.includeHeaders) lines.push(`includeHeaders: true`);
81
- if (json.autoApprovable) lines.push(`autoApprovable: true`);
99
+ if (json.tags?.length) {
100
+ lines.push(`tags: ${JSON.stringify(json.tags)}`);
101
+ }
102
+ if (json.headers?.length) {
103
+ lines.push(`headers: ${JSON.stringify(json.headers)}`);
104
+ }
105
+ if (json.headerPaths?.length) {
106
+ lines.push(`headerPaths: ${JSON.stringify(json.headerPaths)}`);
107
+ }
108
+ if (json.includeHeaders) {
109
+ lines.push(`includeHeaders: true`);
110
+ }
111
+ if (json.autoApprovable) {
112
+ lines.push(`autoApprovable: true`);
113
+ }
82
114
 
83
115
  // ── JSON 值对象(_ 前缀,单行 JSON)──
84
116
  const JSON_FIELDS = [
85
- ['_content', json.content],
86
- ['_relations', json.relations],
87
- ['_constraints', json.constraints],
88
- ['_reasoning', json.reasoning],
89
- ['_quality', json.quality],
90
- ['_stats', json.stats],
117
+ ['_content', json.content],
118
+ ['_relations', json.relations],
119
+ ['_constraints', json.constraints],
120
+ ['_reasoning', json.reasoning],
121
+ ['_quality', json.quality],
122
+ ['_stats', json.stats],
91
123
  ['_lifecycleHistory', json.lifecycleHistory],
92
124
  ];
93
125
  for (const [key, val] of JSON_FIELDS) {
@@ -99,8 +131,12 @@ export class KnowledgeFileWriter {
99
131
  }
100
132
  }
101
133
  }
102
- if (json.agentNotes) lines.push(`_agentNotes: ${JSON.stringify(json.agentNotes)}`);
103
- if (json.aiInsight) lines.push(`_aiInsight: ${JSON.stringify(json.aiInsight)}`);
134
+ if (json.agentNotes) {
135
+ lines.push(`_agentNotes: ${JSON.stringify(json.agentNotes)}`);
136
+ }
137
+ if (json.aiInsight) {
138
+ lines.push(`_aiInsight: ${JSON.stringify(json.aiInsight)}`);
139
+ }
104
140
 
105
141
  // _contentHash 占位(后续替换为真实 hash)
106
142
  const hashPlaceholder = '__HASH_PLACEHOLDER__';
@@ -144,7 +180,7 @@ export class KnowledgeFileWriter {
144
180
  }
145
181
 
146
182
  if (c.pattern) {
147
- lines.push('```' + (entry.language || 'swift'));
183
+ lines.push(`\`\`\`${entry.language || 'text'}`);
148
184
  lines.push(c.pattern);
149
185
  lines.push('```');
150
186
  lines.push('');
@@ -165,7 +201,7 @@ export class KnowledgeFileWriter {
165
201
  lines.push(`${i + 1}. ${step}`);
166
202
  } else {
167
203
  const title = step.title || '步骤';
168
- const desc = step.description || '';
204
+ const desc = step.description || '';
169
205
  lines.push(`${i + 1}. **${title}**: ${desc}`);
170
206
  if (step.code) {
171
207
  lines.push('');
@@ -265,7 +301,9 @@ export class KnowledgeFileWriter {
265
301
  * @returns {boolean}
266
302
  */
267
303
  remove(entry) {
268
- if (!entry?.id) return false;
304
+ if (!entry?.id) {
305
+ return false;
306
+ }
269
307
 
270
308
  // 先尝试 sourceFile 精确删除
271
309
  if (entry.sourceFile) {
@@ -308,9 +346,7 @@ export class KnowledgeFileWriter {
308
346
  * @returns {string|null} 新的文件路径
309
347
  */
310
348
  moveOnLifecycleChange(entry) {
311
- const oldPath = entry.sourceFile
312
- ? path.join(this.projectRoot, entry.sourceFile)
313
- : null;
349
+ const oldPath = entry.sourceFile ? path.join(this.projectRoot, entry.sourceFile) : null;
314
350
 
315
351
  const { dir: newDir, filename } = this._resolveFilePath(entry);
316
352
  const newPath = path.join(newDir, filename);
@@ -351,7 +387,9 @@ export class KnowledgeFileWriter {
351
387
  * 清理旧文件(category 变更或 lifecycle 切换场景)
352
388
  */
353
389
  _cleanupOldFile(entry, newPath) {
354
- if (!entry.sourceFile) return;
390
+ if (!entry.sourceFile) {
391
+ return;
392
+ }
355
393
  const oldPath = path.join(this.projectRoot, entry.sourceFile);
356
394
  if (oldPath !== newPath && fs.existsSync(oldPath)) {
357
395
  fs.unlinkSync(oldPath);
@@ -368,14 +406,18 @@ export class KnowledgeFileWriter {
368
406
  */
369
407
  _removeByIdScan(id) {
370
408
  for (const baseDir of [this.candidatesDir, this.recipesDir]) {
371
- if (!fs.existsSync(baseDir)) continue;
409
+ if (!fs.existsSync(baseDir)) {
410
+ continue;
411
+ }
372
412
  try {
373
413
  const found = _walkAndRemoveById(baseDir, id);
374
414
  if (found) {
375
415
  this.logger.info('Knowledge entry file removed by id scan', { id });
376
416
  return true;
377
417
  }
378
- } catch { /* ignore scan errors */ }
418
+ } catch {
419
+ /* ignore scan errors */
420
+ }
379
421
  }
380
422
  return false;
381
423
  }
@@ -413,11 +455,15 @@ export function parseKnowledgeMarkdown(content, relPath) {
413
455
  for (let i = 0; i < fmLines.length; i++) {
414
456
  const line = fmLines[i];
415
457
  const colonIdx = line.indexOf(':');
416
- if (colonIdx <= 0) continue;
458
+ if (colonIdx <= 0) {
459
+ continue;
460
+ }
417
461
 
418
462
  const key = line.slice(0, colonIdx).trim();
419
463
  // 跳过带空格的非正常 key
420
- if (/\s/.test(key)) continue;
464
+ if (/\s/.test(key)) {
465
+ continue;
466
+ }
421
467
 
422
468
  let value = line.slice(colonIdx + 1).trim();
423
469
 
@@ -439,7 +485,9 @@ export function parseKnowledgeMarkdown(content, relPath) {
439
485
  try {
440
486
  data[dataKey] = JSON.parse(jsonStr);
441
487
  break;
442
- } catch { /* continue concatenating */ }
488
+ } catch {
489
+ /* continue concatenating */
490
+ }
443
491
  }
444
492
  continue;
445
493
  }
@@ -450,29 +498,60 @@ export function parseKnowledgeMarkdown(content, relPath) {
450
498
  try {
451
499
  data[dataKey] = JSON.parse(value);
452
500
  continue;
453
- } catch { /* fall through to plain string */ }
501
+ } catch {
502
+ /* fall through to plain string */
503
+ }
454
504
  }
455
505
 
456
506
  // 纯标量值(如 _content_hash: abc123)
457
- if (/^\d+$/.test(value)) { data[dataKey] = parseInt(value, 10); continue; }
458
- if (/^\d+\.\d+$/.test(value)) { data[dataKey] = parseFloat(value); continue; }
459
- if (value === 'true') { data[dataKey] = true; continue; }
460
- if (value === 'false') { data[dataKey] = false; continue; }
507
+ if (/^\d+$/.test(value)) {
508
+ data[dataKey] = parseInt(value, 10);
509
+ continue;
510
+ }
511
+ if (/^\d+\.\d+$/.test(value)) {
512
+ data[dataKey] = parseFloat(value);
513
+ continue;
514
+ }
515
+ if (value === 'true') {
516
+ data[dataKey] = true;
517
+ continue;
518
+ }
519
+ if (value === 'false') {
520
+ data[dataKey] = false;
521
+ continue;
522
+ }
461
523
  data[dataKey] = value;
462
524
  continue;
463
525
  }
464
526
 
465
527
  // ── 布尔 ──
466
- if (value === 'true') { data[key] = true; continue; }
467
- if (value === 'false') { data[key] = false; continue; }
528
+ if (value === 'true') {
529
+ data[key] = true;
530
+ continue;
531
+ }
532
+ if (value === 'false') {
533
+ data[key] = false;
534
+ continue;
535
+ }
468
536
 
469
537
  // ── 数值(整数或浮点) ──
470
- if (/^\d+$/.test(value)) { data[key] = parseInt(value, 10); continue; }
471
- if (/^\d+\.\d+$/.test(value)) { data[key] = parseFloat(value); continue; }
538
+ if (/^\d+$/.test(value)) {
539
+ data[key] = parseInt(value, 10);
540
+ continue;
541
+ }
542
+ if (/^\d+\.\d+$/.test(value)) {
543
+ data[key] = parseFloat(value);
544
+ continue;
545
+ }
472
546
 
473
547
  // ── JSON 数组(非 _ 前缀) ──
474
548
  if (value.startsWith('[')) {
475
- try { data[key] = JSON.parse(value); continue; } catch { /* fallthrough */ }
549
+ try {
550
+ data[key] = JSON.parse(value);
551
+ continue;
552
+ } catch {
553
+ /* fallthrough */
554
+ }
476
555
  }
477
556
 
478
557
  // ── 去引号 ──
@@ -500,8 +579,8 @@ export function parseKnowledgeMarkdown(content, relPath) {
500
579
 
501
580
  // 如果 content 中没有 markdown 且 body 看起来是 Markdown 文章
502
581
  if (!data.content?.markdown && !data.content?.pattern) {
503
- const isMarkdownArticle = body.includes('— 项目特写') ||
504
- (body.startsWith('#') && body.length > 200);
582
+ const isMarkdownArticle =
583
+ body.includes('— 项目特写') || (body.startsWith('#') && body.length > 200);
505
584
  if (isMarkdownArticle) {
506
585
  data.content = data.content || {};
507
586
  data.content.markdown = body;
@@ -517,7 +596,9 @@ export function parseKnowledgeMarkdown(content, relPath) {
517
596
  // ── fallback: title 从 body heading 提取 ──
518
597
  if (!data.title) {
519
598
  const headingMatch = content.match(/^##?\s+(.+)$/m);
520
- if (headingMatch) data.title = headingMatch[1].trim();
599
+ if (headingMatch) {
600
+ data.title = headingMatch[1].trim();
601
+ }
521
602
  }
522
603
 
523
604
  return data;
@@ -539,7 +620,9 @@ function _slugFilename(trigger, title, id) {
539
620
  .replace(/^@/, '')
540
621
  .replace(/[^a-zA-Z0-9_-]/g, '_')
541
622
  .slice(0, 60);
542
- if (clean.length >= 2) return `${clean}.md`;
623
+ if (clean.length >= 2) {
624
+ return `${clean}.md`;
625
+ }
543
626
  }
544
627
 
545
628
  // 其次用 title
@@ -551,7 +634,9 @@ function _slugFilename(trigger, title, id) {
551
634
  .replace(/-{2,}/g, '-')
552
635
  .replace(/^-|-$/g, '')
553
636
  .slice(0, 60);
554
- if (slug.length >= 3) return `${slug}.md`;
637
+ if (slug.length >= 3) {
638
+ return `${slug}.md`;
639
+ }
555
640
  }
556
641
 
557
642
  // 最后用 id 前 8 位
@@ -562,10 +647,12 @@ function _slugFilename(trigger, title, id) {
562
647
  * 将 YAML 值安全序列化
563
648
  */
564
649
  function _yamlValue(key, val) {
565
- if (typeof val === 'number' || typeof val === 'boolean') return String(val);
650
+ if (typeof val === 'number' || typeof val === 'boolean') {
651
+ return String(val);
652
+ }
566
653
  const str = String(val);
567
654
  // 含特殊字符时加引号
568
- if (/[:#\[\]{}&*!|>'"`,@\n]/.test(str) || str.trim() !== str) {
655
+ if (/[:#[\]{}&*!|>'"`,@\n]/.test(str) || str.trim() !== str) {
569
656
  return `"${str.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`;
570
657
  }
571
658
  return str;
@@ -576,11 +663,15 @@ function _yamlValue(key, val) {
576
663
  * @returns {boolean}
577
664
  */
578
665
  function _walkAndRemoveById(dir, id) {
579
- if (!fs.existsSync(dir)) return false;
666
+ if (!fs.existsSync(dir)) {
667
+ return false;
668
+ }
580
669
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
581
670
  const full = path.join(dir, entry.name);
582
671
  if (entry.isDirectory()) {
583
- if (_walkAndRemoveById(full, id)) return true;
672
+ if (_walkAndRemoveById(full, id)) {
673
+ return true;
674
+ }
584
675
  } else if (entry.name.endsWith('.md') && !entry.name.startsWith('_')) {
585
676
  const head = fs.readFileSync(full, 'utf8').slice(0, 500);
586
677
  if (head.includes(`id: ${id}`)) {
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * KnowledgeGraphService - 知识图谱服务
3
- *
3
+ *
4
4
  * 管理 Recipe 之间的关系(统一模型,包含所有知识类型)
5
5
  * 支持关系查询、路径分析、PageRank 权重计算
6
6
  */
7
7
 
8
- import Logger from '../../infrastructure/logging/Logger.js';
9
8
  import { RelationType } from '../../domain/index.js';
9
+ import Logger from '../../infrastructure/logging/Logger.js';
10
10
 
11
11
  // Re-export unified RelationType for backward compatibility
12
12
  export { RelationType };
@@ -23,10 +23,22 @@ export class KnowledgeGraphService {
23
23
  addEdge(fromId, fromType, toId, toType, relation, metadata = {}) {
24
24
  const now = Math.floor(Date.now() / 1000);
25
25
  try {
26
- this.db.prepare(`
26
+ this.db
27
+ .prepare(`
27
28
  INSERT OR REPLACE INTO knowledge_edges (from_id, from_type, to_id, to_type, relation, weight, metadata_json, created_at, updated_at)
28
29
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
29
- `).run(fromId, fromType, toId, toType, relation, metadata.weight || 1.0, JSON.stringify(metadata), now, now);
30
+ `)
31
+ .run(
32
+ fromId,
33
+ fromType,
34
+ toId,
35
+ toType,
36
+ relation,
37
+ metadata.weight || 1.0,
38
+ JSON.stringify(metadata),
39
+ now,
40
+ now
41
+ );
30
42
 
31
43
  return { success: true };
32
44
  } catch (error) {
@@ -39,22 +51,30 @@ export class KnowledgeGraphService {
39
51
  * 删除关系边
40
52
  */
41
53
  removeEdge(fromId, fromType, toId, toType, relation) {
42
- this.db.prepare(`
54
+ this.db
55
+ .prepare(`
43
56
  DELETE FROM knowledge_edges WHERE from_id = ? AND from_type = ? AND to_id = ? AND to_type = ? AND relation = ?
44
- `).run(fromId, fromType, toId, toType, relation);
57
+ `)
58
+ .run(fromId, fromType, toId, toType, relation);
45
59
  }
46
60
 
47
61
  /**
48
62
  * 查询某个节点的所有关系
49
63
  */
50
64
  getEdges(nodeId, nodeType, direction = 'both') {
51
- const outgoing = direction === 'both' || direction === 'out'
52
- ? this.db.prepare(`SELECT * FROM knowledge_edges WHERE from_id = ? AND from_type = ?`).all(nodeId, nodeType)
53
- : [];
54
-
55
- const incoming = direction === 'both' || direction === 'in'
56
- ? this.db.prepare(`SELECT * FROM knowledge_edges WHERE to_id = ? AND to_type = ?`).all(nodeId, nodeType)
57
- : [];
65
+ const outgoing =
66
+ direction === 'both' || direction === 'out'
67
+ ? this.db
68
+ .prepare(`SELECT * FROM knowledge_edges WHERE from_id = ? AND from_type = ?`)
69
+ .all(nodeId, nodeType)
70
+ : [];
71
+
72
+ const incoming =
73
+ direction === 'both' || direction === 'in'
74
+ ? this.db
75
+ .prepare(`SELECT * FROM knowledge_edges WHERE to_id = ? AND to_type = ?`)
76
+ .all(nodeId, nodeType)
77
+ : [];
58
78
 
59
79
  return {
60
80
  outgoing: outgoing.map(this._mapEdge),
@@ -66,11 +86,13 @@ export class KnowledgeGraphService {
66
86
  * 查询指定关系类型的连接
67
87
  */
68
88
  getRelated(nodeId, nodeType, relation) {
69
- const rows = this.db.prepare(`
89
+ const rows = this.db
90
+ .prepare(`
70
91
  SELECT * FROM knowledge_edges WHERE from_id = ? AND from_type = ? AND relation = ?
71
92
  UNION ALL
72
93
  SELECT * FROM knowledge_edges WHERE to_id = ? AND to_type = ? AND relation = ?
73
- `).all(nodeId, nodeType, relation, nodeId, nodeType, relation);
94
+ `)
95
+ .all(nodeId, nodeType, relation, nodeId, nodeType, relation);
74
96
 
75
97
  return rows.map(this._mapEdge);
76
98
  }
@@ -84,19 +106,32 @@ export class KnowledgeGraphService {
84
106
 
85
107
  while (queue.length > 0) {
86
108
  const { id, type, path } = queue.shift();
87
-
88
- if (path.length >= maxDepth) continue;
89
-
109
+
110
+ if (path.length >= maxDepth) {
111
+ continue;
112
+ }
113
+
90
114
  const key = `${type}:${id}`;
91
- if (visited.has(key)) continue;
115
+ if (visited.has(key)) {
116
+ continue;
117
+ }
92
118
  visited.add(key);
93
119
 
94
- const neighbors = this.db.prepare(`
120
+ const neighbors = this.db
121
+ .prepare(`
95
122
  SELECT to_id, to_type, relation, weight FROM knowledge_edges WHERE from_id = ? AND from_type = ?
96
- `).all(id, type);
123
+ `)
124
+ .all(id, type);
97
125
 
98
126
  for (const neighbor of neighbors) {
99
- const newPath = [...path, { from: { id, type }, to: { id: neighbor.to_id, type: neighbor.to_type }, relation: neighbor.relation }];
127
+ const newPath = [
128
+ ...path,
129
+ {
130
+ from: { id, type },
131
+ to: { id: neighbor.to_id, type: neighbor.to_type },
132
+ relation: neighbor.relation,
133
+ },
134
+ ];
100
135
 
101
136
  if (neighbor.to_id === toId && neighbor.to_type === toType) {
102
137
  return { found: true, path: newPath, depth: newPath.length };
@@ -118,12 +153,16 @@ export class KnowledgeGraphService {
118
153
 
119
154
  while (queue.length > 0) {
120
155
  const { id, type, depth } = queue.shift();
121
- if (depth >= maxDepth) continue;
156
+ if (depth >= maxDepth) {
157
+ continue;
158
+ }
122
159
 
123
- const dependents = this.db.prepare(`
160
+ const dependents = this.db
161
+ .prepare(`
124
162
  SELECT from_id, from_type, relation FROM knowledge_edges
125
163
  WHERE to_id = ? AND to_type = ? AND relation IN ('requires', 'extends', 'enforces', 'depends_on', 'inherits', 'implements', 'calls', 'prerequisite')
126
- `).all(id, type);
164
+ `)
165
+ .all(id, type);
127
166
 
128
167
  for (const dep of dependents) {
129
168
  const key = `${dep.from_type}:${dep.from_id}`;
@@ -149,20 +188,28 @@ export class KnowledgeGraphService {
149
188
  * @param {string} [nodeType] 过滤节点类型(如 'recipe'),为空则返回全部
150
189
  */
151
190
  getStats(nodeType) {
152
- const typeFilter = nodeType ? ` WHERE from_type = '${nodeType}' AND to_type = '${nodeType}'` : '';
153
- const edgeCount = this.db.prepare(`SELECT COUNT(*) as total FROM knowledge_edges${typeFilter}`).get();
154
- const byRelation = this.db.prepare(
155
- `SELECT relation, COUNT(*) as count FROM knowledge_edges${typeFilter} GROUP BY relation`
156
- ).all();
157
- const byType = this.db.prepare(
158
- `SELECT from_type as type, COUNT(DISTINCT from_id) as count FROM knowledge_edges${typeFilter} GROUP BY from_type
191
+ const typeFilter = nodeType
192
+ ? ` WHERE from_type = '${nodeType}' AND to_type = '${nodeType}'`
193
+ : '';
194
+ const edgeCount = this.db
195
+ .prepare(`SELECT COUNT(*) as total FROM knowledge_edges${typeFilter}`)
196
+ .get();
197
+ const byRelation = this.db
198
+ .prepare(
199
+ `SELECT relation, COUNT(*) as count FROM knowledge_edges${typeFilter} GROUP BY relation`
200
+ )
201
+ .all();
202
+ const byType = this.db
203
+ .prepare(
204
+ `SELECT from_type as type, COUNT(DISTINCT from_id) as count FROM knowledge_edges${typeFilter} GROUP BY from_type
159
205
  UNION
160
206
  SELECT to_type as type, COUNT(DISTINCT to_id) as count FROM knowledge_edges${typeFilter} GROUP BY to_type`
161
- ).all();
207
+ )
208
+ .all();
162
209
 
163
210
  return {
164
211
  totalEdges: edgeCount.total,
165
- byRelation: Object.fromEntries(byRelation.map(r => [r.relation, r.count])),
212
+ byRelation: Object.fromEntries(byRelation.map((r) => [r.relation, r.count])),
166
213
  nodeTypes: byType,
167
214
  };
168
215
  }