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
|
@@ -1,30 +1,138 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP Handlers — 知识浏览类 (V3: 使用 knowledgeService)
|
|
3
3
|
* listByKind, listRecipes, getRecipe, recipeInsights, confirmUsage
|
|
4
|
+
*
|
|
5
|
+
* 投影原则:只交付 Agent 可操作的信号,不交付内部运营/审计数据。
|
|
6
|
+
* - _projectItem → list 列表摘要(精简 ~10 字段)
|
|
7
|
+
* - _projectForAgent → get 详情(去除噪音 ~25 字段)
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
10
|
import { envelope } from '../envelope.js';
|
|
7
11
|
|
|
8
|
-
|
|
12
|
+
// ─── 通用投影辅助 ────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 构建 actionHint — "whenClause → doClause" 的一句话可操作摘要。
|
|
16
|
+
* Agent 在列表/搜索中即可判断是否需要深入获取该条目。
|
|
17
|
+
*/
|
|
18
|
+
function _buildActionHint(json) {
|
|
19
|
+
const doText = json.doClause || '';
|
|
20
|
+
const whenText = json.whenClause || '';
|
|
21
|
+
if (!doText && !whenText) return undefined;
|
|
22
|
+
return `${whenText ? `${whenText} → ` : ''}${doText}`.replace(/ → $/, '');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 只保留非空关系桶,压缩 Relations 输出体积。
|
|
27
|
+
*/
|
|
28
|
+
function _compactRelations(relations) {
|
|
29
|
+
if (!relations) return undefined;
|
|
30
|
+
const compact = {};
|
|
31
|
+
for (const [type, list] of Object.entries(relations)) {
|
|
32
|
+
if (Array.isArray(list) && list.length > 0) {
|
|
33
|
+
compact[type] = list;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return Object.keys(compact).length > 0 ? compact : undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ─── 列表投影 ────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 将 KnowledgeEntry 投影为列表摘要(精简版)
|
|
43
|
+
* 移除: quality, stats, scope, tags, knowledgeType, 重复的 status/statistics
|
|
44
|
+
* 新增: actionHint
|
|
45
|
+
*/
|
|
9
46
|
function _projectItem(r) {
|
|
10
47
|
const json = typeof r.toJSON === 'function' ? r.toJSON() : r;
|
|
11
48
|
return {
|
|
12
49
|
id: json.id,
|
|
13
50
|
title: json.title,
|
|
14
|
-
description: json.description,
|
|
15
51
|
trigger: json.trigger || '',
|
|
52
|
+
kind: json.kind,
|
|
53
|
+
language: json.language,
|
|
54
|
+
category: json.category,
|
|
16
55
|
lifecycle: json.lifecycle,
|
|
56
|
+
complexity: json.complexity,
|
|
57
|
+
description: (json.description || '').slice(0, 120),
|
|
58
|
+
actionHint: _buildActionHint(json),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ─── 详情投影 ────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 将 KnowledgeEntry 投影为 Agent 消费的详情格式。
|
|
66
|
+
*
|
|
67
|
+
* Tier 1(黄金字段): trigger, doClause, dontClause, whenClause, coreCode, title, content.pattern, kind
|
|
68
|
+
* Tier 2(上下文) : language, category, description, tags, rationale, steps, headers, reasoning.whyStandard
|
|
69
|
+
* Tier 3(去除噪音): lifecycleHistory, autoApprovable, reviewedBy/At, sourceFile,
|
|
70
|
+
* publishedAt/By, headerPaths, includeHeaders, quality.*, stats.*,
|
|
71
|
+
* reasoning.sources/qualitySignals/alternatives, content.codeChanges/verification
|
|
72
|
+
*/
|
|
73
|
+
function _projectForAgent(json) {
|
|
74
|
+
// content 精简:保留 pattern/markdown/rationale/steps,去除 codeChanges/verification
|
|
75
|
+
const content = json.content
|
|
76
|
+
? {
|
|
77
|
+
...(json.content.pattern ? { pattern: json.content.pattern } : {}),
|
|
78
|
+
...(json.content.markdown ? { markdown: json.content.markdown } : {}),
|
|
79
|
+
...(json.content.rationale ? { rationale: json.content.rationale } : {}),
|
|
80
|
+
...(json.content.steps?.length > 0 ? { steps: json.content.steps } : {}),
|
|
81
|
+
}
|
|
82
|
+
: undefined;
|
|
83
|
+
|
|
84
|
+
// reasoning 精简:仅保留 whyStandard + confidence
|
|
85
|
+
const reasoning = json.reasoning
|
|
86
|
+
? {
|
|
87
|
+
...(json.reasoning.whyStandard ? { whyStandard: json.reasoning.whyStandard } : {}),
|
|
88
|
+
...(json.reasoning.confidence != null ? { confidence: json.reasoning.confidence } : {}),
|
|
89
|
+
}
|
|
90
|
+
: undefined;
|
|
91
|
+
|
|
92
|
+
// constraints 精简:仅保留 guards 和 sideEffects
|
|
93
|
+
const constraints =
|
|
94
|
+
json.constraints?.guards?.length > 0 || json.constraints?.sideEffects?.length > 0
|
|
95
|
+
? {
|
|
96
|
+
...(json.constraints.guards?.length > 0 ? { guards: json.constraints.guards } : {}),
|
|
97
|
+
...(json.constraints.sideEffects?.length > 0
|
|
98
|
+
? { sideEffects: json.constraints.sideEffects }
|
|
99
|
+
: {}),
|
|
100
|
+
}
|
|
101
|
+
: undefined;
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
// ── 标识 ──
|
|
105
|
+
id: json.id,
|
|
106
|
+
title: json.title,
|
|
107
|
+
description: json.description,
|
|
108
|
+
trigger: json.trigger || '',
|
|
109
|
+
|
|
110
|
+
// ── 分类 ──
|
|
17
111
|
kind: json.kind,
|
|
18
112
|
language: json.language,
|
|
19
113
|
category: json.category,
|
|
20
114
|
knowledgeType: json.knowledgeType,
|
|
21
115
|
complexity: json.complexity,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
116
|
+
tags: json.tags?.length > 0 ? json.tags : undefined,
|
|
117
|
+
|
|
118
|
+
// ── Agent 可操作指令(Tier 1 黄金字段) ──
|
|
119
|
+
whenClause: json.whenClause || undefined,
|
|
120
|
+
doClause: json.doClause || undefined,
|
|
121
|
+
dontClause: json.dontClause || undefined,
|
|
122
|
+
coreCode: json.coreCode || undefined,
|
|
123
|
+
|
|
124
|
+
// ── 内容 ──
|
|
125
|
+
content: content && Object.keys(content).length > 0 ? content : undefined,
|
|
126
|
+
|
|
127
|
+
// ── 上下文 ──
|
|
128
|
+
headers: json.headers?.length > 0 ? json.headers : undefined,
|
|
129
|
+
reasoning: reasoning && Object.keys(reasoning).length > 0 ? reasoning : undefined,
|
|
130
|
+
|
|
131
|
+
// ── 关系(仅非空桶) ──
|
|
132
|
+
relations: _compactRelations(json.relations),
|
|
133
|
+
|
|
134
|
+
// ── 约束(仅 guards + sideEffects) ──
|
|
135
|
+
constraints,
|
|
28
136
|
};
|
|
29
137
|
}
|
|
30
138
|
|
|
@@ -86,7 +194,9 @@ export async function getRecipe(ctx, args) {
|
|
|
86
194
|
throw new Error(`Knowledge entry not found: ${args.id}`);
|
|
87
195
|
}
|
|
88
196
|
const json = typeof entry.toJSON === 'function' ? entry.toJSON() : entry;
|
|
89
|
-
|
|
197
|
+
// Agent 投影:去除运营/审计/统计噪音,只交付可操作字段
|
|
198
|
+
const projected = _projectForAgent(json);
|
|
199
|
+
return envelope({ success: true, data: projected, meta: { tool: 'autosnippet_get_recipe' } });
|
|
90
200
|
}
|
|
91
201
|
|
|
92
202
|
export async function recipeInsights(ctx, args) {
|
|
@@ -25,8 +25,7 @@ export async function guardCheck(ctx, args) {
|
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
const engine = new GuardCheckEngine(db);
|
|
28
|
+
const engine = _getOrCreateEngine(ctx, GuardCheckEngine);
|
|
30
29
|
|
|
31
30
|
// 注入 Enhancement Pack Guard 规则
|
|
32
31
|
await _injectEnhancementGuardRules(engine, ctx);
|
|
@@ -77,8 +76,7 @@ export async function guardAuditFiles(ctx, args) {
|
|
|
77
76
|
const scope = args.scope || 'project';
|
|
78
77
|
|
|
79
78
|
const { GuardCheckEngine } = await import('../../../service/guard/GuardCheckEngine.js');
|
|
80
|
-
const
|
|
81
|
-
const engine = new GuardCheckEngine(db);
|
|
79
|
+
const engine = _getOrCreateEngine(ctx, GuardCheckEngine);
|
|
82
80
|
|
|
83
81
|
// 注入 Enhancement Pack Guard 规则
|
|
84
82
|
await _injectEnhancementGuardRules(engine, ctx);
|
|
@@ -207,8 +205,7 @@ export async function scanProject(ctx, args) {
|
|
|
207
205
|
let guardAudit = null;
|
|
208
206
|
try {
|
|
209
207
|
const { GuardCheckEngine } = await import('../../../service/guard/GuardCheckEngine.js');
|
|
210
|
-
const
|
|
211
|
-
const engine = new GuardCheckEngine(db);
|
|
208
|
+
const engine = _getOrCreateEngine(ctx, GuardCheckEngine);
|
|
212
209
|
|
|
213
210
|
// 注入 Enhancement Pack Guard 规则
|
|
214
211
|
await _injectEnhancementGuardRules(engine, ctx);
|
|
@@ -278,11 +275,32 @@ export async function scanProject(ctx, args) {
|
|
|
278
275
|
|
|
279
276
|
// ─── 内部辅助 ─────────────────────────────────────────────
|
|
280
277
|
|
|
278
|
+
/**
|
|
279
|
+
* 获取 DI 容器中的 GuardCheckEngine 单例,回退到新建实例
|
|
280
|
+
* 优先复用 DI 单例以保持 externalRules / cache 的跨调用一致性
|
|
281
|
+
* @param {object} ctx - MCP context with container
|
|
282
|
+
* @param {Function} GuardCheckEngine - 引擎构造函数(用于回退)
|
|
283
|
+
* @returns {import('../../../service/guard/GuardCheckEngine.js').GuardCheckEngine}
|
|
284
|
+
*/
|
|
285
|
+
function _getOrCreateEngine(ctx, GuardCheckEngine) {
|
|
286
|
+
try {
|
|
287
|
+
const engine = ctx.container.get('guardCheckEngine');
|
|
288
|
+
if (engine) return engine;
|
|
289
|
+
} catch {
|
|
290
|
+
/* DI not registered — fall back to new instance */
|
|
291
|
+
}
|
|
292
|
+
const db = ctx.container.get('database');
|
|
293
|
+
return new GuardCheckEngine(db);
|
|
294
|
+
}
|
|
295
|
+
|
|
281
296
|
/**
|
|
282
297
|
* 将 Enhancement Pack 的 Guard 规则注入 GuardCheckEngine
|
|
298
|
+
* 幂等 — 已注入的引擎直接跳过,避免每次请求重复加载 EnhancementRegistry
|
|
283
299
|
* 静默失败 — Enhancement Pack 不可用不应阻断 Guard 审计
|
|
284
300
|
*/
|
|
285
301
|
async function _injectEnhancementGuardRules(engine, ctx) {
|
|
302
|
+
// 幂等保护: 已注入则跳过
|
|
303
|
+
if (engine.isEpInjected?.()) return;
|
|
286
304
|
try {
|
|
287
305
|
const { initEnhancementRegistry } = await import('../../../core/enhancement/index.js');
|
|
288
306
|
const enhReg = await initEnhancementRegistry();
|
|
@@ -303,6 +321,7 @@ async function _injectEnhancementGuardRules(engine, ctx) {
|
|
|
303
321
|
if (allGuardRules.length > 0) {
|
|
304
322
|
engine.injectExternalRules(allGuardRules);
|
|
305
323
|
}
|
|
324
|
+
engine.markEpInjected?.();
|
|
306
325
|
} catch {
|
|
307
326
|
/* Enhancement registry not available — non-critical */
|
|
308
327
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* 1. 通过 container.get('searchEngine') 获取 singleton 实例(含 vectorStore + aiProvider)
|
|
7
7
|
* 2. 4 个工具各有差异化定位,不做简单 mode 包装
|
|
8
8
|
* 3. 统一 responseTime、byKind 分组、kind 过滤
|
|
9
|
+
* 4. 投影原则:只交付 Agent 可操作的信号,去除内部排序信号
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import { envelope } from '../envelope.js';
|
|
@@ -34,18 +35,6 @@ async function getFallbackEngine(ctx) {
|
|
|
34
35
|
return new SearchEngine(db);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
/**
|
|
38
|
-
* items → byKind 分组
|
|
39
|
-
*/
|
|
40
|
-
function groupByKind(items) {
|
|
41
|
-
const byKind = { rule: [], pattern: [], fact: [] };
|
|
42
|
-
for (const it of items) {
|
|
43
|
-
const kind = it.kind || it.metadata?.kind || 'pattern';
|
|
44
|
-
(byKind[kind] || byKind.pattern).push(it);
|
|
45
|
-
}
|
|
46
|
-
return byKind;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
38
|
/**
|
|
50
39
|
* 根据 kind 参数过滤 items
|
|
51
40
|
*/
|
|
@@ -56,6 +45,44 @@ function filterByKind(items, kind) {
|
|
|
56
45
|
return items.filter((it) => (it.kind || it.metadata?.kind || 'pattern') === kind);
|
|
57
46
|
}
|
|
58
47
|
|
|
48
|
+
/**
|
|
49
|
+
* 搜索结果投影 — 去除内部排序信号,只保留 Agent 可操作字段。
|
|
50
|
+
*
|
|
51
|
+
* 移除: type(=recipe, 冗余), status(已 filter), updatedAt, createdAt, difficulty,
|
|
52
|
+
* tags, usageCount, authorityScore, qualityScore, headers, moduleName, content(巨大)
|
|
53
|
+
* 新增: actionHint (whenClause → doClause 一句话摘要)
|
|
54
|
+
*/
|
|
55
|
+
function _slimSearchItem(item) {
|
|
56
|
+
const doText = item.doClause || '';
|
|
57
|
+
const whenText = item.whenClause || '';
|
|
58
|
+
const actionHint =
|
|
59
|
+
doText || whenText
|
|
60
|
+
? `${whenText ? `${whenText} → ` : ''}${doText}`.replace(/ → $/, '')
|
|
61
|
+
: undefined;
|
|
62
|
+
return {
|
|
63
|
+
id: item.id,
|
|
64
|
+
title: item.title,
|
|
65
|
+
trigger: item.trigger || '',
|
|
66
|
+
kind: item.kind || 'pattern',
|
|
67
|
+
language: item.language || '',
|
|
68
|
+
score: item.score,
|
|
69
|
+
description: (item.description || '').slice(0, 120),
|
|
70
|
+
actionHint,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* items → slim byKind 分组
|
|
76
|
+
*/
|
|
77
|
+
function _slimGroupByKind(items) {
|
|
78
|
+
const byKind = { rule: [], pattern: [], fact: [] };
|
|
79
|
+
for (const it of items) {
|
|
80
|
+
const kind = it.kind || 'pattern';
|
|
81
|
+
(byKind[kind] || byKind.pattern).push(it);
|
|
82
|
+
}
|
|
83
|
+
return byKind;
|
|
84
|
+
}
|
|
85
|
+
|
|
59
86
|
// ─── 1. autosnippet_search — 统合搜索入口 ─────────────────────
|
|
60
87
|
|
|
61
88
|
/**
|
|
@@ -86,7 +113,9 @@ export async function search(ctx, args) {
|
|
|
86
113
|
items = filterByKind(items, kind);
|
|
87
114
|
items = items.slice(0, limit);
|
|
88
115
|
|
|
89
|
-
|
|
116
|
+
// Agent 投影:去除内部排序信号
|
|
117
|
+
const slimItems = items.map(_slimSearchItem);
|
|
118
|
+
const byKind = _slimGroupByKind(slimItems);
|
|
90
119
|
const elapsed = Date.now() - t0;
|
|
91
120
|
|
|
92
121
|
return envelope({
|
|
@@ -95,8 +124,8 @@ export async function search(ctx, args) {
|
|
|
95
124
|
query,
|
|
96
125
|
mode: actualMode,
|
|
97
126
|
kind: kind === 'all' ? undefined : kind,
|
|
98
|
-
totalResults:
|
|
99
|
-
items,
|
|
127
|
+
totalResults: slimItems.length,
|
|
128
|
+
items: slimItems,
|
|
100
129
|
byKind,
|
|
101
130
|
kindCounts: {
|
|
102
131
|
rule: byKind.rule.length,
|
|
@@ -136,18 +165,19 @@ export async function contextSearch(ctx, args) {
|
|
|
136
165
|
});
|
|
137
166
|
|
|
138
167
|
const items = (result?.items || []).slice(0, limit);
|
|
139
|
-
const
|
|
168
|
+
const slimItems = items.map(_slimSearchItem);
|
|
169
|
+
const byKind = _slimGroupByKind(slimItems);
|
|
140
170
|
const elapsed = Date.now() - t0;
|
|
141
171
|
const source = result?.ranked ? 'search-engine+ranking' : 'search-engine';
|
|
142
172
|
|
|
143
173
|
return envelope({
|
|
144
174
|
success: true,
|
|
145
175
|
data: {
|
|
146
|
-
items,
|
|
176
|
+
items: slimItems,
|
|
147
177
|
byKind,
|
|
148
178
|
metadata: {
|
|
149
179
|
responseTimeMs: elapsed,
|
|
150
|
-
totalResults:
|
|
180
|
+
totalResults: slimItems.length,
|
|
151
181
|
kindCounts: {
|
|
152
182
|
rule: byKind.rule.length,
|
|
153
183
|
pattern: byKind.pattern.length,
|
|
@@ -183,7 +213,8 @@ export async function keywordSearch(ctx, args) {
|
|
|
183
213
|
|
|
184
214
|
let items = result?.items || [];
|
|
185
215
|
items = filterByKind(items, kind).slice(0, limit);
|
|
186
|
-
const
|
|
216
|
+
const slimItems = items.map(_slimSearchItem);
|
|
217
|
+
const byKind = _slimGroupByKind(slimItems);
|
|
187
218
|
const elapsed = Date.now() - t0;
|
|
188
219
|
|
|
189
220
|
return envelope({
|
|
@@ -192,8 +223,8 @@ export async function keywordSearch(ctx, args) {
|
|
|
192
223
|
query,
|
|
193
224
|
mode: 'keyword',
|
|
194
225
|
kind: kind === 'all' ? undefined : kind,
|
|
195
|
-
totalResults:
|
|
196
|
-
items,
|
|
226
|
+
totalResults: slimItems.length,
|
|
227
|
+
items: slimItems,
|
|
197
228
|
byKind,
|
|
198
229
|
kindCounts: {
|
|
199
230
|
rule: byKind.rule.length,
|
|
@@ -231,7 +262,8 @@ export async function semanticSearch(ctx, args) {
|
|
|
231
262
|
let items = result?.items || [];
|
|
232
263
|
const actualMode = result?.mode || 'semantic';
|
|
233
264
|
items = filterByKind(items, kind).slice(0, limit);
|
|
234
|
-
const
|
|
265
|
+
const slimItems = items.map(_slimSearchItem);
|
|
266
|
+
const byKind = _slimGroupByKind(slimItems);
|
|
235
267
|
const elapsed = Date.now() - t0;
|
|
236
268
|
|
|
237
269
|
// 提示 AI Agent 当前实际模式
|
|
@@ -245,8 +277,8 @@ export async function semanticSearch(ctx, args) {
|
|
|
245
277
|
kind: kind === 'all' ? undefined : kind,
|
|
246
278
|
degraded,
|
|
247
279
|
degradedReason: degraded ? 'vectorStore/aiProvider 不可用,已降级到 BM25' : undefined,
|
|
248
|
-
totalResults:
|
|
249
|
-
items,
|
|
280
|
+
totalResults: slimItems.length,
|
|
281
|
+
items: slimItems,
|
|
250
282
|
byKind,
|
|
251
283
|
kindCounts: {
|
|
252
284
|
rule: byKind.rule.length,
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import express from 'express';
|
|
7
7
|
import { getServiceContainer } from '../../injection/ServiceContainer.js';
|
|
8
|
+
import { LanguageService } from '../../shared/LanguageService.js';
|
|
8
9
|
import { NotFoundError, ValidationError } from '../../shared/errors/index.js';
|
|
9
10
|
import { asyncHandler } from '../middleware/errorHandler.js';
|
|
10
11
|
import { getContext, safeInt } from '../utils/routeHelpers.js';
|
|
@@ -105,31 +106,22 @@ router.get(
|
|
|
105
106
|
const allRules = [...mappedDbRules, ...builtInRules];
|
|
106
107
|
|
|
107
108
|
// 获取当前项目检测到的语言列表,供前端按项目语言筛选
|
|
108
|
-
//
|
|
109
|
-
const DISCOVERER_TO_GUARD_LANGS = {
|
|
110
|
-
spm: ['swift', 'objc'],
|
|
111
|
-
node: ['javascript', 'typescript'],
|
|
112
|
-
go: ['go'],
|
|
113
|
-
jvm: ['java', 'kotlin'],
|
|
114
|
-
python: ['python'],
|
|
115
|
-
dart: ['dart'],
|
|
116
|
-
};
|
|
109
|
+
// 使用 LanguageService 统一检测(支持 Discoverer + Monorepo 文件标记回退)
|
|
117
110
|
let projectLanguages = [];
|
|
118
111
|
try {
|
|
119
112
|
const moduleService = container.get('moduleService');
|
|
113
|
+
await moduleService.load();
|
|
120
114
|
const info = moduleService.getProjectInfo();
|
|
121
115
|
const discovererIds = info.languages || [];
|
|
122
|
-
|
|
123
|
-
for (const did of discovererIds) {
|
|
124
|
-
for (const lang of DISCOVERER_TO_GUARD_LANGS[did] || []) {
|
|
125
|
-
langSet.add(lang);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
projectLanguages = [...langSet];
|
|
116
|
+
projectLanguages = LanguageService.detectProjectLanguages(process.cwd(), { discovererIds });
|
|
129
117
|
} catch {
|
|
130
|
-
|
|
118
|
+
// moduleService 不可用时纯文件扫描回退
|
|
119
|
+
projectLanguages = LanguageService.detectProjectLanguages(process.cwd());
|
|
131
120
|
}
|
|
132
121
|
|
|
122
|
+
// Guard 内置规则中 objectivec 记为 'objc',做向后兼容映射
|
|
123
|
+
projectLanguages = projectLanguages.map((l) => LanguageService.toGuardLangId(l));
|
|
124
|
+
|
|
133
125
|
res.json({
|
|
134
126
|
success: true,
|
|
135
127
|
data: {
|
|
@@ -384,13 +384,19 @@ export class ServiceContainer {
|
|
|
384
384
|
return this.singletons.knowledgeService;
|
|
385
385
|
});
|
|
386
386
|
|
|
387
|
-
// GuardService (V3: uses knowledgeRepository)
|
|
387
|
+
// GuardService (V3: uses knowledgeRepository, delegates to GuardCheckEngine)
|
|
388
388
|
this.register('guardService', () => {
|
|
389
389
|
if (!this.singletons.guardService) {
|
|
390
390
|
const knowledgeRepository = this.get('knowledgeRepository');
|
|
391
391
|
const auditLogger = this.get('auditLogger');
|
|
392
392
|
const gateway = this.get('gateway');
|
|
393
|
-
|
|
393
|
+
let guardCheckEngine = null;
|
|
394
|
+
try {
|
|
395
|
+
guardCheckEngine = this.get('guardCheckEngine');
|
|
396
|
+
} catch { /* engine not yet available */ }
|
|
397
|
+
this.singletons.guardService = new GuardService(knowledgeRepository, auditLogger, gateway, {
|
|
398
|
+
guardCheckEngine,
|
|
399
|
+
});
|
|
394
400
|
}
|
|
395
401
|
return this.singletons.guardService;
|
|
396
402
|
});
|
|
@@ -434,7 +440,10 @@ export class ServiceContainer {
|
|
|
434
440
|
this.register('guardCheckEngine', () => {
|
|
435
441
|
if (!this.singletons.guardCheckEngine) {
|
|
436
442
|
const database = this.get('database');
|
|
437
|
-
this.singletons.
|
|
443
|
+
const config = this.singletons._config || {};
|
|
444
|
+
this.singletons.guardCheckEngine = new GuardCheckEngine(database, {
|
|
445
|
+
guardConfig: config.guard || {},
|
|
446
|
+
});
|
|
438
447
|
}
|
|
439
448
|
return this.singletons.guardCheckEngine;
|
|
440
449
|
});
|