autosnippet 3.0.13 → 3.1.1
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/api-server.js +2 -0
- package/bin/cli.js +24 -19
- package/config/default.json +1 -1
- package/lib/bootstrap.js +4 -4
- package/lib/cli/SetupService.js +29 -29
- package/lib/cli/UpgradeService.js +3 -2
- package/lib/core/AstAnalyzer.js +1 -1
- package/lib/core/ast/ensure-grammars.js +1 -1
- package/lib/core/ast/index.js +62 -11
- package/lib/core/ast/lang-dart.js +27 -21
- package/lib/core/ast/lang-go.js +6 -20
- package/lib/core/ast/lang-rust.js +53 -28
- package/lib/core/ast/parser-init.js +9 -5
- package/lib/core/discovery/DartDiscoverer.js +4 -10
- package/lib/core/discovery/GoDiscoverer.js +45 -25
- package/lib/core/discovery/NodeDiscoverer.js +1 -3
- package/lib/core/discovery/PythonDiscoverer.js +7 -1
- package/lib/core/discovery/RustDiscoverer.js +111 -38
- package/lib/core/discovery/index.js +2 -2
- package/lib/core/enhancement/django-enhancement.js +10 -4
- package/lib/core/enhancement/fastapi-enhancement.js +16 -9
- package/lib/core/enhancement/go-grpc-enhancement.js +2 -1
- package/lib/core/enhancement/go-web-enhancement.js +3 -6
- package/lib/core/enhancement/ml-enhancement.js +6 -3
- package/lib/core/enhancement/nextjs-enhancement.js +17 -7
- package/lib/core/enhancement/node-server-enhancement.js +4 -2
- package/lib/core/enhancement/react-enhancement.js +6 -3
- package/lib/core/enhancement/rust-tokio-enhancement.js +6 -2
- package/lib/core/enhancement/rust-web-enhancement.js +13 -7
- package/lib/core/enhancement/vue-enhancement.js +10 -5
- package/lib/external/ai/AiFactory.js +3 -1
- package/lib/external/ai/AiProvider.js +3 -1
- package/lib/external/mcp/McpServer.js +2 -0
- package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +1 -2
- package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +7 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +55 -26
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +8 -8
- package/lib/external/mcp/handlers/bootstrap/refine.js +3 -1
- package/lib/external/mcp/handlers/bootstrap.js +4 -10
- package/lib/external/mcp/handlers/browse.js +6 -2
- package/lib/external/mcp/handlers/guard.js +6 -2
- package/lib/external/mcp/handlers/skill.js +6 -2
- package/lib/http/HttpServer.js +1 -1
- package/lib/http/routes/candidates.js +3 -1
- package/lib/http/routes/extract.js +4 -5
- package/lib/http/routes/guardRules.js +1 -1
- package/lib/http/routes/modules.js +9 -3
- package/lib/http/routes/skills.js +54 -6
- package/lib/http/routes/violations.js +4 -3
- package/lib/infrastructure/external/ClipboardManager.js +24 -7
- package/lib/infrastructure/external/NativeUi.js +3 -1
- package/lib/infrastructure/external/OpenBrowser.js +1 -0
- package/lib/infrastructure/external/XcodeAutomation.js +5 -5
- package/lib/infrastructure/vector/IndexingPipeline.js +14 -5
- package/lib/injection/ServiceContainer.js +34 -11
- package/lib/platform/ios/index.js +20 -25
- package/lib/platform/ios/routes/spm.js +6 -3
- package/lib/platform/ios/snippet/PlaceholderConverter.js +6 -2
- package/lib/platform/ios/snippet/XcodeCodec.js +4 -2
- package/lib/platform/ios/spm/SpmDiscoverer.js +1 -1
- package/lib/platform/ios/spm/SpmService.js +3 -1
- package/lib/platform/ios/xcode/XcodeIntegration.js +10 -12
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +6 -1
- package/lib/service/automation/FileWatcher.js +1 -3
- package/lib/service/automation/handlers/CreateHandler.js +3 -5
- package/lib/service/automation/handlers/GuardHandler.js +11 -32
- package/lib/service/automation/handlers/SearchHandler.js +9 -9
- package/lib/service/chat/CandidateGuardrail.js +11 -6
- package/lib/service/chat/ChatAgent.js +31 -22
- package/lib/service/chat/HandoffProtocol.js +5 -2
- package/lib/service/chat/tools/composite.js +3 -2
- package/lib/service/chat/tools/index.js +60 -71
- package/lib/service/chat/tools/infrastructure.js +9 -4
- package/lib/service/chat/tools/lifecycle.js +22 -5
- package/lib/service/chat/tools/project-access.js +5 -9
- package/lib/service/chat/tools.js +1 -2
- package/lib/service/cursor/AgentInstructionsGenerator.js +33 -15
- package/lib/service/cursor/CursorDeliveryPipeline.js +2 -1
- package/lib/service/cursor/KnowledgeCompressor.js +16 -7
- package/lib/service/guard/ComplianceReporter.js +5 -2
- package/lib/service/guard/GuardCheckEngine.js +53 -26
- package/lib/service/guard/GuardCodeChecks.js +217 -188
- package/lib/service/guard/GuardCrossFileChecks.js +203 -184
- package/lib/service/guard/GuardPatternUtils.js +17 -10
- package/lib/service/module/ModuleService.js +180 -56
- package/lib/service/recipe/RecipeCandidateValidator.js +11 -8
- package/lib/service/snippet/SnippetFactory.js +3 -3
- package/lib/service/snippet/SnippetInstaller.js +35 -11
- package/lib/service/snippet/codecs/VSCodeCodec.js +2 -2
- package/lib/service/wiki/WikiGenerator.js +67 -40
- package/lib/service/wiki/WikiRenderers.js +105 -80
- package/lib/service/wiki/WikiUtils.js +217 -80
- package/lib/shared/LanguageService.js +111 -53
- package/lib/shared/PathGuard.js +0 -8
- package/package.json +3 -9
- package/scripts/bench-real-projects.mjs +29 -29
- package/scripts/generate-recipe-drafts.js +17 -27
- package/scripts/init-snippets.js +43 -24
- package/scripts/install-vscode-copilot.js +3 -19
- package/scripts/setup-mcp-config.js +0 -4
|
@@ -33,10 +33,22 @@ const AGENT_BUDGET = Object.freeze({
|
|
|
33
33
|
* MCP 工具清单 — 精简版(跟随实际 MCP handler 注册名称)
|
|
34
34
|
*/
|
|
35
35
|
const MCP_TOOLS_SUMMARY = [
|
|
36
|
-
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
{
|
|
37
|
+
name: 'autosnippet_search',
|
|
38
|
+
desc: 'Search knowledge base (mode: auto/context/keyword/semantic)',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'autosnippet_knowledge',
|
|
42
|
+
desc: 'Knowledge CRUD (operation: list/get/insights/confirm_usage)',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'autosnippet_submit_knowledge',
|
|
46
|
+
desc: 'Submit a knowledge candidate (strict validation)',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'autosnippet_submit_knowledge_batch',
|
|
50
|
+
desc: 'Batch submit candidates (with dedup + throttle)',
|
|
51
|
+
},
|
|
40
52
|
{ name: 'autosnippet_guard', desc: 'Code compliance check (single file or batch audit)' },
|
|
41
53
|
{ name: 'autosnippet_structure', desc: 'Project structure discovery (targets/files/metadata)' },
|
|
42
54
|
{ name: 'autosnippet_graph', desc: 'Knowledge graph query (query/impact/path/stats)' },
|
|
@@ -126,7 +138,9 @@ export class AgentInstructionsGenerator {
|
|
|
126
138
|
// 有明确否定词的统一为 "Do NOT",否则保留原文(如 "Avoid ...")
|
|
127
139
|
const hasNegPrefix = /^(Don't|Do not|Never)\s+/i.test(e.dontClause);
|
|
128
140
|
if (hasNegPrefix) {
|
|
129
|
-
const stripped = e.dontClause
|
|
141
|
+
const stripped = e.dontClause
|
|
142
|
+
.replace(/^(Don't|Do not|Never)\s+/i, '')
|
|
143
|
+
.replace(/\.+$/, '');
|
|
130
144
|
line += `. Do NOT ${stripped}`;
|
|
131
145
|
} else {
|
|
132
146
|
line += `. ${e.dontClause.replace(/\.+$/, '')}`;
|
|
@@ -147,14 +161,10 @@ export class AgentInstructionsGenerator {
|
|
|
147
161
|
});
|
|
148
162
|
|
|
149
163
|
// Skills 列表
|
|
150
|
-
const skillLines = skills
|
|
151
|
-
.slice(0, AGENT_BUDGET.MAX_SKILLS)
|
|
152
|
-
.map((s) => `- \`${s}\``);
|
|
164
|
+
const skillLines = skills.slice(0, AGENT_BUDGET.MAX_SKILLS).map((s) => `- \`${s}\``);
|
|
153
165
|
|
|
154
166
|
// MCP 工具列表
|
|
155
|
-
const toolLines = MCP_TOOLS_SUMMARY.map(
|
|
156
|
-
(t) => `- \`${t.name}\` — ${t.desc}`
|
|
157
|
-
);
|
|
167
|
+
const toolLines = MCP_TOOLS_SUMMARY.map((t) => `- \`${t.name}\` — ${t.desc}`);
|
|
158
168
|
|
|
159
169
|
return { ruleLines, patternRows, skillLines, toolLines };
|
|
160
170
|
}
|
|
@@ -267,10 +277,18 @@ export class AgentInstructionsGenerator {
|
|
|
267
277
|
|
|
268
278
|
// Key tools highlight for Claude
|
|
269
279
|
lines.push('### Recommended Workflow', '');
|
|
270
|
-
lines.push(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
lines.push(
|
|
280
|
+
lines.push(
|
|
281
|
+
'1. **Before writing code**: `autosnippet_search({ query: "<topic>" })` to find relevant patterns'
|
|
282
|
+
);
|
|
283
|
+
lines.push(
|
|
284
|
+
'2. **Check compliance**: `autosnippet_guard({ code: "<your code>" })` before committing'
|
|
285
|
+
);
|
|
286
|
+
lines.push(
|
|
287
|
+
'3. **Record knowledge**: `autosnippet_submit_knowledge({ ... })` when discovering reusable patterns'
|
|
288
|
+
);
|
|
289
|
+
lines.push(
|
|
290
|
+
'4. **Confirm adoption**: `autosnippet_knowledge({ operation: "confirm_usage", id: "<id>" })`'
|
|
291
|
+
);
|
|
274
292
|
lines.push('');
|
|
275
293
|
|
|
276
294
|
// Skills
|
|
@@ -101,7 +101,8 @@ export class CursorDeliveryPipeline {
|
|
|
101
101
|
|
|
102
102
|
// NOTE: .qoder/ .trae/ 镜像不再自动执行,由 `asd mirror` 按需触发
|
|
103
103
|
|
|
104
|
-
stats.totalTokensUsed =
|
|
104
|
+
stats.totalTokensUsed =
|
|
105
|
+
channelA.tokensUsed + channelB.totalTokens + (channelF.totalTokens || 0);
|
|
105
106
|
stats.duration = Date.now() - startTime;
|
|
106
107
|
|
|
107
108
|
this.logger.info?.(
|
|
@@ -15,16 +15,22 @@
|
|
|
15
15
|
|
|
16
16
|
/** 从 rationale 提取首句(≤120 字符),用于 Channel B 的 Why 行 */
|
|
17
17
|
function _extractFirstSentence(rationale) {
|
|
18
|
-
if (!rationale)
|
|
18
|
+
if (!rationale) {
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
19
21
|
// 优先按句号或换行分割
|
|
20
22
|
const first = rationale.split(/[.\n。!?!?]/)[0]?.trim();
|
|
21
|
-
if (!first)
|
|
23
|
+
if (!first) {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
22
26
|
return first.length > 120 ? `${first.slice(0, 117)}...` : first;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
/** 骨架化 coreCode:去注释 + 截断 ≤ maxLines 行 */
|
|
26
30
|
function _skeletonize(code, maxLines = 15) {
|
|
27
|
-
if (!code)
|
|
31
|
+
if (!code) {
|
|
32
|
+
return '';
|
|
33
|
+
}
|
|
28
34
|
const lines = code
|
|
29
35
|
.split('\n')
|
|
30
36
|
// 去掉纯注释行(// 或 /* 或 * (JSDoc续行) 或 # 开头)
|
|
@@ -37,7 +43,9 @@ function _skeletonize(code, maxLines = 15) {
|
|
|
37
43
|
acc.push(line);
|
|
38
44
|
return acc;
|
|
39
45
|
}, []);
|
|
40
|
-
if (lines.length <= maxLines)
|
|
46
|
+
if (lines.length <= maxLines) {
|
|
47
|
+
return lines.join('\n');
|
|
48
|
+
}
|
|
41
49
|
return `${lines.slice(0, maxLines).join('\n')}\n// ... (truncated)`;
|
|
42
50
|
}
|
|
43
51
|
|
|
@@ -56,13 +64,14 @@ export class KnowledgeCompressor {
|
|
|
56
64
|
.filter((e) => e.doClause) // 无 doClause → 跳过,不猜
|
|
57
65
|
.map((e) => {
|
|
58
66
|
// 可选 language 前缀
|
|
59
|
-
const langPrefix =
|
|
60
|
-
e.language && e.scope !== 'universal' ? `[${e.language}] ` : '';
|
|
67
|
+
const langPrefix = e.language && e.scope !== 'universal' ? `[${e.language}] ` : '';
|
|
61
68
|
const doText = e.doClause.replace(/\.+$/, ''); // 去尾 .
|
|
62
69
|
let line = `${langPrefix}${doText}`;
|
|
63
70
|
if (e.dontClause) {
|
|
64
71
|
// AI 可能返回 "Don't ..." / "Do not ..." / "Never ..." 开头,去掉冗余前缀后统一为 "Do NOT"
|
|
65
|
-
const stripped = e.dontClause
|
|
72
|
+
const stripped = e.dontClause
|
|
73
|
+
.replace(/^(Don't|Do not|Never)\s+/i, '')
|
|
74
|
+
.replace(/\.+$/, '');
|
|
66
75
|
line += `. Do NOT ${stripped}`;
|
|
67
76
|
}
|
|
68
77
|
return `- ${line}.`;
|
|
@@ -283,11 +283,14 @@ export class ComplianceReporter {
|
|
|
283
283
|
|
|
284
284
|
const lines = [];
|
|
285
285
|
lines.push(`${gateIcon} Quality Gate: ${qualityGate.status} Score: ${qualityGate.score}/100`);
|
|
286
|
-
lines.push(
|
|
286
|
+
lines.push(
|
|
287
|
+
` Files: ${summary.filesScanned} Errors: ${summary.errors} Warnings: ${summary.warnings} Infos: ${summary.infos || 0}`
|
|
288
|
+
);
|
|
287
289
|
|
|
288
290
|
if (trend.hasHistory) {
|
|
289
291
|
const errTrend = trend.errorsChange > 0 ? `+${trend.errorsChange}` : `${trend.errorsChange}`;
|
|
290
|
-
const warnTrend =
|
|
292
|
+
const warnTrend =
|
|
293
|
+
trend.warningsChange > 0 ? `+${trend.warningsChange}` : `${trend.warningsChange}`;
|
|
291
294
|
lines.push(` Trend: Errors ${errTrend} Warnings ${warnTrend}`);
|
|
292
295
|
}
|
|
293
296
|
|
|
@@ -8,9 +8,15 @@
|
|
|
8
8
|
import * as AstAnalyzerModule from '../../core/AstAnalyzer.js';
|
|
9
9
|
import Logger from '../../infrastructure/logging/Logger.js';
|
|
10
10
|
import { LanguageService } from '../../shared/LanguageService.js';
|
|
11
|
-
import { compilePattern, clearPatternCache, buildTestBlockMask, buildCommentMask, detectLanguage } from './GuardPatternUtils.js';
|
|
12
11
|
import { runCodeLevelChecks } from './GuardCodeChecks.js';
|
|
13
12
|
import { runCrossFileChecks } from './GuardCrossFileChecks.js';
|
|
13
|
+
import {
|
|
14
|
+
buildCommentMask,
|
|
15
|
+
buildTestBlockMask,
|
|
16
|
+
clearPatternCache,
|
|
17
|
+
compilePattern,
|
|
18
|
+
detectLanguage,
|
|
19
|
+
} from './GuardPatternUtils.js';
|
|
14
20
|
|
|
15
21
|
/**
|
|
16
22
|
* 内置默认规则集 — 多语言基础规则
|
|
@@ -125,7 +131,8 @@ const BUILT_IN_RULES = {
|
|
|
125
131
|
languages: ['javascript', 'typescript'],
|
|
126
132
|
dimension: 'file',
|
|
127
133
|
category: 'style',
|
|
128
|
-
excludePaths:
|
|
134
|
+
excludePaths:
|
|
135
|
+
/(?:^|[/\\])(?:test|tests|__tests__|spec|__mocks__|mock|mocks|fixtures?)[/\\]|[/\\](?:test_|spec_)[^/\\]*\.(?:js|ts)$|\.(?:test|spec)\.(?:js|ts)$/,
|
|
129
136
|
},
|
|
130
137
|
'js-no-console-log': {
|
|
131
138
|
message: '生产代码应移除 console.log,使用专用日志库',
|
|
@@ -134,7 +141,8 @@ const BUILT_IN_RULES = {
|
|
|
134
141
|
languages: ['javascript', 'typescript'],
|
|
135
142
|
dimension: 'file',
|
|
136
143
|
category: 'style',
|
|
137
|
-
excludePaths:
|
|
144
|
+
excludePaths:
|
|
145
|
+
/(?:^|[/\\])(?:test|tests|__tests__|spec|mock|mocks|__mocks__|scripts|tools|debug)[/\\]|[/\\](?:test_|spec_|mock)[^/\\]*\.(?:js|ts)$|\.(?:test|spec)\.(?:js|ts)$/,
|
|
138
146
|
},
|
|
139
147
|
'js-no-debugger': {
|
|
140
148
|
message: '生产代码中不应包含 debugger 语句',
|
|
@@ -204,7 +212,7 @@ const BUILT_IN_RULES = {
|
|
|
204
212
|
languages: ['python'],
|
|
205
213
|
dimension: 'file',
|
|
206
214
|
category: 'correctness',
|
|
207
|
-
excludePaths: /(?:^|[
|
|
215
|
+
excludePaths: /(?:^|[/\\])tests?[/\\]|[/\\]test_[^/\\]*\.py$|_test\.py$/,
|
|
208
216
|
},
|
|
209
217
|
|
|
210
218
|
// ══════════════════════════════════════════════════════════
|
|
@@ -272,7 +280,7 @@ const BUILT_IN_RULES = {
|
|
|
272
280
|
languages: ['go'],
|
|
273
281
|
dimension: 'file',
|
|
274
282
|
category: 'correctness',
|
|
275
|
-
excludePaths: /(?:^|[
|
|
283
|
+
excludePaths: /(?:^|[/\\])(?:tests?|testdata|_test)[/\\]|_test\.go$/,
|
|
276
284
|
},
|
|
277
285
|
'go-no-init-abuse': {
|
|
278
286
|
message: 'init() 函数副作用难以追踪,避免在 init 中执行复杂逻辑',
|
|
@@ -289,7 +297,7 @@ const BUILT_IN_RULES = {
|
|
|
289
297
|
languages: ['go'],
|
|
290
298
|
dimension: 'file',
|
|
291
299
|
category: 'style',
|
|
292
|
-
excludePaths: /(?:^|[
|
|
300
|
+
excludePaths: /(?:^|[/\\])(?:tests?|testdata)[/\\]|_test\.go$/,
|
|
293
301
|
},
|
|
294
302
|
|
|
295
303
|
// ══════════════════════════════════════════════════════════
|
|
@@ -311,7 +319,8 @@ const BUILT_IN_RULES = {
|
|
|
311
319
|
languages: ['dart'],
|
|
312
320
|
dimension: 'file',
|
|
313
321
|
category: 'style',
|
|
314
|
-
fixSuggestion:
|
|
322
|
+
fixSuggestion:
|
|
323
|
+
'使用 Object? 或具体类型替代 dynamic;Map<String, dynamic> 用于 JSON 序列化时可保留',
|
|
315
324
|
},
|
|
316
325
|
'dart-no-set-state-after-dispose': {
|
|
317
326
|
message: 'setState 调用前应检查 mounted 状态,避免 disposed 后调用',
|
|
@@ -344,7 +353,7 @@ const BUILT_IN_RULES = {
|
|
|
344
353
|
'dart-no-relative-import': {
|
|
345
354
|
message: 'lib/ 目录内应使用 package: 形式的绝对导入,避免相对路径导入',
|
|
346
355
|
severity: 'info',
|
|
347
|
-
pattern:
|
|
356
|
+
pattern: 'import\\s+[\'"]\\.\\.?/',
|
|
348
357
|
languages: ['dart'],
|
|
349
358
|
dimension: 'file',
|
|
350
359
|
category: 'style',
|
|
@@ -352,7 +361,8 @@ const BUILT_IN_RULES = {
|
|
|
352
361
|
'dart-dispose-controller': {
|
|
353
362
|
message: 'TextEditingController/AnimationController 等须在 dispose() 中释放',
|
|
354
363
|
severity: 'warning',
|
|
355
|
-
pattern:
|
|
364
|
+
pattern:
|
|
365
|
+
'(?:TextEditingController|AnimationController|ScrollController|FocusNode|TabController)\\(',
|
|
356
366
|
languages: ['dart'],
|
|
357
367
|
dimension: 'file',
|
|
358
368
|
category: 'correctness',
|
|
@@ -380,7 +390,8 @@ const BUILT_IN_RULES = {
|
|
|
380
390
|
dimension: 'file',
|
|
381
391
|
category: 'correctness',
|
|
382
392
|
fixSuggestion: '使用 ? 操作符传播错误,或 .unwrap_or_default() / .expect("原因")',
|
|
383
|
-
excludePaths:
|
|
393
|
+
excludePaths:
|
|
394
|
+
/(?:^|[/\\])(?:tests?|test_helpers|benches|examples)[/\\]|[/\\]test_[^/\\]*\.rs$|_test\.rs$/,
|
|
384
395
|
skipComments: true,
|
|
385
396
|
skipTestBlocks: true,
|
|
386
397
|
},
|
|
@@ -409,7 +420,7 @@ const BUILT_IN_RULES = {
|
|
|
409
420
|
languages: ['rust'],
|
|
410
421
|
dimension: 'file',
|
|
411
422
|
category: 'correctness',
|
|
412
|
-
excludePaths: /(?:^|[
|
|
423
|
+
excludePaths: /(?:^|[/\\])(?:tests?|test_helpers|benches|examples)[/\\]|_test\.rs$/,
|
|
413
424
|
skipComments: true,
|
|
414
425
|
skipTestBlocks: true,
|
|
415
426
|
},
|
|
@@ -421,7 +432,7 @@ const BUILT_IN_RULES = {
|
|
|
421
432
|
dimension: 'file',
|
|
422
433
|
category: 'performance',
|
|
423
434
|
fixSuggestion: '分析是否可用 &T 借用替代,或使用 Cow<T> 延迟克隆',
|
|
424
|
-
excludePaths: /(?:^|[
|
|
435
|
+
excludePaths: /(?:^|[/\\])(?:tests?|test_helpers|benches|examples)[/\\]|_test\.rs$/,
|
|
425
436
|
skipComments: true,
|
|
426
437
|
skipTestBlocks: true,
|
|
427
438
|
},
|
|
@@ -432,7 +443,7 @@ const BUILT_IN_RULES = {
|
|
|
432
443
|
languages: ['rust'],
|
|
433
444
|
dimension: 'file',
|
|
434
445
|
category: 'correctness',
|
|
435
|
-
excludePaths: /(?:^|[
|
|
446
|
+
excludePaths: /(?:^|[/\\])(?:tests?|test_helpers|benches|examples)[/\\]|main\.rs$/,
|
|
436
447
|
skipComments: true,
|
|
437
448
|
skipTestBlocks: true,
|
|
438
449
|
},
|
|
@@ -454,7 +465,6 @@ const BUILT_IN_RULES = {
|
|
|
454
465
|
category: 'performance',
|
|
455
466
|
fixSuggestion: '使用 Vec<&str> 收集后 .join(),或 String::with_capacity 预分配',
|
|
456
467
|
},
|
|
457
|
-
|
|
458
468
|
};
|
|
459
469
|
|
|
460
470
|
// 向后兼容: 从 GuardPatternUtils 重新导出 detectLanguage
|
|
@@ -489,13 +499,20 @@ export class GuardCheckEngine {
|
|
|
489
499
|
* @param {Array<{ruleId: string, pattern: RegExp|string, severity: string, message: string, category?: string, dimension?: string, languages?: string[], fixSuggestion?: string}>} rules
|
|
490
500
|
*/
|
|
491
501
|
injectExternalRules(rules) {
|
|
492
|
-
if (!Array.isArray(rules))
|
|
502
|
+
if (!Array.isArray(rules)) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
493
505
|
for (const rule of rules) {
|
|
494
|
-
if (!rule.ruleId)
|
|
506
|
+
if (!rule.ruleId) {
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
495
509
|
// 已注入的 ruleId 跳过(幂等)
|
|
496
|
-
if (this._externalRules.has(rule.ruleId))
|
|
510
|
+
if (this._externalRules.has(rule.ruleId)) {
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
497
513
|
// 跳过与 BUILT_IN_RULES 重复的模式(通过比较 pattern 源文本)
|
|
498
|
-
const rulePatternStr =
|
|
514
|
+
const rulePatternStr =
|
|
515
|
+
rule.pattern instanceof RegExp ? rule.pattern.source : String(rule.pattern || '');
|
|
499
516
|
const isDuplicate = Object.entries(this._builtInRules).some(([, builtIn]) => {
|
|
500
517
|
return builtIn.pattern === rulePatternStr;
|
|
501
518
|
});
|
|
@@ -517,14 +534,20 @@ export class GuardCheckEngine {
|
|
|
517
534
|
fixSuggestion: rule.fixSuggestion || null,
|
|
518
535
|
});
|
|
519
536
|
}
|
|
520
|
-
this.logger.debug(
|
|
537
|
+
this.logger.debug(
|
|
538
|
+
`[GuardCheckEngine] External rules injected: ${this._externalRules.size} active`
|
|
539
|
+
);
|
|
521
540
|
}
|
|
522
541
|
|
|
523
542
|
/**
|
|
524
543
|
* EP 注入幂等标记 — 调用者可用此判断是否已完成注入,避免重复加载 EnhancementRegistry
|
|
525
544
|
*/
|
|
526
|
-
isEpInjected() {
|
|
527
|
-
|
|
545
|
+
isEpInjected() {
|
|
546
|
+
return this._epInjected;
|
|
547
|
+
}
|
|
548
|
+
markEpInjected() {
|
|
549
|
+
this._epInjected = true;
|
|
550
|
+
}
|
|
528
551
|
|
|
529
552
|
/**
|
|
530
553
|
* 获取所有启用的规则 (数据库 + 内置)
|
|
@@ -665,7 +688,9 @@ export class GuardCheckEngine {
|
|
|
665
688
|
// 按 excludePaths 过滤(测试文件排除等)
|
|
666
689
|
if (filePath) {
|
|
667
690
|
rules = rules.filter((r) => {
|
|
668
|
-
if (!r.excludePaths)
|
|
691
|
+
if (!r.excludePaths) {
|
|
692
|
+
return true;
|
|
693
|
+
}
|
|
669
694
|
const re = r.excludePaths instanceof RegExp ? r.excludePaths : new RegExp(r.excludePaths);
|
|
670
695
|
return !re.test(filePath);
|
|
671
696
|
});
|
|
@@ -735,10 +760,12 @@ export class GuardCheckEngine {
|
|
|
735
760
|
}
|
|
736
761
|
|
|
737
762
|
// Code-level 检查(不依赖正则)
|
|
738
|
-
violations.push(
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
763
|
+
violations.push(
|
|
764
|
+
...runCodeLevelChecks(code, language, lines, {
|
|
765
|
+
disabledRules: this._guardConfig.disabledRules,
|
|
766
|
+
codeLevelThresholds: this._guardConfig.codeLevelThresholds,
|
|
767
|
+
})
|
|
768
|
+
);
|
|
742
769
|
|
|
743
770
|
// AST 语义规则检查
|
|
744
771
|
violations.push(...this._runAstRuleChecks(code, language));
|