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.
- package/README.md +4 -4
- package/bin/cli.js +5 -33
- package/config/constitution.yaml +9 -2
- package/dashboard/dist/assets/{icons-CH-H9x0E.js → icons-BkT3XrKf.js} +105 -100
- package/dashboard/dist/assets/index-BsB7DzW4.css +1 -0
- package/dashboard/dist/assets/index-DdmQMrJJ.js +155 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/cli/AiScanService.js +13 -11
- package/lib/cli/KnowledgeSyncService.js +343 -0
- package/lib/cli/SetupService.js +8 -26
- package/lib/core/gateway/GatewayActionRegistry.js +48 -58
- package/lib/domain/index.js +16 -11
- package/lib/domain/knowledge/KnowledgeEntry.js +351 -0
- package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
- package/lib/domain/knowledge/Lifecycle.js +109 -0
- package/lib/domain/knowledge/index.js +27 -0
- package/lib/domain/knowledge/values/Constraints.js +125 -0
- package/lib/domain/knowledge/values/Content.js +86 -0
- package/lib/domain/knowledge/values/Quality.js +93 -0
- package/lib/domain/knowledge/values/Reasoning.js +69 -0
- package/lib/domain/knowledge/values/Relations.js +168 -0
- package/lib/domain/knowledge/values/Stats.js +87 -0
- package/lib/domain/knowledge/values/index.js +9 -0
- package/lib/external/ai/AiProvider.js +48 -0
- package/lib/external/mcp/McpServer.js +7 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +2 -2
- package/lib/external/mcp/handlers/bootstrap.js +116 -11
- package/lib/external/mcp/handlers/browse.js +77 -73
- package/lib/external/mcp/handlers/candidate.js +29 -276
- package/lib/external/mcp/handlers/guard.js +2 -0
- package/lib/external/mcp/handlers/knowledge.js +205 -0
- package/lib/external/mcp/handlers/structure.js +25 -23
- package/lib/external/mcp/handlers/system.js +10 -12
- package/lib/external/mcp/tools.js +125 -138
- package/lib/http/HttpServer.js +4 -8
- package/lib/http/routes/extract.js +48 -4
- package/lib/http/routes/knowledge.js +246 -0
- package/lib/http/routes/search.js +12 -17
- package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
- package/lib/infrastructure/external/XcodeAutomation.js +187 -103
- package/lib/injection/ServiceContainer.js +49 -60
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +373 -0
- package/lib/service/automation/DirectiveDetector.js +2 -3
- package/lib/service/automation/FileWatcher.js +67 -28
- package/lib/service/automation/XcodeIntegration.js +931 -156
- package/lib/service/automation/handlers/AlinkHandler.js +6 -4
- package/lib/service/automation/handlers/CreateHandler.js +53 -18
- package/lib/service/automation/handlers/GuardHandler.js +183 -20
- package/lib/service/automation/handlers/SearchHandler.js +35 -17
- package/lib/service/chat/CandidateGuardrail.js +1 -1
- package/lib/service/chat/ChatAgent.js +46 -45
- package/lib/service/chat/ContextWindow.js +5 -5
- package/lib/service/chat/ProducerAgent.js +7 -7
- package/lib/service/chat/tools.js +130 -123
- package/lib/service/guard/GuardCheckEngine.js +114 -10
- package/lib/service/guard/GuardService.js +59 -48
- package/lib/service/knowledge/ConfidenceRouter.js +159 -0
- package/lib/service/knowledge/KnowledgeFileWriter.js +602 -0
- package/lib/service/knowledge/KnowledgeService.js +725 -0
- package/lib/service/search/SearchEngine.js +92 -19
- package/lib/service/skills/SignalCollector.js +12 -7
- package/lib/service/skills/SkillAdvisor.js +13 -11
- package/lib/service/snippet/SnippetFactory.js +5 -5
- package/package.json +1 -1
- package/scripts/install-cursor-skill.js +0 -6
- package/scripts/migrate-md-to-knowledge.mjs +364 -0
- package/skills/autosnippet-analysis/SKILL.md +15 -7
- package/skills/autosnippet-candidates/SKILL.md +6 -6
- package/skills/autosnippet-coldstart/SKILL.md +7 -3
- package/skills/autosnippet-concepts/SKILL.md +7 -6
- package/skills/autosnippet-create/SKILL.md +13 -13
- package/skills/autosnippet-intent/SKILL.md +3 -2
- package/skills/autosnippet-lifecycle/SKILL.md +5 -5
- package/skills/autosnippet-recipes/SKILL.md +16 -4
- package/templates/constitution.yaml +1 -1
- package/templates/copilot-instructions.md +6 -6
- package/templates/recipes-setup/README.md +3 -3
- package/dashboard/dist/assets/index-CqJRvYRL.js +0 -197
- package/dashboard/dist/assets/index-DICm9PNa.css +0 -1
- package/lib/cli/CandidateSyncService.js +0 -261
- package/lib/cli/SyncService.js +0 -356
- package/lib/domain/candidate/Candidate.js +0 -196
- package/lib/domain/candidate/CandidateRepository.js +0 -107
- package/lib/domain/candidate/Reasoning.js +0 -52
- package/lib/domain/recipe/Recipe.js +0 -421
- package/lib/domain/recipe/RecipeRepository.js +0 -54
- package/lib/domain/types/CandidateStatus.js +0 -52
- package/lib/http/routes/candidates.js +0 -559
- package/lib/http/routes/recipes.js +0 -397
- package/lib/repository/candidate/CandidateRepository.impl.js +0 -230
- package/lib/repository/recipe/RecipeRepository.impl.js +0 -498
- package/lib/service/candidate/CandidateAggregator.js +0 -52
- package/lib/service/candidate/CandidateFileWriter.js +0 -383
- package/lib/service/candidate/CandidateService.js +0 -1001
- package/lib/service/recipe/RecipeFileWriter.js +0 -514
- package/lib/service/recipe/RecipeService.js +0 -786
- 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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
898
|
+
if (status) filters.lifecycle = status;
|
|
898
899
|
if (language) filters.language = language;
|
|
899
900
|
if (category) filters.category = category;
|
|
900
901
|
|
|
901
|
-
return
|
|
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
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
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
|
|
935
|
-
const
|
|
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
|
-
|
|
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
|
|
1004
|
-
const
|
|
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
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
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
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
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
|
|
1238
|
-
const found = await
|
|
1229
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
1230
|
+
const found = await knowledgeService.get(params.candidateId);
|
|
1239
1231
|
if (found) {
|
|
1240
|
-
const
|
|
1232
|
+
const json = typeof found.toJSON === 'function' ? found.toJSON() : found;
|
|
1241
1233
|
cand = {
|
|
1242
|
-
title:
|
|
1243
|
-
summary:
|
|
1244
|
-
code:
|
|
1245
|
-
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
|
|
1446
|
-
|
|
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 维度类型校验 —
|
|
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 -
|
|
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(`[
|
|
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,不允许调用
|
|
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(`[
|
|
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.
|
|
1662
|
+
// 16. submit_knowledge
|
|
1670
1663
|
// ────────────────────────────────────────────────────────────
|
|
1671
1664
|
const submitCandidate = {
|
|
1672
|
-
name: '
|
|
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
|
|
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(`[
|
|
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
|
-
//
|
|
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 = []
|
|
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
|
|
1755
|
-
|
|
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
|
|
1774
|
-
return
|
|
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
|
|
1794
|
-
return
|
|
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
|
|
1813
|
-
return
|
|
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
|
|
1833
|
-
return
|
|
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
|
|
1853
|
-
return
|
|
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
|
|
1876
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
1873
1877
|
const type = params.type || 'adoption';
|
|
1874
|
-
await
|
|
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
|
|
1898
|
-
|
|
1899
|
-
|
|
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
|
|
2214
|
-
|
|
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.
|
|
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 +
|
|
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 维度类型校验 (与
|
|
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
|
|
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
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 === '
|
|
2467
|
+
tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check',
|
|
2461
2468
|
);
|
|
2462
2469
|
|
|
2463
2470
|
if (submitted.length === 0) {
|