autosnippet 3.3.0 → 3.3.3
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/dashboard/dist/assets/icons-BJ2mUBi8.js +1 -0
- package/dashboard/dist/assets/index-B659K9t5.js +128 -0
- package/dashboard/dist/assets/index-NCm40PMD.css +1 -0
- package/dashboard/dist/index.html +3 -3
- package/dist/bin/cli.d.ts +1 -0
- package/dist/bin/cli.js +284 -142
- package/dist/lib/agent/context/ExplorationTracker.d.ts +2 -0
- package/dist/lib/agent/context/ExplorationTracker.js +21 -3
- package/dist/lib/agent/core/ToolExecutionPipeline.d.ts +3 -1
- package/dist/lib/agent/core/ToolExecutionPipeline.js +8 -1
- package/dist/lib/agent/forge/DynamicComposer.d.ts +58 -0
- package/dist/lib/agent/forge/DynamicComposer.js +99 -0
- package/dist/lib/agent/forge/SandboxRunner.d.ts +60 -0
- package/dist/lib/agent/forge/SandboxRunner.js +251 -0
- package/dist/lib/agent/forge/TemporaryToolRegistry.d.ts +76 -0
- package/dist/lib/agent/forge/TemporaryToolRegistry.js +154 -0
- package/dist/lib/agent/forge/ToolForge.d.ts +92 -0
- package/dist/lib/agent/forge/ToolForge.js +239 -0
- package/dist/lib/agent/forge/ToolRequirementAnalyzer.d.ts +44 -0
- package/dist/lib/agent/forge/ToolRequirementAnalyzer.js +119 -0
- package/dist/lib/agent/tools/ToolRegistry.d.ts +2 -0
- package/dist/lib/agent/tools/ToolRegistry.js +4 -0
- package/dist/lib/agent/tools/composite.js +0 -1
- package/dist/lib/agent/tools/index.d.ts +2 -50
- package/dist/lib/agent/tools/index.js +2 -3
- package/dist/lib/agent/tools/lifecycle.d.ts +1 -58
- package/dist/lib/agent/tools/lifecycle.js +2 -75
- package/dist/lib/cli/KnowledgeSyncService.d.ts +26 -0
- package/dist/lib/cli/KnowledgeSyncService.js +33 -1
- package/dist/lib/cli/deploy/FileManifest.d.ts +0 -21
- package/dist/lib/cli/deploy/FileManifest.js +0 -11
- package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +10 -0
- package/dist/lib/domain/knowledge/KnowledgeEntry.js +2 -0
- package/dist/lib/domain/knowledge/Lifecycle.d.ts +19 -2
- package/dist/lib/domain/knowledge/Lifecycle.js +32 -6
- package/dist/lib/domain/knowledge/UnifiedValidator.d.ts +1 -5
- package/dist/lib/domain/knowledge/UnifiedValidator.js +7 -44
- package/dist/lib/domain/knowledge/values/Stats.d.ts +29 -0
- package/dist/lib/domain/knowledge/values/Stats.js +41 -0
- package/dist/lib/external/mcp/McpServer.d.ts +19 -38
- package/dist/lib/external/mcp/McpServer.js +145 -117
- package/dist/lib/external/mcp/autoApproveInjector.js +0 -2
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.d.ts +26 -1
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +41 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +49 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +3 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +27 -0
- package/dist/lib/external/mcp/handlers/bootstrap/skills.js +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap-external.js +1 -0
- package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
- package/dist/lib/external/mcp/handlers/browse.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/browse.js +2 -1
- package/dist/lib/external/mcp/handlers/consolidated.d.ts +117 -6
- package/dist/lib/external/mcp/handlers/consolidated.js +251 -71
- package/dist/lib/external/mcp/handlers/guard.d.ts +150 -0
- package/dist/lib/external/mcp/handlers/guard.js +239 -5
- package/dist/lib/external/mcp/handlers/knowledge.d.ts +0 -29
- package/dist/lib/external/mcp/handlers/knowledge.js +1 -76
- package/dist/lib/external/mcp/handlers/panorama.d.ts +36 -0
- package/dist/lib/external/mcp/handlers/panorama.js +156 -0
- package/dist/lib/external/mcp/handlers/system.d.ts +2 -54
- package/dist/lib/external/mcp/handlers/system.js +3 -113
- package/dist/lib/external/mcp/handlers/task.d.ts +13 -24
- package/dist/lib/external/mcp/handlers/task.js +218 -557
- package/dist/lib/external/mcp/handlers/types.d.ts +91 -8
- package/dist/lib/external/mcp/handlers/types.js +18 -1
- package/dist/lib/external/mcp/handlers/wiki-external.d.ts +18 -1
- package/dist/lib/external/mcp/handlers/wiki-external.js +16 -1
- package/dist/lib/external/mcp/tools.d.ts +18 -24
- package/dist/lib/external/mcp/tools.js +132 -159
- package/dist/lib/http/HttpServer.js +52 -0
- package/dist/lib/http/middleware/validate.js +7 -3
- package/dist/lib/http/routes/audit.d.ts +8 -0
- package/dist/lib/http/routes/audit.js +51 -0
- package/dist/lib/http/routes/guardReport.d.ts +10 -0
- package/dist/lib/http/routes/guardReport.js +143 -0
- package/dist/lib/http/routes/knowledge.js +32 -1
- package/dist/lib/http/routes/panorama.d.ts +11 -0
- package/dist/lib/http/routes/panorama.js +322 -0
- package/dist/lib/http/routes/signals.d.ts +10 -0
- package/dist/lib/http/routes/signals.js +104 -0
- package/dist/lib/http/routes/task.d.ts +2 -3
- package/dist/lib/http/routes/task.js +17 -347
- package/dist/lib/http/routes/violations.js +1 -1
- package/dist/lib/infrastructure/audit/AuditLogger.d.ts +6 -1
- package/dist/lib/infrastructure/audit/AuditLogger.js +14 -1
- package/dist/lib/infrastructure/database/drizzle/schema.d.ts +202 -504
- package/dist/lib/infrastructure/database/drizzle/schema.js +38 -69
- package/dist/lib/infrastructure/database/migrations/004_evolution_proposals.d.ts +8 -0
- package/dist/lib/infrastructure/database/migrations/004_evolution_proposals.js +43 -0
- package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.d.ts +9 -0
- package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.js +24 -0
- package/dist/lib/infrastructure/logging/Logger.d.ts +2 -0
- package/dist/lib/infrastructure/logging/Logger.js +34 -7
- package/dist/lib/infrastructure/monitoring/ErrorTracker.js +3 -1
- package/dist/lib/infrastructure/monitoring/PerformanceMonitor.d.ts +2 -2
- package/dist/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -10
- package/dist/lib/infrastructure/notification/LarkNotifier.d.ts +24 -0
- package/dist/lib/infrastructure/notification/LarkNotifier.js +97 -0
- package/dist/lib/infrastructure/report/ReportStore.d.ts +45 -0
- package/dist/lib/infrastructure/report/ReportStore.js +133 -0
- package/dist/lib/infrastructure/signal/SignalAggregator.d.ts +18 -0
- package/dist/lib/infrastructure/signal/SignalAggregator.js +84 -0
- package/dist/lib/infrastructure/signal/SignalBridge.d.ts +13 -0
- package/dist/lib/infrastructure/signal/SignalBridge.js +20 -0
- package/dist/lib/infrastructure/signal/SignalBus.d.ts +63 -0
- package/dist/lib/infrastructure/signal/SignalBus.js +106 -0
- package/dist/lib/infrastructure/signal/SignalTraceWriter.d.ts +36 -0
- package/dist/lib/infrastructure/signal/SignalTraceWriter.js +130 -0
- package/dist/lib/infrastructure/vector/HnswVectorAdapter.js +18 -2
- package/dist/lib/injection/ServiceContainer.js +8 -0
- package/dist/lib/injection/ServiceMap.d.ts +16 -10
- package/dist/lib/injection/modules/AgentModule.d.ts +1 -1
- package/dist/lib/injection/modules/AgentModule.js +7 -1
- package/dist/lib/injection/modules/AppModule.d.ts +1 -1
- package/dist/lib/injection/modules/AppModule.js +4 -13
- package/dist/lib/injection/modules/GuardModule.js +27 -2
- package/dist/lib/injection/modules/InfraModule.d.ts +0 -1
- package/dist/lib/injection/modules/InfraModule.js +9 -7
- package/dist/lib/injection/modules/KnowledgeModule.d.ts +5 -0
- package/dist/lib/injection/modules/KnowledgeModule.js +131 -0
- package/dist/lib/injection/modules/PanoramaModule.d.ts +18 -0
- package/dist/lib/injection/modules/PanoramaModule.js +76 -0
- package/dist/lib/injection/modules/SignalModule.d.ts +10 -0
- package/dist/lib/injection/modules/SignalModule.js +84 -0
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +1 -0
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +6 -0
- package/dist/lib/service/bootstrap/BootstrapTaskManager.d.ts +3 -1
- package/dist/lib/service/bootstrap/BootstrapTaskManager.js +20 -1
- package/dist/lib/service/bootstrap/UiStartupTasks.d.ts +45 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.js +101 -0
- package/dist/lib/service/delivery/AgentInstructionsGenerator.js +4 -5
- package/dist/lib/service/delivery/CursorDeliveryPipeline.d.ts +3 -1
- package/dist/lib/service/delivery/CursorDeliveryPipeline.js +13 -10
- package/dist/lib/service/delivery/RulesGenerator.js +3 -2
- package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +114 -0
- package/dist/lib/service/evolution/ConsolidationAdvisor.js +542 -0
- package/dist/lib/service/evolution/ContradictionDetector.d.ts +54 -0
- package/dist/lib/service/evolution/ContradictionDetector.js +253 -0
- package/dist/lib/service/evolution/DecayDetector.d.ts +71 -0
- package/dist/lib/service/evolution/DecayDetector.js +244 -0
- package/dist/lib/service/evolution/EnhancementSuggester.d.ts +38 -0
- package/dist/lib/service/evolution/EnhancementSuggester.js +220 -0
- package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +82 -0
- package/dist/lib/service/evolution/KnowledgeMetabolism.js +167 -0
- package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +53 -0
- package/dist/lib/service/evolution/RedundancyAnalyzer.js +210 -0
- package/dist/lib/service/evolution/StagingManager.d.ts +57 -0
- package/dist/lib/service/evolution/StagingManager.js +201 -0
- package/dist/lib/service/guard/ComplianceReporter.d.ts +42 -2
- package/dist/lib/service/guard/ComplianceReporter.js +43 -5
- package/dist/lib/service/guard/CoverageAnalyzer.d.ts +54 -0
- package/dist/lib/service/guard/CoverageAnalyzer.js +149 -0
- package/dist/lib/service/guard/GuardCheckEngine.d.ts +42 -0
- package/dist/lib/service/guard/GuardCheckEngine.js +465 -14
- package/dist/lib/service/guard/GuardFeedbackLoop.d.ts +3 -0
- package/dist/lib/service/guard/GuardFeedbackLoop.js +9 -0
- package/dist/lib/service/guard/ReverseGuard.d.ts +73 -0
- package/dist/lib/service/guard/ReverseGuard.js +256 -0
- package/dist/lib/service/guard/RuleLearner.d.ts +12 -0
- package/dist/lib/service/guard/RuleLearner.js +38 -0
- package/dist/lib/service/guard/UncertaintyCollector.d.ts +83 -0
- package/dist/lib/service/guard/UncertaintyCollector.js +149 -0
- package/dist/lib/service/guard/ViolationsStore.d.ts +1 -0
- package/dist/lib/service/guard/ViolationsStore.js +33 -3
- package/dist/lib/service/knowledge/ConfidenceRouter.d.ts +13 -0
- package/dist/lib/service/knowledge/ConfidenceRouter.js +14 -0
- package/dist/lib/service/knowledge/KnowledgeService.js +22 -4
- package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +68 -0
- package/dist/lib/service/knowledge/SourceRefReconciler.js +309 -0
- package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +27 -0
- package/dist/lib/service/panorama/CouplingAnalyzer.js +192 -0
- package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +28 -0
- package/dist/lib/service/panorama/DimensionAnalyzer.js +320 -0
- package/dist/lib/service/panorama/LayerInferrer.d.ts +19 -0
- package/dist/lib/service/panorama/LayerInferrer.js +182 -0
- package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +24 -0
- package/dist/lib/service/panorama/ModuleDiscoverer.js +185 -0
- package/dist/lib/service/panorama/PanoramaAggregator.d.ts +29 -0
- package/dist/lib/service/panorama/PanoramaAggregator.js +228 -0
- package/dist/lib/service/panorama/PanoramaScanner.d.ts +52 -0
- package/dist/lib/service/panorama/PanoramaScanner.js +188 -0
- package/dist/lib/service/panorama/PanoramaService.d.ts +125 -0
- package/dist/lib/service/panorama/PanoramaService.js +363 -0
- package/dist/lib/service/panorama/PanoramaTypes.d.ts +134 -0
- package/dist/lib/service/panorama/PanoramaTypes.js +6 -0
- package/dist/lib/service/panorama/RoleRefiner.d.ts +48 -0
- package/dist/lib/service/panorama/RoleRefiner.js +535 -0
- package/dist/lib/service/search/BM25Scorer.d.ts +2 -2
- package/dist/lib/service/search/CoarseRanker.d.ts +7 -6
- package/dist/lib/service/search/CoarseRanker.js +11 -10
- package/dist/lib/service/search/FieldWeightedScorer.d.ts +81 -0
- package/dist/lib/service/search/FieldWeightedScorer.js +318 -0
- package/dist/lib/service/search/MultiSignalRanker.d.ts +3 -2
- package/dist/lib/service/search/MultiSignalRanker.js +17 -1
- package/dist/lib/service/search/SearchEngine.d.ts +9 -7
- package/dist/lib/service/search/SearchEngine.js +67 -10
- package/dist/lib/service/search/SearchTypes.d.ts +25 -3
- package/dist/lib/service/search/SearchTypes.js +6 -1
- package/dist/lib/service/signal/HitRecorder.d.ts +68 -0
- package/dist/lib/service/signal/HitRecorder.js +173 -0
- package/dist/lib/service/skills/SignalCollector.d.ts +3 -1
- package/dist/lib/service/skills/SignalCollector.js +31 -1
- package/dist/lib/service/task/IntentExtractor.d.ts +66 -0
- package/dist/lib/service/task/IntentExtractor.js +256 -0
- package/dist/lib/service/task/PrimeSearchPipeline.d.ts +54 -0
- package/dist/lib/service/task/PrimeSearchPipeline.js +113 -0
- package/dist/lib/service/vector/VectorService.d.ts +3 -0
- package/dist/lib/service/vector/VectorService.js +38 -4
- package/dist/lib/shared/schemas/mcp-tools.d.ts +41 -96
- package/dist/lib/shared/schemas/mcp-tools.js +59 -119
- package/dist/scripts/analyze-signals.d.ts +20 -0
- package/dist/scripts/analyze-signals.js +155 -0
- package/dist/scripts/diagnose-mcp.js +1 -1
- package/package.json +1 -1
- package/skills/autosnippet-create/SKILL.md +98 -89
- package/skills/autosnippet-devdocs/SKILL.md +55 -57
- package/templates/claude-code/hooks/autosnippet-session.sh +10 -15
- package/templates/cursor-hooks/hooks/session-start.sh +1 -1
- package/templates/guard-ci.yml +2 -2
- package/templates/instructions/agent-static.md +2 -1
- package/templates/instructions/conventions.md +5 -6
- package/templates/recipes-setup/README.md +1 -2
- package/templates/recipes-setup/_template.md +39 -39
- package/dashboard/dist/assets/icons-BofcEZ3f.js +0 -1
- package/dashboard/dist/assets/index-D0whuycy.css +0 -1
- package/dashboard/dist/assets/index-SiN1GChm.js +0 -128
- package/dist/lib/domain/task/Task.d.ts +0 -140
- package/dist/lib/domain/task/Task.js +0 -254
- package/dist/lib/domain/task/TaskDependency.d.ts +0 -23
- package/dist/lib/domain/task/TaskDependency.js +0 -34
- package/dist/lib/domain/task/TaskIdGenerator.d.ts +0 -40
- package/dist/lib/domain/task/TaskIdGenerator.js +0 -75
- package/dist/lib/domain/task/index.d.ts +0 -4
- package/dist/lib/domain/task/index.js +0 -4
- package/dist/lib/infrastructure/database/migrations/002_add_tasks.d.ts +0 -11
- package/dist/lib/infrastructure/database/migrations/002_add_tasks.js +0 -86
- package/dist/lib/repository/task/TaskRepository.impl.d.ts +0 -171
- package/dist/lib/repository/task/TaskRepository.impl.js +0 -347
- package/dist/lib/service/task/TaskGraphService.d.ts +0 -222
- package/dist/lib/service/task/TaskGraphService.js +0 -597
- package/dist/lib/service/task/TaskKnowledgeBridge.d.ts +0 -95
- package/dist/lib/service/task/TaskKnowledgeBridge.js +0 -298
- package/dist/lib/service/task/TaskReadyEngine.d.ts +0 -84
- package/dist/lib/service/task/TaskReadyEngine.js +0 -115
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReverseGuard — Recipe→Code 反向验证
|
|
3
|
+
*
|
|
4
|
+
* 正向: 代码 → Guard → "代码是否符合知识?" ✅ 已有
|
|
5
|
+
* 反向: Recipe → Guard → "知识是否还符合代码?" ← 本文件
|
|
6
|
+
*
|
|
7
|
+
* 对每条 active rule Recipe:
|
|
8
|
+
* 1. 提取 coreCode 中的 API 引用(类名、方法名)
|
|
9
|
+
* 2. 在 code_entities 表中查找这些符号
|
|
10
|
+
* 3. 符号不存在 → PatternDrift
|
|
11
|
+
* 4. 提取 guard regex pattern → 对项目代码运行匹配
|
|
12
|
+
* 5. 匹配率骤降 → 代码模式正在迁移
|
|
13
|
+
*/
|
|
14
|
+
import type { SignalBus } from '../../infrastructure/signal/SignalBus.js';
|
|
15
|
+
interface DatabaseLike {
|
|
16
|
+
prepare(sql: string): {
|
|
17
|
+
all(...params: unknown[]): Record<string, unknown>[];
|
|
18
|
+
get(...params: unknown[]): Record<string, unknown> | undefined;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export type DriftType = 'symbol_missing' | 'match_rate_drop' | 'api_deprecated' | 'zero_match';
|
|
22
|
+
export type DriftSeverity = 'high' | 'medium' | 'low';
|
|
23
|
+
export interface PatternDriftSignal {
|
|
24
|
+
type: DriftType;
|
|
25
|
+
detail: string;
|
|
26
|
+
severity: DriftSeverity;
|
|
27
|
+
evidence: {
|
|
28
|
+
expectedSymbol?: string;
|
|
29
|
+
matchRate?: {
|
|
30
|
+
current: number;
|
|
31
|
+
historical: number;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export type ReverseRecommendation = 'healthy' | 'investigate' | 'decay';
|
|
36
|
+
export interface ReverseGuardResult {
|
|
37
|
+
recipeId: string;
|
|
38
|
+
title: string;
|
|
39
|
+
signals: PatternDriftSignal[];
|
|
40
|
+
recommendation: ReverseRecommendation;
|
|
41
|
+
}
|
|
42
|
+
interface RecipeRow {
|
|
43
|
+
id: string;
|
|
44
|
+
title: string;
|
|
45
|
+
core_code: string | null;
|
|
46
|
+
guard_pattern: string | null;
|
|
47
|
+
stats: string | null;
|
|
48
|
+
}
|
|
49
|
+
export declare class ReverseGuard {
|
|
50
|
+
#private;
|
|
51
|
+
constructor(db: DatabaseLike, options?: {
|
|
52
|
+
signalBus?: SignalBus;
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* 对一条 Recipe 执行反向验证
|
|
56
|
+
*/
|
|
57
|
+
checkRecipe(recipe: RecipeRow, projectFiles: {
|
|
58
|
+
path: string;
|
|
59
|
+
content: string;
|
|
60
|
+
}[]): ReverseGuardResult;
|
|
61
|
+
/**
|
|
62
|
+
* 批量对所有 active rule Recipes 执行反向验证
|
|
63
|
+
*/
|
|
64
|
+
auditAllRules(projectFiles: {
|
|
65
|
+
path: string;
|
|
66
|
+
content: string;
|
|
67
|
+
}[]): ReverseGuardResult[];
|
|
68
|
+
/**
|
|
69
|
+
* 获取需要调查/衰退的 Recipe 结果
|
|
70
|
+
*/
|
|
71
|
+
getDriftResults(results: ReverseGuardResult[]): ReverseGuardResult[];
|
|
72
|
+
}
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReverseGuard — Recipe→Code 反向验证
|
|
3
|
+
*
|
|
4
|
+
* 正向: 代码 → Guard → "代码是否符合知识?" ✅ 已有
|
|
5
|
+
* 反向: Recipe → Guard → "知识是否还符合代码?" ← 本文件
|
|
6
|
+
*
|
|
7
|
+
* 对每条 active rule Recipe:
|
|
8
|
+
* 1. 提取 coreCode 中的 API 引用(类名、方法名)
|
|
9
|
+
* 2. 在 code_entities 表中查找这些符号
|
|
10
|
+
* 3. 符号不存在 → PatternDrift
|
|
11
|
+
* 4. 提取 guard regex pattern → 对项目代码运行匹配
|
|
12
|
+
* 5. 匹配率骤降 → 代码模式正在迁移
|
|
13
|
+
*/
|
|
14
|
+
import Logger from '../../infrastructure/logging/Logger.js';
|
|
15
|
+
/* ────────────────────── 常量 ────────────────────── */
|
|
16
|
+
/** 从 coreCode 中提取符号引用的正则(多语言通用) */
|
|
17
|
+
const SYMBOL_PATTERNS = [
|
|
18
|
+
// ClassName.method / ClassName.shared / ClassName() — Swift/Java/Kotlin/TS/Dart/C#
|
|
19
|
+
/\b([A-Z][A-Za-z0-9_]+)\s*[.(]/g,
|
|
20
|
+
// [ClassName method] — ObjC 消息发送
|
|
21
|
+
/\[\s*([A-Z][A-Za-z0-9_]+)\s+\w/g,
|
|
22
|
+
// import/from 引用 — JS/TS/Python/Dart/Go
|
|
23
|
+
/(?:import|from)\s+['"]([^'"]+)['"]/g,
|
|
24
|
+
// #import / #include — ObjC/C/C++ 头文件引用
|
|
25
|
+
/^\s*#(?:import|include)\s+[<"]([^>"]+)[>"]/gm,
|
|
26
|
+
// package.function / module::Type — Go/Rust 限定名
|
|
27
|
+
/\b([a-z][a-z0-9_]+(?:::[A-Z][A-Za-z0-9_]+|\.[A-Z][A-Za-z0-9_]+))/g,
|
|
28
|
+
// @decorator — Python/TS decorator 引用
|
|
29
|
+
/@([A-Z][A-Za-z0-9_]+)/g,
|
|
30
|
+
];
|
|
31
|
+
/** 定义多少条 drift signal 算 investigate / decay */
|
|
32
|
+
const DRIFT_THRESHOLDS = {
|
|
33
|
+
/** ≥1 high → investigate */
|
|
34
|
+
INVESTIGATE_HIGH: 1,
|
|
35
|
+
/** ≥2 high → decay */
|
|
36
|
+
DECAY_HIGH: 2,
|
|
37
|
+
/** ≥3 medium → investigate */
|
|
38
|
+
INVESTIGATE_MEDIUM: 3,
|
|
39
|
+
};
|
|
40
|
+
/* ────────────────────── Class ────────────────────── */
|
|
41
|
+
export class ReverseGuard {
|
|
42
|
+
#db;
|
|
43
|
+
#signalBus;
|
|
44
|
+
#logger = Logger.getInstance();
|
|
45
|
+
constructor(db, options = {}) {
|
|
46
|
+
this.#db = db;
|
|
47
|
+
this.#signalBus = options.signalBus ?? null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 对一条 Recipe 执行反向验证
|
|
51
|
+
*/
|
|
52
|
+
checkRecipe(recipe, projectFiles) {
|
|
53
|
+
const signals = [];
|
|
54
|
+
// 1. 检查 coreCode 中引用的符号是否还存在于代码库
|
|
55
|
+
if (recipe.core_code) {
|
|
56
|
+
signals.push(...this.#checkSymbolExistence(recipe.core_code));
|
|
57
|
+
}
|
|
58
|
+
// 2. 检查 guard pattern 在项目代码中的匹配率
|
|
59
|
+
if (recipe.guard_pattern) {
|
|
60
|
+
signals.push(...this.#checkPatternMatchRate(recipe.id, recipe.guard_pattern, projectFiles));
|
|
61
|
+
}
|
|
62
|
+
// 3. 综合判定
|
|
63
|
+
const recommendation = this.#computeRecommendation(signals);
|
|
64
|
+
// 4. 发射信号
|
|
65
|
+
if (this.#signalBus && signals.length > 0) {
|
|
66
|
+
const severity = recommendation === 'decay' ? 1 : recommendation === 'investigate' ? 0.5 : 0;
|
|
67
|
+
this.#signalBus.send('quality', 'ReverseGuard', severity, {
|
|
68
|
+
target: recipe.id,
|
|
69
|
+
metadata: {
|
|
70
|
+
signalCount: signals.length,
|
|
71
|
+
recommendation,
|
|
72
|
+
driftTypes: [...new Set(signals.map((s) => s.type))],
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
recipeId: recipe.id,
|
|
78
|
+
title: recipe.title,
|
|
79
|
+
signals,
|
|
80
|
+
recommendation,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 批量对所有 active rule Recipes 执行反向验证
|
|
85
|
+
*/
|
|
86
|
+
auditAllRules(projectFiles) {
|
|
87
|
+
const recipes = this.#loadActiveRuleRecipes();
|
|
88
|
+
const results = [];
|
|
89
|
+
for (const recipe of recipes) {
|
|
90
|
+
try {
|
|
91
|
+
results.push(this.checkRecipe(recipe, projectFiles));
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
this.#logger.debug(`ReverseGuard: failed to check recipe ${recipe.id}: ${err.message}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return results;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 获取需要调查/衰退的 Recipe 结果
|
|
101
|
+
*/
|
|
102
|
+
getDriftResults(results) {
|
|
103
|
+
return results.filter((r) => r.recommendation !== 'healthy');
|
|
104
|
+
}
|
|
105
|
+
/* ── 内部方法 ── */
|
|
106
|
+
#loadActiveRuleRecipes() {
|
|
107
|
+
try {
|
|
108
|
+
const rows = this.#db
|
|
109
|
+
.prepare(`SELECT id, title,
|
|
110
|
+
json_extract(content, '$.coreCode') AS core_code,
|
|
111
|
+
json_extract(content, '$.pattern') AS guard_pattern,
|
|
112
|
+
stats
|
|
113
|
+
FROM knowledge_entries
|
|
114
|
+
WHERE lifecycle = 'active'
|
|
115
|
+
AND kind = 'rule'`)
|
|
116
|
+
.all();
|
|
117
|
+
return rows.map((r) => ({
|
|
118
|
+
id: r.id,
|
|
119
|
+
title: r.title,
|
|
120
|
+
core_code: r.core_code ?? null,
|
|
121
|
+
guard_pattern: r.guard_pattern ?? null,
|
|
122
|
+
stats: r.stats ?? null,
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 检查 coreCode 中引用的符号是否存在于 code_entities 表
|
|
131
|
+
*/
|
|
132
|
+
#checkSymbolExistence(coreCode) {
|
|
133
|
+
const symbols = this.#extractSymbols(coreCode);
|
|
134
|
+
if (symbols.size === 0) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
const signals = [];
|
|
138
|
+
for (const symbol of symbols) {
|
|
139
|
+
try {
|
|
140
|
+
const row = this.#db
|
|
141
|
+
.prepare(`SELECT name FROM code_entities WHERE name = ? LIMIT 1`)
|
|
142
|
+
.get(symbol);
|
|
143
|
+
if (!row) {
|
|
144
|
+
signals.push({
|
|
145
|
+
type: 'symbol_missing',
|
|
146
|
+
detail: `Symbol "${symbol}" referenced in recipe coreCode not found in codebase`,
|
|
147
|
+
severity: 'high',
|
|
148
|
+
evidence: { expectedSymbol: symbol },
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// code_entities 表不存在时静默跳过
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return signals;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* 检查 guard pattern 在项目代码中的匹配情况
|
|
161
|
+
*/
|
|
162
|
+
#checkPatternMatchRate(recipeId, guardPattern, projectFiles) {
|
|
163
|
+
let re;
|
|
164
|
+
try {
|
|
165
|
+
re = new RegExp(guardPattern, 'gm');
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return []; // 无效正则,不产出 drift(由 UncertaintyCollector 处理)
|
|
169
|
+
}
|
|
170
|
+
// 统计当前匹配数
|
|
171
|
+
let currentMatches = 0;
|
|
172
|
+
for (const file of projectFiles) {
|
|
173
|
+
const matches = file.content.match(re);
|
|
174
|
+
if (matches) {
|
|
175
|
+
currentMatches += matches.length;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// 获取历史匹配率(从 stats.guardHits 推断)
|
|
179
|
+
const historicalHits = this.#getHistoricalHits(recipeId);
|
|
180
|
+
const signals = [];
|
|
181
|
+
if (currentMatches === 0 && projectFiles.length > 0) {
|
|
182
|
+
// 完全匹配不到 — 场景已不存在
|
|
183
|
+
signals.push({
|
|
184
|
+
type: 'zero_match',
|
|
185
|
+
detail: `Guard pattern matches 0 times across ${projectFiles.length} files — scenario may no longer exist`,
|
|
186
|
+
severity: 'high',
|
|
187
|
+
evidence: {
|
|
188
|
+
matchRate: { current: 0, historical: historicalHits },
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
else if (historicalHits > 0 && currentMatches > 0) {
|
|
193
|
+
// 匹配率大幅下降
|
|
194
|
+
const dropRatio = currentMatches / historicalHits;
|
|
195
|
+
if (dropRatio < 0.3) {
|
|
196
|
+
signals.push({
|
|
197
|
+
type: 'match_rate_drop',
|
|
198
|
+
detail: `Guard pattern match count dropped significantly: ${currentMatches} current vs ${historicalHits} historical (${Math.round(dropRatio * 100)}%)`,
|
|
199
|
+
severity: 'medium',
|
|
200
|
+
evidence: {
|
|
201
|
+
matchRate: { current: currentMatches, historical: historicalHits },
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return signals;
|
|
207
|
+
}
|
|
208
|
+
#extractSymbols(coreCode) {
|
|
209
|
+
const symbols = new Set();
|
|
210
|
+
for (const pattern of SYMBOL_PATTERNS) {
|
|
211
|
+
// 重置 lastIndex(全局正则需要重置)
|
|
212
|
+
const re = new RegExp(pattern.source, pattern.flags);
|
|
213
|
+
let match;
|
|
214
|
+
while ((match = re.exec(coreCode)) !== null) {
|
|
215
|
+
const symbol = match[1];
|
|
216
|
+
if (!symbol || symbol.length < 3) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
// 过滤纯小写短词(if/for/var 等关键词),但允许含大写、含分隔符(::/.)、含路径分隔符(/)的符号
|
|
220
|
+
if (/^[a-z]+$/.test(symbol) && symbol.length < 6) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
symbols.add(symbol);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return symbols;
|
|
227
|
+
}
|
|
228
|
+
#getHistoricalHits(recipeId) {
|
|
229
|
+
try {
|
|
230
|
+
const row = this.#db
|
|
231
|
+
.prepare(`SELECT json_extract(stats, '$.guardHits') AS hits FROM knowledge_entries WHERE id = ?`)
|
|
232
|
+
.get(recipeId);
|
|
233
|
+
return row?.hits ?? 0;
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
#computeRecommendation(signals) {
|
|
240
|
+
if (signals.length === 0) {
|
|
241
|
+
return 'healthy';
|
|
242
|
+
}
|
|
243
|
+
const highCount = signals.filter((s) => s.severity === 'high').length;
|
|
244
|
+
const mediumCount = signals.filter((s) => s.severity === 'medium').length;
|
|
245
|
+
if (highCount >= DRIFT_THRESHOLDS.DECAY_HIGH) {
|
|
246
|
+
return 'decay';
|
|
247
|
+
}
|
|
248
|
+
if (highCount >= DRIFT_THRESHOLDS.INVESTIGATE_HIGH) {
|
|
249
|
+
return 'investigate';
|
|
250
|
+
}
|
|
251
|
+
if (mediumCount >= DRIFT_THRESHOLDS.INVESTIGATE_MEDIUM) {
|
|
252
|
+
return 'investigate';
|
|
253
|
+
}
|
|
254
|
+
return 'healthy';
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* 追踪规则触发与用户反馈,计算 P/R/F1,识别高误报规则并给出优化建议
|
|
4
4
|
* 持久化到 AutoSnippet/guard-learner.json(Git 友好)
|
|
5
5
|
*/
|
|
6
|
+
import type { SignalBus } from '../../infrastructure/signal/SignalBus.js';
|
|
6
7
|
interface RuleStat {
|
|
7
8
|
triggers: number;
|
|
8
9
|
correct: number;
|
|
@@ -17,6 +18,7 @@ export declare class RuleLearner {
|
|
|
17
18
|
constructor(projectRoot: string, options?: {
|
|
18
19
|
knowledgeBaseDir?: string;
|
|
19
20
|
internalDir?: string;
|
|
21
|
+
signalBus?: SignalBus;
|
|
20
22
|
});
|
|
21
23
|
/**
|
|
22
24
|
* 记录规则触发
|
|
@@ -82,5 +84,15 @@ export declare class RuleLearner {
|
|
|
82
84
|
recommendation: string;
|
|
83
85
|
daysSinceFirstTrigger: number;
|
|
84
86
|
};
|
|
87
|
+
/**
|
|
88
|
+
* RuleLearner→Recipe 桥接: 检查是否有高误报规则需要触发衰退
|
|
89
|
+
* 当 FP > 40% && triggers >= minTriggers 时,发射衰退信号到 SignalBus
|
|
90
|
+
* @returns 需要衰退检查的规则列表
|
|
91
|
+
*/
|
|
92
|
+
checkPrecisionDrop(): {
|
|
93
|
+
ruleId: string;
|
|
94
|
+
falsePositiveRate: number;
|
|
95
|
+
recommendation: string;
|
|
96
|
+
}[];
|
|
85
97
|
}
|
|
86
98
|
export {};
|
|
@@ -16,12 +16,14 @@ const PROBLEMATIC_THRESHOLD = {
|
|
|
16
16
|
export class RuleLearner {
|
|
17
17
|
#learnerPath;
|
|
18
18
|
#data;
|
|
19
|
+
#signalBus;
|
|
19
20
|
constructor(projectRoot, options = {}) {
|
|
20
21
|
const kbDir = options.knowledgeBaseDir || DEFAULT_KNOWLEDGE_BASE_DIR;
|
|
21
22
|
this.#learnerPath = join(projectRoot, kbDir, 'guard-learner.json');
|
|
22
23
|
pathGuard.assertProjectWriteSafe(this.#learnerPath);
|
|
23
24
|
this.#migrateOldPath(projectRoot, options.internalDir || '.autosnippet');
|
|
24
25
|
this.#data = this.#load();
|
|
26
|
+
this.#signalBus = options.signalBus || null;
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
27
29
|
* 记录规则触发
|
|
@@ -51,6 +53,14 @@ export class RuleLearner {
|
|
|
51
53
|
}
|
|
52
54
|
stat.lastFeedback = new Date().toISOString();
|
|
53
55
|
this.#save();
|
|
56
|
+
// ── Signal: quality feedback ──
|
|
57
|
+
if (this.#signalBus) {
|
|
58
|
+
const metrics = this.getMetrics(ruleId);
|
|
59
|
+
this.#signalBus.send('quality', 'RuleLearner', 1 - metrics.falsePositiveRate, {
|
|
60
|
+
target: ruleId,
|
|
61
|
+
metadata: { feedbackType, precision: metrics.precision },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
54
64
|
}
|
|
55
65
|
/**
|
|
56
66
|
* 获取规则精准度指标
|
|
@@ -227,6 +237,34 @@ export class RuleLearner {
|
|
|
227
237
|
daysSinceFirstTrigger: Math.round(daysSinceFirstTrigger),
|
|
228
238
|
};
|
|
229
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* RuleLearner→Recipe 桥接: 检查是否有高误报规则需要触发衰退
|
|
242
|
+
* 当 FP > 40% && triggers >= minTriggers 时,发射衰退信号到 SignalBus
|
|
243
|
+
* @returns 需要衰退检查的规则列表
|
|
244
|
+
*/
|
|
245
|
+
checkPrecisionDrop() {
|
|
246
|
+
const problematic = this.getProblematicRules();
|
|
247
|
+
const results = [];
|
|
248
|
+
for (const p of problematic) {
|
|
249
|
+
results.push({
|
|
250
|
+
ruleId: p.ruleId,
|
|
251
|
+
falsePositiveRate: p.metrics.falsePositiveRate,
|
|
252
|
+
recommendation: p.recommendation,
|
|
253
|
+
});
|
|
254
|
+
// 发射衰退信号
|
|
255
|
+
if (this.#signalBus) {
|
|
256
|
+
this.#signalBus.send('quality', 'RuleLearner.precisionDrop', p.metrics.falsePositiveRate, {
|
|
257
|
+
target: p.ruleId,
|
|
258
|
+
metadata: {
|
|
259
|
+
recommendation: p.recommendation,
|
|
260
|
+
precision: p.metrics.precision,
|
|
261
|
+
triggers: p.metrics.triggers,
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return results;
|
|
267
|
+
}
|
|
230
268
|
// ─── 私有 ─────────────────────────────────────────────
|
|
231
269
|
#ensureStat(ruleId) {
|
|
232
270
|
if (!this.#data.ruleStats[ruleId]) {
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UncertaintyCollector — Guard uncertain 三态收集器
|
|
3
|
+
*
|
|
4
|
+
* 当 Guard 各层检测遇到能力边界(AST 不可用、跨文件缺失、正则冲突等)时,
|
|
5
|
+
* 收集 skip 原因并产出结构化的 uncertain 结果。
|
|
6
|
+
*
|
|
7
|
+
* 设计原则:
|
|
8
|
+
* - uncertain 不是"错误",是"承认能力边界"
|
|
9
|
+
* - Guard 不调用 AI,uncertain 是确定性输出
|
|
10
|
+
* - 保持 <10ms 性能
|
|
11
|
+
*/
|
|
12
|
+
export type SkipLayer = 'regex' | 'code_level' | 'ast' | 'cross_file';
|
|
13
|
+
export type SkipReason = 'invalid_regex' | 'lang_unsupported' | 'ast_unavailable' | 'file_missing' | 'scope_mismatch' | 'layer_conflict';
|
|
14
|
+
export type SkipImpact = 'high' | 'medium' | 'low';
|
|
15
|
+
export interface SkippedCheck {
|
|
16
|
+
layer: SkipLayer;
|
|
17
|
+
ruleId?: string;
|
|
18
|
+
reason: SkipReason;
|
|
19
|
+
detail: string;
|
|
20
|
+
impact: SkipImpact;
|
|
21
|
+
}
|
|
22
|
+
export type BoundaryType = 'ast_language_gap' | 'cross_file_incomplete' | 'rule_regex_invalid' | 'scope_unchecked' | 'transitive_cycle';
|
|
23
|
+
export interface CapabilityBoundary {
|
|
24
|
+
type: BoundaryType;
|
|
25
|
+
description: string;
|
|
26
|
+
affectedRules: string[];
|
|
27
|
+
suggestedAction: string;
|
|
28
|
+
}
|
|
29
|
+
export interface UncertainResult {
|
|
30
|
+
ruleId: string;
|
|
31
|
+
message: string;
|
|
32
|
+
layer: SkipLayer;
|
|
33
|
+
reason: SkipReason;
|
|
34
|
+
detail: string;
|
|
35
|
+
}
|
|
36
|
+
export interface GuardCapabilityReport {
|
|
37
|
+
executedChecks: {
|
|
38
|
+
regex: {
|
|
39
|
+
total: number;
|
|
40
|
+
executed: number;
|
|
41
|
+
skipped: number;
|
|
42
|
+
};
|
|
43
|
+
codeLevel: {
|
|
44
|
+
total: number;
|
|
45
|
+
executed: number;
|
|
46
|
+
skipped: number;
|
|
47
|
+
};
|
|
48
|
+
ast: {
|
|
49
|
+
total: number;
|
|
50
|
+
executed: number;
|
|
51
|
+
skipped: number;
|
|
52
|
+
};
|
|
53
|
+
crossFile: {
|
|
54
|
+
total: number;
|
|
55
|
+
executed: number;
|
|
56
|
+
skipped: number;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
skippedChecks: SkippedCheck[];
|
|
60
|
+
boundaries: CapabilityBoundary[];
|
|
61
|
+
uncertainResults: UncertainResult[];
|
|
62
|
+
checkCoverage: number;
|
|
63
|
+
}
|
|
64
|
+
export declare class UncertaintyCollector {
|
|
65
|
+
#private;
|
|
66
|
+
/** 记录某个规则在某层被跳过 */
|
|
67
|
+
recordSkip(layer: SkipLayer, reason: SkipReason, detail: string, options?: {
|
|
68
|
+
ruleId?: string;
|
|
69
|
+
impact?: SkipImpact;
|
|
70
|
+
}): void;
|
|
71
|
+
/** 追加一条 uncertain 结果 */
|
|
72
|
+
addUncertain(ruleId: string, message: string, layer: SkipLayer, reason: SkipReason, detail: string): void;
|
|
73
|
+
/** 记录各层的检查总数和执行数 */
|
|
74
|
+
recordLayerStats(layer: SkipLayer, total: number, executed: number): void;
|
|
75
|
+
/** 生成能力报告 */
|
|
76
|
+
buildReport(): GuardCapabilityReport;
|
|
77
|
+
/** 获取 uncertain 结果数量 */
|
|
78
|
+
get uncertainCount(): number;
|
|
79
|
+
/** 获取 skipped 总数 */
|
|
80
|
+
get skippedCount(): number;
|
|
81
|
+
/** 重置状态(供多文件审计复用) */
|
|
82
|
+
reset(): void;
|
|
83
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UncertaintyCollector — Guard uncertain 三态收集器
|
|
3
|
+
*
|
|
4
|
+
* 当 Guard 各层检测遇到能力边界(AST 不可用、跨文件缺失、正则冲突等)时,
|
|
5
|
+
* 收集 skip 原因并产出结构化的 uncertain 结果。
|
|
6
|
+
*
|
|
7
|
+
* 设计原则:
|
|
8
|
+
* - uncertain 不是"错误",是"承认能力边界"
|
|
9
|
+
* - Guard 不调用 AI,uncertain 是确定性输出
|
|
10
|
+
* - 保持 <10ms 性能
|
|
11
|
+
*/
|
|
12
|
+
/* ────────────────────── Collector ────────────────────── */
|
|
13
|
+
export class UncertaintyCollector {
|
|
14
|
+
#skippedChecks = [];
|
|
15
|
+
#uncertainResults = [];
|
|
16
|
+
#layerCounts = {
|
|
17
|
+
regex: { total: 0, executed: 0, skipped: 0 },
|
|
18
|
+
codeLevel: { total: 0, executed: 0, skipped: 0 },
|
|
19
|
+
ast: { total: 0, executed: 0, skipped: 0 },
|
|
20
|
+
crossFile: { total: 0, executed: 0, skipped: 0 },
|
|
21
|
+
};
|
|
22
|
+
/** 记录某个规则在某层被跳过 */
|
|
23
|
+
recordSkip(layer, reason, detail, options = {}) {
|
|
24
|
+
const impact = options.impact ?? this.#inferImpact(layer, reason);
|
|
25
|
+
this.#skippedChecks.push({
|
|
26
|
+
layer,
|
|
27
|
+
ruleId: options.ruleId,
|
|
28
|
+
reason,
|
|
29
|
+
detail,
|
|
30
|
+
impact,
|
|
31
|
+
});
|
|
32
|
+
const key = layer === 'code_level' ? 'codeLevel' : layer === 'cross_file' ? 'crossFile' : layer;
|
|
33
|
+
this.#layerCounts[key].skipped++;
|
|
34
|
+
}
|
|
35
|
+
/** 追加一条 uncertain 结果 */
|
|
36
|
+
addUncertain(ruleId, message, layer, reason, detail) {
|
|
37
|
+
this.#uncertainResults.push({ ruleId, message, layer, reason, detail });
|
|
38
|
+
}
|
|
39
|
+
/** 记录各层的检查总数和执行数 */
|
|
40
|
+
recordLayerStats(layer, total, executed) {
|
|
41
|
+
const key = layer === 'code_level' ? 'codeLevel' : layer === 'cross_file' ? 'crossFile' : layer;
|
|
42
|
+
this.#layerCounts[key].total += total;
|
|
43
|
+
this.#layerCounts[key].executed += executed;
|
|
44
|
+
}
|
|
45
|
+
/** 生成能力报告 */
|
|
46
|
+
buildReport() {
|
|
47
|
+
const boundaries = this.#detectBoundaries();
|
|
48
|
+
const totalChecks = this.#layerCounts.regex.total +
|
|
49
|
+
this.#layerCounts.codeLevel.total +
|
|
50
|
+
this.#layerCounts.ast.total +
|
|
51
|
+
this.#layerCounts.crossFile.total;
|
|
52
|
+
const executedChecks = this.#layerCounts.regex.executed +
|
|
53
|
+
this.#layerCounts.codeLevel.executed +
|
|
54
|
+
this.#layerCounts.ast.executed +
|
|
55
|
+
this.#layerCounts.crossFile.executed;
|
|
56
|
+
const checkCoverage = totalChecks > 0 ? Math.round((executedChecks / totalChecks) * 100) : 100;
|
|
57
|
+
return {
|
|
58
|
+
executedChecks: { ...this.#layerCounts },
|
|
59
|
+
skippedChecks: [...this.#skippedChecks],
|
|
60
|
+
boundaries,
|
|
61
|
+
uncertainResults: [...this.#uncertainResults],
|
|
62
|
+
checkCoverage,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** 获取 uncertain 结果数量 */
|
|
66
|
+
get uncertainCount() {
|
|
67
|
+
return this.#uncertainResults.length;
|
|
68
|
+
}
|
|
69
|
+
/** 获取 skipped 总数 */
|
|
70
|
+
get skippedCount() {
|
|
71
|
+
return this.#skippedChecks.length;
|
|
72
|
+
}
|
|
73
|
+
/** 重置状态(供多文件审计复用) */
|
|
74
|
+
reset() {
|
|
75
|
+
this.#skippedChecks = [];
|
|
76
|
+
this.#uncertainResults = [];
|
|
77
|
+
this.#layerCounts = {
|
|
78
|
+
regex: { total: 0, executed: 0, skipped: 0 },
|
|
79
|
+
codeLevel: { total: 0, executed: 0, skipped: 0 },
|
|
80
|
+
ast: { total: 0, executed: 0, skipped: 0 },
|
|
81
|
+
crossFile: { total: 0, executed: 0, skipped: 0 },
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/* ── 内部 ── */
|
|
85
|
+
#inferImpact(layer, reason) {
|
|
86
|
+
// AST 和跨文件中的结构化检查更重要
|
|
87
|
+
if (layer === 'ast' && reason === 'ast_unavailable') {
|
|
88
|
+
return 'high';
|
|
89
|
+
}
|
|
90
|
+
if (layer === 'cross_file' && reason === 'file_missing') {
|
|
91
|
+
return 'medium';
|
|
92
|
+
}
|
|
93
|
+
if (reason === 'invalid_regex') {
|
|
94
|
+
return 'medium';
|
|
95
|
+
}
|
|
96
|
+
if (reason === 'layer_conflict') {
|
|
97
|
+
return 'high';
|
|
98
|
+
}
|
|
99
|
+
return 'low';
|
|
100
|
+
}
|
|
101
|
+
#detectBoundaries() {
|
|
102
|
+
const boundaries = [];
|
|
103
|
+
// 按层+原因分组
|
|
104
|
+
const groups = new Map();
|
|
105
|
+
for (const skip of this.#skippedChecks) {
|
|
106
|
+
const key = `${skip.layer}:${skip.reason}`;
|
|
107
|
+
const list = groups.get(key) || [];
|
|
108
|
+
list.push(skip);
|
|
109
|
+
groups.set(key, list);
|
|
110
|
+
}
|
|
111
|
+
for (const [key, skips] of groups) {
|
|
112
|
+
const [layer, reason] = key.split(':');
|
|
113
|
+
const affectedRules = [...new Set(skips.map((s) => s.ruleId).filter(Boolean))];
|
|
114
|
+
if (reason === 'ast_unavailable') {
|
|
115
|
+
boundaries.push({
|
|
116
|
+
type: 'ast_language_gap',
|
|
117
|
+
description: `AST 检查因 tree-sitter 不可用被跳过 (${skips.length} 条规则)`,
|
|
118
|
+
affectedRules,
|
|
119
|
+
suggestedAction: '确认 tree-sitter 支持该语言,或降级为正则匹配',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
else if (reason === 'file_missing' && layer === 'cross_file') {
|
|
123
|
+
boundaries.push({
|
|
124
|
+
type: 'cross_file_incomplete',
|
|
125
|
+
description: `跨文件检查因文件缺失被跳过 (${skips.length} 次)`,
|
|
126
|
+
affectedRules,
|
|
127
|
+
suggestedAction: '确保审计时传入完整文件列表',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else if (reason === 'invalid_regex') {
|
|
131
|
+
boundaries.push({
|
|
132
|
+
type: 'rule_regex_invalid',
|
|
133
|
+
description: `${skips.length} 条规则的正则表达式编译失败`,
|
|
134
|
+
affectedRules,
|
|
135
|
+
suggestedAction: '修复或替换无效的正则表达式',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
else if (reason === 'layer_conflict') {
|
|
139
|
+
boundaries.push({
|
|
140
|
+
type: 'scope_unchecked',
|
|
141
|
+
description: `${skips.length} 个检查结果因层间冲突存疑`,
|
|
142
|
+
affectedRules,
|
|
143
|
+
suggestedAction: '人工审核冲突规则',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return boundaries;
|
|
148
|
+
}
|
|
149
|
+
}
|