autosnippet 3.0.1 → 3.0.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/README.md +230 -324
- package/bin/api-server.js +1 -1
- package/bin/cli.js +204 -244
- package/bin/mcp-server.js +5 -3
- package/config/knowledge-base.config.js +132 -132
- package/dashboard/dist/assets/{icons-CEfgGaZi.js → icons-Cdq22n2i.js} +95 -100
- package/dashboard/dist/assets/index-ClkyPkDX.js +133 -0
- package/dashboard/dist/assets/index-t4QrJwv1.css +1 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/bootstrap.js +8 -8
- package/lib/cli/AiScanService.js +86 -40
- package/lib/cli/KnowledgeSyncService.js +113 -74
- package/lib/cli/SetupService.js +439 -277
- package/lib/cli/UpgradeService.js +63 -100
- package/lib/core/AstAnalyzer.js +276 -597
- package/lib/core/ast/ProjectGraph.js +101 -40
- package/lib/core/ast/ensure-grammars.js +232 -0
- package/lib/core/ast/index.js +115 -0
- package/lib/core/ast/lang-dart.js +661 -0
- package/lib/core/ast/lang-go.js +530 -0
- package/lib/core/ast/lang-java.js +435 -0
- package/lib/core/ast/lang-javascript.js +272 -0
- package/lib/core/ast/lang-kotlin.js +423 -0
- package/lib/core/ast/lang-objc.js +388 -0
- package/lib/core/ast/lang-python.js +371 -0
- package/lib/core/ast/lang-swift.js +337 -0
- package/lib/core/ast/lang-typescript.js +503 -0
- package/lib/core/capability/CapabilityProbe.js +18 -9
- package/lib/core/constitution/Constitution.js +2 -3
- package/lib/core/constitution/ConstitutionValidator.js +65 -24
- package/lib/core/discovery/DartDiscoverer.js +534 -0
- package/lib/core/discovery/DiscovererRegistry.js +83 -0
- package/lib/core/discovery/GenericDiscoverer.js +225 -0
- package/lib/core/discovery/GoDiscoverer.js +541 -0
- package/lib/core/discovery/JvmDiscoverer.js +506 -0
- package/lib/core/discovery/NodeDiscoverer.js +466 -0
- package/lib/core/discovery/ProjectDiscoverer.js +93 -0
- package/lib/core/discovery/PythonDiscoverer.js +338 -0
- package/lib/core/discovery/SpmDiscoverer.js +5 -0
- package/lib/core/discovery/index.js +53 -0
- package/lib/core/enhancement/EnhancementPack.js +71 -0
- package/lib/core/enhancement/EnhancementRegistry.js +47 -0
- package/lib/core/enhancement/android-enhancement.js +102 -0
- package/lib/core/enhancement/django-enhancement.js +70 -0
- package/lib/core/enhancement/fastapi-enhancement.js +63 -0
- package/lib/core/enhancement/go-grpc-enhancement.js +152 -0
- package/lib/core/enhancement/go-web-enhancement.js +201 -0
- package/lib/core/enhancement/index.js +65 -0
- package/lib/core/enhancement/node-server-enhancement.js +88 -0
- package/lib/core/enhancement/react-enhancement.js +86 -0
- package/lib/core/enhancement/spring-enhancement.js +112 -0
- package/lib/core/enhancement/vue-enhancement.js +96 -0
- package/lib/core/gateway/Gateway.js +8 -9
- package/lib/core/gateway/GatewayActionRegistry.js +1 -1
- package/lib/core/permission/PermissionManager.js +12 -8
- package/lib/domain/index.js +13 -9
- package/lib/domain/knowledge/KnowledgeEntry.js +111 -101
- package/lib/domain/knowledge/KnowledgeRepository.js +0 -1
- package/lib/domain/knowledge/Lifecycle.js +22 -22
- package/lib/domain/knowledge/index.js +9 -12
- package/lib/domain/knowledge/values/Constraints.js +31 -21
- package/lib/domain/knowledge/values/Content.js +21 -13
- package/lib/domain/knowledge/values/Quality.js +31 -18
- package/lib/domain/knowledge/values/Reasoning.js +20 -12
- package/lib/domain/knowledge/values/Relations.js +37 -25
- package/lib/domain/knowledge/values/Stats.js +18 -12
- package/lib/domain/knowledge/values/index.js +4 -3
- package/lib/domain/snippet/Snippet.js +35 -10
- package/lib/external/ai/AiFactory.js +48 -16
- package/lib/external/ai/AiProvider.js +184 -90
- package/lib/external/ai/providers/ClaudeProvider.js +25 -12
- package/lib/external/ai/providers/GoogleGeminiProvider.js +59 -30
- package/lib/external/ai/providers/MockProvider.js +9 -3
- package/lib/external/ai/providers/OpenAiProvider.js +51 -29
- package/lib/external/mcp/McpServer.js +66 -36
- package/lib/external/mcp/errorHandler.js +23 -11
- package/lib/external/mcp/handlers/LanguageExtensions.js +138 -53
- package/lib/external/mcp/handlers/TargetClassifier.js +52 -16
- package/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +81 -20
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +71 -42
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +9 -17
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +14 -9
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +15 -7
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +352 -153
- package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +52 -12
- package/lib/external/mcp/handlers/bootstrap/skills.js +143 -39
- package/lib/external/mcp/handlers/bootstrap.js +691 -168
- package/lib/external/mcp/handlers/browse.js +66 -22
- package/lib/external/mcp/handlers/candidate.js +118 -35
- package/lib/external/mcp/handlers/consolidated.js +49 -17
- package/lib/external/mcp/handlers/guard.js +104 -39
- package/lib/external/mcp/handlers/knowledge.js +60 -36
- package/lib/external/mcp/handlers/search.js +43 -14
- package/lib/external/mcp/handlers/skill.js +120 -45
- package/lib/external/mcp/handlers/structure.js +240 -86
- package/lib/external/mcp/handlers/system.js +42 -12
- package/lib/external/mcp/handlers/wiki.js +58 -33
- package/lib/external/mcp/tools.js +306 -123
- package/lib/http/HttpServer.js +72 -47
- package/lib/http/middleware/RateLimiter.js +5 -3
- package/lib/http/middleware/errorHandler.js +6 -1
- package/lib/http/middleware/requestLogger.js +14 -3
- package/lib/http/middleware/roleResolver.js +30 -23
- package/lib/http/routes/ai.js +387 -265
- package/lib/http/routes/auth.js +81 -61
- package/lib/http/routes/candidates.js +430 -320
- package/lib/http/routes/commands.js +289 -189
- package/lib/http/routes/extract.js +158 -125
- package/lib/http/routes/guardRules.js +309 -217
- package/lib/http/routes/knowledge.js +213 -154
- package/lib/http/routes/modules.js +578 -0
- package/lib/http/routes/monitoring.js +6 -6
- package/lib/http/routes/recipes.js +104 -93
- package/lib/http/routes/search.js +361 -305
- package/lib/http/routes/skills.js +145 -98
- package/lib/http/routes/snippets.js +42 -30
- package/lib/http/routes/spm.js +3 -405
- package/lib/http/routes/violations.js +113 -93
- package/lib/http/routes/wiki.js +211 -170
- package/lib/http/utils/routeHelpers.js +3 -1
- package/lib/http/utils/sse-sessions.js +16 -6
- package/lib/http/utils/sse.js +15 -5
- package/lib/infrastructure/audit/AuditLogger.js +5 -2
- package/lib/infrastructure/audit/AuditStore.js +10 -7
- package/lib/infrastructure/cache/CacheService.js +3 -1
- package/lib/infrastructure/cache/GraphCache.js +8 -4
- package/lib/infrastructure/cache/UnifiedCacheAdapter.js +1 -1
- package/lib/infrastructure/config/ConfigLoader.js +9 -5
- package/lib/infrastructure/config/Defaults.js +30 -10
- package/lib/infrastructure/config/Paths.js +28 -8
- package/lib/infrastructure/config/TriggerSymbol.js +22 -10
- package/lib/infrastructure/database/DatabaseConnection.js +15 -10
- package/lib/infrastructure/database/migrations/001_initial_schema.js +0 -1
- package/lib/infrastructure/external/ClipboardManager.js +6 -2
- package/lib/infrastructure/external/NativeUi.js +50 -43
- package/lib/infrastructure/external/OpenBrowser.js +14 -17
- package/lib/infrastructure/external/XcodeAutomation.js +14 -258
- package/lib/infrastructure/logging/Logger.js +46 -30
- package/lib/infrastructure/monitoring/ErrorTracker.js +7 -5
- package/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -4
- package/lib/infrastructure/paths/HeaderResolver.js +25 -9
- package/lib/infrastructure/paths/PathFinder.js +34 -12
- package/lib/infrastructure/plugin/PluginManager.js +26 -8
- package/lib/infrastructure/realtime/RealtimeService.js +2 -2
- package/lib/infrastructure/vector/Chunker.js +22 -7
- package/lib/infrastructure/vector/IndexingPipeline.js +46 -22
- package/lib/infrastructure/vector/JsonVectorAdapter.js +90 -53
- package/lib/infrastructure/vector/VectorStore.js +28 -10
- package/lib/injection/ServiceContainer.js +247 -93
- package/lib/platform/ios/index.js +63 -0
- package/lib/platform/ios/routes/spm.js +437 -0
- package/lib/platform/ios/snippet/PlaceholderConverter.js +55 -0
- package/lib/platform/ios/snippet/XcodeCodec.js +112 -0
- package/lib/{service → platform/ios}/spm/DependencyGraph.js +41 -17
- package/lib/{service → platform/ios}/spm/PackageSwiftParser.js +41 -14
- package/lib/{service → platform/ios}/spm/PolicyEngine.js +9 -4
- package/lib/platform/ios/spm/SpmDiscoverer.js +122 -0
- package/lib/{service → platform/ios}/spm/SpmService.js +385 -127
- package/lib/{service/automation → platform/ios/xcode}/SaveEventFilter.js +8 -7
- package/lib/platform/ios/xcode/XcodeAutomation.js +350 -0
- package/lib/{service/automation → platform/ios/xcode}/XcodeIntegration.js +325 -145
- package/lib/repository/base/BaseRepository.js +7 -9
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +98 -75
- package/lib/repository/token/TokenUsageStore.js +4 -2
- package/lib/service/automation/ActionPipeline.js +1 -1
- package/lib/service/automation/AutomationOrchestrator.js +8 -4
- package/lib/service/automation/ContextCollector.js +7 -5
- package/lib/service/automation/DirectiveDetector.js +23 -16
- package/lib/service/automation/FileWatcher.js +112 -56
- package/lib/service/automation/TriggerResolver.js +6 -4
- package/lib/service/automation/handlers/AlinkHandler.js +24 -12
- package/lib/service/automation/handlers/CreateHandler.js +19 -20
- package/lib/service/automation/handlers/DraftHandler.js +14 -8
- package/lib/service/automation/handlers/GuardHandler.js +93 -63
- package/lib/service/automation/handlers/HeaderHandler.js +1 -6
- package/lib/service/automation/handlers/SearchHandler.js +155 -88
- package/lib/service/bootstrap/BootstrapTaskManager.js +77 -35
- package/lib/service/candidate/SimilarityService.js +25 -9
- package/lib/service/chat/AnalystAgent.js +50 -24
- package/lib/service/chat/CandidateGuardrail.js +143 -17
- package/lib/service/chat/ChatAgent.js +655 -260
- package/lib/service/chat/ContextWindow.js +116 -71
- package/lib/service/chat/ConversationStore.js +77 -36
- package/lib/service/chat/EpisodicConsolidator.js +47 -23
- package/lib/service/chat/HandoffProtocol.js +98 -22
- package/lib/service/chat/Memory.js +34 -14
- package/lib/service/chat/ProducerAgent.js +40 -20
- package/lib/service/chat/ProjectSemanticMemory.js +109 -78
- package/lib/service/chat/ReasoningLayer.js +148 -70
- package/lib/service/chat/ReasoningTrace.js +44 -32
- package/lib/service/chat/TaskPipeline.js +39 -19
- package/lib/service/chat/ToolRegistry.js +48 -29
- package/lib/service/chat/WorkingMemory.js +44 -18
- package/lib/service/chat/tools.js +1096 -494
- package/lib/service/context/RecipeExtractor.js +132 -51
- package/lib/service/cursor/CursorDeliveryPipeline.js +82 -37
- package/lib/service/cursor/KnowledgeCompressor.js +25 -22
- package/lib/service/cursor/RulesGenerator.js +13 -7
- package/lib/service/cursor/SkillsSyncer.js +77 -27
- package/lib/service/cursor/TokenBudget.js +2 -2
- package/lib/service/cursor/TopicClassifier.js +54 -20
- package/lib/service/guard/ComplianceReporter.js +55 -43
- package/lib/service/guard/ExclusionManager.js +67 -29
- package/lib/service/guard/GuardCheckEngine.js +381 -86
- package/lib/service/guard/GuardFeedbackLoop.js +22 -10
- package/lib/service/guard/GuardService.js +29 -19
- package/lib/service/guard/RuleLearner.js +55 -23
- package/lib/service/guard/SourceFileCollector.js +27 -20
- package/lib/service/guard/ViolationsStore.js +43 -38
- package/lib/service/knowledge/CodeEntityGraph.js +147 -82
- package/lib/service/knowledge/ConfidenceRouter.js +12 -10
- package/lib/service/knowledge/KnowledgeFileWriter.js +147 -56
- package/lib/service/knowledge/KnowledgeGraphService.js +81 -34
- package/lib/service/knowledge/KnowledgeService.js +222 -112
- package/lib/service/module/ModuleService.js +969 -0
- package/lib/service/quality/FeedbackCollector.js +27 -15
- package/lib/service/quality/QualityScorer.js +78 -24
- package/lib/service/recipe/RecipeCandidateValidator.js +110 -44
- package/lib/service/recipe/RecipeParser.js +78 -45
- package/lib/service/search/CoarseRanker.js +43 -28
- package/lib/service/search/CrossEncoderReranker.js +32 -21
- package/lib/service/search/InvertedIndex.js +21 -7
- package/lib/service/search/MultiSignalRanker.js +90 -28
- package/lib/service/search/RetrievalFunnel.js +45 -24
- package/lib/service/search/SearchEngine.js +255 -103
- package/lib/service/skills/EventAggregator.js +32 -15
- package/lib/service/skills/SignalCollector.js +140 -64
- package/lib/service/skills/SkillAdvisor.js +79 -42
- package/lib/service/skills/SkillHooks.js +16 -14
- package/lib/service/snippet/PlaceholderConverter.js +5 -0
- package/lib/service/snippet/SnippetFactory.js +116 -99
- package/lib/service/snippet/SnippetInstaller.js +234 -62
- package/lib/service/snippet/codecs/SnippetCodec.js +67 -0
- package/lib/service/snippet/codecs/VSCodeCodec.js +102 -0
- package/lib/service/snippet/codecs/XcodeCodec.js +5 -0
- package/lib/service/wiki/WikiGenerator.js +637 -263
- package/lib/shared/DimensionCopyRegistry.js +472 -0
- package/lib/shared/LanguageService.js +399 -0
- package/lib/shared/PathGuard.js +45 -28
- package/lib/shared/RecipeReadinessChecker.js +72 -12
- package/lib/shared/constants.js +41 -41
- package/lib/shared/errors/BaseError.js +2 -2
- package/lib/shared/errors/index.js +4 -4
- package/lib/shared/similarity.js +25 -8
- package/lib/shared/token-utils.js +6 -2
- package/lib/shared/utils/common.js +12 -4
- package/package.json +49 -13
- package/scripts/bench-real-projects.mjs +256 -0
- package/scripts/build-native-ui.js +30 -30
- package/scripts/clear-old-vector-index.js +5 -35
- package/scripts/clear-vector-cache.js +7 -37
- package/scripts/collect-test-project-stats.mjs +160 -0
- package/scripts/diagnose-mcp.js +41 -32
- package/scripts/ensure-parse-package.js +6 -9
- package/scripts/generate-recipe-drafts.js +116 -77
- package/scripts/init-db.js +3 -20
- package/scripts/init-snippets.js +305 -0
- package/scripts/init-vector-db.js +173 -170
- package/scripts/install-cursor-skill.js +148 -104
- package/scripts/install-full.js +8 -21
- package/scripts/install-vscode-copilot.js +146 -145
- package/scripts/migrate-md-to-knowledge.mjs +139 -151
- package/scripts/postinstall-safe.js +5 -17
- package/scripts/recipe-audit.js +106 -82
- package/scripts/release.js +283 -323
- package/scripts/setup-mcp-config.js +60 -52
- package/scripts/verify-context-api.js +20 -20
- package/skills/autosnippet-analysis/SKILL.md +10 -6
- package/skills/autosnippet-candidates/SKILL.md +27 -26
- package/skills/autosnippet-coldstart/SKILL.md +555 -38
- package/skills/autosnippet-concepts/SKILL.md +349 -337
- package/skills/autosnippet-create/SKILL.md +5 -5
- package/skills/autosnippet-reference-dart/SKILL.md +543 -0
- package/skills/autosnippet-reference-go/SKILL.md +539 -0
- package/skills/autosnippet-reference-java/SKILL.md +534 -0
- package/skills/autosnippet-reference-jsts/SKILL.md +41 -9
- package/skills/autosnippet-reference-kotlin/SKILL.md +526 -0
- package/skills/autosnippet-reference-objc/SKILL.md +29 -6
- package/skills/autosnippet-reference-python/SKILL.md +800 -0
- package/skills/autosnippet-reference-swift/SKILL.md +70 -14
- package/skills/autosnippet-structure/SKILL.md +4 -4
- package/templates/cursor-rules/autosnippet-conventions.mdc +2 -2
- package/templates/recipes-setup/README.md +2 -2
- package/templates/recipes-setup/_template.md +1 -1
- package/dashboard/dist/assets/index-Bun3ld_J.css +0 -1
- package/dashboard/dist/assets/index-_Sk_Dmg3.js +0 -143
- package/resources/asd-entry/main.swift +0 -159
- package/scripts/build-asd-entry.js +0 -51
- package/scripts/init-xcode-snippets.js +0 -311
- package/template.json +0 -39
|
@@ -9,19 +9,15 @@
|
|
|
9
9
|
* // as:a <keyword> — 检查当前文件 + 搜索相关规范
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { basename, join, extname, dirname } from 'node:path';
|
|
13
12
|
import { readFile } from 'node:fs/promises';
|
|
13
|
+
import { dirname, extname, join } from 'node:path';
|
|
14
|
+
import { LanguageService } from '../../../shared/LanguageService.js';
|
|
14
15
|
|
|
15
16
|
/** 已知的 scope 关键词 */
|
|
16
17
|
const SCOPE_KEYWORDS = new Set(['file', 'target', 'project', 'all']);
|
|
17
18
|
|
|
18
|
-
/** 支持审计的源文件扩展名 */
|
|
19
|
-
const SOURCE_EXTS =
|
|
20
|
-
'.m', '.mm', '.h', '.swift',
|
|
21
|
-
'.c', '.cpp', '.cc', '.cxx', '.hpp',
|
|
22
|
-
'.js', '.ts', '.jsx', '.tsx',
|
|
23
|
-
'.java', '.kt', '.py', '.rb', '.go', '.rs',
|
|
24
|
-
]);
|
|
19
|
+
/** 支持审计的源文件扩展名 — 委托给 LanguageService */
|
|
20
|
+
const SOURCE_EXTS = LanguageService.sourceExts;
|
|
25
21
|
|
|
26
22
|
/**
|
|
27
23
|
* 递归收集目录下所有源文件路径
|
|
@@ -32,9 +28,18 @@ async function collectSourceFiles(dir) {
|
|
|
32
28
|
|
|
33
29
|
// 跳过的目录
|
|
34
30
|
const SKIP_DIRS = new Set([
|
|
35
|
-
'node_modules',
|
|
36
|
-
'
|
|
37
|
-
'
|
|
31
|
+
'node_modules',
|
|
32
|
+
'.git',
|
|
33
|
+
'build',
|
|
34
|
+
'DerivedData',
|
|
35
|
+
'Pods',
|
|
36
|
+
'.build',
|
|
37
|
+
'vendor',
|
|
38
|
+
'dist',
|
|
39
|
+
'.next',
|
|
40
|
+
'Carthage',
|
|
41
|
+
'xcuserdata',
|
|
42
|
+
'__pycache__',
|
|
38
43
|
]);
|
|
39
44
|
|
|
40
45
|
async function walk(currentDir) {
|
|
@@ -45,7 +50,9 @@ async function collectSourceFiles(dir) {
|
|
|
45
50
|
return; // 权限不足等情况跳过
|
|
46
51
|
}
|
|
47
52
|
for (const entry of entries) {
|
|
48
|
-
if (entry.name.startsWith('.') && entry.name !== '.')
|
|
53
|
+
if (entry.name.startsWith('.') && entry.name !== '.') {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
49
56
|
const fullPath = join(currentDir, entry.name);
|
|
50
57
|
if (entry.isDirectory()) {
|
|
51
58
|
if (!SKIP_DIRS.has(entry.name)) {
|
|
@@ -72,10 +79,14 @@ export async function handleGuard(watcher, fullPath, code, guardLine) {
|
|
|
72
79
|
const scopeArg = rest.toLowerCase();
|
|
73
80
|
const isScope = SCOPE_KEYWORDS.has(scopeArg);
|
|
74
81
|
// 确定 scope:无参数或 'file' → file;'target' → target;'project'/'all' → project
|
|
75
|
-
const scope =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
const scope =
|
|
83
|
+
!rest || scopeArg === 'file'
|
|
84
|
+
? 'file'
|
|
85
|
+
: scopeArg === 'target'
|
|
86
|
+
? 'target'
|
|
87
|
+
: scopeArg === 'project' || scopeArg === 'all'
|
|
88
|
+
? 'project'
|
|
89
|
+
: 'file'; // 非 scope 关键词回退到 file
|
|
79
90
|
|
|
80
91
|
try {
|
|
81
92
|
const { detectLanguage } = await import('../../guard/GuardCheckEngine.js');
|
|
@@ -95,18 +106,19 @@ export async function handleGuard(watcher, fullPath, code, guardLine) {
|
|
|
95
106
|
|
|
96
107
|
if (!scanRoot) {
|
|
97
108
|
console.warn(' ⚠️ 无法确定扫描根目录,回退到单文件检查');
|
|
98
|
-
return _auditSingleFile(engine, fullPath, code, detectLanguage, 'file');
|
|
109
|
+
return _auditSingleFile(watcher, engine, fullPath, code, detectLanguage, 'file');
|
|
99
110
|
}
|
|
100
111
|
|
|
101
112
|
const scopeLabel = scope === 'project' ? '整个项目' : '当前目录';
|
|
102
|
-
console.log(`\n
|
|
113
|
+
console.log(`\n🔍 Guard 审计 — ${scopeLabel} (${scanRoot})`);
|
|
103
114
|
const sourcePaths = await collectSourceFiles(scanRoot);
|
|
104
|
-
console.log(` 📁 找到 ${sourcePaths.length} 个源文件`);
|
|
105
115
|
|
|
106
116
|
if (sourcePaths.length === 0) {
|
|
107
|
-
console.log('
|
|
117
|
+
console.log(' ℹ️ 未找到可审计的源文件');
|
|
118
|
+
watcher._notify?.('未找到可审计的源文件');
|
|
108
119
|
return;
|
|
109
120
|
}
|
|
121
|
+
console.log(` 📁 扫描到 ${sourcePaths.length} 个源文件`);
|
|
110
122
|
|
|
111
123
|
// 读取所有文件内容
|
|
112
124
|
const fileEntries = [];
|
|
@@ -120,7 +132,7 @@ export async function handleGuard(watcher, fullPath, code, guardLine) {
|
|
|
120
132
|
}
|
|
121
133
|
}
|
|
122
134
|
if (readErrors > 0) {
|
|
123
|
-
console.
|
|
135
|
+
console.warn(` ⚠️ ${readErrors} 个文件读取失败,已跳过`);
|
|
124
136
|
}
|
|
125
137
|
|
|
126
138
|
// 批量审计(传递 scope 以启用对应维度规则)
|
|
@@ -128,63 +140,66 @@ export async function handleGuard(watcher, fullPath, code, guardLine) {
|
|
|
128
140
|
const { summary } = report;
|
|
129
141
|
|
|
130
142
|
if (summary.totalViolations === 0) {
|
|
131
|
-
console.log(
|
|
143
|
+
console.log(`\n ✅ 审计通过 — ${fileEntries.length} 个文件,无违规`);
|
|
144
|
+
watcher._notify?.(`${scopeLabel}审计通过 ✅ ${fileEntries.length} 个文件,无违规`);
|
|
132
145
|
} else {
|
|
133
|
-
console.log(` 🛡️ 扫描 ${summary.filesChecked} 个文件:`);
|
|
134
|
-
console.log(` ${summary.totalErrors} errors, ${summary.totalViolations - summary.totalErrors} warnings`);
|
|
135
|
-
console.log(` ${summary.filesWithViolations} 个文件存在问题\n`);
|
|
136
|
-
|
|
137
146
|
// 按文件输出详情(限制输出前 10 个有问题的文件)
|
|
138
|
-
|
|
147
|
+
console.log(`\n ❌ 审计发现 ${summary.totalViolations} 个问题 (${summary.errors ?? 0} 错误, ${summary.warnings ?? 0} 警告)`);
|
|
148
|
+
watcher._notify?.(`${scopeLabel}审计: ${summary.totalViolations} 个问题 (${summary.errors ?? 0} 错误, ${summary.warnings ?? 0} 警告)`);
|
|
149
|
+
const filesWithIssues = report.files.filter((f) => f.summary.total > 0);
|
|
139
150
|
for (const file of filesWithIssues.slice(0, 10)) {
|
|
140
|
-
const rel = file.filePath.replace(scanRoot
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
151
|
+
const rel = file.filePath.replace(`${scanRoot}/`, '');
|
|
152
|
+
const errors = file.violations.filter((v) => v.severity === 'error');
|
|
153
|
+
const warnings = file.violations.filter((v) => v.severity === 'warning');
|
|
154
|
+
console.log(`\n 📄 ${rel} (${errors.length} 错误, ${warnings.length} 警告)`);
|
|
144
155
|
for (const v of errors.slice(0, 5)) {
|
|
145
|
-
console.log(`
|
|
156
|
+
console.log(` 🔴 [${v.ruleId || 'unknown'}] ${v.message}${v.line ? ` (行 ${v.line})` : ''}`);
|
|
157
|
+
}
|
|
158
|
+
if (errors.length > 5) {
|
|
159
|
+
console.log(` ... 还有 ${errors.length - 5} 个错误`);
|
|
146
160
|
}
|
|
147
|
-
if (errors.length > 5) console.log(` ... 还有 ${errors.length - 5} 个 errors`);
|
|
148
161
|
for (const v of warnings.slice(0, 3)) {
|
|
149
|
-
console.log(`
|
|
162
|
+
console.log(` 🟡 [${v.ruleId || 'unknown'}] ${v.message}${v.line ? ` (行 ${v.line})` : ''}`);
|
|
163
|
+
}
|
|
164
|
+
if (warnings.length > 3) {
|
|
165
|
+
console.log(` ... 还有 ${warnings.length - 3} 个警告`);
|
|
150
166
|
}
|
|
151
|
-
if (warnings.length > 3) console.log(` ... 还有 ${warnings.length - 3} 个 warnings`);
|
|
152
167
|
}
|
|
153
168
|
if (filesWithIssues.length > 10) {
|
|
154
|
-
console.log(`\n ... 还有 ${filesWithIssues.length - 10}
|
|
169
|
+
console.log(`\n ... 还有 ${filesWithIssues.length - 10} 个文件有问题`);
|
|
155
170
|
}
|
|
156
171
|
|
|
157
172
|
// 跨文件问题汇总
|
|
158
173
|
if (report.crossFileViolations?.length > 0) {
|
|
159
|
-
console.log(`\n 🔗 跨文件问题 (${report.crossFileViolations.length}):`);
|
|
174
|
+
console.log(`\n 🔗 跨文件问题 (${report.crossFileViolations.length} 个):`);
|
|
160
175
|
for (const v of report.crossFileViolations.slice(0, 10)) {
|
|
161
|
-
console.log(`
|
|
176
|
+
console.log(` ⚠️ [${v.ruleId || 'cross-file'}] ${v.message}`);
|
|
162
177
|
if (v.locations) {
|
|
163
178
|
for (const loc of v.locations.slice(0, 5)) {
|
|
164
|
-
const relLoc = loc.filePath.replace(scanRoot
|
|
165
|
-
console.log(`
|
|
179
|
+
const relLoc = loc.filePath.replace(`${scanRoot}/`, '');
|
|
180
|
+
console.log(` 📍 ${relLoc}${loc.line ? `:${loc.line}` : ''}`);
|
|
181
|
+
}
|
|
182
|
+
if (v.locations.length > 5) {
|
|
183
|
+
console.log(` ... 还有 ${v.locations.length - 5} 个位置`);
|
|
166
184
|
}
|
|
167
|
-
if (v.locations.length > 5) console.log(` ... 还有 ${v.locations.length - 5} 处`);
|
|
168
185
|
}
|
|
169
186
|
}
|
|
170
187
|
}
|
|
171
188
|
}
|
|
172
189
|
return;
|
|
173
190
|
}
|
|
174
|
-
|
|
175
|
-
/* ── 单文件审计 (file scope) ── */
|
|
176
|
-
console.log(`\n🛡️ [Guard] 正在检查文件: ${basename(fullPath)}`);
|
|
177
|
-
_auditSingleFile(engine, fullPath, code, detectLanguage, scope);
|
|
191
|
+
_auditSingleFile(watcher, engine, fullPath, code, detectLanguage, scope);
|
|
178
192
|
|
|
179
193
|
// 如果有非 scope 关键词,也做语义搜索
|
|
180
194
|
if (rest && !isScope) {
|
|
181
195
|
try {
|
|
182
196
|
const searchEngine = container.get('searchEngine');
|
|
183
197
|
const results = await searchEngine.search(rest, { limit: 3, mode: 'keyword' });
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
198
|
+
const items = Array.isArray(results) ? results : results.items || [];
|
|
199
|
+
if (items.length > 0) {
|
|
200
|
+
console.log(`\n 📚 相关规范 ("${rest}"):`);
|
|
201
|
+
for (const r of items) {
|
|
202
|
+
console.log(` • ${r.title || r.name || r.id}`);
|
|
188
203
|
}
|
|
189
204
|
}
|
|
190
205
|
} catch {
|
|
@@ -199,32 +214,47 @@ export async function handleGuard(watcher, fullPath, code, guardLine) {
|
|
|
199
214
|
/**
|
|
200
215
|
* 检查单个文件并打印结果
|
|
201
216
|
*/
|
|
202
|
-
function _auditSingleFile(engine, fullPath, code, detectLanguage, scope = 'file') {
|
|
217
|
+
function _auditSingleFile(watcher, engine, fullPath, code, detectLanguage, scope = 'file') {
|
|
203
218
|
const language = detectLanguage(fullPath);
|
|
204
219
|
const violations = engine.checkCode(code, language, { scope });
|
|
205
220
|
|
|
206
221
|
if (violations.length === 0) {
|
|
207
|
-
console.log(
|
|
222
|
+
console.log(`\n ✅ Guard 审计通过 — 无违规 (${fullPath.split('/').pop()})`);
|
|
223
|
+
watcher._notify?.('审计通过 ✅ 无违规');
|
|
208
224
|
} else {
|
|
209
225
|
const errors = violations.filter((v) => v.severity === 'error');
|
|
210
226
|
const warnings = violations.filter((v) => v.severity === 'warning');
|
|
211
|
-
console.log(
|
|
227
|
+
console.log(`\n ❌ 发现 ${violations.length} 个问题 (${errors.length} 错误, ${warnings.length} 警告)`);
|
|
228
|
+
watcher._notify?.(`审计: ${violations.length} 个问题 (${errors.length} 错误, ${warnings.length} 警告)`);
|
|
212
229
|
for (const v of errors) {
|
|
213
|
-
console.log(`
|
|
214
|
-
if (v.fixSuggestion)
|
|
230
|
+
console.log(` 🔴 [${v.ruleId || 'unknown'}] ${v.message}${v.line ? ` (行 ${v.line})` : ''}`);
|
|
231
|
+
if (v.fixSuggestion) {
|
|
232
|
+
console.log(` 💡 建议: ${v.fixSuggestion}`);
|
|
233
|
+
}
|
|
215
234
|
}
|
|
216
235
|
for (const v of warnings.slice(0, 5)) {
|
|
217
|
-
console.log(`
|
|
218
|
-
if (v.fixSuggestion)
|
|
236
|
+
console.log(` 🟡 [${v.ruleId || 'unknown'}] ${v.message}${v.line ? ` (行 ${v.line})` : ''}`);
|
|
237
|
+
if (v.fixSuggestion) {
|
|
238
|
+
console.log(` 💡 建议: ${v.fixSuggestion}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (warnings.length > 5) {
|
|
242
|
+
console.log(` ... 还有 ${warnings.length - 5} 个警告`);
|
|
219
243
|
}
|
|
220
244
|
}
|
|
221
245
|
|
|
222
246
|
// Guard ↔ Recipe 闭环:检测修复并自动确认使用(fire-and-forget)
|
|
223
|
-
import('../../../injection/ServiceContainer.js')
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
247
|
+
import('../../../injection/ServiceContainer.js')
|
|
248
|
+
.then(({ ServiceContainer }) => {
|
|
249
|
+
try {
|
|
250
|
+
const container = ServiceContainer.getInstance();
|
|
251
|
+
const feedbackLoop = container.get('guardFeedbackLoop');
|
|
252
|
+
feedbackLoop.processFixDetection({ violations }, fullPath);
|
|
253
|
+
} catch {
|
|
254
|
+
/* guardFeedbackLoop not available */
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
.catch(() => {
|
|
258
|
+
/* ignored */
|
|
259
|
+
});
|
|
230
260
|
}
|
|
@@ -18,12 +18,9 @@ export async function handleHeader(watcher, fullPath, headerLine, importArray, i
|
|
|
18
18
|
const parsed = HeaderResolver.parseImportLine(headerLine);
|
|
19
19
|
|
|
20
20
|
if (!parsed) {
|
|
21
|
-
console.log(`[Header] 无法解析指令: ${headerLine}`);
|
|
22
21
|
return;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
console.log(`\n📦 [Header] ${isSwift ? 'import' : '#import'} ${parsed.moduleName || parsed.headerName || headerLine}`);
|
|
26
|
-
|
|
27
24
|
const resolved = await HeaderResolver.resolveHeadersForText(
|
|
28
25
|
watcher.projectRoot,
|
|
29
26
|
basename(fullPath),
|
|
@@ -31,11 +28,10 @@ export async function handleHeader(watcher, fullPath, headerLine, importArray, i
|
|
|
31
28
|
);
|
|
32
29
|
|
|
33
30
|
if (!resolved || !resolved.headers || resolved.headers.length === 0) {
|
|
34
|
-
console.log(` ℹ️ 未找到需要添加的头文件`);
|
|
35
31
|
return;
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
const { insertHeaders } = await import('
|
|
34
|
+
const { insertHeaders } = await import('../../../platform/ios/xcode/XcodeIntegration.js');
|
|
39
35
|
const result = await insertHeaders(watcher, fullPath, resolved.headers, {
|
|
40
36
|
isSwift,
|
|
41
37
|
moduleName: resolved.moduleName || null,
|
|
@@ -45,7 +41,6 @@ export async function handleHeader(watcher, fullPath, headerLine, importArray, i
|
|
|
45
41
|
return;
|
|
46
42
|
}
|
|
47
43
|
if (result.inserted.length === 0 && result.skipped.length > 0) {
|
|
48
|
-
console.log(` ✅ 头文件已全部导入`);
|
|
49
44
|
}
|
|
50
45
|
} catch (err) {
|
|
51
46
|
console.warn(` ⚠️ Header 处理失败: ${err.message}`);
|
|
@@ -9,17 +9,12 @@
|
|
|
9
9
|
* @param {string} searchLine
|
|
10
10
|
*/
|
|
11
11
|
export async function handleSearch(watcher, fullPath, relativePath, searchLine) {
|
|
12
|
-
const query = searchLine
|
|
13
|
-
.replace(/^\/\/\s*(?:autosnippet|as):(?:search|s)\s*/, '')
|
|
14
|
-
.trim();
|
|
12
|
+
const query = searchLine.replace(/^\/\/\s*(?:autosnippet|as):(?:search|s)\s*/, '').trim();
|
|
15
13
|
|
|
16
14
|
if (!query) {
|
|
17
|
-
console.log(`[as:search] 请在指令后写搜索关键词,如 // as:s 网络请求`);
|
|
18
15
|
return;
|
|
19
16
|
}
|
|
20
17
|
|
|
21
|
-
console.log(`\n🔍 [Search] "${query}" ...`);
|
|
22
|
-
|
|
23
18
|
let results = [];
|
|
24
19
|
try {
|
|
25
20
|
const { ServiceContainer } = await import('../../../injection/ServiceContainer.js');
|
|
@@ -30,16 +25,16 @@ export async function handleSearch(watcher, fullPath, relativePath, searchLine)
|
|
|
30
25
|
searchEngine.ensureIndex();
|
|
31
26
|
const stats = searchEngine.getStats();
|
|
32
27
|
if (stats.totalDocuments === 0) {
|
|
33
|
-
console.log(` ⚠️ 知识库为空(索引 0 条记录),请先通过 asd setup / Dashboard 添加知识条目`);
|
|
34
28
|
} else {
|
|
35
|
-
console.log(` 📊 索引 ${stats.totalDocuments} 条知识`);
|
|
36
29
|
}
|
|
37
30
|
|
|
38
31
|
// auto (BM25+semantic 融合 + Ranking Pipeline) → keyword (SQL LIKE) 降级链
|
|
39
32
|
// Xcode/IDE 场景: 传递 generate intent,让排序器使用代码生成权重
|
|
40
33
|
try {
|
|
41
34
|
results = await searchEngine.search(query, {
|
|
42
|
-
limit: 10,
|
|
35
|
+
limit: 10,
|
|
36
|
+
mode: 'auto',
|
|
37
|
+
rank: true,
|
|
43
38
|
context: { intent: 'generate' },
|
|
44
39
|
});
|
|
45
40
|
// auto 零结果 → keyword (SQL LIKE) 兆底
|
|
@@ -49,11 +44,12 @@ export async function handleSearch(watcher, fullPath, relativePath, searchLine)
|
|
|
49
44
|
} catch {
|
|
50
45
|
try {
|
|
51
46
|
results = await searchEngine.search(query, { limit: 10, mode: 'keyword' });
|
|
52
|
-
} catch {
|
|
47
|
+
} catch {
|
|
48
|
+
/* 全部失败 */
|
|
49
|
+
}
|
|
53
50
|
}
|
|
54
51
|
} catch (err) {
|
|
55
52
|
console.warn(` ⚠️ 搜索失败: ${err.message}`);
|
|
56
|
-
console.log(` ℹ️ 未找到「${query}」的相关结果`);
|
|
57
53
|
watcher._notify(`搜索「${query}」失败: ${err.message}`);
|
|
58
54
|
return;
|
|
59
55
|
}
|
|
@@ -68,24 +64,19 @@ export async function handleSearch(watcher, fullPath, relativePath, searchLine)
|
|
|
68
64
|
});
|
|
69
65
|
|
|
70
66
|
if (items.length === 0) {
|
|
71
|
-
console.log(` ℹ️ 未找到「${query}」的相关结果`);
|
|
72
67
|
watcher._notify(`未找到「${query}」的相关结果`);
|
|
73
68
|
return;
|
|
74
69
|
}
|
|
75
70
|
|
|
76
|
-
console.log(` 📋 找到 ${items.length} 条结果`);
|
|
77
|
-
|
|
78
71
|
// NativeUI 交互选择
|
|
79
72
|
const NU = await import('../../../infrastructure/external/NativeUi.js');
|
|
80
73
|
const selectedIndex = NU.showCombinedWindow(items, query);
|
|
81
74
|
|
|
82
75
|
if (selectedIndex < 0 || selectedIndex >= items.length) {
|
|
83
|
-
console.log(` ℹ️ 用户取消选择`);
|
|
84
76
|
return;
|
|
85
77
|
}
|
|
86
78
|
|
|
87
79
|
const selected = items[selectedIndex];
|
|
88
|
-
console.log(` ✅ 选中: ${selected.title}`);
|
|
89
80
|
|
|
90
81
|
// 如果 selected 没有 moduleName,尝试从当前文件路径推断
|
|
91
82
|
if (!selected.moduleName && selected.headers && selected.headers.length > 0) {
|
|
@@ -96,14 +87,16 @@ export async function handleSearch(watcher, fullPath, relativePath, searchLine)
|
|
|
96
87
|
relativePath,
|
|
97
88
|
(await import('node:fs')).readFileSync(fullPath, 'utf8')
|
|
98
89
|
);
|
|
99
|
-
if (resolved
|
|
90
|
+
if (resolved?.moduleName) {
|
|
100
91
|
selected.moduleName = resolved.moduleName;
|
|
101
92
|
}
|
|
102
|
-
} catch {
|
|
93
|
+
} catch {
|
|
94
|
+
/* 解析失败不阻塞 */
|
|
95
|
+
}
|
|
103
96
|
}
|
|
104
97
|
|
|
105
|
-
//
|
|
106
|
-
const { insertCodeToXcode } = await import('
|
|
98
|
+
// Xcode 代码自动插入(osascript 跳转 + 粘贴)
|
|
99
|
+
const { insertCodeToXcode } = await import('../../../platform/ios/xcode/XcodeIntegration.js');
|
|
107
100
|
await insertCodeToXcode(watcher, fullPath, selected, searchLine);
|
|
108
101
|
}
|
|
109
102
|
|
|
@@ -111,81 +104,103 @@ export async function handleSearch(watcher, fullPath, relativePath, searchLine)
|
|
|
111
104
|
* 将搜索结果标准化为 NativeUI 可展示格式
|
|
112
105
|
*/
|
|
113
106
|
export function normalizeSearchResults(results) {
|
|
114
|
-
if (!results)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
107
|
+
if (!results) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
const arr = Array.isArray(results) ? results : results.items || [];
|
|
111
|
+
|
|
112
|
+
return arr
|
|
113
|
+
.map((r) => {
|
|
114
|
+
let code = '';
|
|
115
|
+
let explanation = '';
|
|
116
|
+
let headers = [];
|
|
117
|
+
if (r.content) {
|
|
118
|
+
try {
|
|
119
|
+
const content = typeof r.content === 'string' ? JSON.parse(r.content) : r.content;
|
|
120
|
+
// 注意: 不直接使用 content.markdown —— markdown 是完整文档,不是纯代码
|
|
121
|
+
code =
|
|
122
|
+
content.code ||
|
|
123
|
+
content.pattern ||
|
|
124
|
+
content.content ||
|
|
125
|
+
content.body ||
|
|
126
|
+
content.snippet ||
|
|
127
|
+
content.solution ||
|
|
128
|
+
content.example ||
|
|
129
|
+
'';
|
|
130
|
+
explanation =
|
|
131
|
+
content.rationale ||
|
|
132
|
+
content.description ||
|
|
133
|
+
content.summary ||
|
|
134
|
+
content.explanation ||
|
|
135
|
+
'';
|
|
136
|
+
if (Array.isArray(content.headers) && content.headers.length > 0) {
|
|
137
|
+
headers = content.headers;
|
|
138
|
+
}
|
|
139
|
+
// 如果主字段为空,尝试从 Markdown 内容提取代码块
|
|
140
|
+
if (!code && content.markdown) {
|
|
141
|
+
code = _extractCodeFromMarkdown(content.markdown);
|
|
142
|
+
// 若 markdown 中提取不出代码,且 explanation 为空,用 markdown 生成摘要
|
|
143
|
+
if (!code && !explanation) {
|
|
144
|
+
explanation = _stripMarkdownFormatting(content.markdown).substring(0, 500);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
// content 不是 JSON,可能是纯文本/代码 — 直接使用
|
|
149
|
+
if (typeof r.content === 'string' && r.content.length > 10) {
|
|
150
|
+
code = r.content.substring(0, 2000);
|
|
151
|
+
}
|
|
136
152
|
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
153
|
+
}
|
|
154
|
+
// 如果 Ranking Pipeline 已提取 code 字段,优先使用
|
|
155
|
+
if (!code && r.code && r.code.length > 5) {
|
|
156
|
+
code = r.code;
|
|
157
|
+
}
|
|
158
|
+
// V3: headers 是独立 JSON 列(字符串),优先解析
|
|
159
|
+
if (headers.length === 0 && r.headers) {
|
|
160
|
+
try {
|
|
161
|
+
const parsed = typeof r.headers === 'string' ? JSON.parse(r.headers) : r.headers;
|
|
162
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
163
|
+
headers = parsed;
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
/* ignore */
|
|
141
167
|
}
|
|
142
168
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const parsed = typeof r.headers === 'string' ? JSON.parse(r.headers) : r.headers;
|
|
152
|
-
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
153
|
-
headers = parsed;
|
|
169
|
+
// moduleName: 优先从独立列取
|
|
170
|
+
let moduleName = r.moduleName || null;
|
|
171
|
+
if (!moduleName && r.content) {
|
|
172
|
+
try {
|
|
173
|
+
const content = typeof r.content === 'string' ? JSON.parse(r.content) : r.content;
|
|
174
|
+
moduleName = content.moduleName || null;
|
|
175
|
+
} catch {
|
|
176
|
+
/* ignore */
|
|
154
177
|
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (!moduleName && r.content) {
|
|
160
|
-
try {
|
|
161
|
-
const content = typeof r.content === 'string' ? JSON.parse(r.content) : r.content;
|
|
162
|
-
moduleName = content.moduleName || null;
|
|
163
|
-
} catch { /* ignore */ }
|
|
164
|
-
}
|
|
165
|
-
if (!moduleName) {
|
|
166
|
-
moduleName = r.moduleName || null;
|
|
167
|
-
}
|
|
178
|
+
}
|
|
179
|
+
if (!moduleName) {
|
|
180
|
+
moduleName = r.moduleName || null;
|
|
181
|
+
}
|
|
168
182
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
183
|
+
// ── 从 code 中分离 #import / @import / import 行,归入 headers ──
|
|
184
|
+
const finalCode = code || r.code || r.description || r.trigger || '(无预览内容)';
|
|
185
|
+
const { cleanedCode, extractedHeaders } = _separateImportsFromCode(finalCode);
|
|
186
|
+
if (extractedHeaders.length > 0) {
|
|
187
|
+
for (const h of extractedHeaders) {
|
|
188
|
+
if (!headers.some((existing) => existing.trim() === h.trim())) {
|
|
189
|
+
headers.push(h);
|
|
190
|
+
}
|
|
176
191
|
}
|
|
177
192
|
}
|
|
178
|
-
}
|
|
179
193
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
194
|
+
return {
|
|
195
|
+
title: r.title || r.name || r.id || 'Recipe',
|
|
196
|
+
code: cleanedCode || '(无预览内容)',
|
|
197
|
+
explanation: explanation || r.summary || r.description || '',
|
|
198
|
+
headers,
|
|
199
|
+
moduleName,
|
|
200
|
+
trigger: r.trigger || r.completionKey || '',
|
|
201
|
+
};
|
|
202
|
+
})
|
|
203
|
+
.filter((item) => item.title);
|
|
189
204
|
}
|
|
190
205
|
|
|
191
206
|
/**
|
|
@@ -210,7 +225,9 @@ function _separateImportsFromCode(code) {
|
|
|
210
225
|
const trimmed = lines[i].trim();
|
|
211
226
|
if (!trimmed) {
|
|
212
227
|
// 空行:如果前面已有 import,继续扫描
|
|
213
|
-
if (lastImportIdx >= 0)
|
|
228
|
+
if (lastImportIdx >= 0) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
214
231
|
// 前面没 import,遇到前导空行也继续
|
|
215
232
|
continue;
|
|
216
233
|
}
|
|
@@ -236,3 +253,53 @@ function _separateImportsFromCode(code) {
|
|
|
236
253
|
const cleanedCode = remaining.join('\n').trim();
|
|
237
254
|
return { cleanedCode, extractedHeaders };
|
|
238
255
|
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 从 Markdown 文本中提取所有 fenced code blocks,合并为纯代码
|
|
259
|
+
*
|
|
260
|
+
* 支持 ```lang\n...\n``` 格式,提取多个代码块并用空行分隔。
|
|
261
|
+
* 如果没有找到代码块,返回空字符串。
|
|
262
|
+
*
|
|
263
|
+
* @param {string} md - Markdown 文本
|
|
264
|
+
* @returns {string} 提取出的纯代码,或空字符串
|
|
265
|
+
*/
|
|
266
|
+
function _extractCodeFromMarkdown(md) {
|
|
267
|
+
if (!md) {
|
|
268
|
+
return '';
|
|
269
|
+
}
|
|
270
|
+
const fencedRe = /```[\w]*\n([\s\S]*?)```/g;
|
|
271
|
+
const blocks = [];
|
|
272
|
+
let match;
|
|
273
|
+
while ((match = fencedRe.exec(md)) !== null) {
|
|
274
|
+
const block = match[1].trim();
|
|
275
|
+
if (block) {
|
|
276
|
+
blocks.push(block);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return blocks.join('\n\n');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 移除 Markdown 格式标记,返回纯文本摘要
|
|
284
|
+
*
|
|
285
|
+
* 用于在无法提取代码时,从 markdown 生成 explanation 文本。
|
|
286
|
+
*
|
|
287
|
+
* @param {string} md - Markdown 文本
|
|
288
|
+
* @returns {string} 纯文本
|
|
289
|
+
*/
|
|
290
|
+
function _stripMarkdownFormatting(md) {
|
|
291
|
+
if (!md) {
|
|
292
|
+
return '';
|
|
293
|
+
}
|
|
294
|
+
return md
|
|
295
|
+
.replace(/```[\w]*\n[\s\S]*?```/g, '') // 移除代码块
|
|
296
|
+
.replace(/^#{1,6}\s+/gm, '') // 移除标题标记
|
|
297
|
+
.replace(/\*\*([^*]+)\*\*/g, '$1') // 移除粗体
|
|
298
|
+
.replace(/\*([^*]+)\*/g, '$1') // 移除斜体
|
|
299
|
+
.replace(/`([^`]+)`/g, '$1') // 移除行内代码
|
|
300
|
+
.replace(/^\s*[-*+]\s+/gm, '') // 移除列表标记
|
|
301
|
+
.replace(/^\s*\d+\.\s+/gm, '') // 移除有序列表
|
|
302
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // 移除链接,保留文字
|
|
303
|
+
.replace(/\n{3,}/g, '\n\n') // 压缩多余空行
|
|
304
|
+
.trim();
|
|
305
|
+
}
|