autosnippet 2.9.0 → 2.11.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 +12 -12
- package/bin/cli.js +53 -40
- package/config/constitution.yaml +9 -2
- package/dashboard/dist/assets/{icons-CH-H9x0E.js → icons-D4IWpDIk.js} +105 -100
- package/dashboard/dist/assets/index-CWBNcF9z.css +1 -0
- package/dashboard/dist/assets/index-DHtzhbuG.js +120 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/cli/AiScanService.js +35 -36
- package/lib/cli/KnowledgeSyncService.js +345 -0
- package/lib/cli/SetupService.js +8 -26
- package/lib/cli/UpgradeService.js +28 -0
- package/lib/core/gateway/GatewayActionRegistry.js +48 -58
- package/lib/domain/index.js +16 -11
- package/lib/domain/knowledge/KnowledgeEntry.js +289 -0
- package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
- package/lib/domain/knowledge/Lifecycle.js +99 -0
- package/lib/domain/knowledge/index.js +27 -0
- package/lib/domain/knowledge/values/Constraints.js +128 -0
- package/lib/domain/knowledge/values/Content.js +69 -0
- package/lib/domain/knowledge/values/Quality.js +81 -0
- package/lib/domain/knowledge/values/Reasoning.js +70 -0
- package/lib/domain/knowledge/values/Relations.js +142 -0
- package/lib/domain/knowledge/values/Stats.js +72 -0
- package/lib/domain/knowledge/values/index.js +9 -0
- package/lib/external/ai/AiProvider.js +85 -11
- package/lib/external/mcp/McpServer.js +7 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +18 -2
- package/lib/external/mcp/handlers/bootstrap.js +116 -11
- package/lib/external/mcp/handlers/browse.js +76 -73
- package/lib/external/mcp/handlers/candidate.js +26 -275
- package/lib/external/mcp/handlers/guard.js +2 -0
- package/lib/external/mcp/handlers/knowledge.js +267 -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 +134 -140
- package/lib/http/HttpServer.js +14 -8
- package/lib/http/routes/ai.js +4 -3
- 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/database/migrations/017_camelcase_knowledge_entries.js +107 -0
- package/lib/infrastructure/external/XcodeAutomation.js +187 -103
- package/lib/injection/ServiceContainer.js +69 -60
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +338 -0
- package/lib/service/automation/DirectiveDetector.js +2 -3
- package/lib/service/automation/FileWatcher.js +59 -28
- package/lib/service/automation/XcodeIntegration.js +931 -156
- package/lib/service/automation/handlers/AlinkHandler.js +5 -4
- package/lib/service/automation/handlers/CreateHandler.js +53 -19
- package/lib/service/automation/handlers/DraftHandler.js +1 -1
- package/lib/service/automation/handlers/GuardHandler.js +183 -20
- package/lib/service/automation/handlers/SearchHandler.js +25 -22
- package/lib/service/candidate/SimilarityService.js +2 -2
- package/lib/service/chat/AnalystAgent.js +9 -0
- package/lib/service/chat/CandidateGuardrail.js +22 -11
- package/lib/service/chat/ChatAgent.js +132 -54
- package/lib/service/chat/ContextWindow.js +5 -5
- package/lib/service/chat/HandoffProtocol.js +1 -0
- package/lib/service/chat/ProducerAgent.js +40 -13
- package/lib/service/chat/ReasoningLayer.js +854 -0
- package/lib/service/chat/ReasoningTrace.js +329 -0
- package/lib/service/chat/tools.js +308 -205
- package/lib/service/cursor/CursorDeliveryPipeline.js +279 -0
- package/lib/service/cursor/KnowledgeCompressor.js +87 -0
- package/lib/service/cursor/RulesGenerator.js +168 -0
- package/lib/service/cursor/SkillsSyncer.js +268 -0
- package/lib/service/cursor/TokenBudget.js +58 -0
- package/lib/service/cursor/TopicClassifier.js +141 -0
- package/lib/service/guard/GuardCheckEngine.js +99 -10
- package/lib/service/guard/GuardService.js +57 -46
- package/lib/service/knowledge/ConfidenceRouter.js +159 -0
- package/lib/service/knowledge/KnowledgeFileWriter.js +595 -0
- package/lib/service/knowledge/KnowledgeService.js +802 -0
- package/lib/service/recipe/RecipeParser.js +3 -12
- package/lib/service/search/SearchEngine.js +67 -22
- package/lib/service/skills/SignalCollector.js +14 -9
- package/lib/service/skills/SkillAdvisor.js +13 -11
- package/lib/service/snippet/SnippetFactory.js +5 -5
- package/lib/service/spm/SpmService.js +15 -48
- package/lib/shared/RecipeReadinessChecker.js +6 -11
- 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 +8 -8
- package/skills/autosnippet-coldstart/SKILL.md +8 -4
- 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 +18 -6
- 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
|
@@ -262,40 +262,41 @@ export async function graphImpact(ctx, args) {
|
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
/**
|
|
265
|
-
* 降级:从
|
|
265
|
+
* 降级:从 knowledge_entries.relations 提取关系(不依赖 knowledge_edges 表)
|
|
266
266
|
*/
|
|
267
267
|
async function _fallbackRelationsFromRecipe(ctx, nodeId, relation, direction) {
|
|
268
|
-
const recipeService = ctx.container.get('recipeService');
|
|
269
268
|
try {
|
|
270
|
-
const
|
|
271
|
-
|
|
269
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
270
|
+
const entry = await knowledgeService.get(nodeId);
|
|
271
|
+
if (!entry) return { outgoing: [], incoming: [] };
|
|
272
272
|
|
|
273
|
+
const relJson = typeof entry.relations?.toJSON === 'function' ? entry.relations.toJSON() : (entry.relations || {});
|
|
273
274
|
const outgoing = [];
|
|
274
275
|
if (direction === 'both' || direction === 'out') {
|
|
275
|
-
for (const [relType, targets] of Object.entries(
|
|
276
|
+
for (const [relType, targets] of Object.entries(relJson)) {
|
|
276
277
|
if (relation && relType !== relation) continue;
|
|
277
278
|
for (const t of (Array.isArray(targets) ? targets : [])) {
|
|
278
|
-
outgoing.push({ fromId: nodeId, fromType: '
|
|
279
|
+
outgoing.push({ fromId: nodeId, fromType: 'knowledge', toId: t.target || t.id || t, toType: 'knowledge', relation: relType });
|
|
279
280
|
}
|
|
280
281
|
}
|
|
281
282
|
}
|
|
282
283
|
|
|
283
|
-
//
|
|
284
|
+
// 反向查找:其他条目中 relations 包含当前 nodeId
|
|
284
285
|
const incoming = [];
|
|
285
286
|
if (direction === 'both' || direction === 'in') {
|
|
286
|
-
const
|
|
287
|
-
const reverseRows =
|
|
288
|
-
`SELECT id,
|
|
287
|
+
const knowledgeRepo = ctx.container.get('knowledgeRepository');
|
|
288
|
+
const reverseRows = knowledgeRepo.db.prepare(
|
|
289
|
+
`SELECT id, relations FROM knowledge_entries WHERE relations LIKE ? AND id != ?`
|
|
289
290
|
).all(`%${nodeId}%`, nodeId);
|
|
290
291
|
for (const row of reverseRows) {
|
|
291
292
|
try {
|
|
292
|
-
const rels = JSON.parse(row.
|
|
293
|
+
const rels = JSON.parse(row.relations || '{}');
|
|
293
294
|
for (const [relType, targets] of Object.entries(rels)) {
|
|
294
295
|
if (relation && relType !== relation) continue;
|
|
295
296
|
for (const t of (Array.isArray(targets) ? targets : [])) {
|
|
296
297
|
const targetId = t.target || t.id || t;
|
|
297
298
|
if (targetId === nodeId) {
|
|
298
|
-
incoming.push({ fromId: row.id, fromType: '
|
|
299
|
+
incoming.push({ fromId: row.id, fromType: 'knowledge', toId: nodeId, toType: 'knowledge', relation: relType });
|
|
299
300
|
}
|
|
300
301
|
}
|
|
301
302
|
}
|
|
@@ -308,23 +309,23 @@ async function _fallbackRelationsFromRecipe(ctx, nodeId, relation, direction) {
|
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
/**
|
|
311
|
-
* 降级:从
|
|
312
|
+
* 降级:从 knowledge_entries.relations 反查受影响的条目
|
|
312
313
|
*/
|
|
313
314
|
async function _fallbackImpactFromRecipe(ctx, nodeId) {
|
|
314
315
|
try {
|
|
315
|
-
const
|
|
316
|
-
const rows =
|
|
317
|
-
`SELECT id, title,
|
|
316
|
+
const knowledgeRepo = ctx.container.get('knowledgeRepository');
|
|
317
|
+
const rows = knowledgeRepo.db.prepare(
|
|
318
|
+
`SELECT id, title, relations FROM knowledge_entries WHERE relations LIKE ? AND id != ?`
|
|
318
319
|
).all(`%${nodeId}%`, nodeId);
|
|
319
320
|
|
|
320
321
|
const impacted = [];
|
|
321
322
|
for (const row of rows) {
|
|
322
323
|
try {
|
|
323
|
-
const rels = JSON.parse(row.
|
|
324
|
+
const rels = JSON.parse(row.relations || '{}');
|
|
324
325
|
for (const [relType, targets] of Object.entries(rels)) {
|
|
325
326
|
for (const t of (Array.isArray(targets) ? targets : [])) {
|
|
326
327
|
if ((t.target || t.id || t) === nodeId) {
|
|
327
|
-
impacted.push({ id: row.id, title: row.title, type: '
|
|
328
|
+
impacted.push({ id: row.id, title: row.title, type: 'knowledge', relation: relType, depth: 1 });
|
|
328
329
|
}
|
|
329
330
|
}
|
|
330
331
|
}
|
|
@@ -364,17 +365,18 @@ export async function graphPath(ctx, args) {
|
|
|
364
365
|
*/
|
|
365
366
|
async function _fallbackPathFromRecipe(ctx, fromId, toId) {
|
|
366
367
|
try {
|
|
367
|
-
const
|
|
368
|
-
const
|
|
369
|
-
if (!
|
|
368
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
369
|
+
const entry = await knowledgeService.get(fromId);
|
|
370
|
+
if (!entry) return { found: false, path: [], depth: -1 };
|
|
370
371
|
|
|
371
|
-
|
|
372
|
+
const relJson = typeof entry.relations?.toJSON === 'function' ? entry.relations.toJSON() : (entry.relations || {});
|
|
373
|
+
for (const [relType, targets] of Object.entries(relJson)) {
|
|
372
374
|
for (const t of (Array.isArray(targets) ? targets : [])) {
|
|
373
375
|
const targetId = t.target || t.id || t;
|
|
374
376
|
if (targetId === toId) {
|
|
375
377
|
return {
|
|
376
378
|
found: true,
|
|
377
|
-
path: [{ from: { id: fromId, type: '
|
|
379
|
+
path: [{ from: { id: fromId, type: 'knowledge' }, to: { id: toId, type: 'knowledge' }, relation: relType }],
|
|
378
380
|
depth: 1,
|
|
379
381
|
};
|
|
380
382
|
}
|
|
@@ -30,22 +30,23 @@ export async function health(ctx) {
|
|
|
30
30
|
checks.database = true;
|
|
31
31
|
// 知识库统计(轻量聚合查询)
|
|
32
32
|
try {
|
|
33
|
+
// V3: knowledge_entries 统一表(lifecycle 替代 status)
|
|
33
34
|
const rStats = db.prepare(`
|
|
34
35
|
SELECT COUNT(*) as total,
|
|
35
|
-
SUM(CASE WHEN
|
|
36
|
+
SUM(CASE WHEN lifecycle='active' THEN 1 ELSE 0 END) as active,
|
|
36
37
|
SUM(CASE WHEN kind='rule' THEN 1 ELSE 0 END) as rules,
|
|
37
38
|
SUM(CASE WHEN kind='pattern' THEN 1 ELSE 0 END) as patterns,
|
|
38
39
|
SUM(CASE WHEN kind='fact' THEN 1 ELSE 0 END) as facts
|
|
39
|
-
FROM
|
|
40
|
+
FROM knowledge_entries
|
|
40
41
|
`).get();
|
|
41
|
-
const
|
|
42
|
+
const cPending = db.prepare(`
|
|
42
43
|
SELECT COUNT(*) as total,
|
|
43
|
-
SUM(CASE WHEN
|
|
44
|
-
FROM
|
|
44
|
+
SUM(CASE WHEN lifecycle='pending' THEN 1 ELSE 0 END) as pending
|
|
45
|
+
FROM knowledge_entries
|
|
45
46
|
`).get();
|
|
46
47
|
knowledgeBase = {
|
|
47
48
|
recipes: { total: rStats.total, active: rStats.active, rules: rStats.rules, patterns: rStats.patterns, facts: rStats.facts },
|
|
48
|
-
candidates: { total:
|
|
49
|
+
candidates: { total: cPending.total, pending: cPending.pending },
|
|
49
50
|
};
|
|
50
51
|
} catch { /* 统计查询失败不影响 health */ }
|
|
51
52
|
}
|
|
@@ -132,9 +133,6 @@ export function capabilities() {
|
|
|
132
133
|
autosnippet_get_target_metadata: 'structure',
|
|
133
134
|
autosnippet_validate_candidate: 'validate',
|
|
134
135
|
autosnippet_check_duplicate: 'validate',
|
|
135
|
-
autosnippet_submit_candidate: 'submit',
|
|
136
|
-
autosnippet_submit_candidates: 'submit',
|
|
137
|
-
autosnippet_submit_draft_recipes: 'submit',
|
|
138
136
|
autosnippet_guard_check: 'guard',
|
|
139
137
|
autosnippet_guard_audit_files: 'guard',
|
|
140
138
|
autosnippet_scan_project: 'scan',
|
|
@@ -191,9 +189,9 @@ export function capabilities() {
|
|
|
191
189
|
tools,
|
|
192
190
|
workflows: [
|
|
193
191
|
{ name: '知识查询', steps: ['search(推荐首选,auto mode 融合)', 'get_recipe', 'confirm_usage'], tips: '精确匹配用 keyword_search,需意图+会话上下文用 context_search' },
|
|
194
|
-
{ name: '
|
|
195
|
-
{ name: '批量 Target 扫描', steps: ['get_targets', 'get_target_files', '(Agent 分析)', '
|
|
196
|
-
{ name: '全项目深度扫描', steps: ['scan_project', '(Agent 语义分析)', '
|
|
192
|
+
{ name: '单条知识提交', steps: ['check_duplicate', 'validate_candidate', 'submit_knowledge'] },
|
|
193
|
+
{ name: '批量 Target 扫描', steps: ['get_targets', 'get_target_files', '(Agent 分析)', 'submit_knowledge_batch'] },
|
|
194
|
+
{ name: '全项目深度扫描', steps: ['scan_project', '(Agent 语义分析)', 'submit_knowledge_batch', 'enrich_candidates'] },
|
|
197
195
|
{ name: '候选就绪检查', steps: ['enrich_candidates', 'validate_candidate', 'check_duplicate'] },
|
|
198
196
|
],
|
|
199
197
|
},
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP 工具定义(
|
|
2
|
+
* MCP 工具定义(35 个)+ Gateway 映射
|
|
3
3
|
*
|
|
4
4
|
* 只包含 JSON Schema 级别的声明,不含任何业务逻辑。
|
|
5
|
+
* V3: 旧 submit_candidate / submit_candidates / submit_draft_recipes 已移除,
|
|
6
|
+
* 统一使用 submit_knowledge / submit_knowledge_batch / knowledge_lifecycle。
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
9
|
/**
|
|
@@ -9,9 +11,6 @@
|
|
|
9
11
|
* 只读工具不在此映射中,跳过 Gateway 以保持性能
|
|
10
12
|
*/
|
|
11
13
|
export const TOOL_GATEWAY_MAP = {
|
|
12
|
-
autosnippet_submit_candidate: { action: 'candidate:create', resource: 'candidates' },
|
|
13
|
-
autosnippet_submit_candidates: { action: 'candidate:create', resource: 'candidates' },
|
|
14
|
-
autosnippet_submit_draft_recipes: { action: 'candidate:create', resource: 'candidates' },
|
|
15
14
|
autosnippet_guard_audit_files: { action: 'guard_rule:check_code', resource: 'guard_rules' },
|
|
16
15
|
autosnippet_scan_project: { action: 'guard_rule:check_code', resource: 'guard_rules' },
|
|
17
16
|
autosnippet_enrich_candidates: { action: 'candidate:update', resource: 'candidates' },
|
|
@@ -20,6 +19,10 @@ export const TOOL_GATEWAY_MAP = {
|
|
|
20
19
|
autosnippet_create_skill: { action: 'create:skills', resource: 'skills' },
|
|
21
20
|
autosnippet_delete_skill: { action: 'delete:skills', resource: 'skills' },
|
|
22
21
|
autosnippet_update_skill: { action: 'update:skills', resource: 'skills' },
|
|
22
|
+
// V3 知识条目
|
|
23
|
+
autosnippet_submit_knowledge: { action: 'knowledge:create', resource: 'knowledge' },
|
|
24
|
+
autosnippet_submit_knowledge_batch: { action: 'knowledge:create', resource: 'knowledge' },
|
|
25
|
+
autosnippet_knowledge_lifecycle: { action: 'knowledge:update', resource: 'knowledge' },
|
|
23
26
|
};
|
|
24
27
|
|
|
25
28
|
export const TOOLS = [
|
|
@@ -283,132 +286,8 @@ export const TOOLS = [
|
|
|
283
286
|
required: ['candidate'],
|
|
284
287
|
},
|
|
285
288
|
},
|
|
286
|
-
// 18
|
|
287
|
-
|
|
288
|
-
name: 'autosnippet_submit_candidate',
|
|
289
|
-
description:
|
|
290
|
-
'提交单条代码片段候选供审核。支持 V2 结构化全字段。含限流保护。Agent 必须提供 reasoning(推理依据)。\n' +
|
|
291
|
-
'⚠️ Recipe-Ready 要求:为使候选直接审核通过为 Recipe,请尽量填写以下字段:\n' +
|
|
292
|
-
' 必填: title, code, language, category, trigger(@开头), summary_cn, headers(完整import语句)\n' +
|
|
293
|
-
' 强烈建议: summary_en, usageGuide(Markdown ### 章节), reasoning, knowledgeType, complexity\n' +
|
|
294
|
-
' 推荐: usageGuide_en, rationale, steps, constraints, relations\n' +
|
|
295
|
-
'如字段不全,返回值中 recipeReadyHints 会提示缺失字段,Agent 应据此补全后重新提交或调用 enrich_candidates 查漏。',
|
|
296
|
-
inputSchema: {
|
|
297
|
-
type: 'object',
|
|
298
|
-
properties: {
|
|
299
|
-
// ── 核心(必填)──
|
|
300
|
-
title: { type: 'string', description: '中文简短标题(≤20字)' },
|
|
301
|
-
code: { type: 'string', description: '代码片段(映射到 content.pattern),使用 Xcode 占位符 <#name#>' },
|
|
302
|
-
language: { type: 'string', description: '编程语言:swift / objectivec(必须小写)' },
|
|
303
|
-
// ── 分类 ──
|
|
304
|
-
category: { type: 'string', description: '分类(必填):View/Service/Tool/Model/Network/Storage/UI/Utility' },
|
|
305
|
-
knowledgeType: { type: 'string', description: '知识维度:code-pattern|architecture|best-practice|code-standard|code-relation|inheritance|call-chain|data-flow|module-dependency|boundary-constraint|code-style|solution' },
|
|
306
|
-
complexity: { type: 'string', enum: ['beginner', 'intermediate', 'advanced'] },
|
|
307
|
-
scope: { type: 'string', enum: ['universal', 'project-specific', 'target-specific'], description: '适用范围' },
|
|
308
|
-
tags: { type: 'array', items: { type: 'string' }, description: '可搜索标签' },
|
|
309
|
-
// ── 描述与文档(双语) ──
|
|
310
|
-
description: { type: 'string', description: '一句话功能描述' },
|
|
311
|
-
summary: { type: 'string', description: '中文详细摘要(等同 summary_cn,Markdown)' },
|
|
312
|
-
summary_cn: { type: 'string', description: '中文摘要(≤100字)。与 summary 二选一' },
|
|
313
|
-
summary_en: { type: 'string', description: '英文摘要(≤100 words)。强烈建议提供,提升检索与 AI 理解' },
|
|
314
|
-
trigger: { type: 'string', description: '触发关键词(必填,@开头,小写,如 @video-cover-cell)' },
|
|
315
|
-
usageGuide: { type: 'string', description: '中文使用指南(等同 usageGuide_cn,Markdown ### 章节格式)' },
|
|
316
|
-
usageGuide_cn: { type: 'string', description: '中文使用指南。与 usageGuide 二选一' },
|
|
317
|
-
usageGuide_en: { type: 'string', description: '英文使用指南(Markdown ### 章节格式)。推荐提供' },
|
|
318
|
-
// ── 结构化内容 ──
|
|
319
|
-
rationale: { type: 'string', description: '设计原理/为什么这样做' },
|
|
320
|
-
steps: { type: 'array', items: { type: 'object' }, description: '实施步骤 [{title, description, code}]' },
|
|
321
|
-
codeChanges: { type: 'array', items: { type: 'object' }, description: '代码变更 [{file, before, after, explanation}]' },
|
|
322
|
-
verification: { type: 'object', description: '验证方式 {method, expectedResult, testCode}' },
|
|
323
|
-
headers: { type: 'array', items: { type: 'string' }, description: '完整 import/include 语句(必填)如 ["#import <Module/Header.h>"] 或 ["import Foundation"]' },
|
|
324
|
-
// ── 约束与关系 ──
|
|
325
|
-
constraints: { type: 'object', description: '约束 {boundaries[], preconditions[], sideEffects[], guards[{pattern,severity,message}]}' },
|
|
326
|
-
relations: { type: 'object', description: '关系 {dependsOn[], extends[], conflicts[], related[], inherits[], implements[], calls[], dataFlow[]},每项 {target, description}' },
|
|
327
|
-
// ── 质量 & 来源 ──
|
|
328
|
-
quality: { type: 'object', description: '质量评分 {codeCompleteness, projectAdaptation, documentationClarity} (0-1)' },
|
|
329
|
-
sourceFile: { type: 'string', description: '来源文件路径' },
|
|
330
|
-
// ── 推理依据(Reasoning)— 必填 ──
|
|
331
|
-
reasoning: {
|
|
332
|
-
type: 'object',
|
|
333
|
-
description: '推理依据:为什么提取这段代码。Agent 必须填写。',
|
|
334
|
-
properties: {
|
|
335
|
-
whyStandard: { type: 'string', description: '为什么这段代码值得沉淀为知识——如"该模式在项目中被反复使用且新人容易写错"' },
|
|
336
|
-
sources: { type: 'array', items: { type: 'string' }, description: '来源列表:文件路径、文档链接、上下文引用等' },
|
|
337
|
-
confidence: { type: 'number', description: '置信度 0-1,表示 Agent 对这条候选质量的确信程度' },
|
|
338
|
-
qualitySignals: { type: 'object', description: '质量信号 {clarity, reusability, importance} 等自由 KV' },
|
|
339
|
-
alternatives: { type: 'array', items: { type: 'string' }, description: '备选方案描述(如果有替代实现)' },
|
|
340
|
-
},
|
|
341
|
-
required: ['whyStandard', 'sources', 'confidence'],
|
|
342
|
-
},
|
|
343
|
-
source: { type: 'string', description: '来源标识(默认 mcp)' },
|
|
344
|
-
clientId: { type: 'string', description: '客户端标识(用于限流)' },
|
|
345
|
-
},
|
|
346
|
-
required: ['title', 'code', 'language'],
|
|
347
|
-
},
|
|
348
|
-
},
|
|
349
|
-
// 19. 批量候选提交
|
|
350
|
-
{
|
|
351
|
-
name: 'autosnippet_submit_candidates',
|
|
352
|
-
description:
|
|
353
|
-
'批量提交候选到 Candidates,支持去重与限流。每个 item 支持 V2 全字段。返回逐条结果与 recipeReadyHints。\n' +
|
|
354
|
-
'⚠️ Recipe-Ready:请尽量每条填写 category, trigger(@开头), summary_cn, summary_en, headers, reasoning。缺失字段会在 recipeReadyHints 中提示。',
|
|
355
|
-
inputSchema: {
|
|
356
|
-
type: 'object',
|
|
357
|
-
properties: {
|
|
358
|
-
targetName: { type: 'string', description: 'Target 名称' },
|
|
359
|
-
items: {
|
|
360
|
-
type: 'array',
|
|
361
|
-
description: '候选数组,每项字段详见 submit_candidate。尽量填充全部 Recipe-Ready 字段。',
|
|
362
|
-
items: {
|
|
363
|
-
type: 'object',
|
|
364
|
-
properties: {
|
|
365
|
-
title: { type: 'string' }, code: { type: 'string' }, language: { type: 'string' },
|
|
366
|
-
category: { type: 'string' }, knowledgeType: { type: 'string' },
|
|
367
|
-
complexity: { type: 'string' }, scope: { type: 'string' },
|
|
368
|
-
tags: { type: 'array', items: { type: 'string' } },
|
|
369
|
-
description: { type: 'string' },
|
|
370
|
-
summary: { type: 'string', description: '中文摘要(等同 summary_cn)' },
|
|
371
|
-
summary_cn: { type: 'string', description: '中文摘要' },
|
|
372
|
-
summary_en: { type: 'string', description: '英文摘要(强烈建议)' },
|
|
373
|
-
trigger: { type: 'string', description: '触发词(@开头)' },
|
|
374
|
-
usageGuide: { type: 'string', description: '中文使用指南(等同 usageGuide_cn)' },
|
|
375
|
-
usageGuide_cn: { type: 'string', description: '中文使用指南' },
|
|
376
|
-
usageGuide_en: { type: 'string', description: '英文使用指南' },
|
|
377
|
-
rationale: { type: 'string' },
|
|
378
|
-
steps: { type: 'array', items: { type: 'object' } },
|
|
379
|
-
codeChanges: { type: 'array', items: { type: 'object' } },
|
|
380
|
-
verification: { type: 'object' },
|
|
381
|
-
headers: { type: 'array', items: { type: 'string' }, description: '完整 import 语句' },
|
|
382
|
-
constraints: { type: 'object' }, relations: { type: 'object' },
|
|
383
|
-
quality: { type: 'object' }, sourceFile: { type: 'string' },
|
|
384
|
-
reasoning: { type: 'object', description: '{whyStandard, sources[], confidence}(必填)' },
|
|
385
|
-
},
|
|
386
|
-
required: ['title', 'code', 'language'],
|
|
387
|
-
},
|
|
388
|
-
},
|
|
389
|
-
source: { type: 'string', default: 'cursor-scan' },
|
|
390
|
-
deduplicate: { type: 'boolean', default: true },
|
|
391
|
-
clientId: { type: 'string' },
|
|
392
|
-
},
|
|
393
|
-
required: ['targetName', 'items'],
|
|
394
|
-
},
|
|
395
|
-
},
|
|
396
|
-
// 20. 草稿 Recipe 提交
|
|
397
|
-
{
|
|
398
|
-
name: 'autosnippet_submit_draft_recipes',
|
|
399
|
-
description: '解析草稿 Markdown 文件为 Recipe 候选并提交。支持完整 Recipe 和纯介绍。保留解析出的全部结构化字段(trigger/usageGuide/headers 等)。自动生成默认 reasoning。',
|
|
400
|
-
inputSchema: {
|
|
401
|
-
type: 'object',
|
|
402
|
-
properties: {
|
|
403
|
-
filePaths: { description: '草稿文件路径(字符串或数组)' },
|
|
404
|
-
targetName: { type: 'string', default: '_draft' },
|
|
405
|
-
source: { type: 'string', default: 'copilot-draft' },
|
|
406
|
-
deleteAfterSubmit: { type: 'boolean', default: false },
|
|
407
|
-
clientId: { type: 'string' },
|
|
408
|
-
},
|
|
409
|
-
required: ['filePaths'],
|
|
410
|
-
},
|
|
411
|
-
},
|
|
289
|
+
// 18-20: [已移除] 旧 submit_candidate / submit_candidates / submit_draft_recipes
|
|
290
|
+
// 统一使用 V3 submit_knowledge / submit_knowledge_batch / knowledge_lifecycle
|
|
412
291
|
// 21. 能力声明
|
|
413
292
|
{
|
|
414
293
|
name: 'autosnippet_capabilities',
|
|
@@ -542,7 +421,7 @@ export const TOOLS = [
|
|
|
542
421
|
description:
|
|
543
422
|
'① 结构补齐(诊断模式)— 检查候选的字段完整性,返回缺失清单。\n' +
|
|
544
423
|
'检查两层:\n' +
|
|
545
|
-
' • Recipe 必填:category、trigger(@开头)、
|
|
424
|
+
' • Recipe 必填:category、trigger(@开头)、description、headers\n' +
|
|
546
425
|
' • 语义字段:rationale、knowledgeType、complexity、scope、steps、constraints\n' +
|
|
547
426
|
'不调用内置 AI,仅做诊断。返回每条候选的 missingFields 列表。\n' +
|
|
548
427
|
'\n' +
|
|
@@ -574,10 +453,10 @@ export const TOOLS = [
|
|
|
574
453
|
'⚠️ 产出为启发式初稿,必须执行后续步骤提升质量:\n' +
|
|
575
454
|
' Step 1: autosnippet_enrich_candidates — 诊断字段缺失,逐条补全必填字段\n' +
|
|
576
455
|
' Step 2: autosnippet_bootstrap_refine — AI 润色 summary/insight/relations/confidence\n' +
|
|
577
|
-
' Step 3: 逐 Target
|
|
456
|
+
' Step 3: 逐 Target 深入分析,补充更细粒度知识条目(autosnippet_submit_knowledge_batch)\n' +
|
|
578
457
|
' Step 4: 对新候选重复 Step 1-2\n' +
|
|
579
458
|
'\n' +
|
|
580
|
-
'质量标准:每条必须包含 title/code/language/category/trigger/
|
|
459
|
+
'质量标准:每条必须包含 title/code/language/category/trigger/description/headers/reasoning。',
|
|
581
460
|
inputSchema: {
|
|
582
461
|
type: 'object',
|
|
583
462
|
properties: {
|
|
@@ -589,7 +468,7 @@ export const TOOLS = [
|
|
|
589
468
|
required: [],
|
|
590
469
|
},
|
|
591
470
|
},
|
|
592
|
-
//
|
|
471
|
+
// 32. Skills 发现:列出所有可用 Agent Skill 及其适用场景
|
|
593
472
|
{
|
|
594
473
|
name: 'autosnippet_list_skills',
|
|
595
474
|
description:
|
|
@@ -603,7 +482,7 @@ export const TOOLS = [
|
|
|
603
482
|
' • 执行具体任务前,加载对应的 Skill 获取操作指南和最佳实践',
|
|
604
483
|
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
605
484
|
},
|
|
606
|
-
//
|
|
485
|
+
// 33. Skills 加载:按需获取指定 Skill 的完整操作指南
|
|
607
486
|
{
|
|
608
487
|
name: 'autosnippet_load_skill',
|
|
609
488
|
description:
|
|
@@ -627,7 +506,7 @@ export const TOOLS = [
|
|
|
627
506
|
required: ['skillName'],
|
|
628
507
|
},
|
|
629
508
|
},
|
|
630
|
-
//
|
|
509
|
+
// 34. 创建项目级 Skill
|
|
631
510
|
{
|
|
632
511
|
name: 'autosnippet_create_skill',
|
|
633
512
|
description:
|
|
@@ -671,7 +550,7 @@ export const TOOLS = [
|
|
|
671
550
|
required: ['name', 'description', 'content'],
|
|
672
551
|
},
|
|
673
552
|
},
|
|
674
|
-
//
|
|
553
|
+
// 35. Skill 推荐:基于使用模式分析,推荐创建 Skill
|
|
675
554
|
{
|
|
676
555
|
name: 'autosnippet_suggest_skills',
|
|
677
556
|
description:
|
|
@@ -686,7 +565,7 @@ export const TOOLS = [
|
|
|
686
565
|
' • 候选被大量驳回时',
|
|
687
566
|
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
688
567
|
},
|
|
689
|
-
//
|
|
568
|
+
// 36. 删除项目级 Skill
|
|
690
569
|
{
|
|
691
570
|
name: 'autosnippet_delete_skill',
|
|
692
571
|
description:
|
|
@@ -707,7 +586,7 @@ export const TOOLS = [
|
|
|
707
586
|
required: ['name'],
|
|
708
587
|
},
|
|
709
588
|
},
|
|
710
|
-
//
|
|
589
|
+
// 37. 更新项目级 Skill
|
|
711
590
|
{
|
|
712
591
|
name: 'autosnippet_update_skill',
|
|
713
592
|
description:
|
|
@@ -737,7 +616,7 @@ export const TOOLS = [
|
|
|
737
616
|
required: ['name'],
|
|
738
617
|
},
|
|
739
618
|
},
|
|
740
|
-
//
|
|
619
|
+
// 38. ② 内容润色:Bootstrap 候选 AI 精炼(Phase 6)
|
|
741
620
|
{
|
|
742
621
|
name: 'autosnippet_bootstrap_refine',
|
|
743
622
|
description:
|
|
@@ -757,4 +636,119 @@ export const TOOLS = [
|
|
|
757
636
|
required: [],
|
|
758
637
|
},
|
|
759
638
|
},
|
|
639
|
+
// ═══ V3 知识条目(统一实体) ═══════════════════════════════
|
|
640
|
+
// 36. 单条知识提交(V3 wire format 直通)
|
|
641
|
+
{
|
|
642
|
+
name: 'autosnippet_submit_knowledge',
|
|
643
|
+
description:
|
|
644
|
+
'提交单条知识条目到 AutoSnippet 知识库(V3 统一实体)。\n' +
|
|
645
|
+
'参数即 wire format — 直接构造 KnowledgeEntry。\n' +
|
|
646
|
+
'⚠️ Cursor Delivery 字段(kind/doClause/topicHint)直接影响 .mdc 规则文件生成质量,请务必填写。\n' +
|
|
647
|
+
'\n' +
|
|
648
|
+
'核心必填: title + language + content(pattern 或 markdown) + kind + doClause\n' +
|
|
649
|
+
'推荐填写: trigger, topicHint, whenClause, dontClause, coreCode, reasoning, tags\n' +
|
|
650
|
+
'\n' +
|
|
651
|
+
'根据 ConfidenceRouter 自动路由:高置信度自动入库(active),低置信度待审核(pending)。',
|
|
652
|
+
inputSchema: {
|
|
653
|
+
type: 'object',
|
|
654
|
+
properties: {
|
|
655
|
+
// ── 必填 ──
|
|
656
|
+
title: { type: 'string', description: '中文标题(≤20字)' },
|
|
657
|
+
language: { type: 'string', description: '编程语言(swift/objectivec/javascript/typescript/python,小写)' },
|
|
658
|
+
content: {
|
|
659
|
+
type: 'object',
|
|
660
|
+
description: '内容值对象(必须有 pattern 或 markdown)',
|
|
661
|
+
properties: {
|
|
662
|
+
pattern: { type: 'string', description: '完整代码片段(函数/方法/类实现)' },
|
|
663
|
+
markdown: { type: 'string', description: '项目特写 Markdown(技术说明文档)' },
|
|
664
|
+
rationale: { type: 'string', description: '设计原理(英文)' },
|
|
665
|
+
steps: { type: 'array', items: { type: 'object', properties: {
|
|
666
|
+
title: { type: 'string' }, description: { type: 'string' }, code: { type: 'string' },
|
|
667
|
+
}}},
|
|
668
|
+
codeChanges: { type: 'array', items: { type: 'object' } },
|
|
669
|
+
verification: { type: 'object' },
|
|
670
|
+
},
|
|
671
|
+
},
|
|
672
|
+
// ── Cursor Delivery(关键字段,影响 .mdc 规则生成) ──
|
|
673
|
+
kind: { type: 'string', enum: ['rule', 'pattern', 'fact'], description: '知识类型: rule=必须遵守的规则, pattern=可复用代码模板, fact=参考信息' },
|
|
674
|
+
doClause: { type: 'string', description: '正向指令: 英文祈使句 ≤60 tokens(e.g. "Use dependency injection via constructor")' },
|
|
675
|
+
dontClause: { type: 'string', description: '反向约束: 英文(不以 Don\'t 开头),e.g. "Instantiate services with new directly"' },
|
|
676
|
+
whenClause: { type: 'string', description: '触发场景: 英文描述何时应用此知识(e.g. "When creating a new ViewController subclass")' },
|
|
677
|
+
topicHint: { type: 'string', description: '主题分组标签,用于 .mdc 文件归类(e.g. "networking", "ui-layout", "error-handling", "data-model")' },
|
|
678
|
+
coreCode: { type: 'string', description: '3-8 行精华代码骨架,可直接复制使用(无 Markdown 格式)' },
|
|
679
|
+
trigger: { type: 'string', description: '触发关键词(@前缀 kebab-case,e.g. "@json-parse")' },
|
|
680
|
+
// ── 分类 ──
|
|
681
|
+
category: { type: 'string', description: '分类: View/Service/Tool/Model/Network/Storage/UI/Utility' },
|
|
682
|
+
knowledgeType: { type: 'string', description: '知识维度: code-pattern|architecture|best-practice|boundary-constraint|...' },
|
|
683
|
+
complexity: { type: 'string', enum: ['beginner', 'intermediate', 'advanced'] },
|
|
684
|
+
scope: { type: 'string', enum: ['universal', 'project-specific', 'target-specific'] },
|
|
685
|
+
difficulty: { type: 'string' },
|
|
686
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
687
|
+
// ── 描述 ──
|
|
688
|
+
description: { type: 'string', description: '中文简述 ≤80 字(引用真实类名/方法名)' },
|
|
689
|
+
// ── 约束与关系 ──
|
|
690
|
+
constraints: { type: 'object', description: '约束 {guards[], boundaries[], preconditions[], sideEffects[]}' },
|
|
691
|
+
relations: { type: 'object', description: '关系分桶 {inherits[], extends[], depends_on[], conflicts[], related[], ...}' },
|
|
692
|
+
// ── 推理 ──
|
|
693
|
+
reasoning: {
|
|
694
|
+
type: 'object',
|
|
695
|
+
description: '推理依据',
|
|
696
|
+
properties: {
|
|
697
|
+
whyStandard: { type: 'string', description: '为什么值得沉淀为知识' },
|
|
698
|
+
sources: { type: 'array', items: { type: 'string' }, description: '来源文件列表' },
|
|
699
|
+
confidence: { type: 'number', description: '置信度 0-1' },
|
|
700
|
+
qualitySignals: { type: 'object' },
|
|
701
|
+
alternatives: { type: 'array', items: { type: 'string' } },
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
// ── 头文件 ──
|
|
705
|
+
headers: { type: 'array', items: { type: 'string' }, description: '完整 import/include 语句' },
|
|
706
|
+
headerPaths: { type: 'array', items: { type: 'string' } },
|
|
707
|
+
moduleName: { type: 'string' },
|
|
708
|
+
includeHeaders: { type: 'boolean' },
|
|
709
|
+
// ── 控制 ──
|
|
710
|
+
source: { type: 'string', description: '来源标识(默认 mcp)' },
|
|
711
|
+
client_id: { type: 'string', description: '客户端标识(用于限流)' },
|
|
712
|
+
},
|
|
713
|
+
required: ['title', 'language', 'content', 'kind', 'doClause'],
|
|
714
|
+
},
|
|
715
|
+
},
|
|
716
|
+
// 37. 批量知识提交
|
|
717
|
+
{
|
|
718
|
+
name: 'autosnippet_submit_knowledge_batch',
|
|
719
|
+
description:
|
|
720
|
+
'批量提交知识条目到知识库(V3 统一实体)。\n' +
|
|
721
|
+
'每条 item 使用与 autosnippet_submit_knowledge 相同的 wire format。\n' +
|
|
722
|
+
'每条 item 必须包含 Cursor Delivery 字段(kind/doClause),否则生成的 .mdc 规则质量低。\n' +
|
|
723
|
+
'支持去重。返回逐条结果与 recipeReadyHints。',
|
|
724
|
+
inputSchema: {
|
|
725
|
+
type: 'object',
|
|
726
|
+
properties: {
|
|
727
|
+
target_name: { type: 'string', description: 'Target 名称' },
|
|
728
|
+
items: { type: 'array', description: '知识条目数组,每项字段同 submit_knowledge(必须含 kind/doClause)', items: { type: 'object' } },
|
|
729
|
+
source: { type: 'string', default: 'cursor-scan', description: '来源标识' },
|
|
730
|
+
deduplicate: { type: 'boolean', default: true, description: '是否去重' },
|
|
731
|
+
client_id: { type: 'string', description: '客户端标识(用于限流)' },
|
|
732
|
+
},
|
|
733
|
+
required: ['target_name', 'items'],
|
|
734
|
+
},
|
|
735
|
+
},
|
|
736
|
+
// 38. 知识条目生命周期操作
|
|
737
|
+
{
|
|
738
|
+
name: 'autosnippet_knowledge_lifecycle',
|
|
739
|
+
description:
|
|
740
|
+
'知识条目生命周期操作。\n' +
|
|
741
|
+
'可用操作: submit(draft→pending), approve(pending→approved), reject(pending→rejected),\n' +
|
|
742
|
+
'publish(approved→active), deprecate(active→deprecated), reactivate(deprecated→active),\n' +
|
|
743
|
+
'to_draft(rejected→draft), fast_track(draft→active 一键发布)。',
|
|
744
|
+
inputSchema: {
|
|
745
|
+
type: 'object',
|
|
746
|
+
properties: {
|
|
747
|
+
id: { type: 'string', description: '知识条目 ID' },
|
|
748
|
+
action: { type: 'string', enum: ['submit', 'approve', 'reject', 'publish', 'deprecate', 'reactivate', 'to_draft', 'fast_track'], description: '生命周期操作' },
|
|
749
|
+
reason: { type: 'string', description: 'reject/deprecate 时必须提供原因' },
|
|
750
|
+
},
|
|
751
|
+
required: ['id', 'action'],
|
|
752
|
+
},
|
|
753
|
+
},
|
|
760
754
|
];
|
package/lib/http/HttpServer.js
CHANGED
|
@@ -13,8 +13,6 @@ import { requestLogger } from './middleware/requestLogger.js';
|
|
|
13
13
|
import { gatewayMiddleware } from './middleware/gatewayMiddleware.js';
|
|
14
14
|
import { roleResolverMiddleware } from './middleware/roleResolver.js';
|
|
15
15
|
import { CapabilityProbe } from '../core/capability/CapabilityProbe.js';
|
|
16
|
-
import candidateRouter from './routes/candidates.js';
|
|
17
|
-
import recipeRouter from './routes/recipes.js';
|
|
18
16
|
import guardRuleRouter from './routes/guardRules.js';
|
|
19
17
|
import searchRouter from './routes/search.js';
|
|
20
18
|
import healthRouter from './routes/health.js';
|
|
@@ -27,6 +25,7 @@ import spmRouter from './routes/spm.js';
|
|
|
27
25
|
import violationsRouter from './routes/violations.js';
|
|
28
26
|
import authRouter from './routes/auth.js';
|
|
29
27
|
import skillsRouter from './routes/skills.js';
|
|
28
|
+
import knowledgeRouter from './routes/knowledge.js';
|
|
30
29
|
import apiSpec from './api-spec.js';
|
|
31
30
|
import { initRedisService } from '../infrastructure/cache/RedisService.js';
|
|
32
31
|
import { initCacheAdapter } from '../infrastructure/cache/UnifiedCacheAdapter.js';
|
|
@@ -235,12 +234,6 @@ export class HttpServer {
|
|
|
235
234
|
this.app.use(`${apiPrefix}/monitoring`, monitoringRouter);
|
|
236
235
|
}
|
|
237
236
|
|
|
238
|
-
// 候选项路由
|
|
239
|
-
this.app.use(`${apiPrefix}/candidates`, candidateRouter);
|
|
240
|
-
|
|
241
|
-
// 配方路由
|
|
242
|
-
this.app.use(`${apiPrefix}/recipes`, recipeRouter);
|
|
243
|
-
|
|
244
237
|
// 守护规则路由
|
|
245
238
|
this.app.use(`${apiPrefix}/rules`, guardRuleRouter);
|
|
246
239
|
|
|
@@ -268,6 +261,19 @@ export class HttpServer {
|
|
|
268
261
|
// 违规记录路由
|
|
269
262
|
this.app.use(`${apiPrefix}/violations`, violationsRouter);
|
|
270
263
|
|
|
264
|
+
// 知识条目路由 (V3)
|
|
265
|
+
this.app.use(`${apiPrefix}/knowledge`, knowledgeRouter);
|
|
266
|
+
|
|
267
|
+
// 根路径 — 返回 API 元信息(避免外部探测产生无意义 404)
|
|
268
|
+
this.app.all('/', (req, res) => {
|
|
269
|
+
res.json({
|
|
270
|
+
name: 'AutoSnippet API',
|
|
271
|
+
version: '2.0',
|
|
272
|
+
docs: '/api-spec',
|
|
273
|
+
health: `${apiPrefix}/health`,
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
271
277
|
// 404 处理
|
|
272
278
|
this.app.use('*', (req, res) => {
|
|
273
279
|
res.status(404).json({
|
package/lib/http/routes/ai.js
CHANGED
|
@@ -126,7 +126,7 @@ router.post('/translate', asyncHandler(async (req, res) => {
|
|
|
126
126
|
if (!summary && !usageGuide) {
|
|
127
127
|
return res.json({
|
|
128
128
|
success: true,
|
|
129
|
-
data: {
|
|
129
|
+
data: { summaryEn: '', usageGuideEn: '' },
|
|
130
130
|
});
|
|
131
131
|
}
|
|
132
132
|
|
|
@@ -139,7 +139,7 @@ router.post('/translate', asyncHandler(async (req, res) => {
|
|
|
139
139
|
logger.warn('AI translate tool returned error', { error: result.error });
|
|
140
140
|
return res.json({
|
|
141
141
|
success: true,
|
|
142
|
-
data: {
|
|
142
|
+
data: { summaryEn: summary || '', usageGuideEn: usageGuide || '' },
|
|
143
143
|
warning: result.error,
|
|
144
144
|
});
|
|
145
145
|
}
|
|
@@ -149,7 +149,7 @@ router.post('/translate', asyncHandler(async (req, res) => {
|
|
|
149
149
|
logger.warn('AI translate failed, returning original text', { error: err.message });
|
|
150
150
|
res.json({
|
|
151
151
|
success: true,
|
|
152
|
-
data: {
|
|
152
|
+
data: { summaryEn: summary || '', usageGuideEn: usageGuide || '' },
|
|
153
153
|
warning: `Translation failed: ${err.message}`,
|
|
154
154
|
});
|
|
155
155
|
}
|
|
@@ -175,6 +175,7 @@ router.post('/chat', asyncHandler(async (req, res) => {
|
|
|
175
175
|
reply: result.reply,
|
|
176
176
|
hasContext: result.hasContext,
|
|
177
177
|
toolCalls: result.toolCalls,
|
|
178
|
+
reasoningQuality: result.reasoningQuality || null,
|
|
178
179
|
},
|
|
179
180
|
});
|
|
180
181
|
}));
|