autosnippet 3.0.0 → 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 +759 -243
- 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
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
|
|
21
21
|
import Logger from '../../infrastructure/logging/Logger.js';
|
|
22
22
|
|
|
23
|
-
const DEFAULT_WINDOW_MS
|
|
24
|
-
const DEFAULT_MAX_BATCH
|
|
25
|
-
const DEFAULT_DEDUPE_MS
|
|
23
|
+
const DEFAULT_WINDOW_MS = 5000; // 5 秒聚合窗口
|
|
24
|
+
const DEFAULT_MAX_BATCH = 50; // 单次 batch 最大事件数
|
|
25
|
+
const DEFAULT_DEDUPE_MS = 60_000; // 60 秒去重窗口
|
|
26
26
|
|
|
27
27
|
export class EventAggregator {
|
|
28
28
|
/** @type {Map<string, { events: any[], timer: ReturnType<typeof setTimeout> }>} */
|
|
@@ -63,7 +63,7 @@ export class EventAggregator {
|
|
|
63
63
|
// 去重检查
|
|
64
64
|
const dedupe = dedupeId || this.#hashEvent(key, event);
|
|
65
65
|
const lastSeen = this.#dedupeMap.get(dedupe);
|
|
66
|
-
if (lastSeen &&
|
|
66
|
+
if (lastSeen && Date.now() - lastSeen < this.#dedupeMs) {
|
|
67
67
|
this.#logger.debug(`[EventAggregator] dedup skip: ${key}/${dedupe}`);
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
@@ -83,7 +83,9 @@ export class EventAggregator {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// 重置窗口计时器
|
|
86
|
-
if (bucket.timer)
|
|
86
|
+
if (bucket.timer) {
|
|
87
|
+
clearTimeout(bucket.timer);
|
|
88
|
+
}
|
|
87
89
|
bucket.timer = setTimeout(() => this.#flush(key), this.#windowMs);
|
|
88
90
|
}
|
|
89
91
|
|
|
@@ -113,7 +115,9 @@ export class EventAggregator {
|
|
|
113
115
|
*/
|
|
114
116
|
destroy() {
|
|
115
117
|
for (const [, bucket] of this.#buckets) {
|
|
116
|
-
if (bucket.timer)
|
|
118
|
+
if (bucket.timer) {
|
|
119
|
+
clearTimeout(bucket.timer);
|
|
120
|
+
}
|
|
117
121
|
}
|
|
118
122
|
this.#buckets.clear();
|
|
119
123
|
this.#dedupeMap.clear();
|
|
@@ -125,7 +129,9 @@ export class EventAggregator {
|
|
|
125
129
|
*/
|
|
126
130
|
get pendingCount() {
|
|
127
131
|
let count = 0;
|
|
128
|
-
for (const [, bucket] of this.#buckets)
|
|
132
|
+
for (const [, bucket] of this.#buckets) {
|
|
133
|
+
count += bucket.events.length;
|
|
134
|
+
}
|
|
129
135
|
return count;
|
|
130
136
|
}
|
|
131
137
|
|
|
@@ -133,7 +139,9 @@ export class EventAggregator {
|
|
|
133
139
|
|
|
134
140
|
#flush(key) {
|
|
135
141
|
const bucket = this.#buckets.get(key);
|
|
136
|
-
if (!bucket || bucket.events.length === 0)
|
|
142
|
+
if (!bucket || bucket.events.length === 0) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
137
145
|
|
|
138
146
|
if (bucket.timer) {
|
|
139
147
|
clearTimeout(bucket.timer);
|
|
@@ -155,8 +163,9 @@ export class EventAggregator {
|
|
|
155
163
|
// 通知监听器
|
|
156
164
|
const listeners = this.#listeners.get('batch') || [];
|
|
157
165
|
for (const fn of listeners) {
|
|
158
|
-
try {
|
|
159
|
-
|
|
166
|
+
try {
|
|
167
|
+
fn(key, events);
|
|
168
|
+
} catch (err) {
|
|
160
169
|
this.#logger.warn(`[EventAggregator] listener error: ${err.message}`);
|
|
161
170
|
}
|
|
162
171
|
}
|
|
@@ -167,17 +176,25 @@ export class EventAggregator {
|
|
|
167
176
|
#hashEvent(key, event) {
|
|
168
177
|
// 简单 hash: key + 事件关键字段
|
|
169
178
|
const significant = { key };
|
|
170
|
-
if (event.filePath)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (event.
|
|
179
|
+
if (event.filePath) {
|
|
180
|
+
significant.f = event.filePath;
|
|
181
|
+
}
|
|
182
|
+
if (event.ruleName) {
|
|
183
|
+
significant.r = event.ruleName;
|
|
184
|
+
}
|
|
185
|
+
if (event.action) {
|
|
186
|
+
significant.a = event.action;
|
|
187
|
+
}
|
|
188
|
+
if (event.id) {
|
|
189
|
+
significant.i = event.id;
|
|
190
|
+
}
|
|
174
191
|
return JSON.stringify(significant);
|
|
175
192
|
}
|
|
176
193
|
|
|
177
194
|
#cleanupDedupe() {
|
|
178
195
|
const now = Date.now();
|
|
179
196
|
for (const [hash, ts] of this.#dedupeMap) {
|
|
180
|
-
if (
|
|
197
|
+
if (now - ts > this.#dedupeMs) {
|
|
181
198
|
this.#dedupeMap.delete(hash);
|
|
182
199
|
}
|
|
183
200
|
}
|
|
@@ -37,30 +37,30 @@
|
|
|
37
37
|
* new SignalCollector(opts) → instance.start() → ... → instance.stop()
|
|
38
38
|
*/
|
|
39
39
|
|
|
40
|
+
import { execSync } from 'node:child_process';
|
|
40
41
|
import fs from 'node:fs';
|
|
41
42
|
import path from 'node:path';
|
|
42
|
-
import { execSync } from 'node:child_process';
|
|
43
43
|
import Logger from '../../infrastructure/logging/Logger.js';
|
|
44
|
-
import { EventAggregator } from './EventAggregator.js';
|
|
45
44
|
import pathGuard from '../../shared/PathGuard.js';
|
|
45
|
+
import { EventAggregator } from './EventAggregator.js';
|
|
46
46
|
|
|
47
|
-
const DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
|
|
48
|
-
const MIN_INTERVAL_MS = 5 * 60 * 1000;
|
|
47
|
+
const DEFAULT_INTERVAL_MS = 60 * 60 * 1000; // 1 小时(初始值,AI 可动态调整)
|
|
48
|
+
const MIN_INTERVAL_MS = 5 * 60 * 1000; // 最短 5 分钟
|
|
49
49
|
const MAX_INTERVAL_MS = 24 * 60 * 60 * 1000; // 最长 24 小时
|
|
50
50
|
const SNAPSHOT_FILE = 'signal-snapshot.json';
|
|
51
51
|
|
|
52
52
|
export class SignalCollector {
|
|
53
53
|
#projectRoot;
|
|
54
54
|
#db;
|
|
55
|
-
#chatAgent;
|
|
56
|
-
#mode;
|
|
55
|
+
#chatAgent; // ChatAgent 实例 — AI 核心
|
|
56
|
+
#mode; // 'off' | 'suggest' | 'auto'
|
|
57
57
|
#intervalMs;
|
|
58
58
|
#timer = null;
|
|
59
59
|
#running = false;
|
|
60
60
|
#logger;
|
|
61
61
|
#snapshotPath;
|
|
62
62
|
#snapshot;
|
|
63
|
-
#onSuggestions;
|
|
63
|
+
#onSuggestions; // callback(suggestions[]) — 由外部注入(如 RealtimeService 推送)
|
|
64
64
|
/** @type {EventAggregator} 信号聚类引擎 */
|
|
65
65
|
#aggregator;
|
|
66
66
|
|
|
@@ -151,7 +151,9 @@ export class SignalCollector {
|
|
|
151
151
|
* @param {object} event — 事件数据
|
|
152
152
|
*/
|
|
153
153
|
pushEvent(key, event) {
|
|
154
|
-
if (this.#mode === 'off')
|
|
154
|
+
if (this.#mode === 'off') {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
155
157
|
this.#aggregator.push(key, event);
|
|
156
158
|
}
|
|
157
159
|
|
|
@@ -159,13 +161,21 @@ export class SignalCollector {
|
|
|
159
161
|
return this.#tick();
|
|
160
162
|
}
|
|
161
163
|
|
|
162
|
-
getSnapshot() {
|
|
163
|
-
|
|
164
|
+
getSnapshot() {
|
|
165
|
+
return { ...this.#snapshot };
|
|
166
|
+
}
|
|
167
|
+
getMode() {
|
|
168
|
+
return this.#mode;
|
|
169
|
+
}
|
|
164
170
|
|
|
165
171
|
/** 从 pendingSuggestions 中移除已创建的 Skill */
|
|
166
172
|
removePendingSuggestion(name) {
|
|
167
|
-
if (!this.#snapshot.pendingSuggestions?.length)
|
|
168
|
-
|
|
173
|
+
if (!this.#snapshot.pendingSuggestions?.length) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.#snapshot.pendingSuggestions = this.#snapshot.pendingSuggestions.filter(
|
|
177
|
+
(s) => s.name !== name
|
|
178
|
+
);
|
|
169
179
|
if (this.#snapshot.lastResult) {
|
|
170
180
|
this.#snapshot.lastResult.newSuggestions = this.#snapshot.pendingSuggestions.length;
|
|
171
181
|
}
|
|
@@ -173,10 +183,14 @@ export class SignalCollector {
|
|
|
173
183
|
}
|
|
174
184
|
|
|
175
185
|
setMode(mode) {
|
|
176
|
-
if (!['off', 'suggest', 'auto'].includes(mode))
|
|
186
|
+
if (!['off', 'suggest', 'auto'].includes(mode)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
177
189
|
this.#mode = mode;
|
|
178
190
|
this.#logger.info(`[SignalCollector] mode changed to ${mode}`);
|
|
179
|
-
if (mode === 'off')
|
|
191
|
+
if (mode === 'off') {
|
|
192
|
+
this.stop();
|
|
193
|
+
}
|
|
180
194
|
}
|
|
181
195
|
|
|
182
196
|
// ═══════════════════════════════════════════════════════
|
|
@@ -184,17 +198,19 @@ export class SignalCollector {
|
|
|
184
198
|
// ═══════════════════════════════════════════════════════
|
|
185
199
|
|
|
186
200
|
async #tick() {
|
|
187
|
-
if (this.#running)
|
|
201
|
+
if (this.#running) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
188
204
|
this.#running = true;
|
|
189
205
|
|
|
190
206
|
try {
|
|
191
207
|
// 1. 多维度收集信号
|
|
192
208
|
const signals = {
|
|
193
|
-
guard:
|
|
194
|
-
memory:
|
|
195
|
-
recipes:
|
|
196
|
-
candidates:
|
|
197
|
-
actions:
|
|
209
|
+
guard: this.#collectGuardSignals(),
|
|
210
|
+
memory: this.#collectMemorySignals(),
|
|
211
|
+
recipes: this.#collectRecipeSignals(),
|
|
212
|
+
candidates: this.#collectCandidateSignals(),
|
|
213
|
+
actions: this.#collectRecentActions(),
|
|
198
214
|
codeChanges: this.#collectCodeChangeSignals(),
|
|
199
215
|
};
|
|
200
216
|
|
|
@@ -203,7 +219,10 @@ export class SignalCollector {
|
|
|
203
219
|
|
|
204
220
|
// 3. 调用 ChatAgent AI 分析(source: 'system' 确保 Memory 隔离)
|
|
205
221
|
this.#logger.debug('[SignalCollector] invoking ChatAgent for analysis...');
|
|
206
|
-
const { reply, toolCalls } = await this.#chatAgent.execute(prompt, {
|
|
222
|
+
const { reply, toolCalls } = await this.#chatAgent.execute(prompt, {
|
|
223
|
+
history: [],
|
|
224
|
+
source: 'system',
|
|
225
|
+
});
|
|
207
226
|
|
|
208
227
|
// 4. 解析 AI 响应 — 使用 AiProvider.extractJSON 统一 structured output 解析
|
|
209
228
|
const parsed = this.#parseStructuredReply(reply);
|
|
@@ -211,7 +230,7 @@ export class SignalCollector {
|
|
|
211
230
|
|
|
212
231
|
// 5. 过滤已推送
|
|
213
232
|
const newSuggestions = suggestions.filter(
|
|
214
|
-
s => !this.#snapshot.pushedNames.includes(s.name)
|
|
233
|
+
(s) => !this.#snapshot.pushedNames.includes(s.name)
|
|
215
234
|
);
|
|
216
235
|
|
|
217
236
|
// 6. 更新快照
|
|
@@ -225,7 +244,7 @@ export class SignalCollector {
|
|
|
225
244
|
};
|
|
226
245
|
// 持久化 AI 生成的建议,供前端直接读取
|
|
227
246
|
if (newSuggestions.length > 0) {
|
|
228
|
-
this.#snapshot.pendingSuggestions = newSuggestions.map(s => ({
|
|
247
|
+
this.#snapshot.pendingSuggestions = newSuggestions.map((s) => ({
|
|
229
248
|
name: s.name,
|
|
230
249
|
description: s.description || s.reason || '',
|
|
231
250
|
rationale: s.rationale || s.reason || '',
|
|
@@ -244,17 +263,20 @@ export class SignalCollector {
|
|
|
244
263
|
|
|
245
264
|
// 推送建议
|
|
246
265
|
if (this.#onSuggestions) {
|
|
247
|
-
try {
|
|
248
|
-
|
|
266
|
+
try {
|
|
267
|
+
this.#onSuggestions(newSuggestions);
|
|
268
|
+
} catch (err) {
|
|
249
269
|
this.#logger.warn(`[SignalCollector] onSuggestions callback error: ${err.message}`);
|
|
250
270
|
}
|
|
251
271
|
}
|
|
252
272
|
|
|
253
273
|
// 检测 AI 是否在 auto 模式下自主调用了 create_skill
|
|
254
274
|
if (this.#mode === 'auto' && toolCalls?.length) {
|
|
255
|
-
const created = toolCalls.filter(tc => tc.tool === 'create_skill');
|
|
275
|
+
const created = toolCalls.filter((tc) => tc.tool === 'create_skill');
|
|
256
276
|
if (created.length > 0) {
|
|
257
|
-
if (!this.#snapshot.autoCreated)
|
|
277
|
+
if (!this.#snapshot.autoCreated) {
|
|
278
|
+
this.#snapshot.autoCreated = [];
|
|
279
|
+
}
|
|
258
280
|
for (const tc of created) {
|
|
259
281
|
this.#snapshot.autoCreated.push({
|
|
260
282
|
name: tc.params?.name || 'unknown',
|
|
@@ -274,7 +296,9 @@ export class SignalCollector {
|
|
|
274
296
|
if (parsed.nextIntervalMinutes && typeof parsed.nextIntervalMinutes === 'number') {
|
|
275
297
|
const aiMs = parsed.nextIntervalMinutes * 60 * 1000;
|
|
276
298
|
this.#intervalMs = Math.max(MIN_INTERVAL_MS, Math.min(aiMs, MAX_INTERVAL_MS));
|
|
277
|
-
this.#logger.info(
|
|
299
|
+
this.#logger.info(
|
|
300
|
+
`[SignalCollector] AI adjusted next interval to ${parsed.nextIntervalMinutes}min`
|
|
301
|
+
);
|
|
278
302
|
}
|
|
279
303
|
|
|
280
304
|
// 8. 持久化快照
|
|
@@ -295,7 +319,9 @@ export class SignalCollector {
|
|
|
295
319
|
}
|
|
296
320
|
|
|
297
321
|
#scheduleNext(delayMs) {
|
|
298
|
-
if (this.#mode === 'off')
|
|
322
|
+
if (this.#mode === 'off') {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
299
325
|
this.#timer = setTimeout(() => this.#tick(), delayMs);
|
|
300
326
|
}
|
|
301
327
|
|
|
@@ -305,10 +331,13 @@ export class SignalCollector {
|
|
|
305
331
|
|
|
306
332
|
#collectGuardSignals() {
|
|
307
333
|
try {
|
|
308
|
-
if (!this.#db)
|
|
334
|
+
if (!this.#db) {
|
|
335
|
+
return [];
|
|
336
|
+
}
|
|
309
337
|
// audit_logs 中 action='guard:check' + result='violation' 的记录
|
|
310
|
-
const rows = this.#db
|
|
311
|
-
|
|
338
|
+
const rows = this.#db
|
|
339
|
+
.prepare(
|
|
340
|
+
`SELECT json_extract(operation_data, '$.ruleName') as ruleName,
|
|
312
341
|
COUNT(*) as cnt,
|
|
313
342
|
MAX(timestamp) as last_at
|
|
314
343
|
FROM audit_logs
|
|
@@ -317,28 +346,45 @@ export class SignalCollector {
|
|
|
317
346
|
GROUP BY ruleName
|
|
318
347
|
HAVING cnt > 0
|
|
319
348
|
ORDER BY cnt DESC LIMIT 20`
|
|
320
|
-
|
|
349
|
+
)
|
|
350
|
+
.all();
|
|
321
351
|
return rows;
|
|
322
|
-
} catch {
|
|
352
|
+
} catch {
|
|
353
|
+
return [];
|
|
354
|
+
}
|
|
323
355
|
}
|
|
324
356
|
|
|
325
357
|
#collectMemorySignals() {
|
|
326
358
|
try {
|
|
327
359
|
const memoryFile = path.join(this.#projectRoot, '.autosnippet', 'memory.jsonl');
|
|
328
|
-
if (!fs.existsSync(memoryFile))
|
|
360
|
+
if (!fs.existsSync(memoryFile)) {
|
|
361
|
+
return [];
|
|
362
|
+
}
|
|
329
363
|
const lines = fs.readFileSync(memoryFile, 'utf-8').trim().split('\n');
|
|
330
|
-
return lines
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
364
|
+
return lines
|
|
365
|
+
.slice(-20)
|
|
366
|
+
.map((line) => {
|
|
367
|
+
try {
|
|
368
|
+
return JSON.parse(line);
|
|
369
|
+
} catch {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
})
|
|
373
|
+
.filter(Boolean);
|
|
374
|
+
} catch {
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
334
377
|
}
|
|
335
378
|
|
|
336
379
|
#collectRecipeSignals() {
|
|
337
380
|
try {
|
|
338
|
-
if (!this.#db)
|
|
381
|
+
if (!this.#db) {
|
|
382
|
+
return [];
|
|
383
|
+
}
|
|
339
384
|
// V3: knowledge_entries 统一表,统计字段在 stats/quality JSON 中
|
|
340
|
-
const rows = this.#db
|
|
341
|
-
|
|
385
|
+
const rows = this.#db
|
|
386
|
+
.prepare(
|
|
387
|
+
`SELECT id, title, knowledgeType, category, language,
|
|
342
388
|
json_extract(stats, '$.adoptions') as adoption_count,
|
|
343
389
|
json_extract(stats, '$.applications') as application_count,
|
|
344
390
|
json_extract(quality, '$.overall') as quality_overall,
|
|
@@ -346,40 +392,55 @@ export class SignalCollector {
|
|
|
346
392
|
FROM knowledge_entries
|
|
347
393
|
WHERE lifecycle = 'active'
|
|
348
394
|
ORDER BY updatedAt DESC LIMIT 30`
|
|
349
|
-
|
|
395
|
+
)
|
|
396
|
+
.all();
|
|
350
397
|
return rows;
|
|
351
|
-
} catch {
|
|
398
|
+
} catch {
|
|
399
|
+
return [];
|
|
400
|
+
}
|
|
352
401
|
}
|
|
353
402
|
|
|
354
403
|
#collectCandidateSignals() {
|
|
355
404
|
try {
|
|
356
|
-
if (!this.#db)
|
|
405
|
+
if (!this.#db) {
|
|
406
|
+
return [];
|
|
407
|
+
}
|
|
357
408
|
// V3: candidates 已合并到 knowledge_entries,lifecycle='pending' 即为候选
|
|
358
|
-
const rows = this.#db
|
|
359
|
-
|
|
409
|
+
const rows = this.#db
|
|
410
|
+
.prepare(
|
|
411
|
+
`SELECT id, source, lifecycle as status, language, category,
|
|
360
412
|
title, createdAt
|
|
361
413
|
FROM knowledge_entries WHERE lifecycle = 'pending'
|
|
362
414
|
ORDER BY createdAt DESC LIMIT 30`
|
|
363
|
-
|
|
415
|
+
)
|
|
416
|
+
.all();
|
|
364
417
|
return rows;
|
|
365
|
-
} catch {
|
|
418
|
+
} catch {
|
|
419
|
+
return [];
|
|
420
|
+
}
|
|
366
421
|
}
|
|
367
422
|
|
|
368
423
|
#collectRecentActions() {
|
|
369
424
|
try {
|
|
370
|
-
if (!this.#db)
|
|
425
|
+
if (!this.#db) {
|
|
426
|
+
return [];
|
|
427
|
+
}
|
|
371
428
|
// audit_logs.timestamp 是 INTEGER (epoch seconds)
|
|
372
429
|
const sinceStr = this.#snapshot.lastRun;
|
|
373
430
|
const sinceTs = sinceStr
|
|
374
431
|
? Math.floor(new Date(sinceStr).getTime() / 1000)
|
|
375
432
|
: Math.floor((Date.now() - 24 * 3600 * 1000) / 1000);
|
|
376
|
-
const rows = this.#db
|
|
377
|
-
|
|
433
|
+
const rows = this.#db
|
|
434
|
+
.prepare(
|
|
435
|
+
`SELECT actor, action, resource, result, timestamp
|
|
378
436
|
FROM audit_logs WHERE timestamp > ?
|
|
379
437
|
ORDER BY timestamp DESC LIMIT 50`
|
|
380
|
-
|
|
438
|
+
)
|
|
439
|
+
.all(sinceTs);
|
|
381
440
|
return rows;
|
|
382
|
-
} catch {
|
|
441
|
+
} catch {
|
|
442
|
+
return [];
|
|
443
|
+
}
|
|
383
444
|
}
|
|
384
445
|
|
|
385
446
|
#collectCodeChangeSignals() {
|
|
@@ -389,9 +450,13 @@ export class SignalCollector {
|
|
|
389
450
|
encoding: 'utf-8',
|
|
390
451
|
timeout: 5000,
|
|
391
452
|
}).trim();
|
|
392
|
-
if (!diff)
|
|
453
|
+
if (!diff) {
|
|
454
|
+
return [];
|
|
455
|
+
}
|
|
393
456
|
return diff.split('\n').slice(0, 20);
|
|
394
|
-
} catch {
|
|
457
|
+
} catch {
|
|
458
|
+
return [];
|
|
459
|
+
}
|
|
395
460
|
}
|
|
396
461
|
|
|
397
462
|
// ═══════════════════════════════════════════════════════
|
|
@@ -399,9 +464,10 @@ export class SignalCollector {
|
|
|
399
464
|
// ═══════════════════════════════════════════════════════
|
|
400
465
|
|
|
401
466
|
#buildAnalysisPrompt(signals) {
|
|
402
|
-
const modeInstruction =
|
|
403
|
-
|
|
404
|
-
|
|
467
|
+
const modeInstruction =
|
|
468
|
+
this.#mode === 'auto'
|
|
469
|
+
? '你处于 auto 模式:除了推荐之外,对于高优先级的建议,请直接调用 create_skill 工具自动创建 Skill。'
|
|
470
|
+
: '你处于 suggest 模式:只输出推荐,不要自动创建 Skill。';
|
|
405
471
|
|
|
406
472
|
return `你是 AutoSnippet 的后台行为分析 AI。你的任务是分析以下多维度信号,判断用户当前的开发状态,并给出 Skill 推荐建议。
|
|
407
473
|
|
|
@@ -459,14 +525,18 @@ ${JSON.stringify(signals.codeChanges, null, 2)}
|
|
|
459
525
|
*/
|
|
460
526
|
#parseStructuredReply(reply) {
|
|
461
527
|
const defaultResult = { suggestions: [], nextIntervalMinutes: null, summary: '' };
|
|
462
|
-
if (!reply)
|
|
528
|
+
if (!reply) {
|
|
529
|
+
return defaultResult;
|
|
530
|
+
}
|
|
463
531
|
|
|
464
532
|
try {
|
|
465
533
|
// 策略 1: 通过 AiProvider.extractJSON 统一解析
|
|
466
534
|
const aiProvider = this.#chatAgent?.aiProvider;
|
|
467
535
|
if (aiProvider && typeof aiProvider.extractJSON === 'function') {
|
|
468
536
|
const obj = aiProvider.extractJSON(reply, '{', '}');
|
|
469
|
-
if (obj && Array.isArray(obj.suggestions))
|
|
537
|
+
if (obj && Array.isArray(obj.suggestions)) {
|
|
538
|
+
return obj;
|
|
539
|
+
}
|
|
470
540
|
}
|
|
471
541
|
|
|
472
542
|
// 策略 2: 回退 — 从最后一行提取 JSON (兼容 prompt 指令)
|
|
@@ -476,8 +546,12 @@ ${JSON.stringify(signals.codeChanges, null, 2)}
|
|
|
476
546
|
if (line.startsWith('{') && line.endsWith('}')) {
|
|
477
547
|
try {
|
|
478
548
|
const obj = JSON.parse(line);
|
|
479
|
-
if (obj.suggestions)
|
|
480
|
-
|
|
549
|
+
if (obj.suggestions) {
|
|
550
|
+
return obj;
|
|
551
|
+
}
|
|
552
|
+
} catch {
|
|
553
|
+
/* 继续 */
|
|
554
|
+
}
|
|
481
555
|
}
|
|
482
556
|
}
|
|
483
557
|
} catch {
|
|
@@ -506,7 +580,9 @@ ${JSON.stringify(signals.codeChanges, null, 2)}
|
|
|
506
580
|
pendingSuggestions: Array.isArray(data.pendingSuggestions) ? data.pendingSuggestions : [],
|
|
507
581
|
};
|
|
508
582
|
}
|
|
509
|
-
} catch {
|
|
583
|
+
} catch {
|
|
584
|
+
/* corrupt — reset */
|
|
585
|
+
}
|
|
510
586
|
|
|
511
587
|
return {
|
|
512
588
|
lastRun: null,
|