autosnippet 3.0.11 → 3.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +64 -1
- package/config/default.json +9 -0
- package/dashboard/dist/assets/{index-I2ySoCmF.js → index-Bnm26ulL.js} +47 -47
- package/dashboard/dist/index.html +1 -1
- package/lib/cli/SetupService.js +92 -5
- package/lib/cli/UpgradeService.js +14 -5
- package/lib/core/discovery/GenericDiscoverer.js +4 -28
- package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +246 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +80 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +275 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +600 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +125 -342
- package/lib/external/mcp/handlers/bootstrap/refine.js +362 -0
- package/lib/external/mcp/handlers/bootstrap.js +6 -590
- package/lib/external/mcp/handlers/browse.js +119 -9
- package/lib/external/mcp/handlers/guard.js +25 -6
- package/lib/external/mcp/handlers/search.js +56 -24
- package/lib/http/routes/guardRules.js +9 -17
- package/lib/injection/ServiceContainer.js +12 -3
- package/lib/platform/ios/xcode/XcodeImportResolver.js +434 -0
- package/lib/platform/ios/xcode/XcodeIntegration.js +40 -659
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +220 -0
- package/lib/service/chat/ChatAgent.js +39 -418
- package/lib/service/chat/ChatAgentPrompts.js +149 -0
- package/lib/service/chat/ChatAgentTasks.js +297 -0
- package/lib/service/chat/tools/_shared.js +61 -0
- package/lib/service/chat/tools/ai-analysis.js +284 -0
- package/lib/service/chat/tools/ast-graph.js +681 -0
- package/lib/service/chat/tools/composite.js +496 -0
- package/lib/service/chat/tools/guard.js +265 -0
- package/lib/service/chat/tools/index.js +250 -0
- package/lib/service/chat/tools/infrastructure.js +222 -0
- package/lib/service/chat/tools/knowledge-graph.js +234 -0
- package/lib/service/chat/tools/lifecycle.js +469 -0
- package/lib/service/chat/tools/project-access.js +923 -0
- package/lib/service/chat/tools/query.js +264 -0
- package/lib/service/chat/tools.js +14 -3994
- package/lib/service/cursor/AgentInstructionsGenerator.js +395 -0
- package/lib/service/cursor/CursorDeliveryPipeline.js +70 -11
- package/lib/service/cursor/FileProtection.js +116 -0
- package/lib/service/cursor/KnowledgeCompressor.js +61 -11
- package/lib/service/cursor/SkillsSyncer.js +5 -3
- package/lib/service/cursor/TopicClassifier.js +19 -3
- package/lib/service/guard/ExclusionManager.js +26 -2
- package/lib/service/guard/GuardCheckEngine.js +38 -370
- package/lib/service/guard/GuardCodeChecks.js +362 -0
- package/lib/service/guard/GuardCrossFileChecks.js +307 -0
- package/lib/service/guard/GuardPatternUtils.js +180 -0
- package/lib/service/guard/GuardService.js +80 -38
- package/lib/service/module/ModuleService.js +1 -0
- package/lib/service/search/SearchEngine.js +10 -2
- package/lib/service/wiki/WikiGenerator.js +226 -1532
- package/lib/service/wiki/WikiRenderers.js +1878 -0
- package/lib/service/wiki/WikiUtils.js +907 -0
- package/lib/shared/LanguageService.js +299 -0
- package/package.json +1 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatAgentPrompts — ChatAgent 提示词构建和文本处理方法(从 ChatAgent.js 提取)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 构建原生函数调用模式的系统提示词
|
|
9
|
+
*
|
|
10
|
+
* 设计原则:
|
|
11
|
+
* - 精简: bootstrap 模式不注入 SOUL.md 人格(节省 ~500 token)
|
|
12
|
+
* - 分层: 静态指令放 systemPrompt,动态上下文放 user prompt
|
|
13
|
+
* - 控制通过 PhaseRouter 状态机实现,不通过追加 user 消息
|
|
14
|
+
* - 工具描述已通过 functionDeclarations 传递,不重复
|
|
15
|
+
*
|
|
16
|
+
* @param {object} options
|
|
17
|
+
* @param {string} options.currentSource — 'user' | 'system'
|
|
18
|
+
* @param {string} options.projectBriefingCache — 项目概况缓存文本
|
|
19
|
+
* @param {import('./Memory.js').Memory|null} options.memory — 跨对话记忆
|
|
20
|
+
* @param {import('./ProjectSemanticMemory.js').ProjectSemanticMemory|null} options.semanticMemory — Tier 3 语义记忆
|
|
21
|
+
* @param {object} options.budget — 预算配置
|
|
22
|
+
* @param {string} options.soulPath — SOUL.md 文件路径
|
|
23
|
+
* @returns {string}
|
|
24
|
+
*/
|
|
25
|
+
export function buildNativeToolSystemPrompt({
|
|
26
|
+
currentSource,
|
|
27
|
+
projectBriefingCache,
|
|
28
|
+
memory,
|
|
29
|
+
semanticMemory,
|
|
30
|
+
budget = {},
|
|
31
|
+
soulPath,
|
|
32
|
+
}) {
|
|
33
|
+
// 用户对话模式: 完整提示词(含 SOUL、Memory、项目概况)
|
|
34
|
+
if (currentSource !== 'system') {
|
|
35
|
+
let soulSection = '';
|
|
36
|
+
try {
|
|
37
|
+
if (soulPath && fs.existsSync(soulPath)) {
|
|
38
|
+
soulSection = `\n${fs.readFileSync(soulPath, 'utf-8').trim()}\n`;
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
/* SOUL.md not available */
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return `${soulSection}
|
|
45
|
+
你是 AutoSnippet 项目的统一 AI 中心。项目内所有 AI 推理和分析都通过你执行。
|
|
46
|
+
${projectBriefingCache}${memory?.toPromptSection({ source: 'user' }) || ''}${semanticMemory?.toPromptSection({ source: 'user' }) || ''}
|
|
47
|
+
|
|
48
|
+
## 使用规则
|
|
49
|
+
1. 当需要查询数据时,直接调用相应工具。
|
|
50
|
+
2. 工具参数严格按照工具声明中的 schema 传递。
|
|
51
|
+
3. 对于代码分析任务,先 search_project_code 搜索,再 read_project_file 读取。
|
|
52
|
+
4. 当工具返回错误时,尝试不同参数或方法。`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Bootstrap 系统模式: LLM 以领域大脑的能力处理任务
|
|
56
|
+
return `你以「领域大脑」的能力来处理任务 — 你对软件工程领域拥有深厚的专家知识。
|
|
57
|
+
你将分析一个真实项目,自主发现其中有价值的代码知识。
|
|
58
|
+
${projectBriefingCache}
|
|
59
|
+
|
|
60
|
+
## 你的能力定位
|
|
61
|
+
你具备深度技术洞察力,能够理解代码背后的设计意图。
|
|
62
|
+
你知道什么知识对开发团队最有价值 — 不是显而易见的样板代码,
|
|
63
|
+
而是体现项目独有设计决策、架构模式和工程智慧的知识。
|
|
64
|
+
|
|
65
|
+
## 你的工作方式
|
|
66
|
+
1. **全局感知** → list_project_structure 了解项目结构
|
|
67
|
+
2. **定向探索** → get_file_summary 快速了解文件角色
|
|
68
|
+
3. **深入研读** → search_project_code / read_project_file 获取真实代码
|
|
69
|
+
4. **语义发现** → semantic_search_code 在知识库查找相关知识
|
|
70
|
+
5. **知识产出** → submit_knowledge 提交有价值的发现
|
|
71
|
+
|
|
72
|
+
## 高效使用工具(节省轮次)
|
|
73
|
+
- **批量搜索**: search_project_code({ patterns: ["keywordA", "keywordB", "keywordC"] })
|
|
74
|
+
- **批量读文件**: read_project_file({ filePaths: ["path/a.m", "path/b.m"] })
|
|
75
|
+
- 合并同类请求为一次调用,避免逐个搜索/读取浪费轮次。
|
|
76
|
+
|
|
77
|
+
## 「项目特写」= 基本用法 + 项目特征融合
|
|
78
|
+
submit_knowledge 的 code 字段必须是「项目特写」— 将技术的基本用法与本项目的特征融合为一体:
|
|
79
|
+
1. **项目选择了什么**: 采用了哪种写法/模式/约定
|
|
80
|
+
2. **为什么这样选**: 统计数据(N 个文件、占比 M%)
|
|
81
|
+
3. **项目禁止什么**: 被放弃的写法、反模式、显式禁用标记
|
|
82
|
+
4. **新代码怎么写**: 可直接复制使用的代码模板
|
|
83
|
+
|
|
84
|
+
## 核心原则
|
|
85
|
+
- 代码必须真实,来自工具返回,不可编造
|
|
86
|
+
- 引用具体类名、方法名、数字,禁止「本模块」「该文件」等泛化描述
|
|
87
|
+
- 质量优先于数量,证据不足宁可不提交
|
|
88
|
+
- 高效利用步数 (≤${budget.maxIterations} 轮)`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 构建项目概况注入到系统提示词(每次 execute 刷新一次)
|
|
93
|
+
* 单次 SQL 聚合 < 5ms,静默降级
|
|
94
|
+
*
|
|
95
|
+
* @param {object} options
|
|
96
|
+
* @param {import('../../injection/ServiceContainer.js').ServiceContainer} options.container
|
|
97
|
+
* @returns {Promise<string>}
|
|
98
|
+
*/
|
|
99
|
+
export async function buildProjectBriefing({ container }) {
|
|
100
|
+
try {
|
|
101
|
+
const db = container?.get?.('database');
|
|
102
|
+
if (!db) {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
// knowledgeType → kind 映射:
|
|
106
|
+
// rule: code-standard, code-style, best-practice, boundary-constraint
|
|
107
|
+
// pattern: code-pattern, architecture, solution
|
|
108
|
+
// fact: code-relation, inheritance, call-chain, data-flow, module-dependency
|
|
109
|
+
// V3: knowledge_entries 统一表(candidates 已合并,lifecycle 替代 status)
|
|
110
|
+
const stats = db
|
|
111
|
+
.prepare(`
|
|
112
|
+
SELECT
|
|
113
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active') as recipeCount,
|
|
114
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType IN ('code-standard','code-style','best-practice','boundary-constraint')) as ruleCount,
|
|
115
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType IN ('code-pattern','architecture','solution')) as patternCount,
|
|
116
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType IN ('code-relation','inheritance','call-chain','data-flow','module-dependency')) as factCount,
|
|
117
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType = 'boundary-constraint') as guardRuleCount,
|
|
118
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'pending') as pendingCandidates,
|
|
119
|
+
(SELECT COUNT(*) FROM knowledge_entries) as totalCandidates
|
|
120
|
+
`)
|
|
121
|
+
.get();
|
|
122
|
+
if (!stats || stats.recipeCount === 0) {
|
|
123
|
+
return '\n## 项目状态\n⚠️ 知识库为空。建议先执行冷启动(bootstrap_knowledge)。\n';
|
|
124
|
+
}
|
|
125
|
+
let section = `\n## 项目状态\n- 知识库: ${stats.recipeCount} 条 Recipe(${stats.ruleCount || 0} rule / ${stats.patternCount || 0} pattern / ${stats.factCount || 0} fact)\n- Guard 规则: ${stats.guardRuleCount || 0} 条\n- 候选: ${stats.pendingCandidates} 条待审 / ${stats.totalCandidates} 条总计\n`;
|
|
126
|
+
if (stats.pendingCandidates > 10) {
|
|
127
|
+
section += `\n⚠️ 有 ${stats.pendingCandidates} 条候选积压,建议执行批量审核。\n`;
|
|
128
|
+
}
|
|
129
|
+
return section;
|
|
130
|
+
} catch {
|
|
131
|
+
return ''; // DB 不可用时静默降级
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 清理最终回答(去除 Thought/preamble + MEMORY 标签)
|
|
137
|
+
* @param {string} response
|
|
138
|
+
* @returns {string}
|
|
139
|
+
*/
|
|
140
|
+
export function cleanFinalAnswer(response) {
|
|
141
|
+
if (!response) {
|
|
142
|
+
return '';
|
|
143
|
+
}
|
|
144
|
+
return response
|
|
145
|
+
.replace(/^(Final Answer|最终回答|Answer)\s*[::]\s*/i, '')
|
|
146
|
+
.replace(/\[MEMORY:\w+\]\s*[\s\S]*?\s*\[\/MEMORY\]/g, '')
|
|
147
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
148
|
+
.trim();
|
|
149
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatAgentTasks — ChatAgent 预定义任务方法(从 ChatAgent.js 提取)
|
|
3
|
+
*
|
|
4
|
+
* 每个任务接收 context 对象: { executeTool, aiProvider, container, logger }
|
|
5
|
+
* - executeTool(toolName, params) — 执行指定工具
|
|
6
|
+
* - aiProvider — AI Provider 实例
|
|
7
|
+
* - container — ServiceContainer
|
|
8
|
+
* - logger — Logger 实例
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 任务: 提交前查重 + 质量预评
|
|
13
|
+
* 1. check_duplicate → 若发现相似 ≥ 0.7 则建议合并
|
|
14
|
+
* 2. 顺便返回质量评估建议
|
|
15
|
+
*/
|
|
16
|
+
export async function taskCheckAndSubmit(context, { candidate, projectRoot }) {
|
|
17
|
+
const { executeTool, aiProvider } = context;
|
|
18
|
+
|
|
19
|
+
// Step 1: 查重
|
|
20
|
+
const duplicates = await executeTool('check_duplicate', {
|
|
21
|
+
candidate,
|
|
22
|
+
projectRoot,
|
|
23
|
+
threshold: 0.5,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Step 2: 如果有高相似度,使用 AI 分析是否真正重复
|
|
27
|
+
const highSim = (duplicates.similar || []).filter((d) => d.similarity >= 0.7);
|
|
28
|
+
let aiVerdict = null;
|
|
29
|
+
if (highSim.length > 0 && aiProvider) {
|
|
30
|
+
const verdictPrompt = `以下新候选代码与已有 Recipe 高度相似,请判断是否真正重复。
|
|
31
|
+
|
|
32
|
+
新候选:
|
|
33
|
+
- Title: ${candidate.title || '(未命名)'}
|
|
34
|
+
- Code: ${(candidate.code || '').substring(0, 1000)}
|
|
35
|
+
|
|
36
|
+
相似 Recipe:
|
|
37
|
+
${highSim.map((s) => `- ${s.title} (相似度: ${s.similarity})`).join('\n')}
|
|
38
|
+
|
|
39
|
+
请回答: DUPLICATE(真正重复)/ SIMILAR(相似但不同,建议保留并标注关系)/ UNIQUE(误判,可放心提交)
|
|
40
|
+
只回答一个词。`;
|
|
41
|
+
try {
|
|
42
|
+
const raw = await aiProvider.chat(verdictPrompt, { temperature: 0, maxTokens: 20 });
|
|
43
|
+
aiVerdict = (raw || '').trim().toUpperCase().split(/\s/)[0];
|
|
44
|
+
} catch {
|
|
45
|
+
/* ignore */
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
duplicates: duplicates.similar || [],
|
|
51
|
+
highSimilarity: highSim,
|
|
52
|
+
aiVerdict,
|
|
53
|
+
recommendation:
|
|
54
|
+
highSim.length === 0
|
|
55
|
+
? 'safe_to_submit'
|
|
56
|
+
: aiVerdict === 'DUPLICATE'
|
|
57
|
+
? 'block_duplicate'
|
|
58
|
+
: 'review_suggested',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 任务: 批量发现 Recipe 间的知识图谱关系
|
|
64
|
+
* 遍历所有 Recipe,两两分析可能的关系
|
|
65
|
+
*/
|
|
66
|
+
export async function taskDiscoverAllRelations(context, { batchSize = 20 } = {}) {
|
|
67
|
+
const { executeTool, aiProvider, container, logger } = context;
|
|
68
|
+
|
|
69
|
+
const knowledgeService = container.get('knowledgeService');
|
|
70
|
+
if (!knowledgeService) {
|
|
71
|
+
throw new Error('KnowledgeService 不可用');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!aiProvider) {
|
|
75
|
+
throw new Error('AI Provider 未配置,请先设置 API Key');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 获取所有活跃知识条目
|
|
79
|
+
const { items = [], data = [] } = await knowledgeService.list(
|
|
80
|
+
{ lifecycle: 'active' },
|
|
81
|
+
{ page: 1, pageSize: 500 }
|
|
82
|
+
);
|
|
83
|
+
const recipes = items.length > 0 ? items : data;
|
|
84
|
+
if (recipes.length < 2) {
|
|
85
|
+
return {
|
|
86
|
+
discovered: 0,
|
|
87
|
+
totalPairs: 0,
|
|
88
|
+
message: `只有 ${recipes.length} 条 Recipe,至少需要 2 条`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 按 batch 分组分析
|
|
93
|
+
const pairs = [];
|
|
94
|
+
for (let i = 0; i < recipes.length; i++) {
|
|
95
|
+
for (let j = i + 1; j < recipes.length; j++) {
|
|
96
|
+
pairs.push([recipes[i], recipes[j]]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let discovered = 0;
|
|
101
|
+
const results = [];
|
|
102
|
+
let batchErrors = 0;
|
|
103
|
+
|
|
104
|
+
// 分批处理,单批失败不终止整体
|
|
105
|
+
for (let b = 0; b < pairs.length; b += batchSize) {
|
|
106
|
+
const batch = pairs.slice(b, b + batchSize);
|
|
107
|
+
try {
|
|
108
|
+
const result = await executeTool('discover_relations', {
|
|
109
|
+
recipePairs: batch.map(([a, b]) => ({
|
|
110
|
+
a: {
|
|
111
|
+
id: a.id,
|
|
112
|
+
title: a.title,
|
|
113
|
+
category: a.category,
|
|
114
|
+
language: a.language,
|
|
115
|
+
code: String(a.content || a.code || '').substring(0, 500),
|
|
116
|
+
},
|
|
117
|
+
b: {
|
|
118
|
+
id: b.id,
|
|
119
|
+
title: b.title,
|
|
120
|
+
category: b.category,
|
|
121
|
+
language: b.language,
|
|
122
|
+
code: String(b.content || b.code || '').substring(0, 500),
|
|
123
|
+
},
|
|
124
|
+
})),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (result.error) {
|
|
128
|
+
batchErrors++;
|
|
129
|
+
logger.warn(
|
|
130
|
+
`[DiscoverRelations] Batch ${Math.floor(b / batchSize) + 1} error: ${result.error}`
|
|
131
|
+
);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (result.relations) {
|
|
135
|
+
discovered += result.relations.length;
|
|
136
|
+
results.push(...result.relations);
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
batchErrors++;
|
|
140
|
+
logger.warn(
|
|
141
|
+
`[DiscoverRelations] Batch ${Math.floor(b / batchSize) + 1} threw: ${err.message}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
discovered,
|
|
148
|
+
totalPairs: pairs.length,
|
|
149
|
+
totalBatches: Math.ceil(pairs.length / batchSize),
|
|
150
|
+
batchErrors,
|
|
151
|
+
relations: results,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 任务: 批量 AI 补全候选语义字段
|
|
157
|
+
*/
|
|
158
|
+
export async function taskFullEnrich(context, { status = 'pending', maxCount = 50 } = {}) {
|
|
159
|
+
const { executeTool, container } = context;
|
|
160
|
+
|
|
161
|
+
const knowledgeService = container.get('knowledgeService');
|
|
162
|
+
|
|
163
|
+
const { items = [], data = [] } = await knowledgeService.list(
|
|
164
|
+
{ lifecycle: status },
|
|
165
|
+
{ page: 1, pageSize: maxCount }
|
|
166
|
+
);
|
|
167
|
+
const candidates = items.length > 0 ? items : data;
|
|
168
|
+
if (candidates.length === 0) {
|
|
169
|
+
return { enriched: 0, message: 'No candidates to enrich' };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 筛选缺失语义字段的候选
|
|
173
|
+
const needEnrich = candidates.filter((c) => {
|
|
174
|
+
const m = c.metadata || {};
|
|
175
|
+
return !m.rationale || !m.knowledgeType || !m.complexity;
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
if (needEnrich.length === 0) {
|
|
179
|
+
return { enriched: 0, message: 'All candidates already enriched' };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const result = await executeTool('enrich_candidate', {
|
|
183
|
+
candidateIds: needEnrich.map((c) => c.id).slice(0, 20),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 任务: 批量质量审计全部 Recipe
|
|
191
|
+
* 对活跃 Recipe 逐个评分,返回低于阈值的列表
|
|
192
|
+
*/
|
|
193
|
+
export async function taskQualityAudit(context, { threshold = 0.6, maxCount = 100 } = {}) {
|
|
194
|
+
const { executeTool, container } = context;
|
|
195
|
+
|
|
196
|
+
const knowledgeService = container.get('knowledgeService');
|
|
197
|
+
|
|
198
|
+
const { items = [], data = [] } = await knowledgeService.list(
|
|
199
|
+
{ lifecycle: 'active' },
|
|
200
|
+
{ page: 1, pageSize: maxCount }
|
|
201
|
+
);
|
|
202
|
+
const recipes = items.length > 0 ? items : data;
|
|
203
|
+
if (recipes.length === 0) {
|
|
204
|
+
return { total: 0, lowQuality: [], message: 'No active recipes' };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const lowQuality = [];
|
|
208
|
+
const gradeDistribution = { A: 0, B: 0, C: 0, D: 0, F: 0 };
|
|
209
|
+
|
|
210
|
+
for (const recipe of recipes) {
|
|
211
|
+
const scoreResult = await executeTool('quality_score', { recipe });
|
|
212
|
+
if (scoreResult.grade) {
|
|
213
|
+
gradeDistribution[scoreResult.grade] = (gradeDistribution[scoreResult.grade] || 0) + 1;
|
|
214
|
+
}
|
|
215
|
+
if (scoreResult.score < threshold) {
|
|
216
|
+
lowQuality.push({
|
|
217
|
+
id: recipe.id,
|
|
218
|
+
title: recipe.title,
|
|
219
|
+
score: scoreResult.score,
|
|
220
|
+
grade: scoreResult.grade,
|
|
221
|
+
dimensions: scoreResult.dimensions,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
lowQuality.sort((a, b) => a.score - b.score);
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
total: recipes.length,
|
|
230
|
+
threshold,
|
|
231
|
+
gradeDistribution,
|
|
232
|
+
lowQualityCount: lowQuality.length,
|
|
233
|
+
lowQuality,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 任务: Guard 完整扫描
|
|
239
|
+
* 对代码运行全部 Guard 规则 + 生成修复建议
|
|
240
|
+
*/
|
|
241
|
+
export async function taskGuardFullScan(context, { code, language, filePath } = {}) {
|
|
242
|
+
const { executeTool, aiProvider } = context;
|
|
243
|
+
|
|
244
|
+
if (!code) {
|
|
245
|
+
return { error: 'code is required' };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Step 1: 静态检查
|
|
249
|
+
const checkResult = await executeTool('guard_check_code', {
|
|
250
|
+
code,
|
|
251
|
+
language: language || 'unknown',
|
|
252
|
+
scope: 'project',
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Step 2: 如果有违规且 AI 可用,生成修复建议
|
|
256
|
+
let suggestions = null;
|
|
257
|
+
if (checkResult.violationCount > 0 && aiProvider) {
|
|
258
|
+
try {
|
|
259
|
+
const violationSummary = (checkResult.violations || [])
|
|
260
|
+
.slice(0, 5)
|
|
261
|
+
.map(
|
|
262
|
+
(v) =>
|
|
263
|
+
`- [${v.severity}] ${v.message || v.ruleName} (line ${v.line || v.matches?.[0]?.line || '?'})`
|
|
264
|
+
)
|
|
265
|
+
.join('\n');
|
|
266
|
+
|
|
267
|
+
const prompt = `以下代码存在 Guard 规则违规。请为每个违规提供修复建议。
|
|
268
|
+
|
|
269
|
+
违规列表:
|
|
270
|
+
${violationSummary}
|
|
271
|
+
|
|
272
|
+
代码片段:
|
|
273
|
+
\`\`\`${language || ''}
|
|
274
|
+
${code.substring(0, 3000)}
|
|
275
|
+
\`\`\`
|
|
276
|
+
|
|
277
|
+
请用 JSON 数组格式返回建议: [{"violation": "...", "suggestion": "...", "fixExample": "..."}]`;
|
|
278
|
+
|
|
279
|
+
suggestions =
|
|
280
|
+
(await aiProvider.chatWithStructuredOutput(prompt, {
|
|
281
|
+
openChar: '[',
|
|
282
|
+
closeChar: ']',
|
|
283
|
+
temperature: 0.3,
|
|
284
|
+
})) || [];
|
|
285
|
+
} catch {
|
|
286
|
+
/* AI suggestions optional */
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
filePath: filePath || '(inline)',
|
|
292
|
+
language,
|
|
293
|
+
violationCount: checkResult.violationCount,
|
|
294
|
+
violations: checkResult.violations,
|
|
295
|
+
suggestions,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* _shared.js — 多个工具模块共享的常量和辅助函数
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
|
|
10
|
+
export const PROJECT_ROOT = path.resolve(__dirname, '../../../..');
|
|
11
|
+
/** skills/ 目录绝对路径 */
|
|
12
|
+
export const SKILLS_DIR = path.resolve(PROJECT_ROOT, 'skills');
|
|
13
|
+
/** 项目级 skills 目录 */
|
|
14
|
+
export const PROJECT_SKILLS_DIR = path.resolve(PROJECT_ROOT, '.autosnippet', 'skills');
|
|
15
|
+
|
|
16
|
+
// Bootstrap 维度展示分组 — 将 9 个细粒度维度合并为 4 个展示组
|
|
17
|
+
export const DIMENSION_DISPLAY_GROUP = {
|
|
18
|
+
architecture: 'architecture', // → 架构与设计
|
|
19
|
+
'code-pattern': 'architecture', // → 架构与设计
|
|
20
|
+
'project-profile': 'architecture', // → 架构与设计
|
|
21
|
+
'best-practice': 'best-practice', // → 规范与实践
|
|
22
|
+
'code-standard': 'best-practice', // → 规范与实践
|
|
23
|
+
'event-and-data-flow': 'event-and-data-flow', // → 事件与数据流
|
|
24
|
+
'objc-deep-scan': 'objc-deep-scan', // → 深度扫描
|
|
25
|
+
'category-scan': 'objc-deep-scan', // → 深度扫描
|
|
26
|
+
'agent-guidelines': 'agent-guidelines', // skill-only
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 基于维度元数据 (dimensionMeta) 检查提交是否合法
|
|
31
|
+
* @param {{ id: string, outputType: 'candidate'|'skill'|'dual', allowedKnowledgeTypes: string[] }} dimensionMeta
|
|
32
|
+
* @param {object} params - submit_knowledge 的参数
|
|
33
|
+
* @param {object} [logger]
|
|
34
|
+
* @returns {{ status: string, reason: string } | null} 不合法返回 rejected,合法返回 null
|
|
35
|
+
*/
|
|
36
|
+
export function checkDimensionType(dimensionMeta, params, logger) {
|
|
37
|
+
// 1. Skill-only 维度不允许提交 Candidate
|
|
38
|
+
if (dimensionMeta.outputType === 'skill') {
|
|
39
|
+
logger?.info(
|
|
40
|
+
`[submit_knowledge] ✗ rejected — dimension "${dimensionMeta.id}" is skill-only, cannot submit candidates`
|
|
41
|
+
);
|
|
42
|
+
return {
|
|
43
|
+
status: 'rejected',
|
|
44
|
+
reason: `当前维度 "${dimensionMeta.id}" 的输出类型为 skill-only,不允许调用 submit_knowledge。请只在最终回复中提供 dimensionDigest JSON。`,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 2. knowledgeType 校验 — 不在允许列表时自动修正为第一个允许类型
|
|
49
|
+
const allowed = dimensionMeta.allowedKnowledgeTypes || [];
|
|
50
|
+
if (allowed.length > 0 && params.knowledgeType) {
|
|
51
|
+
if (!allowed.includes(params.knowledgeType)) {
|
|
52
|
+
const corrected = allowed[0];
|
|
53
|
+
logger?.warn(
|
|
54
|
+
`[submit_knowledge] knowledgeType "${params.knowledgeType}" → "${corrected}" (auto-corrected for dimension "${dimensionMeta.id}")`
|
|
55
|
+
);
|
|
56
|
+
params.knowledgeType = corrected;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return null;
|
|
61
|
+
}
|