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
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
* 落盘目录:isCandidate() → candidates/ | isActive()/deprecated → recipes/
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import { createHash } from 'node:crypto';
|
|
21
22
|
import fs from 'node:fs';
|
|
22
23
|
import path from 'node:path';
|
|
23
|
-
import {
|
|
24
|
-
import { RECIPES_DIR, CANDIDATES_DIR } from '../../infrastructure/config/Defaults.js';
|
|
24
|
+
import { CANDIDATES_DIR, RECIPES_DIR } from '../../infrastructure/config/Defaults.js';
|
|
25
25
|
import Logger from '../../infrastructure/logging/Logger.js';
|
|
26
26
|
import pathGuard from '../../shared/PathGuard.js';
|
|
27
27
|
|
|
@@ -30,13 +30,35 @@ import pathGuard from '../../shared/PathGuard.js';
|
|
|
30
30
|
* ═══════════════════════════════════════════════════════════ */
|
|
31
31
|
|
|
32
32
|
const SCALAR_FIELDS = [
|
|
33
|
-
'id',
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
33
|
+
'id',
|
|
34
|
+
'title',
|
|
35
|
+
'trigger',
|
|
36
|
+
'lifecycle',
|
|
37
|
+
'language',
|
|
38
|
+
'category',
|
|
39
|
+
'kind',
|
|
40
|
+
'knowledgeType',
|
|
41
|
+
'complexity',
|
|
42
|
+
'scope',
|
|
43
|
+
'difficulty',
|
|
44
|
+
'description',
|
|
45
|
+
'source',
|
|
46
|
+
'moduleName',
|
|
47
|
+
'topicHint',
|
|
48
|
+
'whenClause',
|
|
49
|
+
'doClause',
|
|
50
|
+
'dontClause',
|
|
51
|
+
'coreCode',
|
|
52
|
+
'createdBy',
|
|
53
|
+
'createdAt',
|
|
54
|
+
'updatedAt',
|
|
55
|
+
'publishedAt',
|
|
56
|
+
'publishedBy',
|
|
57
|
+
'reviewedBy',
|
|
58
|
+
'reviewedAt',
|
|
59
|
+
'rejectionReason',
|
|
60
|
+
'sourceFile',
|
|
61
|
+
'sourceCandidateId',
|
|
40
62
|
];
|
|
41
63
|
|
|
42
64
|
/* ═══════════════════════════════════════════════════════════
|
|
@@ -48,10 +70,10 @@ export class KnowledgeFileWriter {
|
|
|
48
70
|
* @param {string} projectRoot 项目根目录
|
|
49
71
|
*/
|
|
50
72
|
constructor(projectRoot) {
|
|
51
|
-
this.projectRoot
|
|
52
|
-
this.recipesDir
|
|
73
|
+
this.projectRoot = projectRoot;
|
|
74
|
+
this.recipesDir = path.join(projectRoot, RECIPES_DIR);
|
|
53
75
|
this.candidatesDir = path.join(projectRoot, CANDIDATES_DIR);
|
|
54
|
-
this.logger
|
|
76
|
+
this.logger = Logger.getInstance();
|
|
55
77
|
}
|
|
56
78
|
|
|
57
79
|
/* ═══ 序列化 ═══════════════════════════════════════════ */
|
|
@@ -74,20 +96,30 @@ export class KnowledgeFileWriter {
|
|
|
74
96
|
}
|
|
75
97
|
|
|
76
98
|
// ── 简单数组字段(行内 JSON)──
|
|
77
|
-
if (json.tags?.length)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (json.
|
|
81
|
-
|
|
99
|
+
if (json.tags?.length) {
|
|
100
|
+
lines.push(`tags: ${JSON.stringify(json.tags)}`);
|
|
101
|
+
}
|
|
102
|
+
if (json.headers?.length) {
|
|
103
|
+
lines.push(`headers: ${JSON.stringify(json.headers)}`);
|
|
104
|
+
}
|
|
105
|
+
if (json.headerPaths?.length) {
|
|
106
|
+
lines.push(`headerPaths: ${JSON.stringify(json.headerPaths)}`);
|
|
107
|
+
}
|
|
108
|
+
if (json.includeHeaders) {
|
|
109
|
+
lines.push(`includeHeaders: true`);
|
|
110
|
+
}
|
|
111
|
+
if (json.autoApprovable) {
|
|
112
|
+
lines.push(`autoApprovable: true`);
|
|
113
|
+
}
|
|
82
114
|
|
|
83
115
|
// ── JSON 值对象(_ 前缀,单行 JSON)──
|
|
84
116
|
const JSON_FIELDS = [
|
|
85
|
-
['_content',
|
|
86
|
-
['_relations',
|
|
87
|
-
['_constraints',
|
|
88
|
-
['_reasoning',
|
|
89
|
-
['_quality',
|
|
90
|
-
['_stats',
|
|
117
|
+
['_content', json.content],
|
|
118
|
+
['_relations', json.relations],
|
|
119
|
+
['_constraints', json.constraints],
|
|
120
|
+
['_reasoning', json.reasoning],
|
|
121
|
+
['_quality', json.quality],
|
|
122
|
+
['_stats', json.stats],
|
|
91
123
|
['_lifecycleHistory', json.lifecycleHistory],
|
|
92
124
|
];
|
|
93
125
|
for (const [key, val] of JSON_FIELDS) {
|
|
@@ -99,8 +131,12 @@ export class KnowledgeFileWriter {
|
|
|
99
131
|
}
|
|
100
132
|
}
|
|
101
133
|
}
|
|
102
|
-
if (json.agentNotes)
|
|
103
|
-
|
|
134
|
+
if (json.agentNotes) {
|
|
135
|
+
lines.push(`_agentNotes: ${JSON.stringify(json.agentNotes)}`);
|
|
136
|
+
}
|
|
137
|
+
if (json.aiInsight) {
|
|
138
|
+
lines.push(`_aiInsight: ${JSON.stringify(json.aiInsight)}`);
|
|
139
|
+
}
|
|
104
140
|
|
|
105
141
|
// _contentHash 占位(后续替换为真实 hash)
|
|
106
142
|
const hashPlaceholder = '__HASH_PLACEHOLDER__';
|
|
@@ -144,7 +180,7 @@ export class KnowledgeFileWriter {
|
|
|
144
180
|
}
|
|
145
181
|
|
|
146
182
|
if (c.pattern) {
|
|
147
|
-
lines.push(
|
|
183
|
+
lines.push(`\`\`\`${entry.language || 'text'}`);
|
|
148
184
|
lines.push(c.pattern);
|
|
149
185
|
lines.push('```');
|
|
150
186
|
lines.push('');
|
|
@@ -165,7 +201,7 @@ export class KnowledgeFileWriter {
|
|
|
165
201
|
lines.push(`${i + 1}. ${step}`);
|
|
166
202
|
} else {
|
|
167
203
|
const title = step.title || '步骤';
|
|
168
|
-
const desc
|
|
204
|
+
const desc = step.description || '';
|
|
169
205
|
lines.push(`${i + 1}. **${title}**: ${desc}`);
|
|
170
206
|
if (step.code) {
|
|
171
207
|
lines.push('');
|
|
@@ -265,7 +301,9 @@ export class KnowledgeFileWriter {
|
|
|
265
301
|
* @returns {boolean}
|
|
266
302
|
*/
|
|
267
303
|
remove(entry) {
|
|
268
|
-
if (!entry?.id)
|
|
304
|
+
if (!entry?.id) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
269
307
|
|
|
270
308
|
// 先尝试 sourceFile 精确删除
|
|
271
309
|
if (entry.sourceFile) {
|
|
@@ -308,9 +346,7 @@ export class KnowledgeFileWriter {
|
|
|
308
346
|
* @returns {string|null} 新的文件路径
|
|
309
347
|
*/
|
|
310
348
|
moveOnLifecycleChange(entry) {
|
|
311
|
-
const oldPath = entry.sourceFile
|
|
312
|
-
? path.join(this.projectRoot, entry.sourceFile)
|
|
313
|
-
: null;
|
|
349
|
+
const oldPath = entry.sourceFile ? path.join(this.projectRoot, entry.sourceFile) : null;
|
|
314
350
|
|
|
315
351
|
const { dir: newDir, filename } = this._resolveFilePath(entry);
|
|
316
352
|
const newPath = path.join(newDir, filename);
|
|
@@ -351,7 +387,9 @@ export class KnowledgeFileWriter {
|
|
|
351
387
|
* 清理旧文件(category 变更或 lifecycle 切换场景)
|
|
352
388
|
*/
|
|
353
389
|
_cleanupOldFile(entry, newPath) {
|
|
354
|
-
if (!entry.sourceFile)
|
|
390
|
+
if (!entry.sourceFile) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
355
393
|
const oldPath = path.join(this.projectRoot, entry.sourceFile);
|
|
356
394
|
if (oldPath !== newPath && fs.existsSync(oldPath)) {
|
|
357
395
|
fs.unlinkSync(oldPath);
|
|
@@ -368,14 +406,18 @@ export class KnowledgeFileWriter {
|
|
|
368
406
|
*/
|
|
369
407
|
_removeByIdScan(id) {
|
|
370
408
|
for (const baseDir of [this.candidatesDir, this.recipesDir]) {
|
|
371
|
-
if (!fs.existsSync(baseDir))
|
|
409
|
+
if (!fs.existsSync(baseDir)) {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
372
412
|
try {
|
|
373
413
|
const found = _walkAndRemoveById(baseDir, id);
|
|
374
414
|
if (found) {
|
|
375
415
|
this.logger.info('Knowledge entry file removed by id scan', { id });
|
|
376
416
|
return true;
|
|
377
417
|
}
|
|
378
|
-
} catch {
|
|
418
|
+
} catch {
|
|
419
|
+
/* ignore scan errors */
|
|
420
|
+
}
|
|
379
421
|
}
|
|
380
422
|
return false;
|
|
381
423
|
}
|
|
@@ -413,11 +455,15 @@ export function parseKnowledgeMarkdown(content, relPath) {
|
|
|
413
455
|
for (let i = 0; i < fmLines.length; i++) {
|
|
414
456
|
const line = fmLines[i];
|
|
415
457
|
const colonIdx = line.indexOf(':');
|
|
416
|
-
if (colonIdx <= 0)
|
|
458
|
+
if (colonIdx <= 0) {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
417
461
|
|
|
418
462
|
const key = line.slice(0, colonIdx).trim();
|
|
419
463
|
// 跳过带空格的非正常 key
|
|
420
|
-
if (/\s/.test(key))
|
|
464
|
+
if (/\s/.test(key)) {
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
421
467
|
|
|
422
468
|
let value = line.slice(colonIdx + 1).trim();
|
|
423
469
|
|
|
@@ -439,7 +485,9 @@ export function parseKnowledgeMarkdown(content, relPath) {
|
|
|
439
485
|
try {
|
|
440
486
|
data[dataKey] = JSON.parse(jsonStr);
|
|
441
487
|
break;
|
|
442
|
-
} catch {
|
|
488
|
+
} catch {
|
|
489
|
+
/* continue concatenating */
|
|
490
|
+
}
|
|
443
491
|
}
|
|
444
492
|
continue;
|
|
445
493
|
}
|
|
@@ -450,29 +498,60 @@ export function parseKnowledgeMarkdown(content, relPath) {
|
|
|
450
498
|
try {
|
|
451
499
|
data[dataKey] = JSON.parse(value);
|
|
452
500
|
continue;
|
|
453
|
-
} catch {
|
|
501
|
+
} catch {
|
|
502
|
+
/* fall through to plain string */
|
|
503
|
+
}
|
|
454
504
|
}
|
|
455
505
|
|
|
456
506
|
// 纯标量值(如 _content_hash: abc123)
|
|
457
|
-
if (/^\d+$/.test(value)) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
507
|
+
if (/^\d+$/.test(value)) {
|
|
508
|
+
data[dataKey] = parseInt(value, 10);
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
if (/^\d+\.\d+$/.test(value)) {
|
|
512
|
+
data[dataKey] = parseFloat(value);
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (value === 'true') {
|
|
516
|
+
data[dataKey] = true;
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (value === 'false') {
|
|
520
|
+
data[dataKey] = false;
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
461
523
|
data[dataKey] = value;
|
|
462
524
|
continue;
|
|
463
525
|
}
|
|
464
526
|
|
|
465
527
|
// ── 布尔 ──
|
|
466
|
-
if (value === 'true')
|
|
467
|
-
|
|
528
|
+
if (value === 'true') {
|
|
529
|
+
data[key] = true;
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
if (value === 'false') {
|
|
533
|
+
data[key] = false;
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
468
536
|
|
|
469
537
|
// ── 数值(整数或浮点) ──
|
|
470
|
-
if (/^\d+$/.test(value)) {
|
|
471
|
-
|
|
538
|
+
if (/^\d+$/.test(value)) {
|
|
539
|
+
data[key] = parseInt(value, 10);
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (/^\d+\.\d+$/.test(value)) {
|
|
543
|
+
data[key] = parseFloat(value);
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
472
546
|
|
|
473
547
|
// ── JSON 数组(非 _ 前缀) ──
|
|
474
548
|
if (value.startsWith('[')) {
|
|
475
|
-
try {
|
|
549
|
+
try {
|
|
550
|
+
data[key] = JSON.parse(value);
|
|
551
|
+
continue;
|
|
552
|
+
} catch {
|
|
553
|
+
/* fallthrough */
|
|
554
|
+
}
|
|
476
555
|
}
|
|
477
556
|
|
|
478
557
|
// ── 去引号 ──
|
|
@@ -500,8 +579,8 @@ export function parseKnowledgeMarkdown(content, relPath) {
|
|
|
500
579
|
|
|
501
580
|
// 如果 content 中没有 markdown 且 body 看起来是 Markdown 文章
|
|
502
581
|
if (!data.content?.markdown && !data.content?.pattern) {
|
|
503
|
-
const isMarkdownArticle =
|
|
504
|
-
(body.startsWith('#') && body.length > 200);
|
|
582
|
+
const isMarkdownArticle =
|
|
583
|
+
body.includes('— 项目特写') || (body.startsWith('#') && body.length > 200);
|
|
505
584
|
if (isMarkdownArticle) {
|
|
506
585
|
data.content = data.content || {};
|
|
507
586
|
data.content.markdown = body;
|
|
@@ -517,7 +596,9 @@ export function parseKnowledgeMarkdown(content, relPath) {
|
|
|
517
596
|
// ── fallback: title 从 body heading 提取 ──
|
|
518
597
|
if (!data.title) {
|
|
519
598
|
const headingMatch = content.match(/^##?\s+(.+)$/m);
|
|
520
|
-
if (headingMatch)
|
|
599
|
+
if (headingMatch) {
|
|
600
|
+
data.title = headingMatch[1].trim();
|
|
601
|
+
}
|
|
521
602
|
}
|
|
522
603
|
|
|
523
604
|
return data;
|
|
@@ -539,7 +620,9 @@ function _slugFilename(trigger, title, id) {
|
|
|
539
620
|
.replace(/^@/, '')
|
|
540
621
|
.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
541
622
|
.slice(0, 60);
|
|
542
|
-
if (clean.length >= 2)
|
|
623
|
+
if (clean.length >= 2) {
|
|
624
|
+
return `${clean}.md`;
|
|
625
|
+
}
|
|
543
626
|
}
|
|
544
627
|
|
|
545
628
|
// 其次用 title
|
|
@@ -551,7 +634,9 @@ function _slugFilename(trigger, title, id) {
|
|
|
551
634
|
.replace(/-{2,}/g, '-')
|
|
552
635
|
.replace(/^-|-$/g, '')
|
|
553
636
|
.slice(0, 60);
|
|
554
|
-
if (slug.length >= 3)
|
|
637
|
+
if (slug.length >= 3) {
|
|
638
|
+
return `${slug}.md`;
|
|
639
|
+
}
|
|
555
640
|
}
|
|
556
641
|
|
|
557
642
|
// 最后用 id 前 8 位
|
|
@@ -562,10 +647,12 @@ function _slugFilename(trigger, title, id) {
|
|
|
562
647
|
* 将 YAML 值安全序列化
|
|
563
648
|
*/
|
|
564
649
|
function _yamlValue(key, val) {
|
|
565
|
-
if (typeof val === 'number' || typeof val === 'boolean')
|
|
650
|
+
if (typeof val === 'number' || typeof val === 'boolean') {
|
|
651
|
+
return String(val);
|
|
652
|
+
}
|
|
566
653
|
const str = String(val);
|
|
567
654
|
// 含特殊字符时加引号
|
|
568
|
-
if (/[
|
|
655
|
+
if (/[:#[\]{}&*!|>'"`,@\n]/.test(str) || str.trim() !== str) {
|
|
569
656
|
return `"${str.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`;
|
|
570
657
|
}
|
|
571
658
|
return str;
|
|
@@ -576,11 +663,15 @@ function _yamlValue(key, val) {
|
|
|
576
663
|
* @returns {boolean}
|
|
577
664
|
*/
|
|
578
665
|
function _walkAndRemoveById(dir, id) {
|
|
579
|
-
if (!fs.existsSync(dir))
|
|
666
|
+
if (!fs.existsSync(dir)) {
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
580
669
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
581
670
|
const full = path.join(dir, entry.name);
|
|
582
671
|
if (entry.isDirectory()) {
|
|
583
|
-
if (_walkAndRemoveById(full, id))
|
|
672
|
+
if (_walkAndRemoveById(full, id)) {
|
|
673
|
+
return true;
|
|
674
|
+
}
|
|
584
675
|
} else if (entry.name.endsWith('.md') && !entry.name.startsWith('_')) {
|
|
585
676
|
const head = fs.readFileSync(full, 'utf8').slice(0, 500);
|
|
586
677
|
if (head.includes(`id: ${id}`)) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* KnowledgeGraphService - 知识图谱服务
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* 管理 Recipe 之间的关系(统一模型,包含所有知识类型)
|
|
5
5
|
* 支持关系查询、路径分析、PageRank 权重计算
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import Logger from '../../infrastructure/logging/Logger.js';
|
|
9
8
|
import { RelationType } from '../../domain/index.js';
|
|
9
|
+
import Logger from '../../infrastructure/logging/Logger.js';
|
|
10
10
|
|
|
11
11
|
// Re-export unified RelationType for backward compatibility
|
|
12
12
|
export { RelationType };
|
|
@@ -23,10 +23,22 @@ export class KnowledgeGraphService {
|
|
|
23
23
|
addEdge(fromId, fromType, toId, toType, relation, metadata = {}) {
|
|
24
24
|
const now = Math.floor(Date.now() / 1000);
|
|
25
25
|
try {
|
|
26
|
-
this.db
|
|
26
|
+
this.db
|
|
27
|
+
.prepare(`
|
|
27
28
|
INSERT OR REPLACE INTO knowledge_edges (from_id, from_type, to_id, to_type, relation, weight, metadata_json, created_at, updated_at)
|
|
28
29
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
29
|
-
`)
|
|
30
|
+
`)
|
|
31
|
+
.run(
|
|
32
|
+
fromId,
|
|
33
|
+
fromType,
|
|
34
|
+
toId,
|
|
35
|
+
toType,
|
|
36
|
+
relation,
|
|
37
|
+
metadata.weight || 1.0,
|
|
38
|
+
JSON.stringify(metadata),
|
|
39
|
+
now,
|
|
40
|
+
now
|
|
41
|
+
);
|
|
30
42
|
|
|
31
43
|
return { success: true };
|
|
32
44
|
} catch (error) {
|
|
@@ -39,22 +51,30 @@ export class KnowledgeGraphService {
|
|
|
39
51
|
* 删除关系边
|
|
40
52
|
*/
|
|
41
53
|
removeEdge(fromId, fromType, toId, toType, relation) {
|
|
42
|
-
this.db
|
|
54
|
+
this.db
|
|
55
|
+
.prepare(`
|
|
43
56
|
DELETE FROM knowledge_edges WHERE from_id = ? AND from_type = ? AND to_id = ? AND to_type = ? AND relation = ?
|
|
44
|
-
`)
|
|
57
|
+
`)
|
|
58
|
+
.run(fromId, fromType, toId, toType, relation);
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
/**
|
|
48
62
|
* 查询某个节点的所有关系
|
|
49
63
|
*/
|
|
50
64
|
getEdges(nodeId, nodeType, direction = 'both') {
|
|
51
|
-
const outgoing =
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
const outgoing =
|
|
66
|
+
direction === 'both' || direction === 'out'
|
|
67
|
+
? this.db
|
|
68
|
+
.prepare(`SELECT * FROM knowledge_edges WHERE from_id = ? AND from_type = ?`)
|
|
69
|
+
.all(nodeId, nodeType)
|
|
70
|
+
: [];
|
|
71
|
+
|
|
72
|
+
const incoming =
|
|
73
|
+
direction === 'both' || direction === 'in'
|
|
74
|
+
? this.db
|
|
75
|
+
.prepare(`SELECT * FROM knowledge_edges WHERE to_id = ? AND to_type = ?`)
|
|
76
|
+
.all(nodeId, nodeType)
|
|
77
|
+
: [];
|
|
58
78
|
|
|
59
79
|
return {
|
|
60
80
|
outgoing: outgoing.map(this._mapEdge),
|
|
@@ -66,11 +86,13 @@ export class KnowledgeGraphService {
|
|
|
66
86
|
* 查询指定关系类型的连接
|
|
67
87
|
*/
|
|
68
88
|
getRelated(nodeId, nodeType, relation) {
|
|
69
|
-
const rows = this.db
|
|
89
|
+
const rows = this.db
|
|
90
|
+
.prepare(`
|
|
70
91
|
SELECT * FROM knowledge_edges WHERE from_id = ? AND from_type = ? AND relation = ?
|
|
71
92
|
UNION ALL
|
|
72
93
|
SELECT * FROM knowledge_edges WHERE to_id = ? AND to_type = ? AND relation = ?
|
|
73
|
-
`)
|
|
94
|
+
`)
|
|
95
|
+
.all(nodeId, nodeType, relation, nodeId, nodeType, relation);
|
|
74
96
|
|
|
75
97
|
return rows.map(this._mapEdge);
|
|
76
98
|
}
|
|
@@ -84,19 +106,32 @@ export class KnowledgeGraphService {
|
|
|
84
106
|
|
|
85
107
|
while (queue.length > 0) {
|
|
86
108
|
const { id, type, path } = queue.shift();
|
|
87
|
-
|
|
88
|
-
if (path.length >= maxDepth)
|
|
89
|
-
|
|
109
|
+
|
|
110
|
+
if (path.length >= maxDepth) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
90
114
|
const key = `${type}:${id}`;
|
|
91
|
-
if (visited.has(key))
|
|
115
|
+
if (visited.has(key)) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
92
118
|
visited.add(key);
|
|
93
119
|
|
|
94
|
-
const neighbors = this.db
|
|
120
|
+
const neighbors = this.db
|
|
121
|
+
.prepare(`
|
|
95
122
|
SELECT to_id, to_type, relation, weight FROM knowledge_edges WHERE from_id = ? AND from_type = ?
|
|
96
|
-
`)
|
|
123
|
+
`)
|
|
124
|
+
.all(id, type);
|
|
97
125
|
|
|
98
126
|
for (const neighbor of neighbors) {
|
|
99
|
-
const newPath = [
|
|
127
|
+
const newPath = [
|
|
128
|
+
...path,
|
|
129
|
+
{
|
|
130
|
+
from: { id, type },
|
|
131
|
+
to: { id: neighbor.to_id, type: neighbor.to_type },
|
|
132
|
+
relation: neighbor.relation,
|
|
133
|
+
},
|
|
134
|
+
];
|
|
100
135
|
|
|
101
136
|
if (neighbor.to_id === toId && neighbor.to_type === toType) {
|
|
102
137
|
return { found: true, path: newPath, depth: newPath.length };
|
|
@@ -118,12 +153,16 @@ export class KnowledgeGraphService {
|
|
|
118
153
|
|
|
119
154
|
while (queue.length > 0) {
|
|
120
155
|
const { id, type, depth } = queue.shift();
|
|
121
|
-
if (depth >= maxDepth)
|
|
156
|
+
if (depth >= maxDepth) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
122
159
|
|
|
123
|
-
const dependents = this.db
|
|
160
|
+
const dependents = this.db
|
|
161
|
+
.prepare(`
|
|
124
162
|
SELECT from_id, from_type, relation FROM knowledge_edges
|
|
125
163
|
WHERE to_id = ? AND to_type = ? AND relation IN ('requires', 'extends', 'enforces', 'depends_on', 'inherits', 'implements', 'calls', 'prerequisite')
|
|
126
|
-
`)
|
|
164
|
+
`)
|
|
165
|
+
.all(id, type);
|
|
127
166
|
|
|
128
167
|
for (const dep of dependents) {
|
|
129
168
|
const key = `${dep.from_type}:${dep.from_id}`;
|
|
@@ -149,20 +188,28 @@ export class KnowledgeGraphService {
|
|
|
149
188
|
* @param {string} [nodeType] 过滤节点类型(如 'recipe'),为空则返回全部
|
|
150
189
|
*/
|
|
151
190
|
getStats(nodeType) {
|
|
152
|
-
const typeFilter = nodeType
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
191
|
+
const typeFilter = nodeType
|
|
192
|
+
? ` WHERE from_type = '${nodeType}' AND to_type = '${nodeType}'`
|
|
193
|
+
: '';
|
|
194
|
+
const edgeCount = this.db
|
|
195
|
+
.prepare(`SELECT COUNT(*) as total FROM knowledge_edges${typeFilter}`)
|
|
196
|
+
.get();
|
|
197
|
+
const byRelation = this.db
|
|
198
|
+
.prepare(
|
|
199
|
+
`SELECT relation, COUNT(*) as count FROM knowledge_edges${typeFilter} GROUP BY relation`
|
|
200
|
+
)
|
|
201
|
+
.all();
|
|
202
|
+
const byType = this.db
|
|
203
|
+
.prepare(
|
|
204
|
+
`SELECT from_type as type, COUNT(DISTINCT from_id) as count FROM knowledge_edges${typeFilter} GROUP BY from_type
|
|
159
205
|
UNION
|
|
160
206
|
SELECT to_type as type, COUNT(DISTINCT to_id) as count FROM knowledge_edges${typeFilter} GROUP BY to_type`
|
|
161
|
-
|
|
207
|
+
)
|
|
208
|
+
.all();
|
|
162
209
|
|
|
163
210
|
return {
|
|
164
211
|
totalEdges: edgeCount.total,
|
|
165
|
-
byRelation: Object.fromEntries(byRelation.map(r => [r.relation, r.count])),
|
|
212
|
+
byRelation: Object.fromEntries(byRelation.map((r) => [r.relation, r.count])),
|
|
166
213
|
nodeTypes: byType,
|
|
167
214
|
};
|
|
168
215
|
}
|