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,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* composite.js — 组合工具 + 元工具 (6)
|
|
3
|
+
*
|
|
4
|
+
* 34. analyze_code Guard + Recipe 搜索组合
|
|
5
|
+
* 35. knowledge_overview 知识库全貌一次获取
|
|
6
|
+
* 36. submit_with_check 查重 + 提交组合
|
|
7
|
+
* ── get_tool_details 元工具: 查询工具 Schema
|
|
8
|
+
* ── plan_task 元工具: 任务规划
|
|
9
|
+
* ── review_my_output 元工具: 自我质量审查
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { findSimilarRecipes } from '../../candidate/SimilarityService.js';
|
|
13
|
+
import { DIMENSION_DISPLAY_GROUP, checkDimensionType } from './_shared.js';
|
|
14
|
+
|
|
15
|
+
// ────────────────────────────────────────────────────────────
|
|
16
|
+
// 34. analyze_code — 组合工具 (Guard + Recipe 搜索)
|
|
17
|
+
// ────────────────────────────────────────────────────────────
|
|
18
|
+
export const analyzeCode = {
|
|
19
|
+
name: 'analyze_code',
|
|
20
|
+
description:
|
|
21
|
+
'综合分析一段代码:Guard 规范检查 + 相关 Recipe 搜索。一次调用完成完整分析,减少多轮工具调用。',
|
|
22
|
+
parameters: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
code: { type: 'string', description: '待分析的源码' },
|
|
26
|
+
language: { type: 'string', description: '编程语言 (swift/objc/javascript 等)' },
|
|
27
|
+
filePath: { type: 'string', description: '文件路径(可选,用于上下文)' },
|
|
28
|
+
},
|
|
29
|
+
required: ['code'],
|
|
30
|
+
},
|
|
31
|
+
handler: async (params, ctx) => {
|
|
32
|
+
const { code, language, filePath } = params;
|
|
33
|
+
const results = {};
|
|
34
|
+
|
|
35
|
+
// 并行执行 Guard 检查 + Recipe 搜索
|
|
36
|
+
const [guardResult, searchResult] = await Promise.all([
|
|
37
|
+
(async () => {
|
|
38
|
+
try {
|
|
39
|
+
const engine = ctx.container.get('guardCheckEngine');
|
|
40
|
+
const violations = engine.checkCode(code, language || 'unknown', { scope: 'file' });
|
|
41
|
+
return { violationCount: violations.length, violations };
|
|
42
|
+
} catch {
|
|
43
|
+
try {
|
|
44
|
+
const guardService = ctx.container.get('guardService');
|
|
45
|
+
const matches = await guardService.checkCode(code, { language });
|
|
46
|
+
return { violationCount: matches.length, violations: matches };
|
|
47
|
+
} catch {
|
|
48
|
+
return { violationCount: 0, violations: [] };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
})(),
|
|
52
|
+
(async () => {
|
|
53
|
+
try {
|
|
54
|
+
const searchEngine = ctx.container.get('searchEngine');
|
|
55
|
+
// 取代码首段作为搜索词
|
|
56
|
+
const query = code.substring(0, 200).replace(/\n/g, ' ');
|
|
57
|
+
const rawResults = await searchEngine.search(query, { limit: 5 });
|
|
58
|
+
return { results: rawResults || [], total: rawResults?.length || 0 };
|
|
59
|
+
} catch {
|
|
60
|
+
return { results: [], total: 0 };
|
|
61
|
+
}
|
|
62
|
+
})(),
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
results.guard = guardResult;
|
|
66
|
+
results.relatedRecipes = searchResult;
|
|
67
|
+
results.filePath = filePath || '(inline)';
|
|
68
|
+
|
|
69
|
+
const hasFindings = guardResult.violationCount > 0 || searchResult.total > 0;
|
|
70
|
+
results._meta = {
|
|
71
|
+
confidence: hasFindings ? 'high' : 'low',
|
|
72
|
+
hint: hasFindings
|
|
73
|
+
? `已完成 Guard 检查(${guardResult.violationCount} 个违规)+ Recipe 搜索(${searchResult.total} 条匹配)。`
|
|
74
|
+
: '未发现 Guard 违规,也未找到相关 Recipe。可能需要先冷启动知识库。',
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return results;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// ────────────────────────────────────────────────────────────
|
|
82
|
+
// 35. knowledge_overview — 组合工具 (一次获取全部类型的 Recipe 统计)
|
|
83
|
+
// ────────────────────────────────────────────────────────────
|
|
84
|
+
export const knowledgeOverview = {
|
|
85
|
+
name: 'knowledge_overview',
|
|
86
|
+
description:
|
|
87
|
+
'一次性获取知识库全貌:各类型 Recipe 分布 + 候选状态 + 知识图谱概况 + 质量概览。比分别调用 get_project_stats + search_recipes 更高效。',
|
|
88
|
+
parameters: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
includeTopRecipes: { type: 'boolean', description: '是否包含热门 Recipe 列表,默认 true' },
|
|
92
|
+
limit: { type: 'number', description: '每类返回数量,默认 5' },
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
handler: async (params, ctx) => {
|
|
96
|
+
const { includeTopRecipes = true, limit = 5 } = params;
|
|
97
|
+
const result = {};
|
|
98
|
+
|
|
99
|
+
// 并行获取统计 + 可选的热门列表
|
|
100
|
+
const [statsResult, feedbackResult] = await Promise.all([
|
|
101
|
+
(async () => {
|
|
102
|
+
try {
|
|
103
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
104
|
+
return knowledgeService.getStats();
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
})(),
|
|
109
|
+
(async () => {
|
|
110
|
+
if (!includeTopRecipes) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const feedbackCollector = ctx.container.get('feedbackCollector');
|
|
115
|
+
return feedbackCollector.getTopRecipes(limit);
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
})(),
|
|
120
|
+
]);
|
|
121
|
+
|
|
122
|
+
if (statsResult) {
|
|
123
|
+
result.knowledge = statsResult;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 知识图谱统计
|
|
127
|
+
try {
|
|
128
|
+
const kgService = ctx.container.get('knowledgeGraphService');
|
|
129
|
+
result.knowledgeGraph = kgService.getStats();
|
|
130
|
+
} catch {
|
|
131
|
+
/* KG not available */
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (feedbackResult) {
|
|
135
|
+
result.topRecipes = feedbackResult;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const recipeCount = result.recipes?.total || result.recipes?.count || 0;
|
|
139
|
+
result._meta = {
|
|
140
|
+
confidence: recipeCount > 0 ? 'high' : 'none',
|
|
141
|
+
hint: recipeCount === 0 ? '知识库为空,建议先执行冷启动(bootstrap_knowledge)。' : null,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return result;
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// ────────────────────────────────────────────────────────────
|
|
149
|
+
// 36. submit_with_check — 组合工具 (查重 + 提交)
|
|
150
|
+
// ────────────────────────────────────────────────────────────
|
|
151
|
+
export const submitWithCheck = {
|
|
152
|
+
name: 'submit_with_check',
|
|
153
|
+
description:
|
|
154
|
+
'安全提交候选:先执行查重检测,无重复则自动提交。一次调用完成 check_duplicate + submit_knowledge。',
|
|
155
|
+
parameters: {
|
|
156
|
+
type: 'object',
|
|
157
|
+
properties: {
|
|
158
|
+
content: {
|
|
159
|
+
type: 'object',
|
|
160
|
+
description: '{ markdown: "项目特写 Markdown", pattern: "核心代码 3-8 行,必须语法完整(括号配对、不能以 } 开头或 { 结尾)" }',
|
|
161
|
+
},
|
|
162
|
+
title: { type: 'string', description: '候选标题' },
|
|
163
|
+
description: { type: 'string', description: '中文简述 ≤80 字' },
|
|
164
|
+
trigger: { type: 'string', description: '@前缀 kebab-case 唯一标识符' },
|
|
165
|
+
kind: { type: 'string', enum: ['rule', 'pattern', 'fact'] },
|
|
166
|
+
topicHint: {
|
|
167
|
+
type: 'string',
|
|
168
|
+
enum: ['networking', 'ui', 'data', 'architecture', 'conventions'],
|
|
169
|
+
},
|
|
170
|
+
whenClause: { type: 'string', description: '触发场景英文' },
|
|
171
|
+
doClause: { type: 'string', description: '正向指令英文' },
|
|
172
|
+
dontClause: { type: 'string', description: '反向约束英文' },
|
|
173
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
174
|
+
reasoning: { type: 'object', description: '{ whyStandard, sources, confidence }' },
|
|
175
|
+
threshold: { type: 'number', description: '相似度阈值,默认 0.7' },
|
|
176
|
+
},
|
|
177
|
+
required: ['content', 'title', 'trigger', 'kind', 'doClause'],
|
|
178
|
+
},
|
|
179
|
+
handler: async (params, ctx) => {
|
|
180
|
+
const projectRoot = ctx.projectRoot;
|
|
181
|
+
|
|
182
|
+
// ── Bootstrap 维度类型校验 ──
|
|
183
|
+
const dimMeta = ctx._dimensionMeta;
|
|
184
|
+
if (dimMeta && ctx.source === 'system') {
|
|
185
|
+
const rejected = checkDimensionType(dimMeta, params, ctx.logger);
|
|
186
|
+
if (rejected) {
|
|
187
|
+
return rejected;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!params.tags) {
|
|
191
|
+
params.tags = [];
|
|
192
|
+
}
|
|
193
|
+
if (!params.tags.includes(dimMeta.id)) {
|
|
194
|
+
params.tags.push(dimMeta.id);
|
|
195
|
+
}
|
|
196
|
+
if (!params.tags.includes('bootstrap')) {
|
|
197
|
+
params.tags.push('bootstrap');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Step 1: 查重
|
|
202
|
+
const threshold = params.threshold || 0.7;
|
|
203
|
+
const contentObj2 =
|
|
204
|
+
params.content && typeof params.content === 'object'
|
|
205
|
+
? params.content
|
|
206
|
+
: { markdown: '', pattern: '' };
|
|
207
|
+
const cand = {
|
|
208
|
+
title: params.title || '',
|
|
209
|
+
summary: params.description || '',
|
|
210
|
+
code: contentObj2.markdown || contentObj2.pattern || '',
|
|
211
|
+
};
|
|
212
|
+
const similar = findSimilarRecipes(projectRoot, cand, { threshold: 0.5, topK: 5 });
|
|
213
|
+
const hasDuplicate = similar.some((s) => s.similarity >= threshold);
|
|
214
|
+
|
|
215
|
+
if (hasDuplicate) {
|
|
216
|
+
return {
|
|
217
|
+
submitted: false,
|
|
218
|
+
reason: 'duplicate_blocked',
|
|
219
|
+
similar,
|
|
220
|
+
highestSimilarity: similar[0]?.similarity || 0,
|
|
221
|
+
_meta: {
|
|
222
|
+
confidence: 'high',
|
|
223
|
+
hint: `发现高度相似 Recipe(相似度 ${(similar[0]?.similarity * 100).toFixed(0)}%),已阻止提交。`,
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Step 2: 提交 — 委托给 submit_knowledge handler
|
|
229
|
+
try {
|
|
230
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
231
|
+
const reasoning = params.reasoning || {
|
|
232
|
+
whyStandard: '',
|
|
233
|
+
sources: ['agent'],
|
|
234
|
+
confidence: 0.7,
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const systemFields = {
|
|
238
|
+
language: ctx._projectLanguage || '',
|
|
239
|
+
category: dimMeta ? DIMENSION_DISPLAY_GROUP[dimMeta.id] || dimMeta.id : 'general',
|
|
240
|
+
knowledgeType: dimMeta?.allowedKnowledgeTypes?.[0] || 'code-pattern',
|
|
241
|
+
source: ctx.source === 'system' ? 'bootstrap' : 'agent',
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const data = {
|
|
245
|
+
...systemFields,
|
|
246
|
+
title: params.title || '',
|
|
247
|
+
description: params.description || '',
|
|
248
|
+
tags: params.tags || [],
|
|
249
|
+
trigger: params.trigger || '',
|
|
250
|
+
kind: params.kind || 'pattern',
|
|
251
|
+
topicHint: params.topicHint || '',
|
|
252
|
+
whenClause: params.whenClause || '',
|
|
253
|
+
doClause: params.doClause || '',
|
|
254
|
+
dontClause: params.dontClause || '',
|
|
255
|
+
coreCode: contentObj2.pattern || '',
|
|
256
|
+
content: contentObj2,
|
|
257
|
+
reasoning,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const created = await knowledgeService.create(data, { userId: 'agent' });
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
submitted: true,
|
|
264
|
+
entry: typeof created.toJSON === 'function' ? created.toJSON() : created,
|
|
265
|
+
similar: similar.length > 0 ? similar : [],
|
|
266
|
+
_meta: {
|
|
267
|
+
confidence: 'high',
|
|
268
|
+
hint:
|
|
269
|
+
similar.length > 0
|
|
270
|
+
? `已提交,但有 ${similar.length} 个低相似度匹配。`
|
|
271
|
+
: '已提交,无重复风险。',
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
} catch (err) {
|
|
275
|
+
return { submitted: false, reason: 'submit_error', error: err.message };
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// ═══════════════════════════════════════════════════════
|
|
281
|
+
// 元工具: Lazy Tool Schema 按需加载
|
|
282
|
+
// ═══════════════════════════════════════════════════════
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* get_tool_details — 查询工具的完整参数 schema
|
|
286
|
+
*
|
|
287
|
+
* 与 Cline .clinerules 按需加载类似:
|
|
288
|
+
* System Prompt 只包含工具名+一行描述,LLM 需要调用某个工具前
|
|
289
|
+
* 先通过此元工具获取完整参数定义,避免 prompt 过长浪费 token。
|
|
290
|
+
*/
|
|
291
|
+
export const getToolDetails = {
|
|
292
|
+
name: 'get_tool_details',
|
|
293
|
+
description: '查询指定工具的完整参数 Schema。在调用不熟悉的工具之前,先用此工具获取参数详情。',
|
|
294
|
+
parameters: {
|
|
295
|
+
type: 'object',
|
|
296
|
+
properties: {
|
|
297
|
+
toolName: {
|
|
298
|
+
type: 'string',
|
|
299
|
+
description: '要查询的工具名称(snake_case)',
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
required: ['toolName'],
|
|
303
|
+
},
|
|
304
|
+
handler: async ({ toolName }, context) => {
|
|
305
|
+
const registry = context.container?.get('toolRegistry');
|
|
306
|
+
if (!registry) {
|
|
307
|
+
return { error: 'ToolRegistry not available' };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const schemas = registry.getToolSchemas();
|
|
311
|
+
const found = schemas.find((t) => t.name === toolName);
|
|
312
|
+
if (!found) {
|
|
313
|
+
const allNames = schemas.map((t) => t.name);
|
|
314
|
+
return {
|
|
315
|
+
error: `Tool "${toolName}" not found`,
|
|
316
|
+
availableTools: allNames,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
name: found.name,
|
|
322
|
+
description: found.description,
|
|
323
|
+
parameters: found.parameters,
|
|
324
|
+
};
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// ─── 元工具: 任务规划 ───────────────────────────────────
|
|
329
|
+
export const planTask = {
|
|
330
|
+
name: 'plan_task',
|
|
331
|
+
description:
|
|
332
|
+
'分析当前任务并制定结构化执行计划。在开始复杂任务前调用此工具可提高执行效率和决策质量。输出将记录到日志供审计,但不会改变实际执行流程。',
|
|
333
|
+
parameters: {
|
|
334
|
+
type: 'object',
|
|
335
|
+
properties: {
|
|
336
|
+
steps: {
|
|
337
|
+
type: 'array',
|
|
338
|
+
description: '执行步骤列表',
|
|
339
|
+
items: {
|
|
340
|
+
type: 'object',
|
|
341
|
+
properties: {
|
|
342
|
+
id: { type: 'number', description: '步骤序号' },
|
|
343
|
+
action: { type: 'string', description: '具体动作描述' },
|
|
344
|
+
tool: { type: 'string', description: '计划使用的工具名' },
|
|
345
|
+
depends_on: { type: 'array', items: { type: 'number' }, description: '依赖的步骤 ID' },
|
|
346
|
+
},
|
|
347
|
+
required: ['id', 'action'],
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
strategy: {
|
|
351
|
+
type: 'string',
|
|
352
|
+
description: '执行策略说明(如: 先搜索补充示例再批量提交)',
|
|
353
|
+
},
|
|
354
|
+
estimated_iterations: {
|
|
355
|
+
type: 'number',
|
|
356
|
+
description: '预估需要的迭代轮数',
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
required: ['steps', 'strategy'],
|
|
360
|
+
},
|
|
361
|
+
handler: async (params, context) => {
|
|
362
|
+
const plan = {
|
|
363
|
+
steps: params.steps || [],
|
|
364
|
+
strategy: params.strategy || '',
|
|
365
|
+
estimatedIterations: params.estimated_iterations || params.steps?.length || 1,
|
|
366
|
+
};
|
|
367
|
+
context.logger?.info('[plan_task] execution plan', plan);
|
|
368
|
+
return {
|
|
369
|
+
status: 'plan_recorded',
|
|
370
|
+
stepCount: plan.steps.length,
|
|
371
|
+
strategy: plan.strategy,
|
|
372
|
+
message: `执行计划已记录 (${plan.steps.length} 步, 预估 ${plan.estimatedIterations} 轮迭代)。开始按计划执行。`,
|
|
373
|
+
};
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
// ─── 元工具: 自我质量审查 ───────────────────────────────
|
|
378
|
+
export const reviewMyOutput = {
|
|
379
|
+
name: 'review_my_output',
|
|
380
|
+
description:
|
|
381
|
+
'回查本次会话中已提交的候选,检查质量红线是否满足。包括: 项目特写风格、description 泛化措辞、代码示例来源标注、Cursor 交付字段完整性等。返回通过/问题列表。建议在提交完所有候选后调用一次进行自检。',
|
|
382
|
+
parameters: {
|
|
383
|
+
type: 'object',
|
|
384
|
+
properties: {
|
|
385
|
+
check_rules: {
|
|
386
|
+
type: 'array',
|
|
387
|
+
description: '要检查的质量规则(可选, 默认检查全部)',
|
|
388
|
+
items: { type: 'string' },
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
handler: async (params, context) => {
|
|
393
|
+
const submitted = (context._sessionToolCalls || []).filter(
|
|
394
|
+
(tc) => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check'
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
if (submitted.length === 0) {
|
|
398
|
+
return { status: 'no_candidates', message: '本次会话尚未提交任何候选。' };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const issues = [];
|
|
402
|
+
const checked = [];
|
|
403
|
+
|
|
404
|
+
for (const tc of submitted) {
|
|
405
|
+
const p = tc.params || {};
|
|
406
|
+
const contentObj3 = p.content && typeof p.content === 'object' ? p.content : {};
|
|
407
|
+
const markdown = contentObj3.markdown || '';
|
|
408
|
+
const title = p.title || '';
|
|
409
|
+
const description = p.description || '';
|
|
410
|
+
const candidateIssues = [];
|
|
411
|
+
|
|
412
|
+
// 检查 1: 项目特写后缀
|
|
413
|
+
if (!title.includes('— 项目特写') && !markdown.includes('— 项目特写')) {
|
|
414
|
+
candidateIssues.push('缺少 "— 项目特写" 后缀');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// 检查 2: 项目特写融合叙事质量 — 必须同时包含代码和描述性文字
|
|
418
|
+
const hasCodeBlock = /```[\s\S]*?```/.test(markdown);
|
|
419
|
+
if (!hasCodeBlock) {
|
|
420
|
+
candidateIssues.push('特写缺少代码示例,应包含基本用法代码');
|
|
421
|
+
}
|
|
422
|
+
// 去掉代码块后,剩余描述性文字应足够
|
|
423
|
+
const proseLength = markdown
|
|
424
|
+
.replace(/```[\s\S]*?```/g, '')
|
|
425
|
+
.replace(/[#>\-*`\n]/g, '')
|
|
426
|
+
.trim().length;
|
|
427
|
+
if (proseLength < 50) {
|
|
428
|
+
candidateIssues.push('特写缺少项目特点描述,应融合基本用法和项目特点');
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// 检查 3: description 泛化措辞
|
|
432
|
+
if (/本模块|该文件|这个类|该项目/.test(description)) {
|
|
433
|
+
candidateIssues.push('description 使用了泛化措辞,应引用具体类名和数字');
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// 检查 4: description 过短
|
|
437
|
+
if (description.length < 15) {
|
|
438
|
+
candidateIssues.push(
|
|
439
|
+
`description 过短 (${description.length} 字), 应≥15字并包含具体类名和数字`
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// 检查 5: content.markdown 过短(可能是空壳)
|
|
444
|
+
if (markdown.length < 200) {
|
|
445
|
+
candidateIssues.push(`content.markdown 文档过短 (${markdown.length} 字), 可能缺少实质内容`);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// 检查 6: 代码示例来源
|
|
449
|
+
const hasSourceAnnotation = /\([^)]*\.\w+[^)]*:\d+\)|\([^)]*\.\w+[^)]*\)/.test(markdown);
|
|
450
|
+
if (hasCodeBlock && !hasSourceAnnotation) {
|
|
451
|
+
candidateIssues.push('代码示例可能缺少来源文件标注 (建议标注 "来源: FileName.m:行号")');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// 检查 7: Cursor 交付字段
|
|
455
|
+
if (!p.trigger) {
|
|
456
|
+
candidateIssues.push('缺少 trigger 字段');
|
|
457
|
+
}
|
|
458
|
+
if (!p.doClause) {
|
|
459
|
+
candidateIssues.push('缺少 doClause 字段');
|
|
460
|
+
}
|
|
461
|
+
if (!p.kind) {
|
|
462
|
+
candidateIssues.push('缺少 kind 字段');
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (candidateIssues.length > 0) {
|
|
466
|
+
issues.push({ title, issues: candidateIssues });
|
|
467
|
+
}
|
|
468
|
+
checked.push({
|
|
469
|
+
title,
|
|
470
|
+
passed: candidateIssues.length === 0,
|
|
471
|
+
issueCount: candidateIssues.length,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (issues.length === 0) {
|
|
476
|
+
return {
|
|
477
|
+
status: 'all_passed',
|
|
478
|
+
checkedCount: submitted.length,
|
|
479
|
+
message: `✅ ${submitted.length} 条候选全部通过质量检查。`,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const issueLines = issues.flatMap(({ title, issues: iss }) =>
|
|
484
|
+
iss.map((i) => `• "${title}": ${i}`)
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
status: 'issues_found',
|
|
489
|
+
checkedCount: submitted.length,
|
|
490
|
+
passedCount: submitted.length - issues.length,
|
|
491
|
+
failedCount: issues.length,
|
|
492
|
+
details: checked,
|
|
493
|
+
message: `⚠️ ${issues.length}/${submitted.length} 条候选存在质量问题:\n${issueLines.join('\n')}\n\n请修正后重新提交。`,
|
|
494
|
+
};
|
|
495
|
+
},
|
|
496
|
+
};
|