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,600 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* noAiFallback.js — AI 不可用时的规则化降级知识提取
|
|
3
|
+
*
|
|
4
|
+
* 当 ChatAgent / AI Provider 不可用时,从 Phase 1-4 的结构化数据中
|
|
5
|
+
* 提取基础知识候选和 Project Skill,覆盖以下维度:
|
|
6
|
+
*
|
|
7
|
+
* ✅ project-profile — 从 langStats + depGraph + targets 构建项目技术画像
|
|
8
|
+
* ✅ architecture — 从 depGraph + targets 推断层级/模块关系
|
|
9
|
+
* ✅ code-standard — 从 AST 统计推断命名约定和代码风格
|
|
10
|
+
* ✅ best-practice — 从 Guard 违规推断反模式
|
|
11
|
+
* ✅ agent-guidelines — 从 Guard 高频违规 + 语言特性生成 Agent 注意事项
|
|
12
|
+
*
|
|
13
|
+
* 产出质量标注为 `source: 'rule-based-fallback'`,区别于 AI 分析产出。
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import Logger from '../../../../../infrastructure/logging/Logger.js';
|
|
17
|
+
|
|
18
|
+
const logger = Logger.getInstance();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 主入口 — 当 AI 不可用时调用
|
|
22
|
+
*
|
|
23
|
+
* @param {object} fillContext - 与 fillDimensionsV3 相同的上下文
|
|
24
|
+
* @returns {{ candidates: object[], skills: object[], report: object }}
|
|
25
|
+
*/
|
|
26
|
+
export async function runNoAiFallback(fillContext) {
|
|
27
|
+
const {
|
|
28
|
+
ctx,
|
|
29
|
+
dimensions,
|
|
30
|
+
depGraphData,
|
|
31
|
+
guardAudit,
|
|
32
|
+
langStats,
|
|
33
|
+
primaryLang,
|
|
34
|
+
astProjectSummary,
|
|
35
|
+
projectRoot,
|
|
36
|
+
taskManager,
|
|
37
|
+
sessionId,
|
|
38
|
+
} = fillContext;
|
|
39
|
+
|
|
40
|
+
const t0 = Date.now();
|
|
41
|
+
logger.info('[Bootstrap-fallback] Starting rule-based fallback (no AI)');
|
|
42
|
+
|
|
43
|
+
const candidates = [];
|
|
44
|
+
const skills = [];
|
|
45
|
+
const report = { dimensionsProcessed: 0, candidatesCreated: 0, skillsCreated: 0, errors: [] };
|
|
46
|
+
|
|
47
|
+
// ── 收集原始数据 ──
|
|
48
|
+
let allFiles = fillContext.allFiles || [];
|
|
49
|
+
const targetFileMap = fillContext.targetFileMap || {};
|
|
50
|
+
const allTargets = Object.keys(targetFileMap);
|
|
51
|
+
|
|
52
|
+
// ── 1. Project Profile ──
|
|
53
|
+
try {
|
|
54
|
+
const profile = _buildProjectProfile({
|
|
55
|
+
langStats,
|
|
56
|
+
primaryLang,
|
|
57
|
+
depGraphData,
|
|
58
|
+
allTargets,
|
|
59
|
+
allFiles,
|
|
60
|
+
astProjectSummary,
|
|
61
|
+
});
|
|
62
|
+
if (profile) {
|
|
63
|
+
candidates.push(profile);
|
|
64
|
+
skills.push(_wrapAsSkill('project-profile', '项目技术画像', profile.content.markdown));
|
|
65
|
+
report.candidatesCreated++;
|
|
66
|
+
report.skillsCreated++;
|
|
67
|
+
}
|
|
68
|
+
_markDimDone(taskManager, sessionId, 'project-profile', 'fallback');
|
|
69
|
+
} catch (e) {
|
|
70
|
+
report.errors.push({ dim: 'project-profile', error: e.message });
|
|
71
|
+
_markDimDone(taskManager, sessionId, 'project-profile', 'error');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── 2. Architecture ──
|
|
75
|
+
try {
|
|
76
|
+
const arch = _buildArchitecture({ depGraphData, allTargets, targetFileMap, primaryLang });
|
|
77
|
+
if (arch) {
|
|
78
|
+
candidates.push(arch);
|
|
79
|
+
skills.push(_wrapAsSkill('architecture', '模块架构', arch.content.markdown));
|
|
80
|
+
report.candidatesCreated++;
|
|
81
|
+
report.skillsCreated++;
|
|
82
|
+
}
|
|
83
|
+
_markDimDone(taskManager, sessionId, 'architecture', 'fallback');
|
|
84
|
+
} catch (e) {
|
|
85
|
+
report.errors.push({ dim: 'architecture', error: e.message });
|
|
86
|
+
_markDimDone(taskManager, sessionId, 'architecture', 'error');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── 3. Code Standard ──
|
|
90
|
+
try {
|
|
91
|
+
const standard = _buildCodeStandard({ astProjectSummary, primaryLang, allFiles });
|
|
92
|
+
if (standard) {
|
|
93
|
+
candidates.push(standard);
|
|
94
|
+
skills.push(_wrapAsSkill('code-standard', '代码规范', standard.content.markdown));
|
|
95
|
+
report.candidatesCreated++;
|
|
96
|
+
report.skillsCreated++;
|
|
97
|
+
}
|
|
98
|
+
_markDimDone(taskManager, sessionId, 'code-standard', 'fallback');
|
|
99
|
+
} catch (e) {
|
|
100
|
+
report.errors.push({ dim: 'code-standard', error: e.message });
|
|
101
|
+
_markDimDone(taskManager, sessionId, 'code-standard', 'error');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ── 4. Best Practice (from Guard violations) ──
|
|
105
|
+
try {
|
|
106
|
+
const bp = _buildBestPractice({ guardAudit, primaryLang });
|
|
107
|
+
if (bp) {
|
|
108
|
+
candidates.push(bp);
|
|
109
|
+
skills.push(_wrapAsSkill('best-practice', '最佳实践', bp.content.markdown));
|
|
110
|
+
report.candidatesCreated++;
|
|
111
|
+
report.skillsCreated++;
|
|
112
|
+
}
|
|
113
|
+
_markDimDone(taskManager, sessionId, 'best-practice', 'fallback');
|
|
114
|
+
} catch (e) {
|
|
115
|
+
report.errors.push({ dim: 'best-practice', error: e.message });
|
|
116
|
+
_markDimDone(taskManager, sessionId, 'best-practice', 'error');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── 5. Agent Guidelines ──
|
|
120
|
+
try {
|
|
121
|
+
const guidelines = _buildAgentGuidelines({ guardAudit, primaryLang, astProjectSummary });
|
|
122
|
+
if (guidelines) {
|
|
123
|
+
candidates.push(guidelines);
|
|
124
|
+
skills.push(
|
|
125
|
+
_wrapAsSkill('agent-guidelines', 'Agent 开发注意事项', guidelines.content.markdown)
|
|
126
|
+
);
|
|
127
|
+
report.candidatesCreated++;
|
|
128
|
+
report.skillsCreated++;
|
|
129
|
+
}
|
|
130
|
+
_markDimDone(taskManager, sessionId, 'agent-guidelines', 'fallback');
|
|
131
|
+
} catch (e) {
|
|
132
|
+
report.errors.push({ dim: 'agent-guidelines', error: e.message });
|
|
133
|
+
_markDimDone(taskManager, sessionId, 'agent-guidelines', 'error');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ── 标记剩余未处理维度 ──
|
|
137
|
+
const processedDims = new Set([
|
|
138
|
+
'project-profile',
|
|
139
|
+
'architecture',
|
|
140
|
+
'code-standard',
|
|
141
|
+
'best-practice',
|
|
142
|
+
'agent-guidelines',
|
|
143
|
+
]);
|
|
144
|
+
for (const dim of dimensions) {
|
|
145
|
+
if (!processedDims.has(dim.id)) {
|
|
146
|
+
_markDimDone(taskManager, sessionId, dim.id, 'skipped-no-ai');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
report.dimensionsProcessed = processedDims.size;
|
|
151
|
+
const elapsed = Date.now() - t0;
|
|
152
|
+
logger.info(
|
|
153
|
+
`[Bootstrap-fallback] Complete: ${report.candidatesCreated} candidates, ${report.skillsCreated} skills in ${elapsed}ms`
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
return { candidates, skills, report };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ═══════════════════════════════════════════════════════════
|
|
160
|
+
// 维度构建器
|
|
161
|
+
// ═══════════════════════════════════════════════════════════
|
|
162
|
+
|
|
163
|
+
function _buildProjectProfile({ langStats, primaryLang, depGraphData, allTargets, allFiles, astProjectSummary }) {
|
|
164
|
+
const lines = ['## 项目技术画像', ''];
|
|
165
|
+
|
|
166
|
+
// 语言统计
|
|
167
|
+
const sortedLangs = Object.entries(langStats || {})
|
|
168
|
+
.sort(([, a], [, b]) => b - a)
|
|
169
|
+
.slice(0, 8);
|
|
170
|
+
if (sortedLangs.length > 0) {
|
|
171
|
+
lines.push('### 语言分布', '');
|
|
172
|
+
lines.push('| 语言 | 文件数 | 占比 |');
|
|
173
|
+
lines.push('|------|--------|------|');
|
|
174
|
+
const total = sortedLangs.reduce((s, [, c]) => s + c, 0);
|
|
175
|
+
for (const [lang, count] of sortedLangs) {
|
|
176
|
+
lines.push(`| ${lang} | ${count} | ${((count / total) * 100).toFixed(1)}% |`);
|
|
177
|
+
}
|
|
178
|
+
lines.push('');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 模块结构
|
|
182
|
+
if (allTargets.length > 0) {
|
|
183
|
+
lines.push(`### 模块结构`, '');
|
|
184
|
+
lines.push(`项目包含 **${allTargets.length}** 个模块/Target:`, '');
|
|
185
|
+
for (const t of allTargets.slice(0, 15)) {
|
|
186
|
+
const fileCount = Array.isArray(allFiles)
|
|
187
|
+
? allFiles.filter((f) => f.target === t).length
|
|
188
|
+
: 0;
|
|
189
|
+
lines.push(`- \`${t}\` (${fileCount} files)`);
|
|
190
|
+
}
|
|
191
|
+
if (allTargets.length > 15) {
|
|
192
|
+
lines.push(`- ...及 ${allTargets.length - 15} 个其他模块`);
|
|
193
|
+
}
|
|
194
|
+
lines.push('');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 依赖关系
|
|
198
|
+
if (depGraphData?.edges?.length > 0) {
|
|
199
|
+
lines.push('### 依赖关系', '');
|
|
200
|
+
lines.push(`共 ${depGraphData.edges.length} 条模块间依赖关系。`, '');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// AST 统计
|
|
204
|
+
if (astProjectSummary) {
|
|
205
|
+
lines.push('### 代码结构统计', '');
|
|
206
|
+
const m = astProjectSummary.projectMetrics || {};
|
|
207
|
+
lines.push(`- 类/结构体: ${astProjectSummary.classes?.length || 0}`);
|
|
208
|
+
lines.push(`- 协议/接口: ${astProjectSummary.protocols?.length || 0}`);
|
|
209
|
+
lines.push(`- 方法总数: ${m.totalMethods || 0}`);
|
|
210
|
+
if (m.maxNestingDepth) lines.push(`- 最大嵌套深度: ${m.maxNestingDepth}`);
|
|
211
|
+
if (m.complexMethods?.length > 0) {
|
|
212
|
+
lines.push(`- 高复杂度方法: ${m.complexMethods.length}`);
|
|
213
|
+
}
|
|
214
|
+
if (m.longMethods?.length > 0) {
|
|
215
|
+
lines.push(`- 过长方法 (>50 行): ${m.longMethods.length}`);
|
|
216
|
+
}
|
|
217
|
+
lines.push('');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const markdown = lines.join('\n');
|
|
221
|
+
if (markdown.length < 50) return null;
|
|
222
|
+
|
|
223
|
+
return _makeCandidate({
|
|
224
|
+
title: `项目技术画像 — ${primaryLang}`,
|
|
225
|
+
knowledgeType: 'architecture',
|
|
226
|
+
category: 'Architecture',
|
|
227
|
+
language: primaryLang,
|
|
228
|
+
markdown,
|
|
229
|
+
rationale: '基于 Bootstrap 扫描的文件统计、AST 分析和依赖图谱自动生成',
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function _buildArchitecture({ depGraphData, allTargets, targetFileMap, primaryLang }) {
|
|
234
|
+
if (!depGraphData?.edges?.length && allTargets.length < 2) return null;
|
|
235
|
+
|
|
236
|
+
const lines = ['## 模块架构', ''];
|
|
237
|
+
|
|
238
|
+
// 依赖图
|
|
239
|
+
if (depGraphData?.edges?.length > 0) {
|
|
240
|
+
lines.push('### 模块依赖关系', '');
|
|
241
|
+
lines.push('```');
|
|
242
|
+
const seen = new Set();
|
|
243
|
+
for (const e of depGraphData.edges.slice(0, 30)) {
|
|
244
|
+
const from = typeof e.from === 'string' ? e.from : e.source;
|
|
245
|
+
const to = typeof e.to === 'string' ? e.to : e.target;
|
|
246
|
+
if (from && to) {
|
|
247
|
+
const key = `${from} → ${to}`;
|
|
248
|
+
if (!seen.has(key)) {
|
|
249
|
+
lines.push(key);
|
|
250
|
+
seen.add(key);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
lines.push('```');
|
|
255
|
+
lines.push('');
|
|
256
|
+
|
|
257
|
+
// 入度/出度分析
|
|
258
|
+
const inDeg = {};
|
|
259
|
+
const outDeg = {};
|
|
260
|
+
for (const e of depGraphData.edges) {
|
|
261
|
+
const from = typeof e.from === 'string' ? e.from : e.source;
|
|
262
|
+
const to = typeof e.to === 'string' ? e.to : e.target;
|
|
263
|
+
if (from) outDeg[from] = (outDeg[from] || 0) + 1;
|
|
264
|
+
if (to) inDeg[to] = (inDeg[to] || 0) + 1;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 核心模块(被依赖最多)
|
|
268
|
+
const coreModules = Object.entries(inDeg)
|
|
269
|
+
.sort(([, a], [, b]) => b - a)
|
|
270
|
+
.slice(0, 5);
|
|
271
|
+
if (coreModules.length > 0) {
|
|
272
|
+
lines.push('### 核心模块(被依赖最多)', '');
|
|
273
|
+
for (const [mod, deg] of coreModules) {
|
|
274
|
+
lines.push(`- \`${mod}\` — 被 ${deg} 个模块依赖`);
|
|
275
|
+
}
|
|
276
|
+
lines.push('');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// 叶子模块(不被任何模块依赖)
|
|
280
|
+
const leafModules = allTargets.filter((t) => !inDeg[t] && outDeg[t]);
|
|
281
|
+
if (leafModules.length > 0) {
|
|
282
|
+
lines.push('### 叶子模块(仅依赖他人)', '');
|
|
283
|
+
for (const mod of leafModules.slice(0, 8)) {
|
|
284
|
+
lines.push(`- \`${mod}\``);
|
|
285
|
+
}
|
|
286
|
+
lines.push('');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const markdown = lines.join('\n');
|
|
291
|
+
if (markdown.length < 80) return null;
|
|
292
|
+
|
|
293
|
+
return _makeCandidate({
|
|
294
|
+
title: '模块架构与依赖关系',
|
|
295
|
+
knowledgeType: 'architecture',
|
|
296
|
+
category: 'Architecture',
|
|
297
|
+
language: primaryLang,
|
|
298
|
+
markdown,
|
|
299
|
+
rationale: '基于项目依赖图谱和模块扫描自动生成',
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function _buildCodeStandard({ astProjectSummary, primaryLang, allFiles }) {
|
|
304
|
+
if (!astProjectSummary) return null;
|
|
305
|
+
|
|
306
|
+
const lines = ['## 代码规范发现', ''];
|
|
307
|
+
|
|
308
|
+
const classes = astProjectSummary.classes || [];
|
|
309
|
+
const methods = [];
|
|
310
|
+
// 从 file 级聚合方法
|
|
311
|
+
if (astProjectSummary.files) {
|
|
312
|
+
for (const f of astProjectSummary.files) {
|
|
313
|
+
if (f.methods) methods.push(...f.methods);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 命名模式分析
|
|
318
|
+
if (classes.length > 0) {
|
|
319
|
+
lines.push('### 类命名模式', '');
|
|
320
|
+
|
|
321
|
+
// 检测常见后缀
|
|
322
|
+
const suffixCounts = {};
|
|
323
|
+
const COMMON_SUFFIXES = [
|
|
324
|
+
'Service',
|
|
325
|
+
'Manager',
|
|
326
|
+
'Controller',
|
|
327
|
+
'Handler',
|
|
328
|
+
'Provider',
|
|
329
|
+
'Repository',
|
|
330
|
+
'Factory',
|
|
331
|
+
'Helper',
|
|
332
|
+
'Utils',
|
|
333
|
+
'ViewModel',
|
|
334
|
+
'View',
|
|
335
|
+
'Model',
|
|
336
|
+
'Store',
|
|
337
|
+
'Client',
|
|
338
|
+
'Adapter',
|
|
339
|
+
'Impl',
|
|
340
|
+
];
|
|
341
|
+
for (const cls of classes) {
|
|
342
|
+
for (const sfx of COMMON_SUFFIXES) {
|
|
343
|
+
if (cls.name?.endsWith(sfx)) {
|
|
344
|
+
suffixCounts[sfx] = (suffixCounts[sfx] || 0) + 1;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const usedSuffixes = Object.entries(suffixCounts)
|
|
349
|
+
.filter(([, c]) => c > 0)
|
|
350
|
+
.sort(([, a], [, b]) => b - a);
|
|
351
|
+
if (usedSuffixes.length > 0) {
|
|
352
|
+
lines.push('| 后缀约定 | 类数量 | 推断角色 |');
|
|
353
|
+
lines.push('|----------|--------|----------|');
|
|
354
|
+
const roleMap = {
|
|
355
|
+
Service: '业务服务',
|
|
356
|
+
Manager: '管理器',
|
|
357
|
+
Controller: '控制器/路由',
|
|
358
|
+
Handler: '事件/请求处理',
|
|
359
|
+
Provider: '数据/功能提供者',
|
|
360
|
+
Repository: '数据访问',
|
|
361
|
+
Factory: '工厂',
|
|
362
|
+
Helper: '辅助工具',
|
|
363
|
+
Utils: '工具类',
|
|
364
|
+
ViewModel: '视图模型',
|
|
365
|
+
View: '视图/UI',
|
|
366
|
+
Model: '数据模型',
|
|
367
|
+
Store: '状态存储',
|
|
368
|
+
Client: 'API 客户端',
|
|
369
|
+
Adapter: '适配器',
|
|
370
|
+
Impl: '接口实现',
|
|
371
|
+
};
|
|
372
|
+
for (const [sfx, count] of usedSuffixes) {
|
|
373
|
+
lines.push(`| *${sfx} | ${count} | ${roleMap[sfx] || sfx} |`);
|
|
374
|
+
}
|
|
375
|
+
lines.push('');
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
lines.push(`共发现 **${classes.length}** 个类/结构体。`, '');
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// 代码质量指标
|
|
382
|
+
const metrics = astProjectSummary.projectMetrics;
|
|
383
|
+
if (metrics) {
|
|
384
|
+
lines.push('### 代码质量指标', '');
|
|
385
|
+
if (metrics.avgMethodsPerClass) {
|
|
386
|
+
lines.push(`- 平均方法数/类: ${metrics.avgMethodsPerClass.toFixed(1)}`);
|
|
387
|
+
}
|
|
388
|
+
if (metrics.complexMethods?.length > 0) {
|
|
389
|
+
lines.push(`- 高圈复杂度方法: ${metrics.complexMethods.length} 个`);
|
|
390
|
+
for (const m of metrics.complexMethods.slice(0, 5)) {
|
|
391
|
+
lines.push(
|
|
392
|
+
` - \`${m.className ? m.className + '.' : ''}${m.name}\` — complexity ${m.complexity}`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (metrics.longMethods?.length > 0) {
|
|
397
|
+
lines.push(`- 过长方法: ${metrics.longMethods.length} 个`);
|
|
398
|
+
for (const m of metrics.longMethods.slice(0, 5)) {
|
|
399
|
+
lines.push(
|
|
400
|
+
` - \`${m.className ? m.className + '.' : ''}${m.name}\` — ${m.bodyLines} 行`
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
lines.push('');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const markdown = lines.join('\n');
|
|
408
|
+
if (markdown.length < 80) return null;
|
|
409
|
+
|
|
410
|
+
return _makeCandidate({
|
|
411
|
+
title: '代码规范与命名约定',
|
|
412
|
+
knowledgeType: 'code-standard',
|
|
413
|
+
category: 'Architecture',
|
|
414
|
+
language: primaryLang,
|
|
415
|
+
markdown,
|
|
416
|
+
rationale: '基于 AST 分析的类名、方法统计和代码复杂度指标自动生成',
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function _buildBestPractice({ guardAudit, primaryLang }) {
|
|
421
|
+
if (!guardAudit?.files?.length) return null;
|
|
422
|
+
|
|
423
|
+
// 聚合所有违规
|
|
424
|
+
const ruleStats = {};
|
|
425
|
+
for (const f of guardAudit.files) {
|
|
426
|
+
for (const v of f.violations || []) {
|
|
427
|
+
if (!ruleStats[v.ruleId]) {
|
|
428
|
+
ruleStats[v.ruleId] = {
|
|
429
|
+
count: 0,
|
|
430
|
+
severity: v.severity,
|
|
431
|
+
message: v.message,
|
|
432
|
+
files: new Set(),
|
|
433
|
+
fixSuggestion: v.fixSuggestion || null,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
ruleStats[v.ruleId].count++;
|
|
437
|
+
ruleStats[v.ruleId].files.add(f.filePath);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const sortedRules = Object.entries(ruleStats)
|
|
442
|
+
.sort(([, a], [, b]) => b.count - a.count);
|
|
443
|
+
|
|
444
|
+
if (sortedRules.length === 0) return null;
|
|
445
|
+
|
|
446
|
+
const lines = ['## 最佳实践(基于 Guard 审计)', ''];
|
|
447
|
+
lines.push(
|
|
448
|
+
`Bootstrap 扫描发现 **${guardAudit.summary?.totalViolations || 0}** 个违规,` +
|
|
449
|
+
`涉及 **${sortedRules.length}** 条规则:`,
|
|
450
|
+
''
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
lines.push('### 高频违规(应优先修复)', '');
|
|
454
|
+
lines.push('| 规则 | 严重性 | 违规数 | 影响文件数 | 说明 |');
|
|
455
|
+
lines.push('|------|--------|--------|-----------|------|');
|
|
456
|
+
for (const [ruleId, stat] of sortedRules.slice(0, 15)) {
|
|
457
|
+
lines.push(
|
|
458
|
+
`| \`${ruleId}\` | ${stat.severity} | ${stat.count} | ${stat.files.size} | ${stat.message} |`
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
lines.push('');
|
|
462
|
+
|
|
463
|
+
// 修复建议
|
|
464
|
+
const withFix = sortedRules.filter(([, s]) => s.fixSuggestion);
|
|
465
|
+
if (withFix.length > 0) {
|
|
466
|
+
lines.push('### 修复建议', '');
|
|
467
|
+
for (const [ruleId, stat] of withFix.slice(0, 10)) {
|
|
468
|
+
lines.push(`- **${ruleId}**: ${stat.fixSuggestion}`);
|
|
469
|
+
}
|
|
470
|
+
lines.push('');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const markdown = lines.join('\n');
|
|
474
|
+
return _makeCandidate({
|
|
475
|
+
title: '最佳实践与常见问题',
|
|
476
|
+
knowledgeType: 'best-practice',
|
|
477
|
+
category: 'Service',
|
|
478
|
+
language: primaryLang,
|
|
479
|
+
markdown,
|
|
480
|
+
rationale: '基于 Guard 静态审计发现的违规模式和修复建议自动生成',
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function _buildAgentGuidelines({ guardAudit, primaryLang, astProjectSummary }) {
|
|
485
|
+
const lines = ['## Agent 开发注意事项', ''];
|
|
486
|
+
lines.push('> 以下规则基于项目静态分析自动生成,AI Agent 在本项目中编写代码时应遵守。', '');
|
|
487
|
+
|
|
488
|
+
// 从 Guard 高频违规推断
|
|
489
|
+
if (guardAudit?.files?.length) {
|
|
490
|
+
const ruleStats = {};
|
|
491
|
+
for (const f of guardAudit.files) {
|
|
492
|
+
for (const v of f.violations || []) {
|
|
493
|
+
ruleStats[v.ruleId] = ruleStats[v.ruleId] || { count: 0, message: v.message, severity: v.severity };
|
|
494
|
+
ruleStats[v.ruleId].count++;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
const topErrors = Object.entries(ruleStats)
|
|
498
|
+
.filter(([, s]) => s.severity === 'error')
|
|
499
|
+
.sort(([, a], [, b]) => b.count - a.count)
|
|
500
|
+
.slice(0, 8);
|
|
501
|
+
const topWarnings = Object.entries(ruleStats)
|
|
502
|
+
.filter(([, s]) => s.severity === 'warning')
|
|
503
|
+
.sort(([, a], [, b]) => b.count - a.count)
|
|
504
|
+
.slice(0, 8);
|
|
505
|
+
|
|
506
|
+
if (topErrors.length > 0) {
|
|
507
|
+
lines.push('### 必须(must)- 基于 error 级违规', '');
|
|
508
|
+
for (const [ruleId, stat] of topErrors) {
|
|
509
|
+
lines.push(`- ❌ **${ruleId}**: ${stat.message} (项目中出现 ${stat.count} 次)`);
|
|
510
|
+
}
|
|
511
|
+
lines.push('');
|
|
512
|
+
}
|
|
513
|
+
if (topWarnings.length > 0) {
|
|
514
|
+
lines.push('### 建议(should)- 基于 warning 级违规', '');
|
|
515
|
+
for (const [ruleId, stat] of topWarnings) {
|
|
516
|
+
lines.push(`- ⚠️ **${ruleId}**: ${stat.message} (${stat.count} 处)`);
|
|
517
|
+
}
|
|
518
|
+
lines.push('');
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// 从 AST 复杂度推断
|
|
523
|
+
if (astProjectSummary?.projectMetrics) {
|
|
524
|
+
const m = astProjectSummary.projectMetrics;
|
|
525
|
+
lines.push('### 代码质量约束', '');
|
|
526
|
+
if (m.maxNestingDepth >= 5) {
|
|
527
|
+
lines.push(`- 当前项目最大嵌套深度 ${m.maxNestingDepth} — 新代码应避免超过 4 层嵌套`);
|
|
528
|
+
}
|
|
529
|
+
if (m.complexMethods?.length > 0) {
|
|
530
|
+
const avgComplexity =
|
|
531
|
+
m.complexMethods.reduce((s, c) => s + c.complexity, 0) / m.complexMethods.length;
|
|
532
|
+
lines.push(
|
|
533
|
+
`- 已有 ${m.complexMethods.length} 个高复杂度方法 (avg ${avgComplexity.toFixed(1)}) — 新方法圈复杂度应 <10`
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
if (m.longMethods?.length > 0) {
|
|
537
|
+
lines.push(`- 已有 ${m.longMethods.length} 个过长方法 — 新方法建议 <50 行`);
|
|
538
|
+
}
|
|
539
|
+
lines.push('');
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const markdown = lines.join('\n');
|
|
543
|
+
if (markdown.length < 100) return null;
|
|
544
|
+
|
|
545
|
+
return _makeCandidate({
|
|
546
|
+
title: 'Agent 开发注意事项',
|
|
547
|
+
knowledgeType: 'boundary-constraint',
|
|
548
|
+
category: 'Architecture',
|
|
549
|
+
language: primaryLang,
|
|
550
|
+
markdown,
|
|
551
|
+
rationale: '基于 Guard 错误级违规和 AST 复杂度指标自动生成的 Agent 约束',
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// ═══════════════════════════════════════════════════════════
|
|
556
|
+
// 工具函数
|
|
557
|
+
// ═══════════════════════════════════════════════════════════
|
|
558
|
+
|
|
559
|
+
function _makeCandidate({ title, knowledgeType, category, language, markdown, rationale }) {
|
|
560
|
+
return {
|
|
561
|
+
title,
|
|
562
|
+
content: { markdown, rationale },
|
|
563
|
+
language: language || '',
|
|
564
|
+
category,
|
|
565
|
+
knowledgeType,
|
|
566
|
+
source: 'rule-based-fallback',
|
|
567
|
+
difficulty: 'beginner',
|
|
568
|
+
scope: 'project-specific',
|
|
569
|
+
reasoning: {
|
|
570
|
+
whyStandard: rationale,
|
|
571
|
+
sources: ['bootstrap-scan'],
|
|
572
|
+
confidence: 0.6,
|
|
573
|
+
},
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function _wrapAsSkill(dimId, label, markdown) {
|
|
578
|
+
return {
|
|
579
|
+
dimId,
|
|
580
|
+
name: `project-${dimId}`,
|
|
581
|
+
description: `Auto-generated from bootstrap scan (no-AI fallback): ${label}`,
|
|
582
|
+
content: [
|
|
583
|
+
`# ${label}`,
|
|
584
|
+
'',
|
|
585
|
+
'> Auto-generated by Bootstrap fallback (rule-based, no AI). Quality: basic.',
|
|
586
|
+
'',
|
|
587
|
+
markdown,
|
|
588
|
+
].join('\n'),
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function _markDimDone(taskManager, sessionId, dimId, type) {
|
|
593
|
+
try {
|
|
594
|
+
if (taskManager && sessionId) {
|
|
595
|
+
taskManager.markTaskCompleted(dimId, { type, reason: type });
|
|
596
|
+
}
|
|
597
|
+
} catch {
|
|
598
|
+
/* non-critical */
|
|
599
|
+
}
|
|
600
|
+
}
|