autosnippet 2.19.7 → 3.0.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 +26 -18
- package/dashboard/dist/assets/{icons-C7FN32VL.js → icons-CEfgGaZi.js} +1 -1
- package/dashboard/dist/assets/{index-B5dbY-cS.js → index-_Sk_Dmg3.js} +42 -42
- package/dashboard/dist/assets/{vendor-Ba1BZjav.js → vendor-CEnWn7aV.js} +384 -380
- package/dashboard/dist/index.html +3 -5
- package/lib/external/mcp/McpServer.js +54 -65
- package/lib/external/mcp/handlers/bootstrap.js +7 -7
- package/lib/external/mcp/handlers/consolidated.js +290 -0
- package/lib/external/mcp/handlers/guard.js +5 -5
- package/lib/external/mcp/handlers/knowledge.js +23 -8
- package/lib/external/mcp/handlers/skill.js +4 -4
- package/lib/external/mcp/handlers/structure.js +16 -16
- package/lib/external/mcp/handlers/system.js +37 -40
- package/lib/external/mcp/tools.js +250 -646
- package/lib/service/cursor/RulesGenerator.js +2 -2
- package/lib/service/skills/SkillAdvisor.js +1 -1
- package/package.json +1 -1
- package/scripts/install-cursor-skill.js +10 -10
- package/skills/autosnippet-analysis/SKILL.md +23 -18
- package/skills/autosnippet-candidates/SKILL.md +38 -39
- package/skills/autosnippet-coldstart/SKILL.md +11 -14
- package/skills/autosnippet-concepts/SKILL.md +26 -31
- package/skills/autosnippet-create/SKILL.md +4 -6
- package/skills/autosnippet-guard/SKILL.md +14 -17
- package/skills/autosnippet-intent/SKILL.md +10 -11
- package/skills/autosnippet-lifecycle/SKILL.md +13 -18
- package/skills/autosnippet-recipes/SKILL.md +29 -62
- package/skills/autosnippet-structure/SKILL.md +19 -19
- package/templates/copilot-instructions.md +42 -41
- package/templates/recipes-setup/README.md +4 -7
- package/dashboard/dist/assets/react-markdown-Dc1U8Kko.js +0 -1
- package/dashboard/dist/assets/syntax-highlighter-BkDyUteW.js +0 -5
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>AutoSnippet Dashboard</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-_Sk_Dmg3.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/yaml-qRaU8Ldn.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-Ba1BZjav.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-CEnWn7aV.js">
|
|
12
11
|
<link rel="modulepreload" crossorigin href="/assets/axios-C0Zqfgkc.js">
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/icons-
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/react-markdown-Dc1U8Kko.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/icons-CEfgGaZi.js">
|
|
15
13
|
<link rel="stylesheet" crossorigin href="/assets/index-Bun3ld_J.css">
|
|
16
14
|
</head>
|
|
17
15
|
<body>
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AutoSnippet
|
|
2
|
+
* AutoSnippet V3 MCP Server — 整合版
|
|
3
3
|
*
|
|
4
4
|
* Model Context Protocol (stdio transport)
|
|
5
5
|
* 提供给 IDE AI Agent (Cursor/VSCode Copilot) 的工具集
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* V3 整合:39 → 16 工具(12 agent + 4 admin)
|
|
8
|
+
* 通过 ASD_MCP_TIER 环境变量控制可见工具集(agent/admin)
|
|
9
|
+
*
|
|
10
|
+
* Gateway 权限 gating: 写操作经过 Gateway 权限/宪法/审计检查(支持动态 resolver)
|
|
8
11
|
*
|
|
9
12
|
* 本文件仅包含服务编排层(初始化、路由、Gateway gating、生命周期)。
|
|
10
13
|
* 工具定义 → tools.js
|
|
11
14
|
* Handler 实现 → handlers/*.js
|
|
15
|
+
* 整合路由 → handlers/consolidated.js
|
|
12
16
|
*/
|
|
13
17
|
|
|
14
18
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
@@ -19,22 +23,15 @@ import {
|
|
|
19
23
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
20
24
|
import Logger from '../../infrastructure/logging/Logger.js';
|
|
21
25
|
import { envelope } from './envelope.js';
|
|
22
|
-
import { TOOLS, TOOL_GATEWAY_MAP } from './tools.js';
|
|
26
|
+
import { TOOLS, TOOL_GATEWAY_MAP, TIER_ORDER } from './tools.js';
|
|
23
27
|
import { wrapHandler } from './errorHandler.js';
|
|
24
28
|
|
|
25
29
|
// ─── Handler 模块 ─────────────────────────────────────────────
|
|
26
30
|
|
|
27
31
|
import * as systemHandlers from './handlers/system.js';
|
|
28
|
-
import * as searchHandlers from './handlers/search.js';
|
|
29
|
-
import * as browseHandlers from './handlers/browse.js';
|
|
30
|
-
import * as structureHandlers from './handlers/structure.js';
|
|
31
32
|
import * as candidateHandlers from './handlers/candidate.js';
|
|
32
|
-
import * as guardHandlers from './handlers/guard.js';
|
|
33
|
-
import * as bootstrapHandlers from './handlers/bootstrap.js';
|
|
34
|
-
import * as skillHandlers from './handlers/skill.js';
|
|
35
33
|
import * as knowledgeHandlers from './handlers/knowledge.js';
|
|
36
|
-
|
|
37
|
-
// import * as wikiHandlers from './handlers/wiki.js';
|
|
34
|
+
import * as consolidated from './handlers/consolidated.js';
|
|
38
35
|
|
|
39
36
|
// ─── McpServer 类 ─────────────────────────────────────────────
|
|
40
37
|
|
|
@@ -89,7 +86,7 @@ export class McpServer {
|
|
|
89
86
|
}
|
|
90
87
|
|
|
91
88
|
this.server = new Server(
|
|
92
|
-
{ name: 'autosnippet-
|
|
89
|
+
{ name: 'autosnippet-v3', version: '3.0.0' },
|
|
93
90
|
{ capabilities: { tools: {} } },
|
|
94
91
|
);
|
|
95
92
|
|
|
@@ -97,9 +94,20 @@ export class McpServer {
|
|
|
97
94
|
return this;
|
|
98
95
|
}
|
|
99
96
|
|
|
97
|
+
/**
|
|
98
|
+
* 注册 ListTools / CallTool 请求处理器
|
|
99
|
+
* ListTools 基于 ASD_MCP_TIER 过滤可见工具
|
|
100
|
+
*/
|
|
100
101
|
_registerHandlers() {
|
|
101
|
-
|
|
102
|
+
// ── ListTools: 按 tier 过滤 ──
|
|
103
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
104
|
+
const tierName = process.env.ASD_MCP_TIER || 'agent';
|
|
105
|
+
const maxTier = TIER_ORDER[tierName] ?? TIER_ORDER.agent;
|
|
106
|
+
const visible = TOOLS.filter(t => (TIER_ORDER[t.tier || 'agent'] ?? 0) <= maxTier);
|
|
107
|
+
return { tools: visible };
|
|
108
|
+
});
|
|
102
109
|
|
|
110
|
+
// ── CallTool: 路由到 handler ──
|
|
103
111
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
104
112
|
const { name, arguments: args } = request.params;
|
|
105
113
|
const t0 = Date.now();
|
|
@@ -129,59 +137,29 @@ export class McpServer {
|
|
|
129
137
|
}
|
|
130
138
|
|
|
131
139
|
/**
|
|
132
|
-
* 解析工具名到 handler
|
|
140
|
+
* 解析工具名到 handler 函数(V3 整合版)
|
|
133
141
|
* @private
|
|
134
142
|
*/
|
|
135
143
|
_resolveHandler(name) {
|
|
136
144
|
const HANDLER_MAP = {
|
|
137
|
-
//
|
|
138
|
-
autosnippet_health:
|
|
139
|
-
autosnippet_capabilities:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
autosnippet_list_rules: (ctx, args) => browseHandlers.listByKind(ctx, 'rule', args),
|
|
147
|
-
autosnippet_list_patterns: (ctx, args) => browseHandlers.listByKind(ctx, 'pattern', args),
|
|
148
|
-
autosnippet_list_facts: (ctx, args) => browseHandlers.listByKind(ctx, 'fact', args),
|
|
149
|
-
autosnippet_list_recipes: (ctx, args) => browseHandlers.listRecipes(ctx, args),
|
|
150
|
-
autosnippet_get_recipe: (ctx, args) => browseHandlers.getRecipe(ctx, args),
|
|
151
|
-
autosnippet_recipe_insights: (ctx, args) => browseHandlers.recipeInsights(ctx, args),
|
|
152
|
-
autosnippet_confirm_usage: (ctx, args) => browseHandlers.confirmUsage(ctx, args),
|
|
153
|
-
// 项目结构 & 图谱
|
|
154
|
-
autosnippet_get_targets: (ctx) => structureHandlers.getTargets(ctx),
|
|
155
|
-
autosnippet_get_target_files: (ctx, args) => structureHandlers.getTargetFiles(ctx, args),
|
|
156
|
-
autosnippet_get_target_metadata: (ctx, args) => structureHandlers.getTargetMetadata(ctx, args),
|
|
157
|
-
autosnippet_graph_query: (ctx, args) => structureHandlers.graphQuery(ctx, args),
|
|
158
|
-
autosnippet_graph_impact: (ctx, args) => structureHandlers.graphImpact(ctx, args),
|
|
159
|
-
autosnippet_graph_path: (ctx, args) => structureHandlers.graphPath(ctx, args),
|
|
160
|
-
autosnippet_graph_stats: (ctx) => structureHandlers.graphStats(ctx),
|
|
161
|
-
// 候选校验 & AI 补全
|
|
162
|
-
autosnippet_validate_candidate: (ctx, args) => candidateHandlers.validateCandidate(ctx, args),
|
|
163
|
-
autosnippet_check_duplicate: (ctx, args) => candidateHandlers.checkDuplicate(ctx, args),
|
|
164
|
-
autosnippet_enrich_candidates: (ctx, args) => candidateHandlers.enrichCandidates(ctx, args),
|
|
165
|
-
// Guard & 扫描
|
|
166
|
-
autosnippet_guard_check: (ctx, args) => guardHandlers.guardCheck(ctx, args),
|
|
167
|
-
autosnippet_guard_audit_files: (ctx, args) => guardHandlers.guardAuditFiles(ctx, args),
|
|
168
|
-
autosnippet_scan_project: (ctx, args) => guardHandlers.scanProject(ctx, args),
|
|
169
|
-
// Bootstrap 冷启动
|
|
170
|
-
autosnippet_bootstrap_knowledge: (ctx, args) => bootstrapHandlers.bootstrapKnowledge(ctx, args),
|
|
171
|
-
autosnippet_bootstrap_refine: (ctx, args) => bootstrapHandlers.bootstrapRefine(ctx, args),
|
|
172
|
-
// Skills
|
|
173
|
-
autosnippet_list_skills: () => skillHandlers.listSkills(),
|
|
174
|
-
autosnippet_load_skill: (ctx, args) => skillHandlers.loadSkill(ctx, args),
|
|
175
|
-
autosnippet_create_skill: (ctx, args) => skillHandlers.createSkill(ctx, args),
|
|
176
|
-
autosnippet_delete_skill: (ctx, args) => skillHandlers.deleteSkill(ctx, args),
|
|
177
|
-
autosnippet_update_skill: (ctx, args) => skillHandlers.updateSkill(ctx, args),
|
|
178
|
-
autosnippet_suggest_skills: (ctx) => skillHandlers.suggestSkills(ctx),
|
|
179
|
-
// V3 知识条目
|
|
180
|
-
autosnippet_submit_knowledge: (ctx, args) => knowledgeHandlers.submitKnowledge(ctx, args),
|
|
145
|
+
// ── Agent 层 (12) ──
|
|
146
|
+
autosnippet_health: (ctx) => systemHandlers.health(ctx),
|
|
147
|
+
autosnippet_capabilities: () => systemHandlers.capabilities(),
|
|
148
|
+
autosnippet_search: (ctx, args) => consolidated.consolidatedSearch(ctx, args),
|
|
149
|
+
autosnippet_knowledge: (ctx, args) => consolidated.consolidatedKnowledge(ctx, args),
|
|
150
|
+
autosnippet_structure: (ctx, args) => consolidated.consolidatedStructure(ctx, args),
|
|
151
|
+
autosnippet_graph: (ctx, args) => consolidated.consolidatedGraph(ctx, args),
|
|
152
|
+
autosnippet_guard: (ctx, args) => consolidated.consolidatedGuard(ctx, args),
|
|
153
|
+
autosnippet_submit_knowledge: (ctx, args) => consolidated.enhancedSubmitKnowledge(ctx, args),
|
|
181
154
|
autosnippet_submit_knowledge_batch: (ctx, args) => knowledgeHandlers.submitKnowledgeBatch(ctx, args),
|
|
182
|
-
autosnippet_knowledge_lifecycle: (ctx, args) => knowledgeHandlers.knowledgeLifecycle(ctx, args),
|
|
183
155
|
autosnippet_save_document: (ctx, args) => knowledgeHandlers.saveDocument(ctx, args),
|
|
184
|
-
|
|
156
|
+
autosnippet_skill: (ctx, args) => consolidated.consolidatedSkill(ctx, args),
|
|
157
|
+
autosnippet_bootstrap: (ctx, args) => consolidated.consolidatedBootstrap(ctx, args),
|
|
158
|
+
// ── Admin 层 (+4) ──
|
|
159
|
+
autosnippet_enrich_candidates: (ctx, args) => candidateHandlers.enrichCandidates(ctx, args),
|
|
160
|
+
autosnippet_knowledge_lifecycle: (ctx, args) => knowledgeHandlers.knowledgeLifecycle(ctx, args),
|
|
161
|
+
autosnippet_validate_candidate: (ctx, args) => candidateHandlers.validateCandidate(ctx, args),
|
|
162
|
+
autosnippet_check_duplicate: (ctx, args) => candidateHandlers.checkDuplicate(ctx, args),
|
|
185
163
|
};
|
|
186
164
|
return HANDLER_MAP[name] || null;
|
|
187
165
|
}
|
|
@@ -189,11 +167,18 @@ export class McpServer {
|
|
|
189
167
|
/**
|
|
190
168
|
* Gateway 权限 gating — 写操作验证权限/宪法/审计
|
|
191
169
|
* 只读工具直接跳过(不在 TOOL_GATEWAY_MAP 中)
|
|
170
|
+
* 支持动态 resolver(operation-based 工具按参数解析 action/resource)
|
|
192
171
|
*/
|
|
193
172
|
async _gatewayGate(toolName, args) {
|
|
194
|
-
|
|
173
|
+
let mapping = TOOL_GATEWAY_MAP[toolName];
|
|
195
174
|
if (!mapping) return; // 只读工具,跳过
|
|
196
175
|
|
|
176
|
+
// 动态 resolver:根据 args 计算实际 action/resource
|
|
177
|
+
if (typeof mapping.resolver === 'function') {
|
|
178
|
+
mapping = mapping.resolver(args);
|
|
179
|
+
if (!mapping) return; // resolver 返回 null 表示只读操作
|
|
180
|
+
}
|
|
181
|
+
|
|
197
182
|
try {
|
|
198
183
|
const gateway = this.container.get('gateway');
|
|
199
184
|
if (!gateway) return; // Gateway 未初始化,降级放行
|
|
@@ -229,9 +214,13 @@ export class McpServer {
|
|
|
229
214
|
await this.initialize();
|
|
230
215
|
const transport = new StdioServerTransport();
|
|
231
216
|
await this.server.connect(transport);
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
217
|
+
|
|
218
|
+
const tierName = process.env.ASD_MCP_TIER || 'agent';
|
|
219
|
+
const maxTier = TIER_ORDER[tierName] ?? TIER_ORDER.agent;
|
|
220
|
+
const visibleCount = TOOLS.filter(t => (TIER_ORDER[t.tier || 'agent'] ?? 0) <= maxTier).length;
|
|
221
|
+
|
|
222
|
+
this.logger.info(`MCP Server started (stdio) — ${visibleCount} tools [tier=${tierName}]`);
|
|
223
|
+
process.stderr.write(`AutoSnippet MCP ready — ${visibleCount} tools [tier=${tierName}]\n`);
|
|
235
224
|
}
|
|
236
225
|
|
|
237
226
|
async shutdown() {
|
|
@@ -190,7 +190,7 @@ export async function bootstrapKnowledge(ctx, args) {
|
|
|
190
190
|
return envelope({
|
|
191
191
|
success: true,
|
|
192
192
|
data: { report, message: 'No source files found, nothing to bootstrap' },
|
|
193
|
-
meta: { tool: '
|
|
193
|
+
meta: { tool: 'autosnippet_bootstrap', responseTimeMs: Date.now() - t0 },
|
|
194
194
|
});
|
|
195
195
|
}
|
|
196
196
|
|
|
@@ -514,13 +514,13 @@ export async function bootstrapKnowledge(ctx, args) {
|
|
|
514
514
|
'',
|
|
515
515
|
'== 完成后可执行的后续操作 ==',
|
|
516
516
|
'1. 调用 autosnippet_enrich_candidates(candidateIds) 补全候选缺失字段',
|
|
517
|
-
'2. 调用
|
|
517
|
+
'2. 调用 autosnippet_bootstrap({ operation: "refine" }) 对候选进行 AI 精炼',
|
|
518
518
|
'3. 使用 autosnippet_submit_knowledge_batch 手动提交更多知识条目',
|
|
519
|
-
'4. 使用
|
|
519
|
+
'4. 使用 autosnippet_skill({ operation: "load", name }) 加载自动生成的 Project Skills',
|
|
520
520
|
'',
|
|
521
521
|
'== 宏观维度 → Project Skills ==',
|
|
522
522
|
'宏观维度(architecture/code-standard/project-profile/agent-guidelines/objc-deep-scan/category-scan)',
|
|
523
|
-
'自动生成 Project Skill 到 AutoSnippet/skills/,可通过
|
|
523
|
+
'自动生成 Project Skill 到 AutoSnippet/skills/,可通过 autosnippet_skill({ operation: "load" }) 加载。',
|
|
524
524
|
],
|
|
525
525
|
};
|
|
526
526
|
|
|
@@ -607,7 +607,7 @@ export async function bootstrapKnowledge(ctx, args) {
|
|
|
607
607
|
return envelope({
|
|
608
608
|
success: true,
|
|
609
609
|
data: responseData,
|
|
610
|
-
meta: { tool: '
|
|
610
|
+
meta: { tool: 'autosnippet_bootstrap', responseTimeMs: Date.now() - t0 },
|
|
611
611
|
});
|
|
612
612
|
}
|
|
613
613
|
|
|
@@ -653,7 +653,7 @@ export async function bootstrapRefine(ctx, args) {
|
|
|
653
653
|
}
|
|
654
654
|
|
|
655
655
|
if (entries.length === 0) {
|
|
656
|
-
return envelope({ success: true, data: { refined: 0, total: 0, errors: [], results: [] }, meta: { tool: '
|
|
656
|
+
return envelope({ success: true, data: { refined: 0, total: 0, errors: [], results: [] }, meta: { tool: 'autosnippet_bootstrap', responseTimeMs: Date.now() - t0 } });
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
onProgress?.('refine:started', { total: entries.length, candidateIds: entries.map(e => e.id) });
|
|
@@ -872,6 +872,6 @@ ${refineInstruction}
|
|
|
872
872
|
results,
|
|
873
873
|
message: `Phase 6 AI 润色完成: ${refined}/${entries.length} 条知识条目已更新${args.dryRun ? '(预览模式)' : ''}`,
|
|
874
874
|
},
|
|
875
|
-
meta: { tool: '
|
|
875
|
+
meta: { tool: 'autosnippet_bootstrap', responseTimeMs: Date.now() - t0 },
|
|
876
876
|
});
|
|
877
877
|
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP 整合 Handler — 参数路由层
|
|
3
|
+
*
|
|
4
|
+
* 将整合后的工具(autosnippet_search / knowledge / structure / graph / guard / skill / bootstrap)
|
|
5
|
+
* 按 operation / mode 参数路由到已有 handler 实现。
|
|
6
|
+
*
|
|
7
|
+
* 不包含业务逻辑,仅做参数解构 → 路由 → 转发。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as searchHandlers from './search.js';
|
|
11
|
+
import * as browseHandlers from './browse.js';
|
|
12
|
+
import * as structureHandlers from './structure.js';
|
|
13
|
+
import * as guardHandlers from './guard.js';
|
|
14
|
+
import * as skillHandlers from './skill.js';
|
|
15
|
+
import * as bootstrapHandlers from './bootstrap.js';
|
|
16
|
+
import * as candidateHandlers from './candidate.js';
|
|
17
|
+
|
|
18
|
+
// ─── autosnippet_search (整合 4 → 1) ────────────────────────
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 统合搜索:根据 mode 参数路由到对应搜索 handler
|
|
22
|
+
* auto (默认) → search()
|
|
23
|
+
* keyword → keywordSearch()
|
|
24
|
+
* semantic → semanticSearch()
|
|
25
|
+
* context → contextSearch()
|
|
26
|
+
*/
|
|
27
|
+
export async function consolidatedSearch(ctx, args) {
|
|
28
|
+
const mode = args.mode || 'auto';
|
|
29
|
+
switch (mode) {
|
|
30
|
+
case 'keyword':
|
|
31
|
+
return searchHandlers.keywordSearch(ctx, args);
|
|
32
|
+
case 'semantic':
|
|
33
|
+
return searchHandlers.semanticSearch(ctx, args);
|
|
34
|
+
case 'context':
|
|
35
|
+
return searchHandlers.contextSearch(ctx, args);
|
|
36
|
+
case 'auto':
|
|
37
|
+
case 'bm25':
|
|
38
|
+
default:
|
|
39
|
+
return searchHandlers.search(ctx, { ...args, mode });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ─── autosnippet_knowledge (整合 7 → 1) ─────────────────────
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 知识浏览:根据 operation 参数路由
|
|
47
|
+
* list (默认) → listByKind() 或 listRecipes()
|
|
48
|
+
* get → getRecipe()
|
|
49
|
+
* insights → recipeInsights()
|
|
50
|
+
* confirm_usage → confirmUsage()
|
|
51
|
+
*/
|
|
52
|
+
export async function consolidatedKnowledge(ctx, args) {
|
|
53
|
+
const op = args.operation || 'list';
|
|
54
|
+
switch (op) {
|
|
55
|
+
case 'list': {
|
|
56
|
+
const kind = args.kind;
|
|
57
|
+
if (kind && kind !== 'all') {
|
|
58
|
+
return browseHandlers.listByKind(ctx, kind, args);
|
|
59
|
+
}
|
|
60
|
+
return browseHandlers.listRecipes(ctx, args);
|
|
61
|
+
}
|
|
62
|
+
case 'get':
|
|
63
|
+
return browseHandlers.getRecipe(ctx, args);
|
|
64
|
+
case 'insights':
|
|
65
|
+
return browseHandlers.recipeInsights(ctx, args);
|
|
66
|
+
case 'confirm_usage':
|
|
67
|
+
// confirmUsage expects { recipeId, usageType, feedback }
|
|
68
|
+
// 适配:如果传了 id 但没传 recipeId,自动映射
|
|
69
|
+
if (args.id && !args.recipeId) {
|
|
70
|
+
args.recipeId = args.id;
|
|
71
|
+
}
|
|
72
|
+
return browseHandlers.confirmUsage(ctx, args);
|
|
73
|
+
default:
|
|
74
|
+
throw new Error(`Unknown knowledge operation: ${op}. Expected: list, get, insights, confirm_usage`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ─── autosnippet_structure (整合 3 → 1) ─────────────────────
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 项目结构:根据 operation 参数路由
|
|
82
|
+
* targets (默认) → getTargets()
|
|
83
|
+
* files → getTargetFiles()
|
|
84
|
+
* metadata → getTargetMetadata()
|
|
85
|
+
*/
|
|
86
|
+
export async function consolidatedStructure(ctx, args) {
|
|
87
|
+
const op = args.operation || 'targets';
|
|
88
|
+
switch (op) {
|
|
89
|
+
case 'targets':
|
|
90
|
+
return structureHandlers.getTargets(ctx, args);
|
|
91
|
+
case 'files':
|
|
92
|
+
return structureHandlers.getTargetFiles(ctx, args);
|
|
93
|
+
case 'metadata':
|
|
94
|
+
return structureHandlers.getTargetMetadata(ctx, args);
|
|
95
|
+
default:
|
|
96
|
+
throw new Error(`Unknown structure operation: ${op}. Expected: targets, files, metadata`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ─── autosnippet_graph (整合 4 → 1) ─────────────────────────
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 知识图谱:根据 operation 参数路由
|
|
104
|
+
* query → graphQuery()
|
|
105
|
+
* impact → graphImpact()
|
|
106
|
+
* path → graphPath()
|
|
107
|
+
* stats → graphStats()
|
|
108
|
+
*/
|
|
109
|
+
export async function consolidatedGraph(ctx, args) {
|
|
110
|
+
const op = args.operation;
|
|
111
|
+
if (!op) throw new Error('Missing required parameter: operation. Expected: query, impact, path, stats');
|
|
112
|
+
switch (op) {
|
|
113
|
+
case 'query':
|
|
114
|
+
return structureHandlers.graphQuery(ctx, args);
|
|
115
|
+
case 'impact':
|
|
116
|
+
return structureHandlers.graphImpact(ctx, args);
|
|
117
|
+
case 'path':
|
|
118
|
+
return structureHandlers.graphPath(ctx, args);
|
|
119
|
+
case 'stats':
|
|
120
|
+
return structureHandlers.graphStats(ctx);
|
|
121
|
+
default:
|
|
122
|
+
throw new Error(`Unknown graph operation: ${op}. Expected: query, impact, path, stats`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─── autosnippet_guard (整合 2 → 1) ─────────────────────────
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Guard 检查:按参数自动路由
|
|
130
|
+
* 有 code → guardCheck() (单文件)
|
|
131
|
+
* 有 files → guardAuditFiles() (多文件)
|
|
132
|
+
*/
|
|
133
|
+
export async function consolidatedGuard(ctx, args) {
|
|
134
|
+
if (args.files && Array.isArray(args.files) && args.files.length > 0) {
|
|
135
|
+
return guardHandlers.guardAuditFiles(ctx, args);
|
|
136
|
+
}
|
|
137
|
+
if (args.code) {
|
|
138
|
+
return guardHandlers.guardCheck(ctx, args);
|
|
139
|
+
}
|
|
140
|
+
throw new Error('autosnippet_guard requires either "code" (single check) or "files" (batch audit) parameter');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── autosnippet_skill (整合 6 → 1) ─────────────────────────
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Skill 管理:根据 operation 参数路由
|
|
147
|
+
* list → listSkills()
|
|
148
|
+
* load → loadSkill()
|
|
149
|
+
* create → createSkill()
|
|
150
|
+
* update → updateSkill()
|
|
151
|
+
* delete → deleteSkill()
|
|
152
|
+
* suggest → suggestSkills()
|
|
153
|
+
*/
|
|
154
|
+
export async function consolidatedSkill(ctx, args) {
|
|
155
|
+
const op = args.operation;
|
|
156
|
+
if (!op) throw new Error('Missing required parameter: operation. Expected: list, load, create, update, delete, suggest');
|
|
157
|
+
|
|
158
|
+
// loadSkill expects { skillName }, map from { name }
|
|
159
|
+
if (args.name && !args.skillName) {
|
|
160
|
+
args.skillName = args.name;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
switch (op) {
|
|
164
|
+
case 'list':
|
|
165
|
+
return skillHandlers.listSkills();
|
|
166
|
+
case 'load':
|
|
167
|
+
return skillHandlers.loadSkill(ctx, args);
|
|
168
|
+
case 'create':
|
|
169
|
+
return skillHandlers.createSkill(ctx, args);
|
|
170
|
+
case 'update':
|
|
171
|
+
return skillHandlers.updateSkill(ctx, args);
|
|
172
|
+
case 'delete':
|
|
173
|
+
return skillHandlers.deleteSkill(ctx, args);
|
|
174
|
+
case 'suggest':
|
|
175
|
+
return skillHandlers.suggestSkills(ctx);
|
|
176
|
+
default:
|
|
177
|
+
throw new Error(`Unknown skill operation: ${op}. Expected: list, load, create, update, delete, suggest`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ─── autosnippet_bootstrap (整合 3 → 1) ─────────────────────
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 冷启动 & 扫描:根据 operation 参数路由
|
|
185
|
+
* knowledge → bootstrapKnowledge() (完整冷启动)
|
|
186
|
+
* refine → bootstrapRefine() (AI 润色)
|
|
187
|
+
* scan → scanProject() (轻量探查)
|
|
188
|
+
*/
|
|
189
|
+
export async function consolidatedBootstrap(ctx, args) {
|
|
190
|
+
const op = args.operation;
|
|
191
|
+
if (!op) throw new Error('Missing required parameter: operation. Expected: knowledge, refine, scan');
|
|
192
|
+
switch (op) {
|
|
193
|
+
case 'knowledge':
|
|
194
|
+
return bootstrapHandlers.bootstrapKnowledge(ctx, args);
|
|
195
|
+
case 'refine':
|
|
196
|
+
return bootstrapHandlers.bootstrapRefine(ctx, args);
|
|
197
|
+
case 'scan':
|
|
198
|
+
return guardHandlers.scanProject(ctx, args);
|
|
199
|
+
default:
|
|
200
|
+
throw new Error(`Unknown bootstrap operation: ${op}. Expected: knowledge, refine, scan`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ─── autosnippet_submit_knowledge (增强:严格前置校验 + dedup) ──
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 增强版提交:严格前置校验,缺少必要字段直接拒绝(不入库)。
|
|
208
|
+
* 通过校验后执行提交 + 去重检测,结果附在响应中。
|
|
209
|
+
*
|
|
210
|
+
* 设计原则:
|
|
211
|
+
* - 不降级:缺字段不自动补全,要求 Agent 一次性生成完整数据
|
|
212
|
+
* - 不重复提交:拒绝时不创建任何记录,Agent 需补齐后重新调用
|
|
213
|
+
*/
|
|
214
|
+
export async function enhancedSubmitKnowledge(ctx, args) {
|
|
215
|
+
const { submitKnowledge } = await import('./knowledge.js');
|
|
216
|
+
const { checkRecipeReadiness } = await import('../../../shared/RecipeReadinessChecker.js');
|
|
217
|
+
const { envelope } = await import('../envelope.js');
|
|
218
|
+
|
|
219
|
+
const skipDuplicateCheck = args.skipDuplicateCheck === true;
|
|
220
|
+
|
|
221
|
+
// ── 严格前置校验:RecipeReady 不通过则直接拒绝 ──
|
|
222
|
+
const readinessInput = {
|
|
223
|
+
title: args.title,
|
|
224
|
+
code: args.content?.pattern || args.code || '',
|
|
225
|
+
language: args.language,
|
|
226
|
+
category: args.category,
|
|
227
|
+
trigger: args.trigger,
|
|
228
|
+
description: args.description,
|
|
229
|
+
headers: args.headers,
|
|
230
|
+
reasoning: args.reasoning,
|
|
231
|
+
knowledgeType: args.knowledgeType,
|
|
232
|
+
complexity: args.complexity,
|
|
233
|
+
usageGuide: args.usageGuide,
|
|
234
|
+
rationale: args.content?.rationale || args.rationale,
|
|
235
|
+
kind: args.kind,
|
|
236
|
+
doClause: args.doClause,
|
|
237
|
+
dontClause: args.dontClause,
|
|
238
|
+
whenClause: args.whenClause,
|
|
239
|
+
topicHint: args.topicHint,
|
|
240
|
+
coreCode: args.coreCode,
|
|
241
|
+
};
|
|
242
|
+
const readiness = checkRecipeReadiness(readinessInput);
|
|
243
|
+
if (!readiness.ready) {
|
|
244
|
+
return envelope({
|
|
245
|
+
success: false,
|
|
246
|
+
message: `提交被拒绝:缺少必要字段 [${readiness.missing.join(', ')}]。请在单次调用中补齐所有字段后重新提交,不要分步提交或先提交再补全。`,
|
|
247
|
+
errorCode: 'INCOMPLETE_SUBMISSION',
|
|
248
|
+
data: {
|
|
249
|
+
missingFields: readiness.missing,
|
|
250
|
+
suggestions: readiness.suggestions,
|
|
251
|
+
requiredFields: ['title', 'language', 'content', 'kind', 'doClause', 'category', 'trigger', 'description', 'headers', 'usageGuide', 'knowledgeType', 'rationale (in content.rationale)'],
|
|
252
|
+
},
|
|
253
|
+
meta: { tool: 'autosnippet_submit_knowledge' },
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ── 校验通过,执行提交 ──
|
|
258
|
+
const result = await submitKnowledge(ctx, args);
|
|
259
|
+
|
|
260
|
+
// 如果提交本身失败,直接返回
|
|
261
|
+
if (result && !result.success) return result;
|
|
262
|
+
|
|
263
|
+
// ── 附加去重检测结果(非阻塞) ──
|
|
264
|
+
let duplicateCheck = null;
|
|
265
|
+
if (!skipDuplicateCheck) {
|
|
266
|
+
try {
|
|
267
|
+
const dedupCandidate = {
|
|
268
|
+
title: args.title,
|
|
269
|
+
summary: args.description || '',
|
|
270
|
+
code: args.content?.pattern || '',
|
|
271
|
+
};
|
|
272
|
+
const dedupResult = await candidateHandlers.checkDuplicate(ctx, { candidate: dedupCandidate, threshold: 0.7, topK: 3 });
|
|
273
|
+
if (dedupResult?.data) {
|
|
274
|
+
duplicateCheck = {
|
|
275
|
+
hasSimilar: dedupResult.data.hasDuplicate ?? (dedupResult.data.matches?.length > 0),
|
|
276
|
+
closest: dedupResult.data.matches?.[0] || null,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
} catch {
|
|
280
|
+
duplicateCheck = { hasSimilar: false, note: 'dedup skipped due to error' };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// 将去重结果附到响应中
|
|
285
|
+
if (result && result.data) {
|
|
286
|
+
result.data.duplicateCheck = duplicateCheck;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
@@ -15,7 +15,7 @@ export async function guardCheck(ctx, args) {
|
|
|
15
15
|
return envelope({
|
|
16
16
|
success: true,
|
|
17
17
|
data: { language: args.language || 'unknown', violations: [], summary: { total: 0, errors: 0, warnings: 0 } },
|
|
18
|
-
meta: { tool: '
|
|
18
|
+
meta: { tool: 'autosnippet_guard', note: 'Empty code — skipped' },
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -43,7 +43,7 @@ export async function guardCheck(ctx, args) {
|
|
|
43
43
|
return envelope({
|
|
44
44
|
success: true,
|
|
45
45
|
data: { language, violations, summary: { total: violations.length, errors: violations.filter(v => v.severity === 'error').length, warnings: violations.filter(v => v.severity === 'warning').length }, ...(warnings.length ? { warnings } : {}) },
|
|
46
|
-
meta: { tool: '
|
|
46
|
+
meta: { tool: 'autosnippet_guard' },
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -97,7 +97,7 @@ export async function guardAuditFiles(ctx, args) {
|
|
|
97
97
|
})),
|
|
98
98
|
...(result.crossFileViolations?.length ? { crossFileViolations: result.crossFileViolations } : {}),
|
|
99
99
|
},
|
|
100
|
-
meta: { tool: '
|
|
100
|
+
meta: { tool: 'autosnippet_guard' },
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -113,7 +113,7 @@ export async function scanProject(ctx, args) {
|
|
|
113
113
|
const allTargets = await spm.listTargets();
|
|
114
114
|
|
|
115
115
|
if (!allTargets || allTargets.length === 0) {
|
|
116
|
-
return envelope({ success: true, data: { targets: [], files: [], guardAudit: null, message: 'No SPM targets found' }, meta: { tool: '
|
|
116
|
+
return envelope({ success: true, data: { targets: [], files: [], guardAudit: null, message: 'No SPM targets found' }, meta: { tool: 'autosnippet_bootstrap' } });
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// 收集所有文件(去重)
|
|
@@ -197,6 +197,6 @@ export async function scanProject(ctx, args) {
|
|
|
197
197
|
...(guardAudit.crossFileViolations?.length ? { crossFileViolations: guardAudit.crossFileViolations } : {}),
|
|
198
198
|
} : null,
|
|
199
199
|
},
|
|
200
|
-
meta: { tool: '
|
|
200
|
+
meta: { tool: 'autosnippet_bootstrap' },
|
|
201
201
|
});
|
|
202
202
|
}
|
|
@@ -115,7 +115,6 @@ export async function submitKnowledge(ctx, args) {
|
|
|
115
115
|
ready: false,
|
|
116
116
|
missingFields: readiness.missing,
|
|
117
117
|
suggestions: readiness.suggestions,
|
|
118
|
-
hint: '请补全以上字段后重新提交,或调用 autosnippet_enrich_candidates 进行完整性诊断',
|
|
119
118
|
};
|
|
120
119
|
}
|
|
121
120
|
|
|
@@ -163,8 +162,22 @@ export async function submitKnowledgeBatch(ctx, args) {
|
|
|
163
162
|
const source = args.source || 'cursor-scan';
|
|
164
163
|
let count = 0;
|
|
165
164
|
const itemErrors = [];
|
|
165
|
+
const rejectedItems = [];
|
|
166
166
|
|
|
167
167
|
for (let i = 0; i < items.length; i++) {
|
|
168
|
+
// ── 严格前置校验:缺少必要字段的条目直接拒绝,不入库 ──
|
|
169
|
+
const readinessInput = _toReadinessInput(items[i]);
|
|
170
|
+
const readiness = checkRecipeReadiness(readinessInput);
|
|
171
|
+
if (!readiness.ready) {
|
|
172
|
+
rejectedItems.push({
|
|
173
|
+
index: i,
|
|
174
|
+
title: items[i].title || '(untitled)',
|
|
175
|
+
missingFields: readiness.missing,
|
|
176
|
+
suggestions: readiness.suggestions,
|
|
177
|
+
});
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
168
181
|
try {
|
|
169
182
|
const itemData = _enrichToV3({ ...items[i], source }, ctx.container);
|
|
170
183
|
await service.create(itemData, { userId: 'mcp' });
|
|
@@ -177,15 +190,15 @@ export async function submitKnowledgeBatch(ctx, args) {
|
|
|
177
190
|
const data = { count, total: items.length, targetName: args.target_name };
|
|
178
191
|
if (itemErrors.length > 0) data.errors = itemErrors;
|
|
179
192
|
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
data.
|
|
185
|
-
|
|
193
|
+
// 被拒绝的条目:告知 Agent 需补齐哪些字段
|
|
194
|
+
if (rejectedItems.length > 0) {
|
|
195
|
+
const allMissing = [...new Set(rejectedItems.flatMap(it => it.missingFields))];
|
|
196
|
+
data.rejectedItems = rejectedItems;
|
|
197
|
+
data.rejectedSummary = {
|
|
198
|
+
rejectedCount: rejectedItems.length,
|
|
186
199
|
totalCount: items.length,
|
|
187
200
|
commonMissingFields: allMissing,
|
|
188
|
-
|
|
201
|
+
message: `${rejectedItems.length}/${items.length} 条知识条目因缺少必要字段被拒绝(${allMissing.join(', ')})。请一次性补齐所有字段后重新提交被拒绝的条目。`,
|
|
189
202
|
};
|
|
190
203
|
}
|
|
191
204
|
|
|
@@ -330,6 +343,8 @@ function _toReadinessInput(args) {
|
|
|
330
343
|
} : undefined,
|
|
331
344
|
knowledgeType: args.knowledgeType,
|
|
332
345
|
complexity: args.complexity,
|
|
346
|
+
usageGuide: args.usageGuide,
|
|
347
|
+
rationale: args.content?.rationale || args.rationale,
|
|
333
348
|
// Cursor Delivery 字段
|
|
334
349
|
kind: args.kind,
|
|
335
350
|
doClause: args.doClause,
|