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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Model Context Protocol (stdio transport)
|
|
5
5
|
* 提供给 IDE AI Agent (Cursor/VSCode Copilot) 的工具集
|
|
6
|
-
*
|
|
6
|
+
* 38 工具,全部基于 V2 服务层,不依赖 V1
|
|
7
7
|
* Gateway 权限 gating: 写操作经过 Gateway 权限/宪法/审计检查
|
|
8
8
|
*
|
|
9
9
|
* 本文件仅包含服务编排层(初始化、路由、Gateway gating、生命周期)。
|
|
@@ -31,6 +31,7 @@ import * as candidateHandlers from './handlers/candidate.js';
|
|
|
31
31
|
import * as guardHandlers from './handlers/guard.js';
|
|
32
32
|
import * as bootstrapHandlers from './handlers/bootstrap.js';
|
|
33
33
|
import * as skillHandlers from './handlers/skill.js';
|
|
34
|
+
import * as knowledgeHandlers from './handlers/knowledge.js';
|
|
34
35
|
|
|
35
36
|
// ─── McpServer 类 ─────────────────────────────────────────────
|
|
36
37
|
|
|
@@ -140,12 +141,9 @@ export class McpServer {
|
|
|
140
141
|
case 'autosnippet_graph_impact': return structureHandlers.graphImpact(ctx, args);
|
|
141
142
|
case 'autosnippet_graph_path': return structureHandlers.graphPath(ctx, args);
|
|
142
143
|
case 'autosnippet_graph_stats': return structureHandlers.graphStats(ctx);
|
|
143
|
-
// 候选校验 &
|
|
144
|
+
// 候选校验 & AI 补全(提交已移至 V3 knowledge handlers)
|
|
144
145
|
case 'autosnippet_validate_candidate': return candidateHandlers.validateCandidate(ctx, args);
|
|
145
146
|
case 'autosnippet_check_duplicate': return candidateHandlers.checkDuplicate(ctx, args);
|
|
146
|
-
case 'autosnippet_submit_candidate': return candidateHandlers.submitSingle(ctx, args);
|
|
147
|
-
case 'autosnippet_submit_candidates': return candidateHandlers.submitBatch(ctx, args);
|
|
148
|
-
case 'autosnippet_submit_draft_recipes': return candidateHandlers.submitDrafts(ctx, args);
|
|
149
147
|
case 'autosnippet_enrich_candidates': return candidateHandlers.enrichCandidates(ctx, args);
|
|
150
148
|
// Guard & 扫描
|
|
151
149
|
case 'autosnippet_guard_check': return guardHandlers.guardCheck(ctx, args);
|
|
@@ -161,6 +159,10 @@ export class McpServer {
|
|
|
161
159
|
case 'autosnippet_delete_skill': return skillHandlers.deleteSkill(ctx, args);
|
|
162
160
|
case 'autosnippet_update_skill': return skillHandlers.updateSkill(ctx, args);
|
|
163
161
|
case 'autosnippet_suggest_skills': return skillHandlers.suggestSkills(ctx);
|
|
162
|
+
// V3 知识条目
|
|
163
|
+
case 'autosnippet_submit_knowledge': return knowledgeHandlers.submitKnowledge(ctx, args);
|
|
164
|
+
case 'autosnippet_submit_knowledge_batch': return knowledgeHandlers.submitKnowledgeBatch(ctx, args);
|
|
165
|
+
case 'autosnippet_knowledge_lifecycle': return knowledgeHandlers.knowledgeLifecycle(ctx, args);
|
|
164
166
|
default: throw new Error(`Unknown tool: ${name}`);
|
|
165
167
|
}
|
|
166
168
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* 1. Analyst Agent 自由探索代码 (AST 工具 + 文件搜索)
|
|
7
7
|
* 2. HandoffProtocol 质量门控
|
|
8
|
-
* 3. Producer Agent 格式化输出 (
|
|
8
|
+
* 3. Producer Agent 格式化输出 (submit_knowledge)
|
|
9
9
|
* 4. TierScheduler 分层并行执行
|
|
10
10
|
*
|
|
11
11
|
* @module pipeline/orchestrator
|
|
@@ -412,7 +412,7 @@ export async function fillDimensionsV3(fillContext) {
|
|
|
412
412
|
// 记录到 DimensionContext
|
|
413
413
|
for (const tc of (producerResult.toolCalls || [])) {
|
|
414
414
|
const tool = tc.tool || tc.name;
|
|
415
|
-
if (tool === '
|
|
415
|
+
if (tool === 'submit_knowledge' || tool === 'submit_with_check') {
|
|
416
416
|
dimContext.addSubmittedCandidate(dimId, {
|
|
417
417
|
title: tc.params?.title || '',
|
|
418
418
|
subTopic: tc.params?.category || '',
|
|
@@ -392,7 +392,7 @@ export async function bootstrapKnowledge(ctx, args) {
|
|
|
392
392
|
skillWorthyDimensions: dimensions.filter(d => d.skillWorthy).map(d => d.id),
|
|
393
393
|
candidateOnlyDimensions: dimensions.filter(d => !d.skillWorthy).map(d => d.id),
|
|
394
394
|
candidateRequiredFields: ['title', 'code', 'language', 'category', 'knowledgeType', 'reasoning'],
|
|
395
|
-
submissionTool: '
|
|
395
|
+
submissionTool: 'autosnippet_submit_knowledge_batch',
|
|
396
396
|
expectedOutput: '候选知识(微观代码维度:code-pattern/best-practice/event-and-data-flow + 深度扫描:objc-deep-scan/category-scan)+ 6 个 Project Skills(宏观叙事维度:code-standard/architecture/project-profile/agent-guidelines + 深度扫描:objc-deep-scan/category-scan)',
|
|
397
397
|
},
|
|
398
398
|
|
|
@@ -423,7 +423,7 @@ export async function bootstrapKnowledge(ctx, args) {
|
|
|
423
423
|
'== 完成后可执行的后续操作 ==',
|
|
424
424
|
'1. 调用 autosnippet_enrich_candidates(candidateIds) 补全候选缺失字段',
|
|
425
425
|
'2. 调用 autosnippet_bootstrap_refine() 对候选进行 AI 精炼',
|
|
426
|
-
'3. 使用
|
|
426
|
+
'3. 使用 autosnippet_submit_knowledge_batch 手动提交更多知识条目',
|
|
427
427
|
'4. 使用 autosnippet_load_skill 加载自动生成的 Project Skills',
|
|
428
428
|
'',
|
|
429
429
|
'== 宏观维度 → Project Skills ==',
|
|
@@ -520,7 +520,7 @@ export async function bootstrapKnowledge(ctx, args) {
|
|
|
520
520
|
/**
|
|
521
521
|
* bootstrapRefine — Phase 6 AI 润色
|
|
522
522
|
*
|
|
523
|
-
* 对 Bootstrap Phase 5
|
|
523
|
+
* 对 Bootstrap Phase 5 产出的知识条目进行 AI 二次精炼:
|
|
524
524
|
* - 改善模板化描述 → 更自然精准
|
|
525
525
|
* - 补充高阶架构洞察
|
|
526
526
|
* - 推断并填充 relations 关联
|
|
@@ -531,7 +531,7 @@ export async function bootstrapKnowledge(ctx, args) {
|
|
|
531
531
|
*/
|
|
532
532
|
export async function bootstrapRefine(ctx, args) {
|
|
533
533
|
const t0 = Date.now();
|
|
534
|
-
const
|
|
534
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
535
535
|
const aiProvider = ctx.container.get('aiProvider');
|
|
536
536
|
|
|
537
537
|
if (!aiProvider) {
|
|
@@ -545,17 +545,122 @@ export async function bootstrapRefine(ctx, args) {
|
|
|
545
545
|
onProgress = (eventName, data) => taskManager.emitProgress(eventName, data);
|
|
546
546
|
} catch { /* optional */ }
|
|
547
547
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
548
|
+
// 1. 收集待润色条目
|
|
549
|
+
let entries;
|
|
550
|
+
if (args.candidateIds?.length) {
|
|
551
|
+
entries = [];
|
|
552
|
+
for (const id of args.candidateIds) {
|
|
553
|
+
const e = await knowledgeService.get(id);
|
|
554
|
+
if (e) entries.push(typeof e.toJSON === 'function' ? e.toJSON() : e);
|
|
555
|
+
}
|
|
556
|
+
} else {
|
|
557
|
+
const result = await knowledgeService.list({ lifecycle: 'pending', source: 'bootstrap' }, { page: 1, pageSize: 200 });
|
|
558
|
+
entries = (result.items || []).map(e => typeof e.toJSON === 'function' ? e.toJSON() : e);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (entries.length === 0) {
|
|
562
|
+
return envelope({ success: true, data: { refined: 0, total: 0, errors: [], results: [] }, meta: { tool: 'autosnippet_bootstrap_refine', responseTimeMs: Date.now() - t0 } });
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
onProgress?.('refine:started', { total: entries.length, candidateIds: entries.map(e => e.id) });
|
|
566
|
+
|
|
567
|
+
// 2. 逐条 AI 润色
|
|
568
|
+
const allTitles = entries.map(e => e.title).filter(Boolean);
|
|
569
|
+
const results = [];
|
|
570
|
+
const errors = [];
|
|
571
|
+
let refined = 0;
|
|
572
|
+
let processed = 0;
|
|
573
|
+
|
|
574
|
+
for (const entry of entries) {
|
|
575
|
+
processed++;
|
|
576
|
+
onProgress?.('refine:item-started', { candidateId: entry.id, title: entry.title, current: processed, total: entries.length, progress: Math.round(((processed - 1) / entries.length) * 100) });
|
|
577
|
+
|
|
578
|
+
try {
|
|
579
|
+
const prompt = `你是一位高级代码知识管理专家。请改进以下知识条目:
|
|
580
|
+
|
|
581
|
+
标题: ${entry.title}
|
|
582
|
+
类型: ${entry.knowledge_type}
|
|
583
|
+
语言: ${entry.language}
|
|
584
|
+
描述: ${entry.description || ''}
|
|
585
|
+
代码:
|
|
586
|
+
\`\`\`
|
|
587
|
+
${(entry.content?.pattern || '').substring(0, 2000)}
|
|
588
|
+
\`\`\`
|
|
589
|
+
|
|
590
|
+
同批次知识: ${allTitles.slice(0, 20).join(', ')}
|
|
591
|
+
${args.userPrompt ? `用户补充: ${args.userPrompt}` : ''}
|
|
592
|
+
|
|
593
|
+
请返回 JSON:
|
|
594
|
+
{
|
|
595
|
+
"summary": "改进后的中文摘要",
|
|
596
|
+
"tags": ["建议标签"],
|
|
597
|
+
"relations": [{"target":"相关条目标题","relation":"depends_on|extends|related_to"}],
|
|
598
|
+
"confidence": 0.0-1.0,
|
|
599
|
+
"insight": "架构洞察(一句话)"
|
|
600
|
+
}`;
|
|
601
|
+
|
|
602
|
+
const response = await aiProvider.chat(prompt, { temperature: 0.3 });
|
|
603
|
+
const parsed = aiProvider.extractJSON(response, '{', '}');
|
|
604
|
+
|
|
605
|
+
if (!parsed) {
|
|
606
|
+
errors.push({ id: entry.id, title: entry.title, error: 'AI returned no valid JSON' });
|
|
607
|
+
onProgress?.('refine:item-failed', { candidateId: entry.id, title: entry.title, error: 'No valid JSON', current: processed, total: entries.length, progress: Math.round((processed / entries.length) * 100) });
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (args.dryRun) {
|
|
612
|
+
results.push({ id: entry.id, title: entry.title, preview: parsed });
|
|
613
|
+
onProgress?.('refine:item-completed', { candidateId: entry.id, title: entry.title, refined: false, current: processed, total: entries.length, progress: Math.round((processed / entries.length) * 100) });
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// 构建更新数据
|
|
618
|
+
const updateData = {};
|
|
619
|
+
let changed = false;
|
|
620
|
+
|
|
621
|
+
if (parsed.summary && parsed.summary !== entry.summary_cn) {
|
|
622
|
+
updateData.summary_cn = parsed.summary;
|
|
623
|
+
changed = true;
|
|
624
|
+
}
|
|
625
|
+
if (parsed.tags && Array.isArray(parsed.tags)) {
|
|
626
|
+
const merged = [...new Set([...(entry.tags || []), ...parsed.tags])];
|
|
627
|
+
if (merged.length > (entry.tags || []).length) {
|
|
628
|
+
updateData.tags = merged;
|
|
629
|
+
changed = true;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
if (typeof parsed.confidence === 'number' && parsed.confidence !== 0.6) {
|
|
633
|
+
updateData.reasoning = { ...(entry.reasoning || {}), confidence: parsed.confidence };
|
|
634
|
+
changed = true;
|
|
635
|
+
}
|
|
636
|
+
if (parsed.insight) {
|
|
637
|
+
updateData.description = `${entry.description || ''}\n\n**AI Insight**: ${parsed.insight}`.trim();
|
|
638
|
+
changed = true;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (changed) {
|
|
642
|
+
await knowledgeService.update(entry.id, updateData);
|
|
643
|
+
refined++;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
results.push({ id: entry.id, title: entry.title, refined: changed, fields: Object.keys(parsed) });
|
|
647
|
+
onProgress?.('refine:item-completed', { candidateId: entry.id, title: entry.title, refined: changed, current: processed, total: entries.length, progress: Math.round((processed / entries.length) * 100), refinedSoFar: refined });
|
|
648
|
+
} catch (err) {
|
|
649
|
+
errors.push({ id: entry.id, title: entry.title, error: err.message });
|
|
650
|
+
onProgress?.('refine:item-failed', { candidateId: entry.id, title: entry.title, error: err.message, current: processed, total: entries.length, progress: Math.round((processed / entries.length) * 100) });
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
onProgress?.('refine:completed', { total: entries.length, refined, failed: errors.length });
|
|
553
655
|
|
|
554
656
|
return envelope({
|
|
555
657
|
success: true,
|
|
556
658
|
data: {
|
|
557
|
-
|
|
558
|
-
|
|
659
|
+
refined,
|
|
660
|
+
total: entries.length,
|
|
661
|
+
errors,
|
|
662
|
+
results,
|
|
663
|
+
message: `Phase 6 AI 润色完成: ${refined}/${entries.length} 条知识条目已更新${args.dryRun ? '(预览模式)' : ''}`,
|
|
559
664
|
},
|
|
560
665
|
meta: { tool: 'autosnippet_bootstrap_refine', responseTimeMs: Date.now() - t0 },
|
|
561
666
|
});
|
|
@@ -1,65 +1,70 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP Handlers — 知识浏览类
|
|
3
|
-
* listByKind, listRecipes, getRecipe, recipeInsights,
|
|
2
|
+
* MCP Handlers — 知识浏览类 (V3: 使用 knowledgeService)
|
|
3
|
+
* listByKind, listRecipes, getRecipe, recipeInsights, confirmUsage
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { envelope } from '../envelope.js';
|
|
7
7
|
|
|
8
|
+
/** 将 KnowledgeEntry 的 V3 字段投影为列表摘要 */
|
|
9
|
+
function _projectItem(r) {
|
|
10
|
+
const json = typeof r.toJSON === 'function' ? r.toJSON() : r;
|
|
11
|
+
return {
|
|
12
|
+
id: json.id, title: json.title, description: json.description,
|
|
13
|
+
trigger: json.trigger || '', lifecycle: json.lifecycle, kind: json.kind,
|
|
14
|
+
language: json.language, category: json.category,
|
|
15
|
+
knowledge_type: json.knowledge_type, complexity: json.complexity,
|
|
16
|
+
scope: json.scope, tags: json.tags || [],
|
|
17
|
+
quality: json.quality || null, stats: json.stats || null,
|
|
18
|
+
// 兼容旧字段名
|
|
19
|
+
status: json.lifecycle, knowledgeType: json.knowledge_type,
|
|
20
|
+
statistics: json.stats,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
8
24
|
export async function listByKind(ctx, kind, args) {
|
|
9
|
-
const
|
|
25
|
+
const ks = ctx.container.get('knowledgeService');
|
|
10
26
|
const filters = { kind };
|
|
11
|
-
if (args.
|
|
12
|
-
if (args.
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const items = (result?.data || result?.items || []).map(r => ({
|
|
16
|
-
id: r.id, title: r.title || r.name, description: r.description,
|
|
17
|
-
trigger: r.trigger || '', status: r.status, language: r.language, category: r.category,
|
|
18
|
-
knowledgeType: r.knowledgeType, kind: r.kind,
|
|
19
|
-
complexity: r.complexity, scope: r.scope, tags: r.tags || [],
|
|
20
|
-
quality: r.quality || null, statistics: r.statistics || null,
|
|
21
|
-
}));
|
|
27
|
+
if (args.language) filters.language = args.language;
|
|
28
|
+
if (args.category) filters.category = args.category;
|
|
29
|
+
const result = await ks.list(filters, { page: 1, pageSize: args.limit || 20 });
|
|
30
|
+
const items = (result?.data || []).map(_projectItem);
|
|
22
31
|
return envelope({ success: true, data: { kind, count: items.length, total: result?.pagination?.total || items.length, items }, meta: { tool: `autosnippet_list_${kind}s` } });
|
|
23
32
|
}
|
|
24
33
|
|
|
25
34
|
export async function listRecipes(ctx, args) {
|
|
26
|
-
const
|
|
35
|
+
const ks = ctx.container.get('knowledgeService');
|
|
27
36
|
const filters = {};
|
|
28
|
-
if (args.kind)
|
|
29
|
-
if (args.language)
|
|
30
|
-
if (args.category)
|
|
31
|
-
if (args.knowledgeType)
|
|
32
|
-
if (args.
|
|
33
|
-
if (args.
|
|
34
|
-
const result = await
|
|
35
|
-
const items = (result?.data ||
|
|
36
|
-
id: r.id, title: r.title, description: r.description,
|
|
37
|
-
trigger: r.trigger || '', status: r.status, language: r.language, category: r.category,
|
|
38
|
-
kind: r.kind, knowledgeType: r.knowledgeType, complexity: r.complexity,
|
|
39
|
-
scope: r.scope, tags: r.tags,
|
|
40
|
-
quality: r.quality, statistics: r.statistics,
|
|
41
|
-
}));
|
|
37
|
+
if (args.kind) filters.kind = args.kind;
|
|
38
|
+
if (args.language) filters.language = args.language;
|
|
39
|
+
if (args.category) filters.category = args.category;
|
|
40
|
+
if (args.knowledgeType) filters.knowledgeType = args.knowledgeType;
|
|
41
|
+
if (args.complexity) filters.complexity = args.complexity;
|
|
42
|
+
if (args.status) filters.lifecycle = args.status;
|
|
43
|
+
const result = await ks.list(filters, { page: 1, pageSize: args.limit || 20 });
|
|
44
|
+
const items = (result?.data || []).map(_projectItem);
|
|
42
45
|
return envelope({ success: true, data: { count: items.length, total: result?.pagination?.total || items.length, items }, meta: { tool: 'autosnippet_list_recipes' } });
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
export async function getRecipe(ctx, args) {
|
|
46
49
|
if (!args.id) throw new Error('id is required');
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
if (!
|
|
50
|
-
|
|
50
|
+
const ks = ctx.container.get('knowledgeService');
|
|
51
|
+
const entry = await ks.get(args.id);
|
|
52
|
+
if (!entry) throw new Error(`Knowledge entry not found: ${args.id}`);
|
|
53
|
+
const json = typeof entry.toJSON === 'function' ? entry.toJSON() : entry;
|
|
54
|
+
return envelope({ success: true, data: json, meta: { tool: 'autosnippet_get_recipe' } });
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
export async function recipeInsights(ctx, args) {
|
|
54
58
|
if (!args.id) throw new Error('id is required');
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
if (!
|
|
59
|
+
const ks = ctx.container.get('knowledgeService');
|
|
60
|
+
const entry = await ks.get(args.id);
|
|
61
|
+
if (!entry) throw new Error(`Knowledge entry not found: ${args.id}`);
|
|
62
|
+
const json = typeof entry.toJSON === 'function' ? entry.toJSON() : entry;
|
|
58
63
|
|
|
59
64
|
// 聚合关系摘要
|
|
60
65
|
const relationsSummary = {};
|
|
61
|
-
if (
|
|
62
|
-
for (const [type, targets] of Object.entries(
|
|
66
|
+
if (json.relations) {
|
|
67
|
+
for (const [type, targets] of Object.entries(json.relations)) {
|
|
63
68
|
if (Array.isArray(targets) && targets.length > 0) {
|
|
64
69
|
relationsSummary[type] = targets.length;
|
|
65
70
|
}
|
|
@@ -68,8 +73,8 @@ export async function recipeInsights(ctx, args) {
|
|
|
68
73
|
|
|
69
74
|
// 约束条件概览
|
|
70
75
|
const constraintsSummary = {};
|
|
71
|
-
if (
|
|
72
|
-
for (const [type, items] of Object.entries(
|
|
76
|
+
if (json.constraints) {
|
|
77
|
+
for (const [type, items] of Object.entries(json.constraints)) {
|
|
73
78
|
if (Array.isArray(items) && items.length > 0) {
|
|
74
79
|
constraintsSummary[type] = items;
|
|
75
80
|
}
|
|
@@ -77,43 +82,42 @@ export async function recipeInsights(ctx, args) {
|
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
const insights = {
|
|
80
|
-
id:
|
|
81
|
-
title:
|
|
82
|
-
trigger:
|
|
83
|
-
kind:
|
|
84
|
-
|
|
85
|
-
language:
|
|
86
|
-
category:
|
|
87
|
-
|
|
85
|
+
id: json.id,
|
|
86
|
+
title: json.title,
|
|
87
|
+
trigger: json.trigger || '',
|
|
88
|
+
kind: json.kind,
|
|
89
|
+
lifecycle: json.lifecycle,
|
|
90
|
+
language: json.language,
|
|
91
|
+
category: json.category,
|
|
92
|
+
knowledge_type: json.knowledge_type,
|
|
88
93
|
quality: {
|
|
89
|
-
overall:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
overall: json.quality?.overall ?? null,
|
|
95
|
+
completeness: json.quality?.completeness ?? null,
|
|
96
|
+
adaptation: json.quality?.adaptation ?? null,
|
|
97
|
+
documentation: json.quality?.documentation ?? null,
|
|
93
98
|
},
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
feedbackScore: recipe.statistics?.feedbackScore ?? 0,
|
|
99
|
+
stats: {
|
|
100
|
+
adoptions: json.stats?.adoptions ?? 0,
|
|
101
|
+
applications: json.stats?.applications ?? 0,
|
|
102
|
+
guard_hits: json.stats?.guard_hits ?? 0,
|
|
103
|
+
views: json.stats?.views ?? 0,
|
|
104
|
+
search_hits: json.stats?.search_hits ?? 0,
|
|
101
105
|
},
|
|
102
106
|
content: {
|
|
103
|
-
hasPattern: !!
|
|
104
|
-
hasRationale: !!
|
|
105
|
-
hasMarkdown: !!
|
|
106
|
-
stepsCount:
|
|
107
|
-
codeChangesCount:
|
|
107
|
+
hasPattern: !!json.content?.pattern,
|
|
108
|
+
hasRationale: !!json.content?.rationale,
|
|
109
|
+
hasMarkdown: !!json.content?.markdown,
|
|
110
|
+
stepsCount: json.content?.steps?.length ?? 0,
|
|
111
|
+
codeChangesCount: json.content?.code_changes?.length ?? 0,
|
|
108
112
|
},
|
|
109
113
|
relations: relationsSummary,
|
|
110
114
|
constraints: constraintsSummary,
|
|
111
|
-
tags:
|
|
112
|
-
complexity:
|
|
113
|
-
scope:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
tags: json.tags || [],
|
|
116
|
+
complexity: json.complexity,
|
|
117
|
+
scope: json.scope,
|
|
118
|
+
created_by: json.created_by,
|
|
119
|
+
created_at: json.created_at,
|
|
120
|
+
updated_at: json.updated_at,
|
|
117
121
|
};
|
|
118
122
|
|
|
119
123
|
return envelope({ success: true, data: insights, meta: { tool: 'autosnippet_recipe_insights' } });
|
|
@@ -121,11 +125,11 @@ export async function recipeInsights(ctx, args) {
|
|
|
121
125
|
|
|
122
126
|
export async function confirmUsage(ctx, args) {
|
|
123
127
|
if (!args.recipeId) throw new Error('recipeId is required');
|
|
124
|
-
const
|
|
128
|
+
const ks = ctx.container.get('knowledgeService');
|
|
125
129
|
const usageType = args.usageType || 'adoption';
|
|
126
130
|
const feedback = args.feedback || null;
|
|
127
131
|
|
|
128
|
-
await
|
|
132
|
+
await ks.incrementUsage(args.recipeId, usageType, {
|
|
129
133
|
feedback,
|
|
130
134
|
actor: 'mcp_user',
|
|
131
135
|
});
|
|
@@ -146,7 +150,7 @@ export async function confirmUsage(ctx, args) {
|
|
|
146
150
|
return envelope({
|
|
147
151
|
success: true,
|
|
148
152
|
data: { recipeId: args.recipeId, usageType, feedback },
|
|
149
|
-
message:
|
|
153
|
+
message: `已记录使用 ${args.recipeId} 的${usageType === 'adoption' ? '采纳' : '应用'}`,
|
|
150
154
|
meta: { tool: 'autosnippet_confirm_usage' },
|
|
151
155
|
});
|
|
152
156
|
}
|