autosnippet 3.2.21 → 3.3.2
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 -1
- package/dist/bin/cli.js +244 -261
- 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/SetupService.d.ts +46 -2
- package/dist/lib/cli/SetupService.js +2 -27
- package/dist/lib/cli/deploy/FileManifest.d.ts +0 -21
- package/dist/lib/cli/deploy/FileManifest.js +0 -11
- package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.d.ts +2 -5
- package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.js +159 -44
- package/dist/lib/core/discovery/index.d.ts +1 -1
- package/dist/lib/core/discovery/index.js +2 -2
- 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/consolidated.d.ts +116 -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 +245 -8
- 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 +217 -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 +14 -20
- package/dist/lib/external/mcp/tools.js +62 -91
- package/dist/lib/http/HttpServer.js +52 -6
- package/dist/lib/http/routes/{snippets.d.ts → audit.d.ts} +4 -2
- package/dist/lib/http/routes/audit.js +51 -0
- package/dist/lib/http/routes/commands.d.ts +1 -1
- package/dist/lib/http/routes/commands.js +1 -66
- 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/remote.js +0 -5
- 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 +181 -583
- package/dist/lib/infrastructure/database/drizzle/schema.js +28 -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/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/injection/ServiceContainer.js +6 -0
- package/dist/lib/injection/ServiceMap.d.ts +16 -19
- 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 +3 -4
- package/dist/lib/injection/modules/AppModule.js +7 -43
- package/dist/lib/injection/modules/GuardModule.js +59 -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.js +51 -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/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 +55 -1
- package/dist/lib/service/guard/GuardCheckEngine.js +508 -15
- 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/module/ModuleService.js +3 -13
- 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 +108 -0
- package/dist/lib/service/panorama/PanoramaService.js +220 -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/MultiSignalRanker.d.ts +1 -0
- package/dist/lib/service/search/MultiSignalRanker.js +16 -0
- package/dist/lib/service/search/SearchEngine.d.ts +1 -0
- package/dist/lib/service/search/SearchEngine.js +9 -1
- package/dist/lib/service/search/SearchTypes.d.ts +2 -0
- 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 +58 -0
- package/dist/lib/service/task/IntentExtractor.js +142 -0
- package/dist/lib/service/task/PrimeSearchPipeline.d.ts +54 -0
- package/dist/lib/service/task/PrimeSearchPipeline.js +98 -0
- package/dist/lib/shared/constants.d.ts +0 -15
- package/dist/lib/shared/constants.js +0 -10
- package/dist/lib/shared/schemas/config.d.ts +4 -1
- package/dist/lib/shared/schemas/config.js +8 -1
- 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/dist/scripts/release.js +2 -10
- package/package.json +4 -19
- package/skills/autosnippet-devdocs/SKILL.md +11 -8
- package/templates/claude-code/hooks/autosnippet-session.sh +10 -15
- package/templates/cursor-hooks/hooks/session-start.sh +1 -1
- package/templates/instructions/agent-static.md +2 -1
- package/templates/instructions/conventions.md +5 -6
- package/templates/recipes-setup/README.md +1 -2
- package/dashboard/dist/assets/icons-C1dUryS-.js +0 -1
- package/dashboard/dist/assets/index-D0whuycy.css +0 -1
- package/dashboard/dist/assets/index-DdvZE4Yd.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/http/routes/snippets.js +0 -49
- 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/platform/ClipboardManager.d.ts +0 -24
- package/dist/lib/platform/ClipboardManager.js +0 -142
- package/dist/lib/platform/NativeUi.d.ts +0 -53
- package/dist/lib/platform/NativeUi.js +0 -284
- package/dist/lib/platform/ios/index.d.ts +0 -38
- package/dist/lib/platform/ios/index.js +0 -42
- package/dist/lib/platform/ios/routes/spm.d.ts +0 -9
- package/dist/lib/platform/ios/routes/spm.js +0 -371
- package/dist/lib/platform/ios/snippet/PlaceholderConverter.d.ts +0 -21
- package/dist/lib/platform/ios/snippet/PlaceholderConverter.js +0 -48
- package/dist/lib/platform/ios/snippet/XcodeCodec.d.ts +0 -23
- package/dist/lib/platform/ios/snippet/XcodeCodec.js +0 -96
- package/dist/lib/platform/ios/spm/DependencyGraph.d.ts +0 -56
- package/dist/lib/platform/ios/spm/DependencyGraph.js +0 -195
- package/dist/lib/platform/ios/spm/PackageSwiftParser.d.ts +0 -69
- package/dist/lib/platform/ios/spm/PackageSwiftParser.js +0 -231
- package/dist/lib/platform/ios/spm/PathFinder.d.ts +0 -28
- package/dist/lib/platform/ios/spm/PathFinder.js +0 -117
- package/dist/lib/platform/ios/spm/PolicyEngine.d.ts +0 -44
- package/dist/lib/platform/ios/spm/PolicyEngine.js +0 -79
- package/dist/lib/platform/ios/spm/SpmHelper.d.ts +0 -102
- package/dist/lib/platform/ios/spm/SpmHelper.js +0 -464
- package/dist/lib/platform/ios/xcode/HeaderResolver.d.ts +0 -33
- package/dist/lib/platform/ios/xcode/HeaderResolver.js +0 -90
- package/dist/lib/platform/ios/xcode/SaveEventFilter.d.ts +0 -66
- package/dist/lib/platform/ios/xcode/SaveEventFilter.js +0 -142
- package/dist/lib/platform/ios/xcode/XcodeAutomation.d.ts +0 -71
- package/dist/lib/platform/ios/xcode/XcodeAutomation.js +0 -327
- package/dist/lib/platform/ios/xcode/XcodeImportResolver.d.ts +0 -130
- package/dist/lib/platform/ios/xcode/XcodeImportResolver.js +0 -404
- package/dist/lib/platform/ios/xcode/XcodeIntegration.d.ts +0 -89
- package/dist/lib/platform/ios/xcode/XcodeIntegration.js +0 -588
- package/dist/lib/platform/ios/xcode/XcodeWriteUtils.d.ts +0 -99
- package/dist/lib/platform/ios/xcode/XcodeWriteUtils.js +0 -190
- 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/automation/ActionPipeline.d.ts +0 -34
- package/dist/lib/service/automation/ActionPipeline.js +0 -53
- package/dist/lib/service/automation/AutomationOrchestrator.d.ts +0 -86
- package/dist/lib/service/automation/AutomationOrchestrator.js +0 -57
- package/dist/lib/service/automation/ContextCollector.d.ts +0 -24
- package/dist/lib/service/automation/ContextCollector.js +0 -35
- package/dist/lib/service/automation/DirectiveDetector.d.ts +0 -51
- package/dist/lib/service/automation/DirectiveDetector.js +0 -112
- package/dist/lib/service/automation/FileWatcher.d.ts +0 -51
- package/dist/lib/service/automation/FileWatcher.js +0 -366
- package/dist/lib/service/automation/TriggerResolver.d.ts +0 -36
- package/dist/lib/service/automation/TriggerResolver.js +0 -62
- package/dist/lib/service/automation/handlers/AlinkHandler.d.ts +0 -7
- package/dist/lib/service/automation/handlers/AlinkHandler.js +0 -80
- package/dist/lib/service/automation/handlers/CreateHandler.d.ts +0 -11
- package/dist/lib/service/automation/handlers/CreateHandler.js +0 -170
- package/dist/lib/service/automation/handlers/GuardHandler.d.ts +0 -17
- package/dist/lib/service/automation/handlers/GuardHandler.js +0 -218
- package/dist/lib/service/automation/handlers/HeaderHandler.d.ts +0 -2
- package/dist/lib/service/automation/handlers/HeaderHandler.js +0 -32
- package/dist/lib/service/automation/handlers/SearchHandler.d.ts +0 -11
- package/dist/lib/service/automation/handlers/SearchHandler.js +0 -278
- package/dist/lib/service/snippet/SnippetFactory.d.ts +0 -101
- package/dist/lib/service/snippet/SnippetFactory.js +0 -145
- package/dist/lib/service/snippet/SnippetInstaller.d.ts +0 -91
- package/dist/lib/service/snippet/SnippetInstaller.js +0 -276
- package/dist/lib/service/snippet/codecs/SnippetCodec.d.ts +0 -44
- package/dist/lib/service/snippet/codecs/SnippetCodec.js +0 -35
- package/dist/lib/service/snippet/codecs/VSCodeCodec.d.ts +0 -27
- package/dist/lib/service/snippet/codecs/VSCodeCodec.js +0 -82
- 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
- package/dist/scripts/build-native-ui.d.ts +0 -3
- package/dist/scripts/build-native-ui.js +0 -62
- package/dist/scripts/init-snippets.d.ts +0 -30
- package/dist/scripts/init-snippets.js +0 -298
- package/dist/scripts/install-full.d.ts +0 -7
- package/dist/scripts/install-full.js +0 -38
- package/resources/native-ui/README.md +0 -29
- package/resources/native-ui/combined-window.swift +0 -494
- package/resources/native-ui/main.swift +0 -598
- package/scripts/postinstall-safe.mjs +0 -89
|
@@ -10,6 +10,7 @@ import { LanguageService } from '../../shared/LanguageService.js';
|
|
|
10
10
|
import { runCodeLevelChecks } from './GuardCodeChecks.js';
|
|
11
11
|
import { runCrossFileChecks } from './GuardCrossFileChecks.js';
|
|
12
12
|
import { buildCommentMask, buildTestBlockMask, clearPatternCache, compilePattern, detectLanguage, } from './GuardPatternUtils.js';
|
|
13
|
+
import { UncertaintyCollector } from './UncertaintyCollector.js';
|
|
13
14
|
/**
|
|
14
15
|
* 内置默认规则集 — 多语言基础规则
|
|
15
16
|
*
|
|
@@ -75,6 +76,13 @@ const BUILT_IN_RULES = {
|
|
|
75
76
|
dimension: 'file',
|
|
76
77
|
category: 'safety',
|
|
77
78
|
fixSuggestion: '使用 as? 配合 guard let / if let 进行安全转换',
|
|
79
|
+
// UIKit 框架契约保证安全的 as! 场景
|
|
80
|
+
excludeLinePatterns: [
|
|
81
|
+
'dequeueReusableCell.*as\\s*!',
|
|
82
|
+
'dequeueReusableSupplementaryView.*as\\s*!',
|
|
83
|
+
'dequeueReusableHeaderFooterView.*as\\s*!',
|
|
84
|
+
'\\blayer\\s+as\\s*!',
|
|
85
|
+
],
|
|
78
86
|
},
|
|
79
87
|
'swift-force-try': {
|
|
80
88
|
message: 'try! 在异常时崩溃,建议 do-catch 或 try?',
|
|
@@ -449,6 +457,11 @@ export class GuardCheckEngine {
|
|
|
449
457
|
_epInjected;
|
|
450
458
|
_externalRules;
|
|
451
459
|
_guardConfig;
|
|
460
|
+
_signalBus;
|
|
461
|
+
/** 上次 guard 信号指纹,用于去重(相同结果不重复发射) */
|
|
462
|
+
_lastGuardSignalKey;
|
|
463
|
+
_lastBlindSpotSignalKey;
|
|
464
|
+
_uncertaintyCollector;
|
|
452
465
|
db;
|
|
453
466
|
logger;
|
|
454
467
|
constructor(db, options = {}) {
|
|
@@ -466,6 +479,10 @@ export class GuardCheckEngine {
|
|
|
466
479
|
this._epInjected = false;
|
|
467
480
|
/** Guard 配置 — 允许禁用特定规则或调整 Code-Level 检查阈值 */
|
|
468
481
|
this._guardConfig = options.guardConfig || {};
|
|
482
|
+
this._signalBus = options.signalBus || null;
|
|
483
|
+
this._lastGuardSignalKey = '';
|
|
484
|
+
this._lastBlindSpotSignalKey = '';
|
|
485
|
+
this._uncertaintyCollector = new UncertaintyCollector();
|
|
469
486
|
}
|
|
470
487
|
/**
|
|
471
488
|
* 注入 Enhancement Pack 外部规则(支持 RegExp 和 string pattern)
|
|
@@ -527,10 +544,10 @@ export class GuardCheckEngine {
|
|
|
527
544
|
let rows = [];
|
|
528
545
|
try {
|
|
529
546
|
rows = this.db
|
|
530
|
-
.prepare(`SELECT id, title, description, language, scope, constraints
|
|
547
|
+
.prepare(`SELECT id, title, description, language, scope, constraints, lifecycle
|
|
531
548
|
FROM knowledge_entries
|
|
532
549
|
WHERE (kind = 'rule' OR knowledgeType = 'boundary-constraint')
|
|
533
|
-
AND lifecycle
|
|
550
|
+
AND lifecycle IN ('active', 'staging', 'evolving', 'decaying')`)
|
|
534
551
|
.all();
|
|
535
552
|
}
|
|
536
553
|
catch {
|
|
@@ -550,12 +567,14 @@ export class GuardCheckEngine {
|
|
|
550
567
|
for (const g of guards) {
|
|
551
568
|
const ruleType = g.type || 'regex';
|
|
552
569
|
const lang = r.language;
|
|
570
|
+
const isDecaying = r.lifecycle === 'decaying';
|
|
571
|
+
const rawSeverity = (g.severity || 'warning');
|
|
553
572
|
const base = {
|
|
554
573
|
id: (g.id || r.id),
|
|
555
574
|
name: (g.name || r.title),
|
|
556
575
|
message: (g.message || r.description || r.title),
|
|
557
576
|
languages: lang ? [lang, LanguageService.toGuardLangId(lang)] : [],
|
|
558
|
-
severity:
|
|
577
|
+
severity: isDecaying && rawSeverity === 'error' ? 'warning' : rawSeverity,
|
|
559
578
|
dimension: (r.scope || 'file'),
|
|
560
579
|
source: 'database',
|
|
561
580
|
fixSuggestion: (g.fixSuggestion || null),
|
|
@@ -600,6 +619,7 @@ export class GuardCheckEngine {
|
|
|
600
619
|
...(rule.excludePaths ? { excludePaths: rule.excludePaths } : {}),
|
|
601
620
|
...(rule.skipComments ? { skipComments: true } : {}),
|
|
602
621
|
...(rule.skipTestBlocks ? { skipTestBlocks: true } : {}),
|
|
622
|
+
...(rule.excludeLinePatterns ? { excludeLinePatterns: rule.excludeLinePatterns } : {}),
|
|
603
623
|
});
|
|
604
624
|
}
|
|
605
625
|
}
|
|
@@ -685,10 +705,15 @@ export class GuardCheckEngine {
|
|
|
685
705
|
}
|
|
686
706
|
catch {
|
|
687
707
|
this.logger.debug(`Invalid regex in rule ${rule.id}: ${rule.pattern}`);
|
|
708
|
+
this._uncertaintyCollector.recordSkip('regex', 'invalid_regex', `Rule ${rule.id}: pattern "${rule.pattern}" failed to compile`, { ruleId: rule.id || rule.name });
|
|
709
|
+
this._uncertaintyCollector.addUncertain(rule.id || rule.name, rule.message, 'regex', 'invalid_regex', `Pattern compilation failed: ${rule.pattern}`);
|
|
688
710
|
continue;
|
|
689
711
|
}
|
|
690
712
|
const shouldSkipComments = !!rule.skipComments;
|
|
691
713
|
const shouldSkipTestBlocks = !!rule.skipTestBlocks;
|
|
714
|
+
// 合并内置 + 配置级排除行模式
|
|
715
|
+
const ruleId = rule.id || rule.name;
|
|
716
|
+
const excludeLineRegexes = this._getExcludeLineRegexes(ruleId, rule.excludeLinePatterns);
|
|
692
717
|
for (let i = 0; i < lines.length; i++) {
|
|
693
718
|
// skipComments: 跳过注释行(doc comments / 行注释 / 块注释内)
|
|
694
719
|
if (shouldSkipComments && commentLines[i]) {
|
|
@@ -699,6 +724,10 @@ export class GuardCheckEngine {
|
|
|
699
724
|
continue;
|
|
700
725
|
}
|
|
701
726
|
if (re.test(lines[i])) {
|
|
727
|
+
// excludeLinePatterns: 跳过匹配排除模式的行(UIKit 框架契约安全等场景)
|
|
728
|
+
if (excludeLineRegexes.length > 0 && excludeLineRegexes.some((ep) => ep.test(lines[i]))) {
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
702
731
|
violations.push({
|
|
703
732
|
ruleId: rule.id || rule.name,
|
|
704
733
|
message: rule.message,
|
|
@@ -711,13 +740,21 @@ export class GuardCheckEngine {
|
|
|
711
740
|
}
|
|
712
741
|
}
|
|
713
742
|
}
|
|
714
|
-
// Code-level
|
|
743
|
+
// Code-level 检查(不依赖正则)— 仅传递数字类型阈值
|
|
744
|
+
const numericThresholds = {};
|
|
745
|
+
for (const [k, v] of Object.entries(this._guardConfig.codeLevelThresholds || {})) {
|
|
746
|
+
if (typeof v === 'number') {
|
|
747
|
+
numericThresholds[k] = v;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
715
750
|
violations.push(...runCodeLevelChecks(code, language, lines, {
|
|
716
751
|
disabledRules: this._guardConfig.disabledRules,
|
|
717
|
-
codeLevelThresholds:
|
|
752
|
+
codeLevelThresholds: numericThresholds,
|
|
718
753
|
}));
|
|
719
|
-
// AST
|
|
754
|
+
// AST 语义规则检查(Layer 1: 3 查询函数)
|
|
720
755
|
violations.push(...this._runAstRuleChecks(code, language));
|
|
756
|
+
// AST Layer 2: analyzeFile() 深层检查(复杂度、类膨胀、深嵌套)
|
|
757
|
+
violations.push(...this._runAstLayer2Checks(code, language, filePath));
|
|
721
758
|
// 跟踪 Guard 命中次数(回写 Recipe 统计)
|
|
722
759
|
this.trackGuardHits(violations);
|
|
723
760
|
// ── Reasoning Enrichment: 推理信息跟随数据流动 ──
|
|
@@ -760,11 +797,24 @@ export class GuardCheckEngine {
|
|
|
760
797
|
// AstAnalyzer 作为 ESM 模块,在 constructor 时已被引入
|
|
761
798
|
AstAnalyzer = this._getAstAnalyzer();
|
|
762
799
|
if (!AstAnalyzer || !AstAnalyzer.isAvailable()) {
|
|
800
|
+
// AST 不可用 — 记录 uncertain
|
|
801
|
+
for (const rule of astRules) {
|
|
802
|
+
this._uncertaintyCollector.recordSkip('ast', 'ast_unavailable', `AST check skipped: tree-sitter not available for lang "${language}"`, { ruleId: rule.id });
|
|
803
|
+
this._uncertaintyCollector.addUncertain(rule.id, rule.message, 'ast', 'ast_unavailable', `Tree-sitter not available for language "${language}"`);
|
|
804
|
+
}
|
|
805
|
+
this._uncertaintyCollector.recordLayerStats('ast', astRules.length, 0);
|
|
763
806
|
return [];
|
|
764
807
|
}
|
|
765
808
|
}
|
|
766
809
|
catch {
|
|
767
810
|
this.logger.debug('AstAnalyzer not available, skipping AST rules');
|
|
811
|
+
for (const rule of astRules) {
|
|
812
|
+
this._uncertaintyCollector.recordSkip('ast', 'ast_unavailable', `AST module load failed`, {
|
|
813
|
+
ruleId: rule.id,
|
|
814
|
+
});
|
|
815
|
+
this._uncertaintyCollector.addUncertain(rule.id, rule.message, 'ast', 'ast_unavailable', 'AstAnalyzer module failed to load');
|
|
816
|
+
}
|
|
817
|
+
this._uncertaintyCollector.recordLayerStats('ast', astRules.length, 0);
|
|
768
818
|
return [];
|
|
769
819
|
}
|
|
770
820
|
const violations = [];
|
|
@@ -847,12 +897,399 @@ export class GuardCheckEngine {
|
|
|
847
897
|
this.logger.debug(`AST rule ${rule.id} check failed: ${err.message}`);
|
|
848
898
|
}
|
|
849
899
|
}
|
|
900
|
+
// AST 层统计
|
|
901
|
+
this._uncertaintyCollector.recordLayerStats('ast', astRules.length, astRules.length);
|
|
902
|
+
return violations;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* AST Layer 2: analyzeFile() 深层检查
|
|
906
|
+
*
|
|
907
|
+
* 利用 AstAnalyzer.analyzeFile() 的完整输出产出 violations:
|
|
908
|
+
*
|
|
909
|
+
* --- 方法度量 ---
|
|
910
|
+
* - ast_class_bloat: 类方法数过多 (>20)
|
|
911
|
+
* - ast_method_complexity: 高圈复杂度 (>15)
|
|
912
|
+
* - ast_method_too_long: 方法行数过长 (>80)
|
|
913
|
+
* - ast_deep_nesting: 方法嵌套过深 (>5)
|
|
914
|
+
*
|
|
915
|
+
* --- 继承图检查 ---
|
|
916
|
+
* - ast_deep_inheritance: 继承链过深 (>4)
|
|
917
|
+
* - ast_wide_protocol_conformance: 单类遵守协议过多 (>5)
|
|
918
|
+
* - ast_missing_super: 子类未调用 super 的关键方法
|
|
919
|
+
*
|
|
920
|
+
* --- 属性规范 ---
|
|
921
|
+
* - ast_assign_object_property: ObjC assign 修饰对象类型属性
|
|
922
|
+
* - ast_missing_nonatomic: ObjC 属性缺少 nonatomic
|
|
923
|
+
* - ast_mutable_public_collection: 公开可变集合属性
|
|
924
|
+
*
|
|
925
|
+
* --- 设计模式/反模式检测 ---
|
|
926
|
+
* - ast_god_class: 方法+属性过多的上帝类 (>30 methods + >15 properties)
|
|
927
|
+
* - ast_singleton_abuse: 过多单例模式
|
|
928
|
+
* - ast_missing_weakify: block 内 self 捕获但未使用 weakify
|
|
929
|
+
*/
|
|
930
|
+
_runAstLayer2Checks(code, language, filePath) {
|
|
931
|
+
const disabled = this._guardConfig.disabledRules || [];
|
|
932
|
+
const allLayer2Rules = [
|
|
933
|
+
'ast_class_bloat',
|
|
934
|
+
'ast_method_complexity',
|
|
935
|
+
'ast_method_too_long',
|
|
936
|
+
'ast_deep_nesting',
|
|
937
|
+
'ast_deep_inheritance',
|
|
938
|
+
'ast_wide_protocol_conformance',
|
|
939
|
+
'ast_missing_super',
|
|
940
|
+
'ast_assign_object_property',
|
|
941
|
+
'ast_missing_nonatomic',
|
|
942
|
+
'ast_mutable_public_collection',
|
|
943
|
+
'ast_god_class',
|
|
944
|
+
'ast_singleton_abuse',
|
|
945
|
+
'ast_missing_weakify',
|
|
946
|
+
];
|
|
947
|
+
const allDisabled = allLayer2Rules.every((id) => disabled.includes(id));
|
|
948
|
+
if (allDisabled) {
|
|
949
|
+
return [];
|
|
950
|
+
}
|
|
951
|
+
// 语言标准化
|
|
952
|
+
const astLang = LanguageService.isKnownLang(language)
|
|
953
|
+
? language
|
|
954
|
+
: language === 'objc'
|
|
955
|
+
? 'objectivec'
|
|
956
|
+
: language;
|
|
957
|
+
if (!LanguageService.isKnownLang(astLang)) {
|
|
958
|
+
return [];
|
|
959
|
+
}
|
|
960
|
+
let AstAnalyzer;
|
|
961
|
+
try {
|
|
962
|
+
AstAnalyzer = this._getAstAnalyzer();
|
|
963
|
+
if (!AstAnalyzer || !AstAnalyzer.isAvailable()) {
|
|
964
|
+
this._uncertaintyCollector.recordSkip('ast', 'ast_unavailable', `AST Layer 2 skipped: tree-sitter not available for "${language}"`);
|
|
965
|
+
return [];
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
catch {
|
|
969
|
+
return [];
|
|
970
|
+
}
|
|
971
|
+
let fileSummary;
|
|
972
|
+
try {
|
|
973
|
+
fileSummary = AstAnalyzer.analyzeFile(code, astLang, { extractCallSites: false });
|
|
974
|
+
}
|
|
975
|
+
catch (err) {
|
|
976
|
+
this.logger.debug(`AST Layer 2 analyzeFile failed: ${err.message}`);
|
|
977
|
+
return [];
|
|
978
|
+
}
|
|
979
|
+
if (!fileSummary) {
|
|
980
|
+
return [];
|
|
981
|
+
}
|
|
982
|
+
const violations = [];
|
|
983
|
+
// — 阈值配置(可通过 codeLevelThresholds 覆盖) —
|
|
984
|
+
const thresholds = this._guardConfig.codeLevelThresholds || {};
|
|
985
|
+
const classBloatLimit = (typeof thresholds['ast_class_bloat'] === 'number' ? thresholds['ast_class_bloat'] : 20);
|
|
986
|
+
const complexityLimit = (typeof thresholds['ast_method_complexity'] === 'number'
|
|
987
|
+
? thresholds['ast_method_complexity']
|
|
988
|
+
: 15);
|
|
989
|
+
const methodLengthLimit = (typeof thresholds['ast_method_too_long'] === 'number' ? thresholds['ast_method_too_long'] : 80);
|
|
990
|
+
const nestingLimit = (typeof thresholds['ast_deep_nesting'] === 'number' ? thresholds['ast_deep_nesting'] : 5);
|
|
991
|
+
const inheritanceDepthLimit = (typeof thresholds['ast_deep_inheritance'] === 'number'
|
|
992
|
+
? thresholds['ast_deep_inheritance']
|
|
993
|
+
: 4);
|
|
994
|
+
const protocolConformanceLimit = (typeof thresholds['ast_wide_protocol_conformance'] === 'number'
|
|
995
|
+
? thresholds['ast_wide_protocol_conformance']
|
|
996
|
+
: 5);
|
|
997
|
+
const godClassMethodLimit = (typeof thresholds['ast_god_class_methods'] === 'number'
|
|
998
|
+
? thresholds['ast_god_class_methods']
|
|
999
|
+
: 30);
|
|
1000
|
+
const godClassPropertyLimit = (typeof thresholds['ast_god_class_properties'] === 'number'
|
|
1001
|
+
? thresholds['ast_god_class_properties']
|
|
1002
|
+
: 15);
|
|
1003
|
+
// ══════════════════════════════════════════════════════════
|
|
1004
|
+
// Section A: 方法度量(原有 4 条规则)
|
|
1005
|
+
// ══════════════════════════════════════════════════════════
|
|
1006
|
+
// 1. Class bloat — 类方法数过多
|
|
1007
|
+
if (!disabled.includes('ast_class_bloat')) {
|
|
1008
|
+
const methodCountByClass = {};
|
|
1009
|
+
for (const m of fileSummary.methods) {
|
|
1010
|
+
if (m.className && m.kind === 'definition') {
|
|
1011
|
+
if (!methodCountByClass[m.className]) {
|
|
1012
|
+
const cls = fileSummary.classes.find((c) => c.name === m.className);
|
|
1013
|
+
methodCountByClass[m.className] = { count: 0, line: cls?.line || 1 };
|
|
1014
|
+
}
|
|
1015
|
+
methodCountByClass[m.className].count++;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
for (const [className, { count, line }] of Object.entries(methodCountByClass)) {
|
|
1019
|
+
if (count > classBloatLimit) {
|
|
1020
|
+
violations.push({
|
|
1021
|
+
ruleId: 'ast_class_bloat',
|
|
1022
|
+
message: `类 ${className} 有 ${count} 个方法,超过阈值 ${classBloatLimit},建议拆分职责`,
|
|
1023
|
+
severity: 'warning',
|
|
1024
|
+
line,
|
|
1025
|
+
snippet: `class ${className} — ${count} methods`,
|
|
1026
|
+
dimension: 'file',
|
|
1027
|
+
fixSuggestion: '将职责拆分到多个类或使用 Extension/Category 分组',
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
// 2. Method complexity — 高圈复杂度
|
|
1033
|
+
if (!disabled.includes('ast_method_complexity')) {
|
|
1034
|
+
for (const m of fileSummary.methods) {
|
|
1035
|
+
if (m.complexity && m.complexity > complexityLimit) {
|
|
1036
|
+
violations.push({
|
|
1037
|
+
ruleId: 'ast_method_complexity',
|
|
1038
|
+
message: `方法 ${m.className ? `${m.className}.` : ''}${m.name} 圈复杂度 ${m.complexity},超过阈值 ${complexityLimit}`,
|
|
1039
|
+
severity: 'warning',
|
|
1040
|
+
line: m.line || 1,
|
|
1041
|
+
snippet: `${m.name} — complexity: ${m.complexity}`,
|
|
1042
|
+
dimension: 'file',
|
|
1043
|
+
fixSuggestion: '提取子方法、使用 early return 或策略模式降低复杂度',
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
// 3. Method too long — 方法行数过长
|
|
1049
|
+
if (!disabled.includes('ast_method_too_long')) {
|
|
1050
|
+
for (const m of fileSummary.methods) {
|
|
1051
|
+
if (m.bodyLines && m.bodyLines > methodLengthLimit) {
|
|
1052
|
+
violations.push({
|
|
1053
|
+
ruleId: 'ast_method_too_long',
|
|
1054
|
+
message: `方法 ${m.className ? `${m.className}.` : ''}${m.name} 有 ${m.bodyLines} 行,超过阈值 ${methodLengthLimit}`,
|
|
1055
|
+
severity: 'warning',
|
|
1056
|
+
line: m.line || 1,
|
|
1057
|
+
snippet: `${m.name} — ${m.bodyLines} lines`,
|
|
1058
|
+
dimension: 'file',
|
|
1059
|
+
fixSuggestion: '将长方法拆分为多个更小的、职责单一的方法',
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
// 4. Deep nesting — 方法嵌套过深
|
|
1065
|
+
if (!disabled.includes('ast_deep_nesting')) {
|
|
1066
|
+
for (const m of fileSummary.methods) {
|
|
1067
|
+
if (m.nestingDepth && m.nestingDepth > nestingLimit) {
|
|
1068
|
+
violations.push({
|
|
1069
|
+
ruleId: 'ast_deep_nesting',
|
|
1070
|
+
message: `方法 ${m.className ? `${m.className}.` : ''}${m.name} 嵌套深度 ${m.nestingDepth},超过阈值 ${nestingLimit}`,
|
|
1071
|
+
severity: 'warning',
|
|
1072
|
+
line: m.line || 1,
|
|
1073
|
+
snippet: `${m.name} — nesting depth: ${m.nestingDepth}`,
|
|
1074
|
+
dimension: 'file',
|
|
1075
|
+
fixSuggestion: '使用 guard/early return 减少嵌套,或提取内层逻辑为独立方法',
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
// ══════════════════════════════════════════════════════════
|
|
1081
|
+
// Section B: 继承图检查(inheritanceGraph + classes.protocols)
|
|
1082
|
+
// ══════════════════════════════════════════════════════════
|
|
1083
|
+
// 5. Deep inheritance — 继承链过深
|
|
1084
|
+
if (!disabled.includes('ast_deep_inheritance') && fileSummary.inheritanceGraph?.length > 0) {
|
|
1085
|
+
// 构建父类映射: child → parent
|
|
1086
|
+
const parentMap = {};
|
|
1087
|
+
for (const edge of fileSummary.inheritanceGraph) {
|
|
1088
|
+
if (edge.type === 'extends' || edge.type === 'inherits') {
|
|
1089
|
+
parentMap[edge.from] = edge.to;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
// 计算每个类的继承深度
|
|
1093
|
+
for (const cls of fileSummary.classes) {
|
|
1094
|
+
let depth = 0;
|
|
1095
|
+
let current = cls.name;
|
|
1096
|
+
const visited = new Set();
|
|
1097
|
+
while (parentMap[current] && !visited.has(current)) {
|
|
1098
|
+
visited.add(current);
|
|
1099
|
+
current = parentMap[current];
|
|
1100
|
+
depth++;
|
|
1101
|
+
}
|
|
1102
|
+
if (depth > inheritanceDepthLimit) {
|
|
1103
|
+
violations.push({
|
|
1104
|
+
ruleId: 'ast_deep_inheritance',
|
|
1105
|
+
message: `类 ${cls.name} 继承链深度 ${depth},超过阈值 ${inheritanceDepthLimit},过深继承增加理解和维护成本`,
|
|
1106
|
+
severity: 'warning',
|
|
1107
|
+
line: cls.line || 1,
|
|
1108
|
+
snippet: `class ${cls.name} — inheritance depth: ${depth}`,
|
|
1109
|
+
dimension: 'file',
|
|
1110
|
+
fixSuggestion: '优先使用组合(Composition)替代继承,或使用协议/接口解耦',
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
// 6. Wide protocol conformance — 单类遵守协议过多
|
|
1116
|
+
if (!disabled.includes('ast_wide_protocol_conformance')) {
|
|
1117
|
+
for (const cls of fileSummary.classes) {
|
|
1118
|
+
const protocolCount = cls.protocols?.length || 0;
|
|
1119
|
+
if (protocolCount > protocolConformanceLimit) {
|
|
1120
|
+
violations.push({
|
|
1121
|
+
ruleId: 'ast_wide_protocol_conformance',
|
|
1122
|
+
message: `类 ${cls.name} 遵守 ${protocolCount} 个协议,超过阈值 ${protocolConformanceLimit},职责可能过重`,
|
|
1123
|
+
severity: 'warning',
|
|
1124
|
+
line: cls.line || 1,
|
|
1125
|
+
snippet: `class ${cls.name} — ${protocolCount} protocols: ${cls.protocols.slice(0, 5).join(', ')}${protocolCount > 5 ? '...' : ''}`,
|
|
1126
|
+
dimension: 'file',
|
|
1127
|
+
fixSuggestion: '将协议实现拆分到 Extension/Category 中,或拆分类职责',
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
// ══════════════════════════════════════════════════════════
|
|
1133
|
+
// Section C: 属性规范(properties + attributes)
|
|
1134
|
+
// ══════════════════════════════════════════════════════════
|
|
1135
|
+
const isObjcLike = ['objc', 'objectivec', 'objective-c'].includes(language.toLowerCase());
|
|
1136
|
+
if (isObjcLike && fileSummary.properties?.length > 0) {
|
|
1137
|
+
for (const prop of fileSummary.properties) {
|
|
1138
|
+
const attrs = prop.attributes || [];
|
|
1139
|
+
const attrsLower = attrs.map((a) => a.toLowerCase());
|
|
1140
|
+
// 7. assign 修饰对象类型属性
|
|
1141
|
+
if (!disabled.includes('ast_assign_object_property')) {
|
|
1142
|
+
if (attrsLower.includes('assign') && !attrsLower.includes('readonly')) {
|
|
1143
|
+
// assign 用于对象类型(通过属性名启发:delegate, block, handler 等常为对象)
|
|
1144
|
+
const likelyObject = /delegate|block|handler|callback|completion|dataSource|view|controller|manager|service/i.test(prop.name);
|
|
1145
|
+
if (likelyObject) {
|
|
1146
|
+
violations.push({
|
|
1147
|
+
ruleId: 'ast_assign_object_property',
|
|
1148
|
+
message: `属性 ${prop.className ? `${prop.className}.` : ''}${prop.name} 使用 assign 修饰,疑似对象类型,应改为 weak`,
|
|
1149
|
+
severity: 'warning',
|
|
1150
|
+
line: prop.line || 1,
|
|
1151
|
+
snippet: `@property (assign) ... ${prop.name}`,
|
|
1152
|
+
dimension: 'file',
|
|
1153
|
+
fixSuggestion: '对象类型属性使用 weak(delegate)或 strong/copy,避免悬垂指针',
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
// 8. 缺少 nonatomic
|
|
1159
|
+
if (!disabled.includes('ast_missing_nonatomic')) {
|
|
1160
|
+
if (!attrsLower.includes('nonatomic') &&
|
|
1161
|
+
!attrsLower.includes('atomic') &&
|
|
1162
|
+
attrs.length > 0) {
|
|
1163
|
+
violations.push({
|
|
1164
|
+
ruleId: 'ast_missing_nonatomic',
|
|
1165
|
+
message: `属性 ${prop.className ? `${prop.className}.` : ''}${prop.name} 缺少 nonatomic,iOS 中应默认使用 nonatomic 提升性能`,
|
|
1166
|
+
severity: 'info',
|
|
1167
|
+
line: prop.line || 1,
|
|
1168
|
+
snippet: `@property (${attrs.join(', ')}) ... ${prop.name}`,
|
|
1169
|
+
dimension: 'file',
|
|
1170
|
+
fixSuggestion: '添加 nonatomic 修饰符:@property (nonatomic, ...) ...',
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
// 9. 公开可变集合属性
|
|
1175
|
+
if (!disabled.includes('ast_mutable_public_collection')) {
|
|
1176
|
+
const isMutable = /NSMutableArray|NSMutableDictionary|NSMutableSet|NSMutableString|NSMutableData|NSMutableOrderedSet/i.test(`${attrs.join(' ')} ${prop.name}`);
|
|
1177
|
+
if (isMutable && !attrsLower.includes('readonly')) {
|
|
1178
|
+
violations.push({
|
|
1179
|
+
ruleId: 'ast_mutable_public_collection',
|
|
1180
|
+
message: `属性 ${prop.className ? `${prop.className}.` : ''}${prop.name} 暴露可变集合,外部可直接修改内部状态`,
|
|
1181
|
+
severity: 'warning',
|
|
1182
|
+
line: prop.line || 1,
|
|
1183
|
+
snippet: `@property ... NSMutable* ${prop.name}`,
|
|
1184
|
+
dimension: 'file',
|
|
1185
|
+
fixSuggestion: '对外使用 readonly + 不可变类型(NSArray/NSDictionary),内部用 readwrite + 可变类型',
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
// ══════════════════════════════════════════════════════════
|
|
1192
|
+
// Section D: 设计模式 / 反模式检测(patterns + aggregated metrics)
|
|
1193
|
+
// ══════════════════════════════════════════════════════════
|
|
1194
|
+
// 10. God class — 方法+属性过多的上帝类
|
|
1195
|
+
if (!disabled.includes('ast_god_class')) {
|
|
1196
|
+
// 按类聚合方法数和属性数
|
|
1197
|
+
const classStats = {};
|
|
1198
|
+
for (const m of fileSummary.methods) {
|
|
1199
|
+
if (m.className && m.kind === 'definition') {
|
|
1200
|
+
if (!classStats[m.className]) {
|
|
1201
|
+
const cls = fileSummary.classes.find((c) => c.name === m.className);
|
|
1202
|
+
classStats[m.className] = { methods: 0, properties: 0, line: cls?.line || 1 };
|
|
1203
|
+
}
|
|
1204
|
+
classStats[m.className].methods++;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
for (const p of fileSummary.properties) {
|
|
1208
|
+
if (p.className) {
|
|
1209
|
+
if (!classStats[p.className]) {
|
|
1210
|
+
const cls = fileSummary.classes.find((c) => c.name === p.className);
|
|
1211
|
+
classStats[p.className] = { methods: 0, properties: 0, line: cls?.line || 1 };
|
|
1212
|
+
}
|
|
1213
|
+
classStats[p.className].properties++;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
for (const [className, stats] of Object.entries(classStats)) {
|
|
1217
|
+
if (stats.methods > godClassMethodLimit && stats.properties > godClassPropertyLimit) {
|
|
1218
|
+
violations.push({
|
|
1219
|
+
ruleId: 'ast_god_class',
|
|
1220
|
+
message: `类 ${className} 有 ${stats.methods} 个方法和 ${stats.properties} 个属性,疑似上帝类(God Class),职责过重`,
|
|
1221
|
+
severity: 'warning',
|
|
1222
|
+
line: stats.line,
|
|
1223
|
+
snippet: `class ${className} — ${stats.methods} methods, ${stats.properties} properties`,
|
|
1224
|
+
dimension: 'file',
|
|
1225
|
+
fixSuggestion: '遵循单一职责原则(SRP),将类拆分为多个更小的、职责明确的类',
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
// 11. Singleton abuse — 过多单例模式(文件级别)
|
|
1231
|
+
if (!disabled.includes('ast_singleton_abuse') && fileSummary.patterns?.length > 0) {
|
|
1232
|
+
const singletonPatterns = fileSummary.patterns.filter((p) => p.type === 'singleton');
|
|
1233
|
+
if (singletonPatterns.length > 2) {
|
|
1234
|
+
violations.push({
|
|
1235
|
+
ruleId: 'ast_singleton_abuse',
|
|
1236
|
+
message: `文件中检测到 ${singletonPatterns.length} 个单例模式,过多单例增加耦合和测试难度`,
|
|
1237
|
+
severity: 'info',
|
|
1238
|
+
line: singletonPatterns[0]?.line || 1,
|
|
1239
|
+
snippet: `${singletonPatterns.length} singletons: ${singletonPatterns
|
|
1240
|
+
.map((p) => p.className || p.methodName || 'unknown')
|
|
1241
|
+
.slice(0, 3)
|
|
1242
|
+
.join(', ')}`,
|
|
1243
|
+
dimension: 'file',
|
|
1244
|
+
fixSuggestion: '考虑使用依赖注入(DI)替代单例,提升可测试性和解耦',
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
// 12. Missing weakify — block 内 self 捕获但未使用 weakify 模式
|
|
1249
|
+
if (!disabled.includes('ast_missing_weakify') &&
|
|
1250
|
+
isObjcLike &&
|
|
1251
|
+
fileSummary.patterns?.length > 0) {
|
|
1252
|
+
const selfCaptures = fileSummary.patterns.filter((p) => p.type === 'block_self_capture' && !p.isWeakRef);
|
|
1253
|
+
for (const cap of selfCaptures) {
|
|
1254
|
+
violations.push({
|
|
1255
|
+
ruleId: 'ast_missing_weakify',
|
|
1256
|
+
message: `${cap.className ? `${cap.className}.` : ''}${cap.methodName || 'block'} 中 block 捕获 self 但未使用 @weakify/@strongify`,
|
|
1257
|
+
severity: 'warning',
|
|
1258
|
+
line: cap.line || 1,
|
|
1259
|
+
snippet: `block captures self without weakify in ${cap.methodName || 'anonymous block'}`,
|
|
1260
|
+
dimension: 'file',
|
|
1261
|
+
fixSuggestion: '使用 @weakify(self) / @strongify(self) 或 __weak typeof(self) weakSelf = self',
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
850
1265
|
return violations;
|
|
851
1266
|
}
|
|
852
1267
|
/** 获取 AstAnalyzer 模块(静态 import,带可用性检测) */
|
|
853
1268
|
_getAstAnalyzer() {
|
|
854
1269
|
return AstAnalyzerModule;
|
|
855
1270
|
}
|
|
1271
|
+
/**
|
|
1272
|
+
* 合并内置 + 配置级行排除模式,编译为 RegExp 数组
|
|
1273
|
+
* 配置来自 guardConfig.codeLevelThresholds[ruleId].exclude
|
|
1274
|
+
*/
|
|
1275
|
+
_getExcludeLineRegexes(ruleId, builtIn) {
|
|
1276
|
+
const patterns = [...(builtIn || [])];
|
|
1277
|
+
// 合并项目配置中的 exclude
|
|
1278
|
+
const override = this._guardConfig.codeLevelThresholds?.[ruleId];
|
|
1279
|
+
if (override && typeof override === 'object' && Array.isArray(override.exclude)) {
|
|
1280
|
+
patterns.push(...override.exclude);
|
|
1281
|
+
}
|
|
1282
|
+
const regexes = [];
|
|
1283
|
+
for (const p of patterns) {
|
|
1284
|
+
try {
|
|
1285
|
+
regexes.push(new RegExp(p));
|
|
1286
|
+
}
|
|
1287
|
+
catch {
|
|
1288
|
+
this.logger.debug(`Invalid excludeLinePattern in rule ${ruleId}: ${p}`);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
return regexes;
|
|
1292
|
+
}
|
|
856
1293
|
/**
|
|
857
1294
|
* 将 Guard 命中计数回写到对应 Recipe 的 guard_hit_count
|
|
858
1295
|
* @param violations
|
|
@@ -901,15 +1338,20 @@ export class GuardCheckEngine {
|
|
|
901
1338
|
*/
|
|
902
1339
|
auditFile(filePath, code, options = {}) {
|
|
903
1340
|
const language = detectLanguage(filePath);
|
|
1341
|
+
// 每次文件审计前重置 collector(单文件粒度)
|
|
1342
|
+
this._uncertaintyCollector.reset();
|
|
904
1343
|
const violations = this.checkCode(code, language, { ...options, filePath });
|
|
1344
|
+
const report = this._uncertaintyCollector.buildReport();
|
|
905
1345
|
return {
|
|
906
1346
|
filePath,
|
|
907
1347
|
language,
|
|
908
1348
|
violations,
|
|
1349
|
+
uncertainResults: report.uncertainResults,
|
|
909
1350
|
summary: {
|
|
910
1351
|
total: violations.length,
|
|
911
1352
|
errors: violations.filter((v) => v.severity === 'error').length,
|
|
912
1353
|
warnings: violations.filter((v) => v.severity === 'warning').length,
|
|
1354
|
+
uncertain: report.uncertainResults.length,
|
|
913
1355
|
},
|
|
914
1356
|
};
|
|
915
1357
|
}
|
|
@@ -935,16 +1377,67 @@ export class GuardCheckEngine {
|
|
|
935
1377
|
});
|
|
936
1378
|
totalViolations += crossFileViolations.length;
|
|
937
1379
|
totalErrors += crossFileViolations.filter((v) => v.severity === 'error').length;
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
totalErrors,
|
|
945
|
-
filesWithViolations: results.filter((r) => r.summary.total > 0).length,
|
|
946
|
-
},
|
|
1380
|
+
const summary = {
|
|
1381
|
+
filesChecked: results.length,
|
|
1382
|
+
totalViolations,
|
|
1383
|
+
totalErrors,
|
|
1384
|
+
totalUncertain: results.reduce((s, r) => s + r.summary.uncertain, 0),
|
|
1385
|
+
filesWithViolations: results.filter((r) => r.summary.total > 0).length,
|
|
947
1386
|
};
|
|
1387
|
+
// ── Signal emission (去重:相同检查结果不重复发射) ──
|
|
1388
|
+
if (this._signalBus && totalViolations > 0) {
|
|
1389
|
+
const signalKey = `${summary.filesChecked}:${summary.totalViolations}:${summary.totalErrors}:${summary.totalUncertain}:${summary.filesWithViolations}`;
|
|
1390
|
+
if (signalKey !== this._lastGuardSignalKey) {
|
|
1391
|
+
this._lastGuardSignalKey = signalKey;
|
|
1392
|
+
this._signalBus.send('guard', 'GuardCheckEngine', totalErrors > 0 ? 1 : 0.5, {
|
|
1393
|
+
metadata: { ...summary },
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
// ── 聚合 capability report ──
|
|
1398
|
+
const aggregateCollector = new UncertaintyCollector();
|
|
1399
|
+
for (const r of results) {
|
|
1400
|
+
for (const u of r.uncertainResults) {
|
|
1401
|
+
aggregateCollector.addUncertain(u.ruleId, u.message, u.layer, u.reason, u.detail);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
const capabilityReport = aggregateCollector.buildReport();
|
|
1405
|
+
// ── guard_blind_spot: uncertain 超阈值时发射 CapabilityRequest 信号(去重) ──
|
|
1406
|
+
if (this._signalBus && capabilityReport.uncertainResults.length > 0) {
|
|
1407
|
+
const uncertainTotal = capabilityReport.uncertainResults.length;
|
|
1408
|
+
const blindSpotThreshold = 5; // 触发阈值
|
|
1409
|
+
if (uncertainTotal >= blindSpotThreshold) {
|
|
1410
|
+
const blindSpotKey = `${uncertainTotal}:${capabilityReport.checkCoverage}`;
|
|
1411
|
+
if (blindSpotKey !== this._lastBlindSpotSignalKey) {
|
|
1412
|
+
this._lastBlindSpotSignalKey = blindSpotKey;
|
|
1413
|
+
// 按 layer 聚合盲区
|
|
1414
|
+
const byLayer = {};
|
|
1415
|
+
for (const u of capabilityReport.uncertainResults) {
|
|
1416
|
+
byLayer[u.layer] = (byLayer[u.layer] || 0) + 1;
|
|
1417
|
+
}
|
|
1418
|
+
this._signalBus.send('guard_blind_spot', 'GuardCheckEngine', uncertainTotal >= 20 ? 1 : 0.5, {
|
|
1419
|
+
metadata: {
|
|
1420
|
+
type: 'CapabilityRequest',
|
|
1421
|
+
uncertainTotal,
|
|
1422
|
+
checkCoverage: capabilityReport.checkCoverage,
|
|
1423
|
+
byLayer,
|
|
1424
|
+
boundaries: capabilityReport.boundaries.map((b) => ({
|
|
1425
|
+
type: b.type,
|
|
1426
|
+
description: b.description,
|
|
1427
|
+
affectedRules: b.affectedRules,
|
|
1428
|
+
suggestedAction: b.suggestedAction,
|
|
1429
|
+
})),
|
|
1430
|
+
suggestedAction: 'Extend Guard capability: add AST support for uncovered languages or implement missing cross-file checks',
|
|
1431
|
+
},
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
return { files: results, crossFileViolations, summary, capabilityReport };
|
|
1437
|
+
}
|
|
1438
|
+
/** 获取 uncertainty collector(供外部读取单文件 uncertain 状态) */
|
|
1439
|
+
getUncertaintyCollector() {
|
|
1440
|
+
return this._uncertaintyCollector;
|
|
948
1441
|
}
|
|
949
1442
|
/** 清除规则缓存 */
|
|
950
1443
|
clearCache() {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* 3. 集成到 guardAuditFiles MCP handler 和 GuardHandler (FileWatcher)
|
|
8
8
|
*/
|
|
9
9
|
import Logger from '../../infrastructure/logging/Logger.js';
|
|
10
|
+
import type { SignalBus } from '../../infrastructure/signal/SignalBus.js';
|
|
10
11
|
interface ViolationsStoreLike {
|
|
11
12
|
getRunsByFile(filePath: string): {
|
|
12
13
|
violations: {
|
|
@@ -40,9 +41,11 @@ export declare class GuardFeedbackLoop {
|
|
|
40
41
|
guardCheckEngine: GuardCheckEngineLike | null;
|
|
41
42
|
logger: ReturnType<typeof Logger.getInstance>;
|
|
42
43
|
violationsStore: ViolationsStoreLike | null;
|
|
44
|
+
_signalBus: SignalBus | null;
|
|
43
45
|
/** @param [options.guardCheckEngine] 用于查找规则 */
|
|
44
46
|
constructor(violationsStore: ViolationsStoreLike | null, feedbackCollector: FeedbackCollectorLike | null, options?: {
|
|
45
47
|
guardCheckEngine?: GuardCheckEngineLike;
|
|
48
|
+
signalBus?: SignalBus;
|
|
46
49
|
});
|
|
47
50
|
/**
|
|
48
51
|
* 对比当前和历史 violations,检测已修复的违规
|