autosnippet 2.9.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +4 -4
  2. package/bin/cli.js +5 -33
  3. package/config/constitution.yaml +9 -2
  4. package/dashboard/dist/assets/{icons-CH-H9x0E.js → icons-BkT3XrKf.js} +105 -100
  5. package/dashboard/dist/assets/index-BsB7DzW4.css +1 -0
  6. package/dashboard/dist/assets/index-DdmQMrJJ.js +155 -0
  7. package/dashboard/dist/index.html +3 -3
  8. package/lib/cli/AiScanService.js +13 -11
  9. package/lib/cli/KnowledgeSyncService.js +343 -0
  10. package/lib/cli/SetupService.js +8 -26
  11. package/lib/core/gateway/GatewayActionRegistry.js +48 -58
  12. package/lib/domain/index.js +16 -11
  13. package/lib/domain/knowledge/KnowledgeEntry.js +351 -0
  14. package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
  15. package/lib/domain/knowledge/Lifecycle.js +109 -0
  16. package/lib/domain/knowledge/index.js +27 -0
  17. package/lib/domain/knowledge/values/Constraints.js +125 -0
  18. package/lib/domain/knowledge/values/Content.js +86 -0
  19. package/lib/domain/knowledge/values/Quality.js +93 -0
  20. package/lib/domain/knowledge/values/Reasoning.js +69 -0
  21. package/lib/domain/knowledge/values/Relations.js +168 -0
  22. package/lib/domain/knowledge/values/Stats.js +87 -0
  23. package/lib/domain/knowledge/values/index.js +9 -0
  24. package/lib/external/ai/AiProvider.js +48 -0
  25. package/lib/external/mcp/McpServer.js +7 -5
  26. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +2 -2
  27. package/lib/external/mcp/handlers/bootstrap.js +116 -11
  28. package/lib/external/mcp/handlers/browse.js +77 -73
  29. package/lib/external/mcp/handlers/candidate.js +29 -276
  30. package/lib/external/mcp/handlers/guard.js +2 -0
  31. package/lib/external/mcp/handlers/knowledge.js +205 -0
  32. package/lib/external/mcp/handlers/structure.js +25 -23
  33. package/lib/external/mcp/handlers/system.js +10 -12
  34. package/lib/external/mcp/tools.js +125 -138
  35. package/lib/http/HttpServer.js +4 -8
  36. package/lib/http/routes/extract.js +48 -4
  37. package/lib/http/routes/knowledge.js +246 -0
  38. package/lib/http/routes/search.js +12 -17
  39. package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
  40. package/lib/infrastructure/external/XcodeAutomation.js +187 -103
  41. package/lib/injection/ServiceContainer.js +49 -60
  42. package/lib/repository/knowledge/KnowledgeRepository.impl.js +373 -0
  43. package/lib/service/automation/DirectiveDetector.js +2 -3
  44. package/lib/service/automation/FileWatcher.js +67 -28
  45. package/lib/service/automation/XcodeIntegration.js +931 -156
  46. package/lib/service/automation/handlers/AlinkHandler.js +6 -4
  47. package/lib/service/automation/handlers/CreateHandler.js +53 -18
  48. package/lib/service/automation/handlers/GuardHandler.js +183 -20
  49. package/lib/service/automation/handlers/SearchHandler.js +35 -17
  50. package/lib/service/chat/CandidateGuardrail.js +1 -1
  51. package/lib/service/chat/ChatAgent.js +46 -45
  52. package/lib/service/chat/ContextWindow.js +5 -5
  53. package/lib/service/chat/ProducerAgent.js +7 -7
  54. package/lib/service/chat/tools.js +130 -123
  55. package/lib/service/guard/GuardCheckEngine.js +114 -10
  56. package/lib/service/guard/GuardService.js +59 -48
  57. package/lib/service/knowledge/ConfidenceRouter.js +159 -0
  58. package/lib/service/knowledge/KnowledgeFileWriter.js +602 -0
  59. package/lib/service/knowledge/KnowledgeService.js +725 -0
  60. package/lib/service/search/SearchEngine.js +92 -19
  61. package/lib/service/skills/SignalCollector.js +12 -7
  62. package/lib/service/skills/SkillAdvisor.js +13 -11
  63. package/lib/service/snippet/SnippetFactory.js +5 -5
  64. package/package.json +1 -1
  65. package/scripts/install-cursor-skill.js +0 -6
  66. package/scripts/migrate-md-to-knowledge.mjs +364 -0
  67. package/skills/autosnippet-analysis/SKILL.md +15 -7
  68. package/skills/autosnippet-candidates/SKILL.md +6 -6
  69. package/skills/autosnippet-coldstart/SKILL.md +7 -3
  70. package/skills/autosnippet-concepts/SKILL.md +7 -6
  71. package/skills/autosnippet-create/SKILL.md +13 -13
  72. package/skills/autosnippet-intent/SKILL.md +3 -2
  73. package/skills/autosnippet-lifecycle/SKILL.md +5 -5
  74. package/skills/autosnippet-recipes/SKILL.md +16 -4
  75. package/templates/constitution.yaml +1 -1
  76. package/templates/copilot-instructions.md +6 -6
  77. package/templates/recipes-setup/README.md +3 -3
  78. package/dashboard/dist/assets/index-CqJRvYRL.js +0 -197
  79. package/dashboard/dist/assets/index-DICm9PNa.css +0 -1
  80. package/lib/cli/CandidateSyncService.js +0 -261
  81. package/lib/cli/SyncService.js +0 -356
  82. package/lib/domain/candidate/Candidate.js +0 -196
  83. package/lib/domain/candidate/CandidateRepository.js +0 -107
  84. package/lib/domain/candidate/Reasoning.js +0 -52
  85. package/lib/domain/recipe/Recipe.js +0 -421
  86. package/lib/domain/recipe/RecipeRepository.js +0 -54
  87. package/lib/domain/types/CandidateStatus.js +0 -52
  88. package/lib/http/routes/candidates.js +0 -559
  89. package/lib/http/routes/recipes.js +0 -397
  90. package/lib/repository/candidate/CandidateRepository.impl.js +0 -230
  91. package/lib/repository/recipe/RecipeRepository.impl.js +0 -498
  92. package/lib/service/candidate/CandidateAggregator.js +0 -52
  93. package/lib/service/candidate/CandidateFileWriter.js +0 -383
  94. package/lib/service/candidate/CandidateService.js +0 -1001
  95. package/lib/service/recipe/RecipeFileWriter.js +0 -514
  96. package/lib/service/recipe/RecipeService.js +0 -786
  97. package/lib/service/recipe/RecipeStatsTracker.js +0 -148
@@ -33,7 +33,7 @@
33
33
  * │ 17. generate_guard_rule AI 生成 Guard 规则 │
34
34
  * └─────────────────────────────────────────────────────┘
35
35
  * ┌─── 生命周期操作类 (7) ─────────────────────────────┐
36
- * │ 18. submit_candidate 提交候选 │
36
+ * │ 18. submit_knowledge 提交候选 │
37
37
  * │ 19. approve_candidate 批准候选 │
38
38
  * │ 20. reject_candidate 驳回候选 │
39
39
  * │ 21. publish_recipe 发布 Recipe │
@@ -853,19 +853,19 @@ const searchRecipes = {
853
853
  },
854
854
  },
855
855
  handler: async (params, ctx) => {
856
- const recipeService = ctx.container.get('recipeService');
856
+ const knowledgeService = ctx.container.get('knowledgeService');
857
857
  const { keyword, category, language, knowledgeType, limit = 10 } = params;
858
858
 
859
859
  if (keyword) {
860
- return recipeService.searchRecipes(keyword, { page: 1, pageSize: limit });
860
+ return knowledgeService.search(keyword, { page: 1, pageSize: limit });
861
861
  }
862
862
 
863
- const filters = {};
863
+ const filters = { lifecycle: 'active' };
864
864
  if (category) filters.category = category;
865
865
  if (language) filters.language = language;
866
866
  if (knowledgeType) filters.knowledgeType = knowledgeType;
867
867
 
868
- return recipeService.listRecipes(filters, { page: 1, pageSize: limit });
868
+ return knowledgeService.list(filters, { page: 1, pageSize: limit });
869
869
  },
870
870
  };
871
871
 
@@ -886,19 +886,20 @@ const searchCandidates = {
886
886
  },
887
887
  },
888
888
  handler: async (params, ctx) => {
889
- const candidateService = ctx.container.get('candidateService');
889
+ const knowledgeService = ctx.container.get('knowledgeService');
890
890
  const { keyword, status, language, category, limit = 10 } = params;
891
891
 
892
892
  if (keyword) {
893
- return candidateService.searchCandidates(keyword, { page: 1, pageSize: limit });
893
+ return knowledgeService.search(keyword, { page: 1, pageSize: limit });
894
894
  }
895
895
 
896
+ // V3: status 映射为 lifecycle
896
897
  const filters = {};
897
- if (status) filters.status = status;
898
+ if (status) filters.lifecycle = status;
898
899
  if (language) filters.language = language;
899
900
  if (category) filters.category = category;
900
901
 
901
- return candidateService.listCandidates(filters, { page: 1, pageSize: limit });
902
+ return knowledgeService.list(filters, { page: 1, pageSize: limit });
902
903
  },
903
904
  };
904
905
 
@@ -916,10 +917,13 @@ const getRecipeDetail = {
916
917
  required: ['recipeId'],
917
918
  },
918
919
  handler: async (params, ctx) => {
919
- const recipeRepo = ctx.container.get('recipeRepository');
920
- const recipe = await recipeRepo.findById(params.recipeId);
921
- if (!recipe) return { error: `Recipe '${params.recipeId}' not found` };
922
- return recipe;
920
+ const knowledgeService = ctx.container.get('knowledgeService');
921
+ try {
922
+ const entry = await knowledgeService.get(params.recipeId);
923
+ return typeof entry.toJSON === 'function' ? entry.toJSON() : entry;
924
+ } catch {
925
+ return { error: `Knowledge entry '${params.recipeId}' not found` };
926
+ }
923
927
  },
924
928
  };
925
929
 
@@ -931,13 +935,8 @@ const getProjectStats = {
931
935
  description: '获取项目知识库的整体统计:Recipe 数量/分类分布、候选项数量/状态分布、知识图谱节点/边数。',
932
936
  parameters: { type: 'object', properties: {} },
933
937
  handler: async (_params, ctx) => {
934
- const recipeService = ctx.container.get('recipeService');
935
- const candidateService = ctx.container.get('candidateService');
936
-
937
- const [recipeStats, candidateStats] = await Promise.all([
938
- recipeService.getRecipeStats(),
939
- candidateService.getCandidateStats(),
940
- ]);
938
+ const knowledgeService = ctx.container.get('knowledgeService');
939
+ const stats = await knowledgeService.getStats();
941
940
 
942
941
  // 尝试获取知识图谱统计
943
942
  let graphStats = null;
@@ -947,8 +946,7 @@ const getProjectStats = {
947
946
  } catch { /* KG not available */ }
948
947
 
949
948
  return {
950
- recipes: recipeStats,
951
- candidates: candidateStats,
949
+ knowledge: stats,
952
950
  knowledgeGraph: graphStats,
953
951
  };
954
952
  },
@@ -1000,8 +998,9 @@ const searchKnowledge = {
1000
998
  // 降级: RetrievalFunnel + 全量候选
1001
999
  try {
1002
1000
  const funnel = ctx.container.get('retrievalFunnel');
1003
- const recipeRepo = ctx.container.get('recipeRepository');
1004
- const allRecipes = await recipeRepo.findAll?.() || [];
1001
+ const knowledgeRepo = ctx.container.get('knowledgeRepository');
1002
+ const allResult = await knowledgeRepo.findWithPagination({}, { page: 1, pageSize: 500 });
1003
+ const allRecipes = allResult?.items || [];
1005
1004
 
1006
1005
  // 规范化为 funnel 输入格式
1007
1006
  const candidates = allRecipes.map(r => ({
@@ -1088,7 +1087,7 @@ const extractRecipes = {
1088
1087
  },
1089
1088
  handler: async (params, ctx) => {
1090
1089
  if (!ctx.aiProvider) return { error: 'AI provider not available' };
1091
- const { targetName, files } = params;
1090
+ const { targetName, files, comprehensive } = params;
1092
1091
 
1093
1092
  // 加载语言参考 Skill(如有),注入到 AI 提取 prompt
1094
1093
  let skillReference = null;
@@ -1124,6 +1123,7 @@ const extractRecipes = {
1124
1123
  const extractOpts = {};
1125
1124
  if (skillReference) extractOpts.skillReference = skillReference;
1126
1125
  if (astContext) extractOpts.astContext = astContext;
1126
+ if (comprehensive) extractOpts.comprehensive = true;
1127
1127
 
1128
1128
  // 首选:使用当前 aiProvider
1129
1129
  try {
@@ -1169,12 +1169,10 @@ const enrichCandidate = {
1169
1169
  },
1170
1170
  handler: async (params, ctx) => {
1171
1171
  if (!ctx.aiProvider) return { error: 'AI provider not available' };
1172
- const candidateService = ctx.container.get('candidateService');
1173
- return candidateService.enrichCandidates(
1174
- params.candidateIds,
1175
- ctx.aiProvider,
1176
- { userId: 'agent' },
1177
- );
1172
+ // V3: 使用 MCP handler enrichCandidates 的逻辑
1173
+ const { enrichCandidates: enrichFn } = await import('../../external/mcp/handlers/candidate.js');
1174
+ const result = await enrichFn(ctx, { candidateIds: params.candidateIds });
1175
+ return result?.data || result;
1178
1176
  },
1179
1177
  };
1180
1178
 
@@ -1194,20 +1192,14 @@ const refineBootstrapCandidates = {
1194
1192
  },
1195
1193
  handler: async (params, ctx) => {
1196
1194
  if (!ctx.aiProvider) return { error: 'AI provider not available' };
1197
- const candidateService = ctx.container.get('candidateService');
1198
-
1199
- // 接入 BootstrapTaskManager 双通道推送 refine:* 事件到前端
1200
- let onProgress = null;
1201
- try {
1202
- const taskManager = ctx.container.get('bootstrapTaskManager');
1203
- onProgress = (eventName, data) => taskManager.emitProgress(eventName, data);
1204
- } catch { /* optional: no realtime push */ }
1205
-
1206
- return candidateService.refineBootstrapCandidates(
1207
- ctx.aiProvider,
1208
- { candidateIds: params.candidateIds, userPrompt: params.userPrompt, dryRun: params.dryRun, onProgress },
1209
- { userId: 'agent' },
1210
- );
1195
+ // V3: 委托给 bootstrap handler 的 refine 逻辑
1196
+ const { bootstrapRefine } = await import('../../external/mcp/handlers/bootstrap.js');
1197
+ const result = await bootstrapRefine(ctx, {
1198
+ candidateIds: params.candidateIds,
1199
+ userPrompt: params.userPrompt,
1200
+ dryRun: params.dryRun,
1201
+ });
1202
+ return result?.data || result;
1211
1203
  },
1212
1204
  };
1213
1205
 
@@ -1231,18 +1223,18 @@ const checkDuplicate = {
1231
1223
  const projectRoot = params.projectRoot || ctx.projectRoot;
1232
1224
  const threshold = params.threshold ?? 0.5;
1233
1225
 
1234
- // 如果提供 candidateId,从数据库读取候选信息
1226
+ // 如果提供 candidateId,从数据库读取条目信息
1235
1227
  if (!cand && params.candidateId) {
1236
1228
  try {
1237
- const candidateRepo = ctx.container.get('candidateRepository');
1238
- const found = await candidateRepo.findById(params.candidateId);
1229
+ const knowledgeService = ctx.container.get('knowledgeService');
1230
+ const found = await knowledgeService.get(params.candidateId);
1239
1231
  if (found) {
1240
- const meta = found.metadata || {};
1232
+ const json = typeof found.toJSON === 'function' ? found.toJSON() : found;
1241
1233
  cand = {
1242
- title: meta.title || '',
1243
- summary: meta.summary_cn || meta.summary || '',
1244
- code: found.code || '',
1245
- usageGuide: meta.usageGuide_cn || meta.usageGuide || '',
1234
+ title: json.title || '',
1235
+ summary: json.summary_cn || json.description || '',
1236
+ code: json.content?.pattern || '',
1237
+ usageGuide: json.usage_guide_cn || '',
1246
1238
  };
1247
1239
  }
1248
1240
  } catch { /* ignore */ }
@@ -1442,8 +1434,9 @@ const getRecommendations = {
1442
1434
  },
1443
1435
  },
1444
1436
  handler: async (params, ctx) => {
1445
- const recipeService = ctx.container.get('recipeService');
1446
- return recipeService.getRecommendations(params.limit || 10);
1437
+ const knowledgeService = ctx.container.get('knowledgeService');
1438
+ // V3: 推荐 = 活跃条目按使用量排序
1439
+ return knowledgeService.list({ lifecycle: 'active' }, { page: 1, pageSize: params.limit || 10 });
1447
1440
  },
1448
1441
  };
1449
1442
 
@@ -1632,24 +1625,24 @@ const DIMENSION_DISPLAY_GROUP = {
1632
1625
  };
1633
1626
 
1634
1627
  // ────────────────────────────────────────────────────────────
1635
- // Bootstrap 维度类型校验 — submit_candidate / submit_with_check 共用
1628
+ // Bootstrap 维度类型校验 — submit_knowledge / submit_with_check 共用
1636
1629
  // 基于 dimensionMeta 类型标注系统,而非关键词模糊匹配
1637
1630
  // ────────────────────────────────────────────────────────────
1638
1631
 
1639
1632
  /**
1640
1633
  * 基于维度元数据 (dimensionMeta) 检查提交是否合法
1641
1634
  * @param {{ id: string, outputType: 'candidate'|'skill'|'dual', allowedKnowledgeTypes: string[] }} dimensionMeta
1642
- * @param {object} params - submit_candidate 的参数
1635
+ * @param {object} params - submit_knowledge 的参数
1643
1636
  * @param {object} [logger]
1644
1637
  * @returns {{ status: string, reason: string } | null} 不合法返回 rejected,合法返回 null
1645
1638
  */
1646
1639
  function _checkDimensionType(dimensionMeta, params, logger) {
1647
1640
  // 1. Skill-only 维度不允许提交 Candidate
1648
1641
  if (dimensionMeta.outputType === 'skill') {
1649
- logger?.info(`[submit_candidate] ✗ rejected — dimension "${dimensionMeta.id}" is skill-only, cannot submit candidates`);
1642
+ logger?.info(`[submit_knowledge] ✗ rejected — dimension "${dimensionMeta.id}" is skill-only, cannot submit candidates`);
1650
1643
  return {
1651
1644
  status: 'rejected',
1652
- reason: `当前维度 "${dimensionMeta.id}" 的输出类型为 skill-only,不允许调用 submit_candidate。请只在最终回复中提供 dimensionDigest JSON。`,
1645
+ reason: `当前维度 "${dimensionMeta.id}" 的输出类型为 skill-only,不允许调用 submit_knowledge。请只在最终回复中提供 dimensionDigest JSON。`,
1653
1646
  };
1654
1647
  }
1655
1648
 
@@ -1658,7 +1651,7 @@ function _checkDimensionType(dimensionMeta, params, logger) {
1658
1651
  if (allowed.length > 0 && params.knowledgeType) {
1659
1652
  if (!allowed.includes(params.knowledgeType)) {
1660
1653
  const corrected = allowed[0];
1661
- logger?.warn(`[submit_candidate] knowledgeType "${params.knowledgeType}" → "${corrected}" (auto-corrected for dimension "${dimensionMeta.id}")`);
1654
+ logger?.warn(`[submit_knowledge] knowledgeType "${params.knowledgeType}" → "${corrected}" (auto-corrected for dimension "${dimensionMeta.id}")`);
1662
1655
  params.knowledgeType = corrected;
1663
1656
  }
1664
1657
  }
@@ -1666,10 +1659,10 @@ function _checkDimensionType(dimensionMeta, params, logger) {
1666
1659
  return null;
1667
1660
  }
1668
1661
 
1669
- // 16. submit_candidate
1662
+ // 16. submit_knowledge
1670
1663
  // ────────────────────────────────────────────────────────────
1671
1664
  const submitCandidate = {
1672
- name: 'submit_candidate',
1665
+ name: 'submit_knowledge',
1673
1666
  description: '提交新的代码候选项到知识库审核队列。',
1674
1667
  parameters: {
1675
1668
  type: 'object',
@@ -1688,7 +1681,7 @@ const submitCandidate = {
1688
1681
  required: ['code', 'language', 'category', 'title'],
1689
1682
  },
1690
1683
  handler: async (params, ctx) => {
1691
- const candidateService = ctx.container.get('candidateService');
1684
+ const knowledgeService = ctx.container.get('knowledgeService');
1692
1685
 
1693
1686
  // ── Bootstrap 维度类型校验 (基于 dimensionMeta 类型标注) ──
1694
1687
  const dimMeta = ctx._dimensionMeta;
@@ -1706,8 +1699,7 @@ const submitCandidate = {
1706
1699
  params.knowledgeType = dimMeta.allowedKnowledgeTypes[0];
1707
1700
  }
1708
1701
 
1709
- // Bootstrap 模式: 将 category 覆盖为展示分组 ID,确保前端按合并后的分组展示
1710
- // AI 可能填 "UI"/"Core" 等功能分类,但前端通过 BOOTSTRAP_DIM_LABELS 按展示分组分组
1702
+ // Bootstrap 模式: 将 category 覆盖为展示分组 ID
1711
1703
  params.category = DIMENSION_DISPLAY_GROUP[dimMeta.id] || dimMeta.id;
1712
1704
 
1713
1705
  // ── CandidateGuardrail 质量验证 (Bootstrap 模式) ──
@@ -1717,7 +1709,7 @@ const submitCandidate = {
1717
1709
  );
1718
1710
  const guardResult = guardrail.validate(params);
1719
1711
  if (!guardResult.valid) {
1720
- ctx.logger?.info(`[submit_candidate] ✗ guardrail rejected: ${guardResult.error}`);
1712
+ ctx.logger?.info(`[submit_knowledge] ✗ guardrail rejected: ${guardResult.error}`);
1721
1713
  return {
1722
1714
  status: 'rejected',
1723
1715
  error: guardResult.error,
@@ -1726,11 +1718,10 @@ const submitCandidate = {
1726
1718
  }
1727
1719
  }
1728
1720
 
1729
- // 将所有顶层字段展开到 item LLM 可能把 title/summary/tags 等
1730
- // 放在顶层而非 metadata 中(production prompt 指引)
1721
+ // V3: 将顶层字段映射为 wire format
1731
1722
  const { code, language, category, source, reasoning, metadata, ...rest } = params;
1732
1723
 
1733
- // 防御性修复: AI 可能提交 reasoning.sources = [] 空数组,自动从 filePaths 补充
1724
+ // 防御性修复: AI 可能提交 reasoning.sources = [] 空数组
1734
1725
  const finalReasoning = reasoning || { whyStandard: 'Submitted via ChatAgent', sources: ['agent'], confidence: 0.7 };
1735
1726
  if (Array.isArray(finalReasoning.sources) && finalReasoning.sources.length === 0) {
1736
1727
  const fallbackSources = params.filePaths || rest.filePaths;
@@ -1741,18 +1732,31 @@ const submitCandidate = {
1741
1732
  }
1742
1733
  }
1743
1734
 
1744
- const item = {
1745
- code,
1746
- language,
1747
- category,
1748
- ...rest, // 顶层扩展字段 (title, summary, knowledgeType, tags 等)
1749
- ...metadata, // metadata 对象 (如有)
1750
- reasoning: finalReasoning,
1751
- };
1752
- // Bootstrap 模式额外注入 targetName — 前端优先按 meta.targetName 分组
1753
1735
  const displayGroup = dimMeta ? (DIMENSION_DISPLAY_GROUP[dimMeta.id] || dimMeta.id) : null;
1754
- const extraMeta = (dimMeta && ctx.source === 'system') ? { targetName: displayGroup } : {};
1755
- return candidateService.createFromToolParams(item, source || 'agent', extraMeta, { userId: 'agent' });
1736
+ const data = {
1737
+ title: rest.title || '',
1738
+ description: rest.summary || rest.description || '',
1739
+ language: language || '',
1740
+ category: category || 'general',
1741
+ knowledge_type: rest.knowledgeType || 'code-pattern',
1742
+ tags: rest.tags || [],
1743
+ summary_cn: rest.summary || '',
1744
+ source: source || 'agent',
1745
+ content: {
1746
+ pattern: code || '',
1747
+ rationale: rest.rationale || '',
1748
+ },
1749
+ reasoning: {
1750
+ why_standard: finalReasoning.whyStandard || finalReasoning.why_standard || 'Submitted via ChatAgent',
1751
+ sources: finalReasoning.sources || ['agent'],
1752
+ confidence: finalReasoning.confidence || 0.7,
1753
+ },
1754
+ ...(metadata || {}),
1755
+ };
1756
+ if (dimMeta && ctx.source === 'system' && displayGroup) {
1757
+ data.tags = [...new Set([...(data.tags || []), displayGroup])];
1758
+ }
1759
+ return knowledgeService.create(data, { userId: 'agent' });
1756
1760
  },
1757
1761
  };
1758
1762
 
@@ -1770,8 +1774,8 @@ const approveCandidate = {
1770
1774
  required: ['candidateId'],
1771
1775
  },
1772
1776
  handler: async (params, ctx) => {
1773
- const candidateService = ctx.container.get('candidateService');
1774
- return candidateService.approveCandidate(params.candidateId, { userId: 'agent' });
1777
+ const knowledgeService = ctx.container.get('knowledgeService');
1778
+ return knowledgeService.approve(params.candidateId, { userId: 'agent' });
1775
1779
  },
1776
1780
  };
1777
1781
 
@@ -1790,8 +1794,8 @@ const rejectCandidate = {
1790
1794
  required: ['candidateId', 'reason'],
1791
1795
  },
1792
1796
  handler: async (params, ctx) => {
1793
- const candidateService = ctx.container.get('candidateService');
1794
- return candidateService.rejectCandidate(params.candidateId, params.reason, { userId: 'agent' });
1797
+ const knowledgeService = ctx.container.get('knowledgeService');
1798
+ return knowledgeService.reject(params.candidateId, params.reason, { userId: 'agent' });
1795
1799
  },
1796
1800
  };
1797
1801
 
@@ -1809,8 +1813,8 @@ const publishRecipe = {
1809
1813
  required: ['recipeId'],
1810
1814
  },
1811
1815
  handler: async (params, ctx) => {
1812
- const recipeService = ctx.container.get('recipeService');
1813
- return recipeService.publishRecipe(params.recipeId, { userId: 'agent' });
1816
+ const knowledgeService = ctx.container.get('knowledgeService');
1817
+ return knowledgeService.publish(params.recipeId, { userId: 'agent' });
1814
1818
  },
1815
1819
  };
1816
1820
 
@@ -1829,8 +1833,8 @@ const deprecateRecipe = {
1829
1833
  required: ['recipeId', 'reason'],
1830
1834
  },
1831
1835
  handler: async (params, ctx) => {
1832
- const recipeService = ctx.container.get('recipeService');
1833
- return recipeService.deprecateRecipe(params.recipeId, params.reason, { userId: 'agent' });
1836
+ const knowledgeService = ctx.container.get('knowledgeService');
1837
+ return knowledgeService.deprecate(params.recipeId, params.reason, { userId: 'agent' });
1834
1838
  },
1835
1839
  };
1836
1840
 
@@ -1849,8 +1853,8 @@ const updateRecipe = {
1849
1853
  required: ['recipeId', 'updates'],
1850
1854
  },
1851
1855
  handler: async (params, ctx) => {
1852
- const recipeService = ctx.container.get('recipeService');
1853
- return recipeService.updateRecipe(params.recipeId, params.updates, { userId: 'agent' });
1856
+ const knowledgeService = ctx.container.get('knowledgeService');
1857
+ return knowledgeService.update(params.recipeId, params.updates, { userId: 'agent' });
1854
1858
  },
1855
1859
  };
1856
1860
 
@@ -1869,9 +1873,9 @@ const recordUsage = {
1869
1873
  required: ['recipeId'],
1870
1874
  },
1871
1875
  handler: async (params, ctx) => {
1872
- const recipeService = ctx.container.get('recipeService');
1876
+ const knowledgeService = ctx.container.get('knowledgeService');
1873
1877
  const type = params.type || 'adoption';
1874
- await recipeService.incrementUsage(params.recipeId, type);
1878
+ await knowledgeService.incrementUsage(params.recipeId, type);
1875
1879
  return { success: true, recipeId: params.recipeId, type };
1876
1880
  },
1877
1881
  };
@@ -1894,9 +1898,13 @@ const qualityScore = {
1894
1898
  let recipe = params.recipe;
1895
1899
 
1896
1900
  if (!recipe && params.recipeId) {
1897
- const recipeRepo = ctx.container.get('recipeRepository');
1898
- recipe = await recipeRepo.findById(params.recipeId);
1899
- if (!recipe) return { error: `Recipe '${params.recipeId}' not found` };
1901
+ const knowledgeService = ctx.container.get('knowledgeService');
1902
+ try {
1903
+ const entry = await knowledgeService.get(params.recipeId);
1904
+ recipe = typeof entry.toJSON === 'function' ? entry.toJSON() : entry;
1905
+ } catch {
1906
+ return { error: `Knowledge entry '${params.recipeId}' not found` };
1907
+ }
1900
1908
  }
1901
1909
  if (!recipe) return { error: 'Provide recipeId or recipe object' };
1902
1910
 
@@ -2210,13 +2218,8 @@ const knowledgeOverview = {
2210
2218
  const [statsResult, feedbackResult] = await Promise.all([
2211
2219
  (async () => {
2212
2220
  try {
2213
- const recipeService = ctx.container.get('recipeService');
2214
- const candidateService = ctx.container.get('candidateService');
2215
- const [rs, cs] = await Promise.all([
2216
- recipeService.getRecipeStats(),
2217
- candidateService.getCandidateStats(),
2218
- ]);
2219
- return { recipes: rs, candidates: cs };
2221
+ const knowledgeService = ctx.container.get('knowledgeService');
2222
+ return knowledgeService.getStats();
2220
2223
  } catch { return null; }
2221
2224
  })(),
2222
2225
  (async () => {
@@ -2229,8 +2232,7 @@ const knowledgeOverview = {
2229
2232
  ]);
2230
2233
 
2231
2234
  if (statsResult) {
2232
- result.recipes = statsResult.recipes;
2233
- result.candidates = statsResult.candidates;
2235
+ result.knowledge = statsResult;
2234
2236
  }
2235
2237
 
2236
2238
  // 知识图谱统计
@@ -2256,7 +2258,7 @@ const knowledgeOverview = {
2256
2258
  // ────────────────────────────────────────────────────────────
2257
2259
  const submitWithCheck = {
2258
2260
  name: 'submit_with_check',
2259
- description: '安全提交候选:先执行查重检测,无重复则自动提交。如果发现高度相似 Recipe 则阻止并返回相似列表。一次调用完成 check_duplicate + submit_candidate。',
2261
+ description: '安全提交候选:先执行查重检测,无重复则自动提交。如果发现高度相似 Recipe 则阻止并返回相似列表。一次调用完成 check_duplicate + submit_knowledge。',
2260
2262
  parameters: {
2261
2263
  type: 'object',
2262
2264
  properties: {
@@ -2273,7 +2275,7 @@ const submitWithCheck = {
2273
2275
  const { code, language, category, title, summary, threshold = 0.7 } = params;
2274
2276
  const projectRoot = ctx.projectRoot;
2275
2277
 
2276
- // ── Bootstrap 维度类型校验 (与 submit_candidate 共用逻辑) ──
2278
+ // ── Bootstrap 维度类型校验 (与 submit_knowledge 共用逻辑) ──
2277
2279
  const dimMeta = ctx._dimensionMeta;
2278
2280
  if (dimMeta && ctx.source === 'system') {
2279
2281
  const rejected = _checkDimensionType(dimMeta, params, ctx.logger);
@@ -2311,28 +2313,33 @@ const submitWithCheck = {
2311
2313
  };
2312
2314
  }
2313
2315
 
2314
- // Step 2: 提交
2316
+ // Step 2: 提交 (V3: 使用 knowledgeService.create)
2315
2317
  try {
2316
- const candidateService = ctx.container.get('candidateService');
2318
+ const knowledgeService = ctx.container.get('knowledgeService');
2317
2319
  const { code: _c, language: _l, category: _cat, title: _t, summary: _s, threshold: _th, source: paramSource, reasoning: userReasoning, metadata, ...rest } = params;
2318
- const item = {
2319
- code,
2320
- language,
2321
- category,
2322
- ...rest, // 顶层扩展字段 (title, summary, knowledgeType, tags 等)
2323
- ...metadata, // metadata 对象 (如有)
2324
- title: title || '',
2325
- summary: summary || '',
2326
- reasoning: userReasoning || { whyStandard: 'Submitted via submit_with_check', sources: ['agent'], confidence: 0.7 },
2320
+ const swcReasoning = userReasoning || { whyStandard: 'Submitted via submit_with_check', sources: ['agent'], confidence: 0.7 };
2321
+ const data = {
2322
+ title: title || '',
2323
+ description: summary || '',
2324
+ language: language || '',
2325
+ category: category || 'general',
2326
+ knowledge_type: rest.knowledgeType || 'code-pattern',
2327
+ tags: rest.tags || [],
2328
+ summary_cn: summary || '',
2329
+ source: paramSource || 'agent',
2330
+ content: { pattern: code || '' },
2331
+ reasoning: {
2332
+ why_standard: swcReasoning.whyStandard || swcReasoning.why_standard || 'Submitted via submit_with_check',
2333
+ sources: swcReasoning.sources || ['agent'],
2334
+ confidence: swcReasoning.confidence || 0.7,
2335
+ },
2336
+ ...(metadata || {}),
2327
2337
  };
2328
- // Bootstrap 模式注入展示分组 targetName
2329
- const swcDisplayGroup = dimMeta ? (DIMENSION_DISPLAY_GROUP[dimMeta.id] || dimMeta.id) : null;
2330
- const swcExtraMeta = (dimMeta && ctx.source === 'system') ? { targetName: swcDisplayGroup } : {};
2331
- const created = await candidateService.createFromToolParams(item, paramSource || 'agent', swcExtraMeta, { userId: 'agent' });
2338
+ const created = await knowledgeService.create(data, { userId: 'agent' });
2332
2339
 
2333
2340
  return {
2334
2341
  submitted: true,
2335
- candidate: created,
2342
+ entry: typeof created.toJSON === 'function' ? created.toJSON() : created,
2336
2343
  similar: similar.length > 0 ? similar : [],
2337
2344
  _meta: {
2338
2345
  confidence: 'high',
@@ -2457,7 +2464,7 @@ const reviewMyOutput = {
2457
2464
  },
2458
2465
  handler: async (params, context) => {
2459
2466
  const submitted = (context._sessionToolCalls || []).filter(
2460
- tc => tc.tool === 'submit_candidate' || tc.tool === 'submit_with_check',
2467
+ tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check',
2461
2468
  );
2462
2469
 
2463
2470
  if (submitted.length === 0) {