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
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module LanguageService
|
|
3
|
+
* @description 统一语言服务 — 项目中唯一的语言映射与检测来源
|
|
4
|
+
*
|
|
5
|
+
* 所有文件扩展名→语言映射、扩展名→显示名、主语言推断都必须通过此服务。
|
|
6
|
+
* 禁止在业务代码中自建 langMap / _inferLang。
|
|
7
|
+
*
|
|
8
|
+
* ---
|
|
9
|
+
* 使用方式:
|
|
10
|
+
* import { LanguageService } from '../shared/LanguageService.js';
|
|
11
|
+
* const lang = LanguageService.inferLang('App.swift'); // 'swift'
|
|
12
|
+
* const display = LanguageService.displayName('swift'); // 'Swift'
|
|
13
|
+
* const primary = LanguageService.detectPrimary(langStats); // 'typescript'
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// ═══════════════════════════════════════════════════════════
|
|
17
|
+
// 1) 文件扩展名 → 规范化语言 ID
|
|
18
|
+
// ═══════════════════════════════════════════════════════════
|
|
19
|
+
|
|
20
|
+
/** @type {Readonly<Record<string, string>>} */
|
|
21
|
+
const EXT_TO_LANG = Object.freeze({
|
|
22
|
+
// Apple
|
|
23
|
+
'.swift': 'swift',
|
|
24
|
+
'.m': 'objectivec',
|
|
25
|
+
'.mm': 'objectivec',
|
|
26
|
+
'.h': 'objectivec', // C/ObjC 头文件默认归 objectivec
|
|
27
|
+
|
|
28
|
+
// C/C++
|
|
29
|
+
'.c': 'c',
|
|
30
|
+
'.cpp': 'cpp',
|
|
31
|
+
'.cc': 'cpp',
|
|
32
|
+
'.cxx': 'cpp',
|
|
33
|
+
'.hpp': 'cpp',
|
|
34
|
+
|
|
35
|
+
// JavaScript/TypeScript
|
|
36
|
+
'.js': 'javascript',
|
|
37
|
+
'.mjs': 'javascript',
|
|
38
|
+
'.cjs': 'javascript',
|
|
39
|
+
'.jsx': 'javascript',
|
|
40
|
+
'.ts': 'typescript',
|
|
41
|
+
'.tsx': 'typescript',
|
|
42
|
+
'.vue': 'javascript',
|
|
43
|
+
'.svelte': 'javascript',
|
|
44
|
+
|
|
45
|
+
// Python
|
|
46
|
+
'.py': 'python',
|
|
47
|
+
|
|
48
|
+
// JVM
|
|
49
|
+
'.java': 'java',
|
|
50
|
+
'.kt': 'kotlin',
|
|
51
|
+
'.kts': 'kotlin',
|
|
52
|
+
|
|
53
|
+
// Go / Rust / Ruby
|
|
54
|
+
'.go': 'go',
|
|
55
|
+
'.rs': 'rust',
|
|
56
|
+
'.rb': 'ruby',
|
|
57
|
+
|
|
58
|
+
// Dart / C#
|
|
59
|
+
'.dart': 'dart',
|
|
60
|
+
'.cs': 'csharp',
|
|
61
|
+
|
|
62
|
+
// Markup / Data (常用)
|
|
63
|
+
'.md': 'markdown',
|
|
64
|
+
'.json': 'json',
|
|
65
|
+
'.yaml': 'yaml',
|
|
66
|
+
'.yml': 'yaml',
|
|
67
|
+
'.toml': 'toml',
|
|
68
|
+
'.xml': 'xml',
|
|
69
|
+
'.plist': 'plist',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ═══════════════════════════════════════════════════════════
|
|
73
|
+
// 2) 裸扩展名(不带 dot)→ 规范化语言 ID
|
|
74
|
+
// 用于 langStats(bootstrap 按 extname('.').replace('.','') 做 key)
|
|
75
|
+
// ═══════════════════════════════════════════════════════════
|
|
76
|
+
|
|
77
|
+
/** @type {Readonly<Record<string, string>>} */
|
|
78
|
+
const BARE_EXT_TO_LANG = Object.freeze({
|
|
79
|
+
swift: 'swift',
|
|
80
|
+
m: 'objectivec',
|
|
81
|
+
mm: 'objectivec',
|
|
82
|
+
h: 'objectivec',
|
|
83
|
+
c: 'c',
|
|
84
|
+
cpp: 'cpp',
|
|
85
|
+
cc: 'cpp',
|
|
86
|
+
cxx: 'cpp',
|
|
87
|
+
hpp: 'cpp',
|
|
88
|
+
js: 'javascript',
|
|
89
|
+
mjs: 'javascript',
|
|
90
|
+
cjs: 'javascript',
|
|
91
|
+
jsx: 'javascript',
|
|
92
|
+
ts: 'typescript',
|
|
93
|
+
tsx: 'typescript',
|
|
94
|
+
vue: 'javascript',
|
|
95
|
+
svelte: 'javascript',
|
|
96
|
+
py: 'python',
|
|
97
|
+
java: 'java',
|
|
98
|
+
kt: 'kotlin',
|
|
99
|
+
kts: 'kotlin',
|
|
100
|
+
go: 'go',
|
|
101
|
+
rs: 'rust',
|
|
102
|
+
rb: 'ruby',
|
|
103
|
+
dart: 'dart',
|
|
104
|
+
cs: 'csharp',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ═══════════════════════════════════════════════════════════
|
|
108
|
+
// 3) 语言 ID → 人类可读显示名
|
|
109
|
+
// ═══════════════════════════════════════════════════════════
|
|
110
|
+
|
|
111
|
+
/** @type {Readonly<Record<string, string>>} */
|
|
112
|
+
const LANG_DISPLAY_NAMES = Object.freeze({
|
|
113
|
+
swift: 'Swift',
|
|
114
|
+
objectivec: 'Objective-C',
|
|
115
|
+
c: 'C',
|
|
116
|
+
cpp: 'C++',
|
|
117
|
+
javascript: 'JavaScript',
|
|
118
|
+
typescript: 'TypeScript',
|
|
119
|
+
python: 'Python',
|
|
120
|
+
java: 'Java',
|
|
121
|
+
kotlin: 'Kotlin',
|
|
122
|
+
go: 'Go',
|
|
123
|
+
rust: 'Rust',
|
|
124
|
+
ruby: 'Ruby',
|
|
125
|
+
dart: 'Dart',
|
|
126
|
+
csharp: 'C#',
|
|
127
|
+
markdown: 'Markdown',
|
|
128
|
+
json: 'JSON',
|
|
129
|
+
yaml: 'YAML',
|
|
130
|
+
toml: 'TOML',
|
|
131
|
+
xml: 'XML',
|
|
132
|
+
plist: 'Property List',
|
|
133
|
+
unknown: 'Unknown',
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ═══════════════════════════════════════════════════════════
|
|
137
|
+
// 4) 已知可分析的编程语言集合
|
|
138
|
+
// ═══════════════════════════════════════════════════════════
|
|
139
|
+
|
|
140
|
+
/** @type {ReadonlySet<string>} */
|
|
141
|
+
const KNOWN_PROGRAMMING_LANGS = Object.freeze(
|
|
142
|
+
new Set([
|
|
143
|
+
'swift',
|
|
144
|
+
'objectivec',
|
|
145
|
+
'c',
|
|
146
|
+
'cpp',
|
|
147
|
+
'javascript',
|
|
148
|
+
'typescript',
|
|
149
|
+
'python',
|
|
150
|
+
'java',
|
|
151
|
+
'kotlin',
|
|
152
|
+
'go',
|
|
153
|
+
'rust',
|
|
154
|
+
'ruby',
|
|
155
|
+
'dart',
|
|
156
|
+
'csharp',
|
|
157
|
+
])
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// ═══════════════════════════════════════════════════════════
|
|
161
|
+
// 5) 源代码扩展名(Guard / 文件收集时使用)
|
|
162
|
+
// ═══════════════════════════════════════════════════════════
|
|
163
|
+
|
|
164
|
+
/** @type {ReadonlySet<string>} */
|
|
165
|
+
const SOURCE_CODE_EXTS = Object.freeze(
|
|
166
|
+
new Set([
|
|
167
|
+
'.m',
|
|
168
|
+
'.mm',
|
|
169
|
+
'.h',
|
|
170
|
+
'.swift',
|
|
171
|
+
'.c',
|
|
172
|
+
'.cpp',
|
|
173
|
+
'.cc',
|
|
174
|
+
'.cxx',
|
|
175
|
+
'.hpp',
|
|
176
|
+
'.js',
|
|
177
|
+
'.mjs',
|
|
178
|
+
'.cjs',
|
|
179
|
+
'.jsx',
|
|
180
|
+
'.ts',
|
|
181
|
+
'.tsx',
|
|
182
|
+
'.vue',
|
|
183
|
+
'.svelte',
|
|
184
|
+
'.py',
|
|
185
|
+
'.java',
|
|
186
|
+
'.kt',
|
|
187
|
+
'.kts',
|
|
188
|
+
'.go',
|
|
189
|
+
'.rs',
|
|
190
|
+
'.rb',
|
|
191
|
+
'.dart',
|
|
192
|
+
'.cs',
|
|
193
|
+
])
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// ═══════════════════════════════════════════════════════════
|
|
197
|
+
// LanguageService — 静态单例
|
|
198
|
+
// ═══════════════════════════════════════════════════════════
|
|
199
|
+
|
|
200
|
+
export class LanguageService {
|
|
201
|
+
// ─── 文件名 → 语言 ────────────────────────────
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 从文件名(或路径)推断规范化语言 ID
|
|
205
|
+
* @param {string} filename
|
|
206
|
+
* @returns {string} 语言 ID,如 'swift', 'typescript', 'python', 'unknown'
|
|
207
|
+
*/
|
|
208
|
+
static inferLang(filename) {
|
|
209
|
+
const dot = filename.lastIndexOf('.');
|
|
210
|
+
if (dot === -1) {
|
|
211
|
+
return 'unknown';
|
|
212
|
+
}
|
|
213
|
+
const ext = filename.slice(dot).toLowerCase();
|
|
214
|
+
return EXT_TO_LANG[ext] || 'unknown';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 从文件扩展名(带 dot)推断语言
|
|
219
|
+
* @param {string} ext - 如 '.ts', '.py'
|
|
220
|
+
* @returns {string}
|
|
221
|
+
*/
|
|
222
|
+
static langFromExt(ext) {
|
|
223
|
+
return EXT_TO_LANG[ext.toLowerCase()] || 'unknown';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ─── 显示名 ────────────────────────────────────
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* 语言 ID → 人类可读名称
|
|
230
|
+
* @param {string} langId
|
|
231
|
+
* @returns {string}
|
|
232
|
+
*/
|
|
233
|
+
static displayName(langId) {
|
|
234
|
+
return LANG_DISPLAY_NAMES[langId] || langId;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 文件扩展名(带 dot)→ 人类可读语言名
|
|
239
|
+
* @param {string} ext - 如 '.swift', '.ts'
|
|
240
|
+
* @returns {string}
|
|
241
|
+
*/
|
|
242
|
+
static displayNameFromExt(ext) {
|
|
243
|
+
const lang = EXT_TO_LANG[ext.toLowerCase()];
|
|
244
|
+
return lang ? LANG_DISPLAY_NAMES[lang] || lang : ext;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ─── 主语言检测 ────────────────────────────────
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* 从文件扩展名统计推断主语言
|
|
251
|
+
* @param {Record<string, number>} langStats - key = 裸扩展名 (如 'ts', 'm', 'py'),value = 文件数
|
|
252
|
+
* @returns {string} 主语言 ID
|
|
253
|
+
*/
|
|
254
|
+
static detectPrimary(langStats) {
|
|
255
|
+
if (!langStats || typeof langStats !== 'object') {
|
|
256
|
+
return 'unknown';
|
|
257
|
+
}
|
|
258
|
+
// 按规范化语言聚合计数(避免 ObjC 的 .h/.m/.mm 分散)
|
|
259
|
+
const aggregated = {};
|
|
260
|
+
for (const [ext, count] of Object.entries(langStats)) {
|
|
261
|
+
const lang = BARE_EXT_TO_LANG[ext] || ext;
|
|
262
|
+
aggregated[lang] = (aggregated[lang] || 0) + count;
|
|
263
|
+
}
|
|
264
|
+
let best = 'unknown',
|
|
265
|
+
bestCount = 0;
|
|
266
|
+
for (const [lang, count] of Object.entries(aggregated)) {
|
|
267
|
+
if (count > bestCount && KNOWN_PROGRAMMING_LANGS.has(lang)) {
|
|
268
|
+
best = lang;
|
|
269
|
+
bestCount = count;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return best;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 从文件扩展名统计返回所有检测到的编程语言(按文件数降序)
|
|
277
|
+
* @param {Record<string, number>} langStats
|
|
278
|
+
* @returns {Array<{ lang: string, count: number }>}
|
|
279
|
+
*/
|
|
280
|
+
static detectAll(langStats) {
|
|
281
|
+
if (!langStats || typeof langStats !== 'object') {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
const aggregated = {};
|
|
285
|
+
for (const [ext, count] of Object.entries(langStats)) {
|
|
286
|
+
const lang = BARE_EXT_TO_LANG[ext] || ext;
|
|
287
|
+
aggregated[lang] = (aggregated[lang] || 0) + count;
|
|
288
|
+
}
|
|
289
|
+
return Object.entries(aggregated)
|
|
290
|
+
.filter(([lang]) => KNOWN_PROGRAMMING_LANGS.has(lang))
|
|
291
|
+
.sort((a, b) => b[1] - a[1])
|
|
292
|
+
.map(([lang, count]) => ({ lang, count }));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* 多语言项目画像 — 返回主语言 + 次要语言 + 完整排序列表
|
|
297
|
+
*
|
|
298
|
+
* 与 detectPrimary 的区别:
|
|
299
|
+
* - detectPrimary 只给出一个语言,适用于需要单值场景
|
|
300
|
+
* - detectProfile 给出完整画像,适用于维度文案、AI prompt 等需要
|
|
301
|
+
* 感知多语言的场景
|
|
302
|
+
*
|
|
303
|
+
* @param {Record<string, number>} langStats - key=裸扩展名, value=文件数
|
|
304
|
+
* @param {object} [opts]
|
|
305
|
+
* @param {number} [opts.secondaryThreshold=0.1] 次要语言文件占比阈值(≥此比例才算次要语言)
|
|
306
|
+
* @returns {{ primary: string, secondary: string[], all: Array<{lang:string, count:number, ratio:number}>, totalFiles: number, isMultiLang: boolean }}
|
|
307
|
+
*/
|
|
308
|
+
static detectProfile(langStats, opts = {}) {
|
|
309
|
+
const threshold = opts.secondaryThreshold ?? 0.1;
|
|
310
|
+
const all = LanguageService.detectAll(langStats);
|
|
311
|
+
if (all.length === 0) {
|
|
312
|
+
return { primary: 'unknown', secondary: [], all: [], totalFiles: 0, isMultiLang: false };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const totalFiles = all.reduce((s, e) => s + e.count, 0);
|
|
316
|
+
const enriched = all.map((e) => ({ ...e, ratio: e.count / totalFiles }));
|
|
317
|
+
const primary = enriched[0].lang;
|
|
318
|
+
const secondary = enriched
|
|
319
|
+
.slice(1)
|
|
320
|
+
.filter((e) => e.ratio >= threshold)
|
|
321
|
+
.map((e) => e.lang);
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
primary,
|
|
325
|
+
secondary,
|
|
326
|
+
all: enriched,
|
|
327
|
+
totalFiles,
|
|
328
|
+
isMultiLang: secondary.length > 0,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ─── 查询方法 ─────────────────────────────────
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* 该语言 ID 是否是已知编程语言
|
|
336
|
+
* @param {string} langId
|
|
337
|
+
* @returns {boolean}
|
|
338
|
+
*/
|
|
339
|
+
static isKnownLang(langId) {
|
|
340
|
+
return KNOWN_PROGRAMMING_LANGS.has(langId);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* 该扩展名是否为源代码文件
|
|
345
|
+
* @param {string} ext - 带 dot,如 '.ts'
|
|
346
|
+
* @returns {boolean}
|
|
347
|
+
*/
|
|
348
|
+
static isSourceExt(ext) {
|
|
349
|
+
return SOURCE_CODE_EXTS.has(ext.toLowerCase());
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* 获取所有源代码扩展名(不可变)
|
|
354
|
+
* @returns {ReadonlySet<string>}
|
|
355
|
+
*/
|
|
356
|
+
static get sourceExts() {
|
|
357
|
+
return SOURCE_CODE_EXTS;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* 获取所有已知编程语言 ID(不可变)
|
|
362
|
+
* @returns {ReadonlySet<string>}
|
|
363
|
+
*/
|
|
364
|
+
static get knownLangs() {
|
|
365
|
+
return KNOWN_PROGRAMMING_LANGS;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* 获取完整的 ext→lang 映射(不可变)
|
|
370
|
+
* @returns {Readonly<Record<string, string>>}
|
|
371
|
+
*/
|
|
372
|
+
static get extToLangMap() {
|
|
373
|
+
return EXT_TO_LANG;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* 获取完整的 bareExt→lang 映射(不可变)
|
|
378
|
+
* @returns {Readonly<Record<string, string>>}
|
|
379
|
+
*/
|
|
380
|
+
static get bareExtToLangMap() {
|
|
381
|
+
return BARE_EXT_TO_LANG;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* 根据语言 ID 返回主扩展名(带 dot)
|
|
386
|
+
* @param {string} langId - 如 'go', 'swift', 'python'
|
|
387
|
+
* @returns {string|null} - 如 '.go', '.swift', '.py';未知返回 null
|
|
388
|
+
*/
|
|
389
|
+
static extForLang(langId) {
|
|
390
|
+
if (!langId) return null;
|
|
391
|
+
const lower = langId.toLowerCase();
|
|
392
|
+
for (const [ext, lang] of Object.entries(EXT_TO_LANG)) {
|
|
393
|
+
if (lang === lower) return ext;
|
|
394
|
+
}
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export default LanguageService;
|
package/lib/shared/PathGuard.js
CHANGED
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
* - 错误不静默:越界写操作抛出 PathGuardError
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
import path from 'node:path';
|
|
30
29
|
import fs from 'node:fs';
|
|
30
|
+
import path from 'node:path';
|
|
31
31
|
|
|
32
32
|
export class PathGuardError extends Error {
|
|
33
33
|
/**
|
|
@@ -41,8 +41,8 @@ export class PathGuardError extends Error {
|
|
|
41
41
|
: `[PathGuard] 写入路径越界: "${targetPath}" 不在允许范围内。`;
|
|
42
42
|
super(
|
|
43
43
|
msg +
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
`\n projectRoot: ${projectRoot}` +
|
|
45
|
+
`\n 提示: 检查 process.cwd() 或 projectRoot 配置是否正确`
|
|
46
46
|
);
|
|
47
47
|
this.name = 'PathGuardError';
|
|
48
48
|
this.targetPath = targetPath;
|
|
@@ -55,19 +55,16 @@ export class PathGuardError extends Error {
|
|
|
55
55
|
* 注意:这是相对于 projectRoot 的前缀列表
|
|
56
56
|
*/
|
|
57
57
|
const PROJECT_WRITE_SCOPE_PREFIXES = [
|
|
58
|
-
'.autosnippet',
|
|
59
|
-
'.cursor',
|
|
60
|
-
'.vscode',
|
|
61
|
-
'.github',
|
|
58
|
+
'.autosnippet', // 运行时 DB、记忆、对话、信号快照
|
|
59
|
+
'.cursor', // Cursor IDE 集成
|
|
60
|
+
'.vscode', // VSCode 集成
|
|
61
|
+
'.github', // Copilot instructions
|
|
62
62
|
];
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* 项目根目录下允许直接写入的文件(非目录前缀匹配)
|
|
66
66
|
*/
|
|
67
|
-
const PROJECT_ROOT_WRITABLE_FILES = [
|
|
68
|
-
'.gitignore',
|
|
69
|
-
'.env',
|
|
70
|
-
];
|
|
67
|
+
const PROJECT_ROOT_WRITABLE_FILES = ['.gitignore', '.env'];
|
|
71
68
|
|
|
72
69
|
class PathGuard {
|
|
73
70
|
/** @type {string|null} 项目根目录(绝对路径) */
|
|
@@ -85,8 +82,6 @@ class PathGuard {
|
|
|
85
82
|
/** @type {boolean} 是否已配置 */
|
|
86
83
|
#configured = false;
|
|
87
84
|
|
|
88
|
-
constructor() {}
|
|
89
|
-
|
|
90
85
|
/**
|
|
91
86
|
* 配置 PathGuard(每个进程执行一次)
|
|
92
87
|
* @param {object} opts
|
|
@@ -102,7 +97,7 @@ class PathGuard {
|
|
|
102
97
|
|
|
103
98
|
this.#projectRoot = path.resolve(projectRoot);
|
|
104
99
|
this.#packageRoot = packageRoot ? path.resolve(packageRoot) : null;
|
|
105
|
-
this.#knowledgeBaseDir = knowledgeBaseDir || null;
|
|
100
|
+
this.#knowledgeBaseDir = knowledgeBaseDir || null; // 延迟解析
|
|
106
101
|
|
|
107
102
|
// 默认白名单:Xcode snippets 目录、全局缓存(cache 子目录,不含整个 ~/.autosnippet)
|
|
108
103
|
const HOME = process.env.HOME || process.env.USERPROFILE || '';
|
|
@@ -148,7 +143,9 @@ class PathGuard {
|
|
|
148
143
|
* @throws {PathGuardError}
|
|
149
144
|
*/
|
|
150
145
|
assertSafe(targetPath) {
|
|
151
|
-
if (!this.#configured)
|
|
146
|
+
if (!this.#configured) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
152
149
|
|
|
153
150
|
if (!targetPath || typeof targetPath !== 'string') {
|
|
154
151
|
throw new PathGuardError(String(targetPath), this.#projectRoot);
|
|
@@ -157,14 +154,20 @@ class PathGuard {
|
|
|
157
154
|
const resolved = path.resolve(targetPath);
|
|
158
155
|
|
|
159
156
|
// 1. 项目目录内 — 允许
|
|
160
|
-
if (this.#isUnder(resolved, this.#projectRoot))
|
|
157
|
+
if (this.#isUnder(resolved, this.#projectRoot)) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
161
160
|
|
|
162
161
|
// 2. AutoSnippet 包自身目录内(logs/ 等)— 允许
|
|
163
|
-
if (this.#packageRoot && this.#isUnder(resolved, this.#packageRoot))
|
|
162
|
+
if (this.#packageRoot && this.#isUnder(resolved, this.#packageRoot)) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
164
165
|
|
|
165
166
|
// 3. 白名单目录 — 允许
|
|
166
167
|
for (const allowed of this.#allowList) {
|
|
167
|
-
if (this.#isUnder(resolved, allowed))
|
|
168
|
+
if (this.#isUnder(resolved, allowed)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
168
171
|
}
|
|
169
172
|
|
|
170
173
|
// 越界
|
|
@@ -179,7 +182,9 @@ class PathGuard {
|
|
|
179
182
|
* @throws {PathGuardError}
|
|
180
183
|
*/
|
|
181
184
|
assertProjectWriteSafe(targetPath) {
|
|
182
|
-
if (!this.#configured)
|
|
185
|
+
if (!this.#configured) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
183
188
|
|
|
184
189
|
// 先做边界检查
|
|
185
190
|
this.assertSafe(targetPath);
|
|
@@ -187,7 +192,9 @@ class PathGuard {
|
|
|
187
192
|
const resolved = path.resolve(targetPath);
|
|
188
193
|
|
|
189
194
|
// 如果不在 projectRoot 内(在白名单/packageRoot 中),跳过项目内检查
|
|
190
|
-
if (!this.#isUnder(resolved, this.#projectRoot))
|
|
195
|
+
if (!this.#isUnder(resolved, this.#projectRoot)) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
191
198
|
|
|
192
199
|
// 计算相对于 projectRoot 的路径
|
|
193
200
|
const relative = path.relative(this.#projectRoot, resolved);
|
|
@@ -195,21 +202,27 @@ class PathGuard {
|
|
|
195
202
|
|
|
196
203
|
// 检查是否在允许的前缀中
|
|
197
204
|
for (const prefix of PROJECT_WRITE_SCOPE_PREFIXES) {
|
|
198
|
-
if (firstSegment === prefix)
|
|
205
|
+
if (firstSegment === prefix) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
199
208
|
}
|
|
200
209
|
|
|
201
210
|
// 检查知识库目录(动态解析)
|
|
202
211
|
const kbDir = this.#resolveKnowledgeBaseDir();
|
|
203
|
-
if (kbDir && firstSegment === kbDir)
|
|
212
|
+
if (kbDir && firstSegment === kbDir) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
204
215
|
|
|
205
216
|
// 检查根目录可写文件(如 .gitignore)
|
|
206
|
-
if (PROJECT_ROOT_WRITABLE_FILES.includes(relative))
|
|
217
|
+
if (PROJECT_ROOT_WRITABLE_FILES.includes(relative)) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
207
220
|
|
|
208
221
|
// 不在允许的写入范围内
|
|
209
222
|
throw new PathGuardError(
|
|
210
223
|
resolved,
|
|
211
224
|
this.#projectRoot,
|
|
212
|
-
`项目内写入范围受限: "${relative}" 不在允许的目录中(允许: ${[...PROJECT_WRITE_SCOPE_PREFIXES, kbDir || 'AutoSnippet'].join(', ')}
|
|
225
|
+
`项目内写入范围受限: "${relative}" 不在允许的目录中(允许: ${[...PROJECT_WRITE_SCOPE_PREFIXES, kbDir || 'AutoSnippet'].join(', ')})`
|
|
213
226
|
);
|
|
214
227
|
}
|
|
215
228
|
|
|
@@ -280,7 +293,9 @@ class PathGuard {
|
|
|
280
293
|
* 优先使用 configure 阶段传入的值,否则尝试运行时探测
|
|
281
294
|
*/
|
|
282
295
|
#resolveKnowledgeBaseDir() {
|
|
283
|
-
if (this.#knowledgeBaseDir)
|
|
296
|
+
if (this.#knowledgeBaseDir) {
|
|
297
|
+
return this.#knowledgeBaseDir;
|
|
298
|
+
}
|
|
284
299
|
|
|
285
300
|
// 运行时探测: 查找包含 AutoSnippet.boxspec.json 的子目录
|
|
286
301
|
try {
|
|
@@ -293,7 +308,9 @@ class PathGuard {
|
|
|
293
308
|
}
|
|
294
309
|
}
|
|
295
310
|
}
|
|
296
|
-
} catch {
|
|
311
|
+
} catch {
|
|
312
|
+
/* ignore */
|
|
313
|
+
}
|
|
297
314
|
|
|
298
315
|
// 默认
|
|
299
316
|
return 'AutoSnippet';
|
|
@@ -303,8 +320,8 @@ class PathGuard {
|
|
|
303
320
|
/**
|
|
304
321
|
* 延迟加载 fs(避免循环依赖)
|
|
305
322
|
*/
|
|
306
|
-
function
|
|
307
|
-
//
|
|
323
|
+
function _await_fs() {
|
|
324
|
+
// biome-ignore lint/security/noGlobalEval: intentional CJS require fallback for avoiding circular deps
|
|
308
325
|
return eval("require('fs')");
|
|
309
326
|
}
|
|
310
327
|
|
|
@@ -7,7 +7,18 @@
|
|
|
7
7
|
* @returns {{ ready: boolean, missing: string[], suggestions: string[] }}
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
import { LanguageService } from './LanguageService.js';
|
|
11
|
+
|
|
12
|
+
const STANDARD_CATEGORIES = [
|
|
13
|
+
'View',
|
|
14
|
+
'Service',
|
|
15
|
+
'Tool',
|
|
16
|
+
'Model',
|
|
17
|
+
'Network',
|
|
18
|
+
'Storage',
|
|
19
|
+
'UI',
|
|
20
|
+
'Utility',
|
|
21
|
+
];
|
|
11
22
|
|
|
12
23
|
/**
|
|
13
24
|
* Bootstrap 等特殊来源使用的 category 白名单 —— 这些 category
|
|
@@ -28,12 +39,49 @@ export function checkRecipeReadiness(item) {
|
|
|
28
39
|
const missing = [];
|
|
29
40
|
const suggestions = [];
|
|
30
41
|
|
|
31
|
-
// ──
|
|
42
|
+
// ── 核心必填 ──
|
|
43
|
+
if (!item.title || !String(item.title).trim()) {
|
|
44
|
+
missing.push('title');
|
|
45
|
+
suggestions.push('title 必须非空(中文简短标题 ≤20 字)');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// content 有效性(pattern/markdown/rationale 至少有实质内容)
|
|
49
|
+
const hasCode = !!(item.code || item.content?.pattern || item.content?.markdown);
|
|
50
|
+
if (!hasCode) {
|
|
51
|
+
missing.push('content');
|
|
52
|
+
suggestions.push('content 需包含 pattern 或 markdown(代码片段或正文)');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!item.content?.rationale && !item.rationale) {
|
|
56
|
+
missing.push('rationale');
|
|
57
|
+
suggestions.push('content.rationale 必须提供设计原理说明');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!item.language) {
|
|
61
|
+
missing.push('language');
|
|
62
|
+
suggestions.push('language 必须指定(如 swift/typescript/python/java/go 等)');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!item.kind) {
|
|
66
|
+
missing.push('kind');
|
|
67
|
+
suggestions.push('kind 必须为 rule/pattern/fact');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!item.doClause) {
|
|
71
|
+
missing.push('doClause');
|
|
72
|
+
suggestions.push('doClause 需为英文祈使句正向指令(≤60 tokens)');
|
|
73
|
+
}
|
|
74
|
+
|
|
32
75
|
if (!item.category) {
|
|
33
76
|
missing.push('category');
|
|
34
|
-
suggestions.push(
|
|
35
|
-
} else if (
|
|
36
|
-
|
|
77
|
+
suggestions.push(`category 必须为: ${STANDARD_CATEGORIES.join('/')}`);
|
|
78
|
+
} else if (
|
|
79
|
+
!STANDARD_CATEGORIES.includes(item.category) &&
|
|
80
|
+
!WHITELISTED_CATEGORIES.includes(item.category)
|
|
81
|
+
) {
|
|
82
|
+
suggestions.push(
|
|
83
|
+
`category "${item.category}" 非标准值,应为: ${STANDARD_CATEGORIES.join('/')}(bootstrap/knowledge 等特殊来源可忽略此建议)`
|
|
84
|
+
);
|
|
37
85
|
}
|
|
38
86
|
|
|
39
87
|
if (!item.trigger) {
|
|
@@ -49,12 +97,11 @@ export function checkRecipeReadiness(item) {
|
|
|
49
97
|
suggestions.push('请提供描述(≤100字)');
|
|
50
98
|
}
|
|
51
99
|
|
|
52
|
-
if (!Array.isArray(item.headers)
|
|
100
|
+
if (!Array.isArray(item.headers)) {
|
|
53
101
|
missing.push('headers');
|
|
54
|
-
suggestions.push('请提供完整 import
|
|
102
|
+
suggestions.push('请提供完整 import 语句数组,无 import 时传 []');
|
|
55
103
|
}
|
|
56
104
|
|
|
57
|
-
// ── 建议 ──
|
|
58
105
|
if (!item.usageGuide) {
|
|
59
106
|
missing.push('usageGuide');
|
|
60
107
|
suggestions.push('请提供使用指南(Markdown ### 章节格式)');
|
|
@@ -62,15 +109,28 @@ export function checkRecipeReadiness(item) {
|
|
|
62
109
|
|
|
63
110
|
if (!item.knowledgeType) {
|
|
64
111
|
missing.push('knowledgeType');
|
|
112
|
+
suggestions.push('knowledgeType 必须指定(如 code-pattern/architecture/best-practice)');
|
|
65
113
|
}
|
|
66
114
|
|
|
67
|
-
|
|
68
|
-
|
|
115
|
+
// reasoning 检查
|
|
116
|
+
if (!item.reasoning || typeof item.reasoning !== 'object') {
|
|
117
|
+
missing.push('reasoning');
|
|
118
|
+
suggestions.push('reasoning 必须包含 whyStandard + sources + confidence');
|
|
119
|
+
} else {
|
|
120
|
+
if (!item.reasoning.whyStandard?.trim()) {
|
|
121
|
+
missing.push('reasoning.whyStandard');
|
|
122
|
+
}
|
|
123
|
+
if (!Array.isArray(item.reasoning.sources) || item.reasoning.sources.length === 0) {
|
|
124
|
+
missing.push('reasoning.sources');
|
|
125
|
+
}
|
|
69
126
|
}
|
|
70
127
|
|
|
71
128
|
const lang = item.language?.toLowerCase();
|
|
72
|
-
|
|
73
|
-
|
|
129
|
+
// 使用 LanguageService 统一语言集,额外接受 'objc' 别名和 'markdown'
|
|
130
|
+
if (lang && !LanguageService.isKnownLang(lang) && lang !== 'objc' && lang !== 'markdown') {
|
|
131
|
+
suggestions.push(
|
|
132
|
+
`language "${item.language}" — 请使用标准语言标识 (swift/typescript/python/java/kotlin 等)`
|
|
133
|
+
);
|
|
74
134
|
}
|
|
75
135
|
|
|
76
136
|
return { ready: missing.length === 0, missing, suggestions };
|