autosnippet 3.0.11 → 3.0.13
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/bin/cli.js +64 -1
- package/config/default.json +9 -0
- package/dashboard/dist/assets/{index-I2ySoCmF.js → index-Bnm26ulL.js} +47 -47
- package/dashboard/dist/index.html +1 -1
- package/lib/cli/SetupService.js +92 -5
- package/lib/cli/UpgradeService.js +14 -5
- package/lib/core/discovery/GenericDiscoverer.js +4 -28
- package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +246 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +80 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +275 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +600 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +125 -342
- package/lib/external/mcp/handlers/bootstrap/refine.js +362 -0
- package/lib/external/mcp/handlers/bootstrap.js +6 -590
- package/lib/external/mcp/handlers/browse.js +119 -9
- package/lib/external/mcp/handlers/guard.js +25 -6
- package/lib/external/mcp/handlers/search.js +56 -24
- package/lib/http/routes/guardRules.js +9 -17
- package/lib/injection/ServiceContainer.js +12 -3
- package/lib/platform/ios/xcode/XcodeImportResolver.js +434 -0
- package/lib/platform/ios/xcode/XcodeIntegration.js +40 -659
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +220 -0
- package/lib/service/chat/ChatAgent.js +39 -418
- package/lib/service/chat/ChatAgentPrompts.js +149 -0
- package/lib/service/chat/ChatAgentTasks.js +297 -0
- package/lib/service/chat/tools/_shared.js +61 -0
- package/lib/service/chat/tools/ai-analysis.js +284 -0
- package/lib/service/chat/tools/ast-graph.js +681 -0
- package/lib/service/chat/tools/composite.js +496 -0
- package/lib/service/chat/tools/guard.js +265 -0
- package/lib/service/chat/tools/index.js +250 -0
- package/lib/service/chat/tools/infrastructure.js +222 -0
- package/lib/service/chat/tools/knowledge-graph.js +234 -0
- package/lib/service/chat/tools/lifecycle.js +469 -0
- package/lib/service/chat/tools/project-access.js +923 -0
- package/lib/service/chat/tools/query.js +264 -0
- package/lib/service/chat/tools.js +14 -3994
- package/lib/service/cursor/AgentInstructionsGenerator.js +395 -0
- package/lib/service/cursor/CursorDeliveryPipeline.js +70 -11
- package/lib/service/cursor/FileProtection.js +116 -0
- package/lib/service/cursor/KnowledgeCompressor.js +61 -11
- package/lib/service/cursor/SkillsSyncer.js +5 -3
- package/lib/service/cursor/TopicClassifier.js +19 -3
- package/lib/service/guard/ExclusionManager.js +26 -2
- package/lib/service/guard/GuardCheckEngine.js +38 -370
- package/lib/service/guard/GuardCodeChecks.js +362 -0
- package/lib/service/guard/GuardCrossFileChecks.js +307 -0
- package/lib/service/guard/GuardPatternUtils.js +180 -0
- package/lib/service/guard/GuardService.js +80 -38
- package/lib/service/module/ModuleService.js +1 -0
- package/lib/service/search/SearchEngine.js +10 -2
- package/lib/service/wiki/WikiGenerator.js +226 -1532
- package/lib/service/wiki/WikiRenderers.js +1878 -0
- package/lib/service/wiki/WikiUtils.js +907 -0
- package/lib/shared/LanguageService.js +299 -0
- package/package.json +1 -1
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lifecycle.js — 生命周期操作类工具 (11)
|
|
3
|
+
*
|
|
4
|
+
* 16. submit_knowledge 提交候选项
|
|
5
|
+
* 16b. save_document 保存开发文档
|
|
6
|
+
* 17. approve_candidate 批准候选
|
|
7
|
+
* 18. reject_candidate 驳回候选
|
|
8
|
+
* 19. publish_recipe 发布 Recipe
|
|
9
|
+
* 20. deprecate_recipe 弃用 Recipe
|
|
10
|
+
* 21. update_recipe 更新 Recipe
|
|
11
|
+
* 22. record_usage 记录使用
|
|
12
|
+
* 23. quality_score 质量评分
|
|
13
|
+
* 24. validate_candidate 候选校验
|
|
14
|
+
* 25. get_feedback_stats 反馈统计
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { CandidateGuardrail } from '../CandidateGuardrail.js';
|
|
18
|
+
import { DIMENSION_DISPLAY_GROUP, checkDimensionType } from './_shared.js';
|
|
19
|
+
|
|
20
|
+
// ────────────────────────────────────────────────────────────
|
|
21
|
+
// 16. submit_knowledge
|
|
22
|
+
// ────────────────────────────────────────────────────────────
|
|
23
|
+
export const submitCandidate = {
|
|
24
|
+
name: 'submit_knowledge',
|
|
25
|
+
description: '提交新的代码候选项到知识库审核队列。',
|
|
26
|
+
parameters: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
// ── 内容(V3 content 子对象) ──
|
|
30
|
+
content: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
description: '{ markdown: "项目特写 Markdown(≥200字)", pattern: "核心代码 3-8 行", rationale: "设计原理" }',
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
// ── 基本信息 ──
|
|
36
|
+
title: { type: 'string', description: '候选标题(中文 ≤20 字)' },
|
|
37
|
+
description: { type: 'string', description: '中文简述 ≤80 字,引用真实类名' },
|
|
38
|
+
tags: { type: 'array', items: { type: 'string' }, description: '标签列表' },
|
|
39
|
+
|
|
40
|
+
// ── Cursor 交付(AI 必填)──
|
|
41
|
+
trigger: { type: 'string', description: '@前缀 kebab-case 唯一标识符' },
|
|
42
|
+
kind: { type: 'string', enum: ['rule', 'pattern', 'fact'], description: '知识类型' },
|
|
43
|
+
topicHint: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
enum: ['networking', 'ui', 'data', 'architecture', 'conventions'],
|
|
46
|
+
description: '主题分类',
|
|
47
|
+
},
|
|
48
|
+
whenClause: { type: 'string', description: '触发场景英文' },
|
|
49
|
+
doClause: { type: 'string', description: '正向指令英文祈使句 ≤60 tokens' },
|
|
50
|
+
dontClause: { type: 'string', description: "反向约束英文(不以 Don't 开头)" },
|
|
51
|
+
|
|
52
|
+
// ── 推理(必填) ──
|
|
53
|
+
reasoning: {
|
|
54
|
+
type: 'object',
|
|
55
|
+
description: '{ whyStandard: string, sources: string[], confidence: number } — 全部必填',
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
// ── V3 扩展字段 ──
|
|
59
|
+
scope: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
enum: ['universal', 'project-specific', 'team-convention'],
|
|
62
|
+
description: '适用范围',
|
|
63
|
+
},
|
|
64
|
+
complexity: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
enum: ['basic', 'intermediate', 'advanced'],
|
|
67
|
+
description: '复杂度',
|
|
68
|
+
},
|
|
69
|
+
headers: {
|
|
70
|
+
type: 'array',
|
|
71
|
+
items: { type: 'string' },
|
|
72
|
+
description: '依赖的 import/require 行(无 import 时传 [])',
|
|
73
|
+
},
|
|
74
|
+
knowledgeType: { type: 'string', description: '知识维度:code-pattern / architecture / best-practice 等' },
|
|
75
|
+
usageGuide: { type: 'string', description: '使用指南 Markdown(### 章节格式)' },
|
|
76
|
+
sourceFile: { type: 'string', description: '来源文件相对路径' },
|
|
77
|
+
},
|
|
78
|
+
required: ['content', 'title', 'trigger', 'kind', 'doClause', 'description', 'headers', 'reasoning'],
|
|
79
|
+
},
|
|
80
|
+
handler: async (params, ctx) => {
|
|
81
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
82
|
+
|
|
83
|
+
// ── Bootstrap 维度类型校验 ──
|
|
84
|
+
const dimMeta = ctx._dimensionMeta;
|
|
85
|
+
if (dimMeta && ctx.source === 'system') {
|
|
86
|
+
const rejected = checkDimensionType(dimMeta, params, ctx.logger);
|
|
87
|
+
if (rejected) {
|
|
88
|
+
return rejected;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 自动注入维度标签
|
|
92
|
+
if (!params.tags) {
|
|
93
|
+
params.tags = [];
|
|
94
|
+
}
|
|
95
|
+
if (!params.tags.includes(dimMeta.id)) {
|
|
96
|
+
params.tags.push(dimMeta.id);
|
|
97
|
+
}
|
|
98
|
+
if (!params.tags.includes('bootstrap')) {
|
|
99
|
+
params.tags.push('bootstrap');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Bootstrap 模式: 将 category 覆盖为展示分组 ID
|
|
103
|
+
params._category = DIMENSION_DISPLAY_GROUP[dimMeta.id] || dimMeta.id;
|
|
104
|
+
|
|
105
|
+
// ── CandidateGuardrail 质量验证 ──
|
|
106
|
+
const guardrail = new CandidateGuardrail(ctx._submittedTitles || new Set(), dimMeta, ctx._submittedPatterns || new Set());
|
|
107
|
+
const guardResult = guardrail.validate(params);
|
|
108
|
+
if (!guardResult.valid) {
|
|
109
|
+
ctx.logger?.info(`[submit_knowledge] ✗ guardrail rejected: ${guardResult.error}`);
|
|
110
|
+
return {
|
|
111
|
+
status: 'rejected',
|
|
112
|
+
error: guardResult.error,
|
|
113
|
+
hint: '请根据错误信息调整内容后重新提交。',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── 系统自动设置 ──
|
|
119
|
+
const systemFields = {
|
|
120
|
+
language: ctx._projectLanguage || '',
|
|
121
|
+
category: dimMeta ? DIMENSION_DISPLAY_GROUP[dimMeta.id] || dimMeta.id : 'general',
|
|
122
|
+
knowledgeType: dimMeta?.allowedKnowledgeTypes?.[0] || 'code-pattern',
|
|
123
|
+
source: ctx.source === 'system' ? 'bootstrap' : 'agent',
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// ── 直传 → KnowledgeEntry ──
|
|
127
|
+
const reasoning = params.reasoning || { whyStandard: '', sources: ['agent'], confidence: 0.7 };
|
|
128
|
+
if (Array.isArray(reasoning.sources) && reasoning.sources.length === 0) {
|
|
129
|
+
reasoning.sources = ['agent'];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// V3 content 直透
|
|
133
|
+
const contentObj =
|
|
134
|
+
params.content && typeof params.content === 'object'
|
|
135
|
+
? params.content
|
|
136
|
+
: { markdown: '', pattern: '' };
|
|
137
|
+
|
|
138
|
+
const data = {
|
|
139
|
+
...systemFields,
|
|
140
|
+
title: params.title || '',
|
|
141
|
+
description: params.description || '',
|
|
142
|
+
tags: params.tags || [],
|
|
143
|
+
trigger: params.trigger || '',
|
|
144
|
+
kind: params.kind || 'pattern',
|
|
145
|
+
topicHint: params.topicHint || '',
|
|
146
|
+
whenClause: params.whenClause || '',
|
|
147
|
+
doClause: params.doClause || '',
|
|
148
|
+
dontClause: params.dontClause || '',
|
|
149
|
+
coreCode: contentObj.pattern || '',
|
|
150
|
+
content: contentObj,
|
|
151
|
+
reasoning,
|
|
152
|
+
// V3 扩展字段直透
|
|
153
|
+
scope: params.scope || '',
|
|
154
|
+
complexity: params.complexity || '',
|
|
155
|
+
headers: params.headers || [],
|
|
156
|
+
// sourceFile: 优先取 params,Bootstrap 回退从 reasoning.sources 推断
|
|
157
|
+
sourceFile:
|
|
158
|
+
params.sourceFile ||
|
|
159
|
+
(Array.isArray(reasoning.sources) &&
|
|
160
|
+
reasoning.sources.length > 0 &&
|
|
161
|
+
reasoning.sources[0] !== 'agent'
|
|
162
|
+
? reasoning.sources[0]
|
|
163
|
+
: ''),
|
|
164
|
+
// 7.3.9 agentNotes/aiInsight 注入
|
|
165
|
+
agentNotes: dimMeta
|
|
166
|
+
? { dimensionId: dimMeta.id, outputType: dimMeta.outputType || 'candidate' }
|
|
167
|
+
: null,
|
|
168
|
+
aiInsight: reasoning.whyStandard || params.description || null,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
if (dimMeta && ctx.source === 'system') {
|
|
172
|
+
const displayGroup = DIMENSION_DISPLAY_GROUP[dimMeta.id] || dimMeta.id;
|
|
173
|
+
data.tags = [...new Set([...(data.tags || []), displayGroup])];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const saved = await knowledgeService.create(data, { userId: 'agent' });
|
|
177
|
+
|
|
178
|
+
// ── QualityScorer 自动评分 ──
|
|
179
|
+
try {
|
|
180
|
+
await knowledgeService.updateQuality(saved.id, { userId: 'agent' });
|
|
181
|
+
} catch {
|
|
182
|
+
/* best effort — 不阻塞创建流程 */
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return saved;
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// ────────────────────────────────────────────────────────────
|
|
190
|
+
// 16b. save_document — 保存开发文档到知识库
|
|
191
|
+
// ────────────────────────────────────────────────────────────
|
|
192
|
+
export const saveDocument = {
|
|
193
|
+
name: 'save_document',
|
|
194
|
+
description:
|
|
195
|
+
'保存开发文档到知识库(架构设计、排查报告、决策记录、调研笔记等)。仅需 title + markdown,无需 Cursor Delivery 字段。文档自动发布,可通过 autosnippet_search 检索。',
|
|
196
|
+
parameters: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
properties: {
|
|
199
|
+
title: { type: 'string', description: '文档标题' },
|
|
200
|
+
markdown: { type: 'string', description: '文档 Markdown 全文' },
|
|
201
|
+
description: { type: 'string', description: '一句话摘要(可选)' },
|
|
202
|
+
tags: {
|
|
203
|
+
type: 'array',
|
|
204
|
+
items: { type: 'string' },
|
|
205
|
+
description: '标签: adr, debug-report, design-doc, research, performance 等',
|
|
206
|
+
},
|
|
207
|
+
scope: {
|
|
208
|
+
type: 'string',
|
|
209
|
+
enum: ['universal', 'project-specific'],
|
|
210
|
+
description: '适用范围(默认 project-specific)',
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
required: ['title', 'markdown'],
|
|
214
|
+
},
|
|
215
|
+
handler: async (params, ctx) => {
|
|
216
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
217
|
+
|
|
218
|
+
const data = {
|
|
219
|
+
title: params.title.trim(),
|
|
220
|
+
description: params.description || '',
|
|
221
|
+
knowledgeType: 'dev-document',
|
|
222
|
+
kind: 'fact',
|
|
223
|
+
source: 'agent',
|
|
224
|
+
scope: params.scope || 'project-specific',
|
|
225
|
+
tags: params.tags || [],
|
|
226
|
+
content: {
|
|
227
|
+
markdown: params.markdown,
|
|
228
|
+
pattern: '',
|
|
229
|
+
},
|
|
230
|
+
trigger: '',
|
|
231
|
+
doClause: '',
|
|
232
|
+
dontClause: '',
|
|
233
|
+
whenClause: '',
|
|
234
|
+
topicHint: '',
|
|
235
|
+
coreCode: '',
|
|
236
|
+
reasoning: {
|
|
237
|
+
whyStandard: 'Agent development document',
|
|
238
|
+
sources: ['agent'],
|
|
239
|
+
confidence: 0.8,
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const saved = await knowledgeService.create(data, { userId: 'agent' });
|
|
244
|
+
|
|
245
|
+
// 自动发布(文档不需要人工审核)
|
|
246
|
+
try {
|
|
247
|
+
await knowledgeService.publish(saved.id, { userId: 'agent' });
|
|
248
|
+
} catch {
|
|
249
|
+
/* best effort */
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
id: saved.id,
|
|
254
|
+
title: saved.title,
|
|
255
|
+
lifecycle: 'active',
|
|
256
|
+
knowledgeType: 'dev-document',
|
|
257
|
+
message: `文档「${saved.title}」已保存到知识库`,
|
|
258
|
+
};
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// ────────────────────────────────────────────────────────────
|
|
263
|
+
// 17. approve_candidate
|
|
264
|
+
// ────────────────────────────────────────────────────────────
|
|
265
|
+
export const approveCandidate = {
|
|
266
|
+
name: 'approve_candidate',
|
|
267
|
+
description: '批准候选项(PENDING → APPROVED)。',
|
|
268
|
+
parameters: {
|
|
269
|
+
type: 'object',
|
|
270
|
+
properties: {
|
|
271
|
+
candidateId: { type: 'string', description: '候选 ID' },
|
|
272
|
+
},
|
|
273
|
+
required: ['candidateId'],
|
|
274
|
+
},
|
|
275
|
+
handler: async (params, ctx) => {
|
|
276
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
277
|
+
return knowledgeService.approve(params.candidateId, { userId: 'agent' });
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// ────────────────────────────────────────────────────────────
|
|
282
|
+
// 18. reject_candidate
|
|
283
|
+
// ────────────────────────────────────────────────────────────
|
|
284
|
+
export const rejectCandidate = {
|
|
285
|
+
name: 'reject_candidate',
|
|
286
|
+
description: '驳回候选项并填写驳回理由。',
|
|
287
|
+
parameters: {
|
|
288
|
+
type: 'object',
|
|
289
|
+
properties: {
|
|
290
|
+
candidateId: { type: 'string', description: '候选 ID' },
|
|
291
|
+
reason: { type: 'string', description: '驳回理由' },
|
|
292
|
+
},
|
|
293
|
+
required: ['candidateId', 'reason'],
|
|
294
|
+
},
|
|
295
|
+
handler: async (params, ctx) => {
|
|
296
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
297
|
+
return knowledgeService.reject(params.candidateId, params.reason, { userId: 'agent' });
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// ────────────────────────────────────────────────────────────
|
|
302
|
+
// 19. publish_recipe
|
|
303
|
+
// ────────────────────────────────────────────────────────────
|
|
304
|
+
export const publishRecipe = {
|
|
305
|
+
name: 'publish_recipe',
|
|
306
|
+
description: '发布 Recipe(DRAFT → ACTIVE)。',
|
|
307
|
+
parameters: {
|
|
308
|
+
type: 'object',
|
|
309
|
+
properties: {
|
|
310
|
+
recipeId: { type: 'string', description: 'Recipe ID' },
|
|
311
|
+
},
|
|
312
|
+
required: ['recipeId'],
|
|
313
|
+
},
|
|
314
|
+
handler: async (params, ctx) => {
|
|
315
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
316
|
+
return knowledgeService.publish(params.recipeId, { userId: 'agent' });
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// ────────────────────────────────────────────────────────────
|
|
321
|
+
// 20. deprecate_recipe
|
|
322
|
+
// ────────────────────────────────────────────────────────────
|
|
323
|
+
export const deprecateRecipe = {
|
|
324
|
+
name: 'deprecate_recipe',
|
|
325
|
+
description: '弃用 Recipe 并填写弃用原因。',
|
|
326
|
+
parameters: {
|
|
327
|
+
type: 'object',
|
|
328
|
+
properties: {
|
|
329
|
+
recipeId: { type: 'string', description: 'Recipe ID' },
|
|
330
|
+
reason: { type: 'string', description: '弃用原因' },
|
|
331
|
+
},
|
|
332
|
+
required: ['recipeId', 'reason'],
|
|
333
|
+
},
|
|
334
|
+
handler: async (params, ctx) => {
|
|
335
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
336
|
+
return knowledgeService.deprecate(params.recipeId, params.reason, { userId: 'agent' });
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
// ────────────────────────────────────────────────────────────
|
|
341
|
+
// 21. update_recipe
|
|
342
|
+
// ────────────────────────────────────────────────────────────
|
|
343
|
+
export const updateRecipe = {
|
|
344
|
+
name: 'update_recipe',
|
|
345
|
+
description: '更新 Recipe 的指定字段(title/description/content/category/tags 等)。',
|
|
346
|
+
parameters: {
|
|
347
|
+
type: 'object',
|
|
348
|
+
properties: {
|
|
349
|
+
recipeId: { type: 'string', description: 'Recipe ID' },
|
|
350
|
+
updates: { type: 'object', description: '要更新的字段和值' },
|
|
351
|
+
},
|
|
352
|
+
required: ['recipeId', 'updates'],
|
|
353
|
+
},
|
|
354
|
+
handler: async (params, ctx) => {
|
|
355
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
356
|
+
return knowledgeService.update(params.recipeId, params.updates, { userId: 'agent' });
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// ────────────────────────────────────────────────────────────
|
|
361
|
+
// 22. record_usage
|
|
362
|
+
// ────────────────────────────────────────────────────────────
|
|
363
|
+
export const recordUsage = {
|
|
364
|
+
name: 'record_usage',
|
|
365
|
+
description: '记录 Recipe 的使用(adoption 被采纳 / application 被应用)。',
|
|
366
|
+
parameters: {
|
|
367
|
+
type: 'object',
|
|
368
|
+
properties: {
|
|
369
|
+
recipeId: { type: 'string', description: 'Recipe ID' },
|
|
370
|
+
type: { type: 'string', description: 'adoption 或 application,默认 adoption' },
|
|
371
|
+
},
|
|
372
|
+
required: ['recipeId'],
|
|
373
|
+
},
|
|
374
|
+
handler: async (params, ctx) => {
|
|
375
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
376
|
+
const type = params.type || 'adoption';
|
|
377
|
+
await knowledgeService.incrementUsage(params.recipeId, type);
|
|
378
|
+
return { success: true, recipeId: params.recipeId, type };
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
// ────────────────────────────────────────────────────────────
|
|
383
|
+
// 23. quality_score
|
|
384
|
+
// ────────────────────────────────────────────────────────────
|
|
385
|
+
export const qualityScore = {
|
|
386
|
+
name: 'quality_score',
|
|
387
|
+
description:
|
|
388
|
+
'Recipe 质量评分 — 5 维度综合评估(完整性/格式/代码质量/元数据/互动),返回分数和等级(A-F)。',
|
|
389
|
+
parameters: {
|
|
390
|
+
type: 'object',
|
|
391
|
+
properties: {
|
|
392
|
+
recipeId: { type: 'string', description: 'Recipe ID(从数据库读取后评分)' },
|
|
393
|
+
recipe: {
|
|
394
|
+
type: 'object',
|
|
395
|
+
description: '或直接提供 Recipe 对象 { title, trigger, code, language, ... }',
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
handler: async (params, ctx) => {
|
|
400
|
+
const qualityScorer = ctx.container.get('qualityScorer');
|
|
401
|
+
let recipe = params.recipe;
|
|
402
|
+
|
|
403
|
+
if (!recipe && params.recipeId) {
|
|
404
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
405
|
+
try {
|
|
406
|
+
const entry = await knowledgeService.get(params.recipeId);
|
|
407
|
+
recipe = typeof entry.toJSON === 'function' ? entry.toJSON() : entry;
|
|
408
|
+
} catch {
|
|
409
|
+
return { error: `Knowledge entry '${params.recipeId}' not found` };
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (!recipe) {
|
|
413
|
+
return { error: 'Provide recipeId or recipe object' };
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return qualityScorer.score(recipe);
|
|
417
|
+
},
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// ────────────────────────────────────────────────────────────
|
|
421
|
+
// 24. validate_candidate
|
|
422
|
+
// ────────────────────────────────────────────────────────────
|
|
423
|
+
export const validateCandidate = {
|
|
424
|
+
name: 'validate_candidate',
|
|
425
|
+
description:
|
|
426
|
+
'候选校验 — 检查候选是否满足提交要求(必填字段/格式/质量),返回 errors 和 warnings。',
|
|
427
|
+
parameters: {
|
|
428
|
+
type: 'object',
|
|
429
|
+
properties: {
|
|
430
|
+
candidate: {
|
|
431
|
+
type: 'object',
|
|
432
|
+
description: '候选对象 { title, trigger, category, language, code, reasoning, ... }',
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
required: ['candidate'],
|
|
436
|
+
},
|
|
437
|
+
handler: async (params, ctx) => {
|
|
438
|
+
const validator = ctx.container.get('recipeCandidateValidator');
|
|
439
|
+
return validator.validate(params.candidate);
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
// ────────────────────────────────────────────────────────────
|
|
444
|
+
// 25. get_feedback_stats
|
|
445
|
+
// ────────────────────────────────────────────────────────────
|
|
446
|
+
export const getFeedbackStats = {
|
|
447
|
+
name: 'get_feedback_stats',
|
|
448
|
+
description: '获取用户反馈统计 — 全局交互事件统计 + 热门 Recipe + 指定 Recipe 的详细反馈。',
|
|
449
|
+
parameters: {
|
|
450
|
+
type: 'object',
|
|
451
|
+
properties: {
|
|
452
|
+
recipeId: { type: 'string', description: '查询指定 Recipe 的反馈(可选)' },
|
|
453
|
+
topN: { type: 'number', description: '热门 Recipe 数量,默认 10' },
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
handler: async (params, ctx) => {
|
|
457
|
+
const feedbackCollector = ctx.container.get('feedbackCollector');
|
|
458
|
+
const result = {};
|
|
459
|
+
|
|
460
|
+
result.global = feedbackCollector.getGlobalStats();
|
|
461
|
+
result.topRecipes = feedbackCollector.getTopRecipes(params.topN || 10);
|
|
462
|
+
|
|
463
|
+
if (params.recipeId) {
|
|
464
|
+
result.recipeStats = feedbackCollector.getRecipeStats(params.recipeId);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return result;
|
|
468
|
+
},
|
|
469
|
+
};
|