autosnippet 2.9.0 → 2.11.0
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 +12 -12
- package/bin/cli.js +53 -40
- package/config/constitution.yaml +9 -2
- package/dashboard/dist/assets/{icons-CH-H9x0E.js → icons-D4IWpDIk.js} +105 -100
- package/dashboard/dist/assets/index-CWBNcF9z.css +1 -0
- package/dashboard/dist/assets/index-DHtzhbuG.js +120 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/cli/AiScanService.js +35 -36
- package/lib/cli/KnowledgeSyncService.js +345 -0
- package/lib/cli/SetupService.js +8 -26
- package/lib/cli/UpgradeService.js +28 -0
- package/lib/core/gateway/GatewayActionRegistry.js +48 -58
- package/lib/domain/index.js +16 -11
- package/lib/domain/knowledge/KnowledgeEntry.js +289 -0
- package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
- package/lib/domain/knowledge/Lifecycle.js +99 -0
- package/lib/domain/knowledge/index.js +27 -0
- package/lib/domain/knowledge/values/Constraints.js +128 -0
- package/lib/domain/knowledge/values/Content.js +69 -0
- package/lib/domain/knowledge/values/Quality.js +81 -0
- package/lib/domain/knowledge/values/Reasoning.js +70 -0
- package/lib/domain/knowledge/values/Relations.js +142 -0
- package/lib/domain/knowledge/values/Stats.js +72 -0
- package/lib/domain/knowledge/values/index.js +9 -0
- package/lib/external/ai/AiProvider.js +85 -11
- package/lib/external/mcp/McpServer.js +7 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +18 -2
- package/lib/external/mcp/handlers/bootstrap.js +116 -11
- package/lib/external/mcp/handlers/browse.js +76 -73
- package/lib/external/mcp/handlers/candidate.js +26 -275
- package/lib/external/mcp/handlers/guard.js +2 -0
- package/lib/external/mcp/handlers/knowledge.js +267 -0
- package/lib/external/mcp/handlers/structure.js +25 -23
- package/lib/external/mcp/handlers/system.js +10 -12
- package/lib/external/mcp/tools.js +134 -140
- package/lib/http/HttpServer.js +14 -8
- package/lib/http/routes/ai.js +4 -3
- package/lib/http/routes/extract.js +48 -4
- package/lib/http/routes/knowledge.js +246 -0
- package/lib/http/routes/search.js +12 -17
- package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
- package/lib/infrastructure/database/migrations/017_camelcase_knowledge_entries.js +107 -0
- package/lib/infrastructure/external/XcodeAutomation.js +187 -103
- package/lib/injection/ServiceContainer.js +69 -60
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +338 -0
- package/lib/service/automation/DirectiveDetector.js +2 -3
- package/lib/service/automation/FileWatcher.js +59 -28
- package/lib/service/automation/XcodeIntegration.js +931 -156
- package/lib/service/automation/handlers/AlinkHandler.js +5 -4
- package/lib/service/automation/handlers/CreateHandler.js +53 -19
- package/lib/service/automation/handlers/DraftHandler.js +1 -1
- package/lib/service/automation/handlers/GuardHandler.js +183 -20
- package/lib/service/automation/handlers/SearchHandler.js +25 -22
- package/lib/service/candidate/SimilarityService.js +2 -2
- package/lib/service/chat/AnalystAgent.js +9 -0
- package/lib/service/chat/CandidateGuardrail.js +22 -11
- package/lib/service/chat/ChatAgent.js +132 -54
- package/lib/service/chat/ContextWindow.js +5 -5
- package/lib/service/chat/HandoffProtocol.js +1 -0
- package/lib/service/chat/ProducerAgent.js +40 -13
- package/lib/service/chat/ReasoningLayer.js +854 -0
- package/lib/service/chat/ReasoningTrace.js +329 -0
- package/lib/service/chat/tools.js +308 -205
- package/lib/service/cursor/CursorDeliveryPipeline.js +279 -0
- package/lib/service/cursor/KnowledgeCompressor.js +87 -0
- package/lib/service/cursor/RulesGenerator.js +168 -0
- package/lib/service/cursor/SkillsSyncer.js +268 -0
- package/lib/service/cursor/TokenBudget.js +58 -0
- package/lib/service/cursor/TopicClassifier.js +141 -0
- package/lib/service/guard/GuardCheckEngine.js +99 -10
- package/lib/service/guard/GuardService.js +57 -46
- package/lib/service/knowledge/ConfidenceRouter.js +159 -0
- package/lib/service/knowledge/KnowledgeFileWriter.js +595 -0
- package/lib/service/knowledge/KnowledgeService.js +802 -0
- package/lib/service/recipe/RecipeParser.js +3 -12
- package/lib/service/search/SearchEngine.js +67 -22
- package/lib/service/skills/SignalCollector.js +14 -9
- package/lib/service/skills/SkillAdvisor.js +13 -11
- package/lib/service/snippet/SnippetFactory.js +5 -5
- package/lib/service/spm/SpmService.js +15 -48
- package/lib/shared/RecipeReadinessChecker.js +6 -11
- package/package.json +1 -1
- package/scripts/install-cursor-skill.js +0 -6
- package/scripts/migrate-md-to-knowledge.mjs +364 -0
- package/skills/autosnippet-analysis/SKILL.md +15 -7
- package/skills/autosnippet-candidates/SKILL.md +8 -8
- package/skills/autosnippet-coldstart/SKILL.md +8 -4
- package/skills/autosnippet-concepts/SKILL.md +7 -6
- package/skills/autosnippet-create/SKILL.md +13 -13
- package/skills/autosnippet-intent/SKILL.md +3 -2
- package/skills/autosnippet-lifecycle/SKILL.md +5 -5
- package/skills/autosnippet-recipes/SKILL.md +18 -6
- package/templates/constitution.yaml +1 -1
- package/templates/copilot-instructions.md +6 -6
- package/templates/recipes-setup/README.md +3 -3
- package/dashboard/dist/assets/index-CqJRvYRL.js +0 -197
- package/dashboard/dist/assets/index-DICm9PNa.css +0 -1
- package/lib/cli/CandidateSyncService.js +0 -261
- package/lib/cli/SyncService.js +0 -356
- package/lib/domain/candidate/Candidate.js +0 -196
- package/lib/domain/candidate/CandidateRepository.js +0 -107
- package/lib/domain/candidate/Reasoning.js +0 -52
- package/lib/domain/recipe/Recipe.js +0 -421
- package/lib/domain/recipe/RecipeRepository.js +0 -54
- package/lib/domain/types/CandidateStatus.js +0 -52
- package/lib/http/routes/candidates.js +0 -559
- package/lib/http/routes/recipes.js +0 -397
- package/lib/repository/candidate/CandidateRepository.impl.js +0 -230
- package/lib/repository/recipe/RecipeRepository.impl.js +0 -498
- package/lib/service/candidate/CandidateAggregator.js +0 -52
- package/lib/service/candidate/CandidateFileWriter.js +0 -383
- package/lib/service/candidate/CandidateService.js +0 -1001
- package/lib/service/recipe/RecipeFileWriter.js +0 -514
- package/lib/service/recipe/RecipeService.js +0 -786
- package/lib/service/recipe/RecipeStatsTracker.js +0 -148
|
@@ -17,133 +17,125 @@ const logger = Logger.getInstance();
|
|
|
17
17
|
* @param {import('../../injection/ServiceContainer.js').ServiceContainer} container
|
|
18
18
|
*/
|
|
19
19
|
export function registerGatewayActions(gateway, container) {
|
|
20
|
-
// ==========
|
|
20
|
+
// ========== Knowledge Actions (V3: replaces Candidate + Recipe) ==========
|
|
21
21
|
|
|
22
22
|
gateway.register('candidate:create', async (ctx) => {
|
|
23
|
-
const service = container.get('
|
|
24
|
-
return service.
|
|
23
|
+
const service = container.get('knowledgeService');
|
|
24
|
+
return service.create(ctx.data, {
|
|
25
25
|
userId: ctx.actor,
|
|
26
|
-
ip: ctx.data._ip,
|
|
27
|
-
userAgent: ctx.data._userAgent,
|
|
28
26
|
});
|
|
29
27
|
});
|
|
30
28
|
|
|
31
29
|
gateway.register('candidate:approve', async (ctx) => {
|
|
32
|
-
const service = container.get('
|
|
33
|
-
return service.
|
|
30
|
+
const service = container.get('knowledgeService');
|
|
31
|
+
return service.approve(ctx.data.candidateId, {
|
|
34
32
|
userId: ctx.actor,
|
|
35
33
|
});
|
|
36
34
|
});
|
|
37
35
|
|
|
38
36
|
gateway.register('candidate:reject', async (ctx) => {
|
|
39
|
-
const service = container.get('
|
|
40
|
-
return service.
|
|
37
|
+
const service = container.get('knowledgeService');
|
|
38
|
+
return service.reject(ctx.data.candidateId, ctx.data.reason, {
|
|
41
39
|
userId: ctx.actor,
|
|
42
40
|
});
|
|
43
41
|
});
|
|
44
42
|
|
|
45
43
|
gateway.register('candidate:apply_to_recipe', async (ctx) => {
|
|
46
|
-
const service = container.get('
|
|
47
|
-
return service.
|
|
48
|
-
userId: ctx.actor,
|
|
49
|
-
});
|
|
44
|
+
const service = container.get('knowledgeService');
|
|
45
|
+
return service.publish(ctx.data.candidateId, { userId: ctx.actor });
|
|
50
46
|
});
|
|
51
47
|
|
|
52
48
|
gateway.register('candidate:list', async (ctx) => {
|
|
53
|
-
const service = container.get('
|
|
54
|
-
return service.
|
|
49
|
+
const service = container.get('knowledgeService');
|
|
50
|
+
return service.list(ctx.data.filters, ctx.data.pagination);
|
|
55
51
|
});
|
|
56
52
|
|
|
57
53
|
gateway.register('candidate:search', async (ctx) => {
|
|
58
|
-
const service = container.get('
|
|
59
|
-
return service.
|
|
54
|
+
const service = container.get('knowledgeService');
|
|
55
|
+
return service.search(ctx.data.keyword, ctx.data.pagination);
|
|
60
56
|
});
|
|
61
57
|
|
|
62
58
|
gateway.register('candidate:get_stats', async (ctx) => {
|
|
63
|
-
const service = container.get('
|
|
64
|
-
return service.
|
|
59
|
+
const service = container.get('knowledgeService');
|
|
60
|
+
return service.getStats();
|
|
65
61
|
});
|
|
66
62
|
|
|
67
63
|
gateway.register('candidate:get', async (ctx) => {
|
|
68
|
-
const
|
|
69
|
-
return
|
|
64
|
+
const service = container.get('knowledgeService');
|
|
65
|
+
return service.get(ctx.data.id);
|
|
70
66
|
});
|
|
71
67
|
|
|
72
68
|
gateway.register('candidate:delete', async (ctx) => {
|
|
73
|
-
const service = container.get('
|
|
74
|
-
return service.
|
|
69
|
+
const service = container.get('knowledgeService');
|
|
70
|
+
return service.delete(ctx.data.candidateId, { userId: ctx.actor });
|
|
75
71
|
});
|
|
76
72
|
|
|
77
|
-
// ========== Recipe Actions ==========
|
|
73
|
+
// ========== Recipe Actions (V3: routed to knowledgeService) ==========
|
|
78
74
|
|
|
79
75
|
gateway.register('recipe:create', async (ctx) => {
|
|
80
|
-
const service = container.get('
|
|
81
|
-
return service.
|
|
76
|
+
const service = container.get('knowledgeService');
|
|
77
|
+
return service.create(ctx.data, {
|
|
82
78
|
userId: ctx.actor,
|
|
83
|
-
ip: ctx.data._ip,
|
|
84
|
-
userAgent: ctx.data._userAgent,
|
|
85
79
|
});
|
|
86
80
|
});
|
|
87
81
|
|
|
88
82
|
gateway.register('recipe:publish', async (ctx) => {
|
|
89
|
-
const service = container.get('
|
|
90
|
-
return service.
|
|
83
|
+
const service = container.get('knowledgeService');
|
|
84
|
+
return service.publish(ctx.data.recipeId, {
|
|
91
85
|
userId: ctx.actor,
|
|
92
86
|
});
|
|
93
87
|
});
|
|
94
88
|
|
|
95
89
|
gateway.register('recipe:deprecate', async (ctx) => {
|
|
96
|
-
const service = container.get('
|
|
97
|
-
return service.
|
|
90
|
+
const service = container.get('knowledgeService');
|
|
91
|
+
return service.deprecate(ctx.data.recipeId, ctx.data.reason, {
|
|
98
92
|
userId: ctx.actor,
|
|
99
93
|
});
|
|
100
94
|
});
|
|
101
95
|
|
|
102
96
|
gateway.register('recipe:update_quality', async (ctx) => {
|
|
103
|
-
const service = container.get('
|
|
104
|
-
return service.updateQuality(ctx.data.recipeId, ctx.data.metrics
|
|
105
|
-
userId: ctx.actor,
|
|
106
|
-
});
|
|
97
|
+
const service = container.get('knowledgeService');
|
|
98
|
+
return service.updateQuality(ctx.data.recipeId, ctx.data.metrics);
|
|
107
99
|
});
|
|
108
100
|
|
|
109
101
|
gateway.register('recipe:adopt', async (ctx) => {
|
|
110
|
-
const service = container.get('
|
|
111
|
-
return service.
|
|
102
|
+
const service = container.get('knowledgeService');
|
|
103
|
+
return service.incrementUsage(ctx.data.recipeId, 'adoption');
|
|
112
104
|
});
|
|
113
105
|
|
|
114
106
|
gateway.register('recipe:apply', async (ctx) => {
|
|
115
|
-
const service = container.get('
|
|
116
|
-
return service.
|
|
107
|
+
const service = container.get('knowledgeService');
|
|
108
|
+
return service.incrementUsage(ctx.data.recipeId, 'application');
|
|
117
109
|
});
|
|
118
110
|
|
|
119
111
|
gateway.register('recipe:list', async (ctx) => {
|
|
120
|
-
const service = container.get('
|
|
121
|
-
return service.
|
|
112
|
+
const service = container.get('knowledgeService');
|
|
113
|
+
return service.list(ctx.data.filters, ctx.data.pagination);
|
|
122
114
|
});
|
|
123
115
|
|
|
124
116
|
gateway.register('recipe:search', async (ctx) => {
|
|
125
|
-
const service = container.get('
|
|
126
|
-
return service.
|
|
117
|
+
const service = container.get('knowledgeService');
|
|
118
|
+
return service.search(ctx.data.keyword, ctx.data.pagination);
|
|
127
119
|
});
|
|
128
120
|
|
|
129
121
|
gateway.register('recipe:get_stats', async (ctx) => {
|
|
130
|
-
const service = container.get('
|
|
131
|
-
return service.
|
|
122
|
+
const service = container.get('knowledgeService');
|
|
123
|
+
return service.getStats();
|
|
132
124
|
});
|
|
133
125
|
|
|
134
126
|
gateway.register('recipe:get', async (ctx) => {
|
|
135
|
-
const
|
|
136
|
-
return
|
|
127
|
+
const service = container.get('knowledgeService');
|
|
128
|
+
return service.get(ctx.data.id);
|
|
137
129
|
});
|
|
138
130
|
|
|
139
131
|
gateway.register('recipe:get_recommendations', async (ctx) => {
|
|
140
|
-
const service = container.get('
|
|
141
|
-
return service.
|
|
132
|
+
const service = container.get('knowledgeService');
|
|
133
|
+
return service.list({ lifecycle: 'active' }, { page: 1, pageSize: ctx.data.limit || 10 });
|
|
142
134
|
});
|
|
143
135
|
|
|
144
136
|
gateway.register('recipe:delete', async (ctx) => {
|
|
145
|
-
const service = container.get('
|
|
146
|
-
return service.
|
|
137
|
+
const service = container.get('knowledgeService');
|
|
138
|
+
return service.delete(ctx.data.recipeId, {
|
|
147
139
|
userId: ctx.actor,
|
|
148
140
|
});
|
|
149
141
|
});
|
|
@@ -200,19 +192,17 @@ export function registerGatewayActions(gateway, container) {
|
|
|
200
192
|
});
|
|
201
193
|
|
|
202
194
|
gateway.register('guard_rule:get', async (ctx) => {
|
|
203
|
-
const repo = container.get('
|
|
195
|
+
const repo = container.get('knowledgeRepository');
|
|
204
196
|
return repo.findById(ctx.data.id);
|
|
205
197
|
});
|
|
206
198
|
|
|
207
199
|
// ========== Search Actions ==========
|
|
208
200
|
|
|
209
|
-
// ==========
|
|
201
|
+
// ========== Knowledge Update (enrich/refine) ==========
|
|
210
202
|
|
|
211
203
|
gateway.register('candidate:update', async (ctx) => {
|
|
212
|
-
const service = container.get('
|
|
213
|
-
return service.
|
|
214
|
-
? service.updateCandidate(ctx.data.id, ctx.data, { userId: ctx.actor })
|
|
215
|
-
: service.createCandidate(ctx.data, { userId: ctx.actor });
|
|
204
|
+
const service = container.get('knowledgeService');
|
|
205
|
+
return service.update(ctx.data.id, ctx.data, { userId: ctx.actor });
|
|
216
206
|
});
|
|
217
207
|
|
|
218
208
|
// ========== Search ==========
|
package/lib/domain/index.js
CHANGED
|
@@ -3,18 +3,23 @@
|
|
|
3
3
|
* 导出所有实体、值对象和仓储接口
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
export { Candidate } from './candidate/Candidate.js';
|
|
8
|
-
export { default as Reasoning } from './candidate/Reasoning.js';
|
|
9
|
-
export { CandidateStatus, isValidCandidateStatus, isValidStateTransition } from './types/CandidateStatus.js';
|
|
10
|
-
export { default as CandidateRepository } from './candidate/CandidateRepository.js';
|
|
11
|
-
|
|
12
|
-
// Recipe 相关(统一知识实体)
|
|
6
|
+
// Knowledge 统一知识实体 (V3)
|
|
13
7
|
export {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
KnowledgeEntry,
|
|
9
|
+
Lifecycle,
|
|
10
|
+
isValidTransition,
|
|
11
|
+
isValidLifecycle,
|
|
12
|
+
isCandidate as isLifecycleCandidate,
|
|
13
|
+
CANDIDATE_STATES,
|
|
14
|
+
inferKind as inferKindV3,
|
|
15
|
+
} from './knowledge/index.js';
|
|
16
|
+
export { Content } from './knowledge/values/Content.js';
|
|
17
|
+
export { Relations, RELATION_BUCKETS, RELATION_BUCKETS as RelationType } from './knowledge/values/Relations.js';
|
|
18
|
+
export { Constraints } from './knowledge/values/Constraints.js';
|
|
19
|
+
export { Reasoning as ReasoningV3 } from './knowledge/values/Reasoning.js';
|
|
20
|
+
export { Quality } from './knowledge/values/Quality.js';
|
|
21
|
+
export { Stats } from './knowledge/values/Stats.js';
|
|
22
|
+
export { KnowledgeRepository } from './knowledge/KnowledgeRepository.js';
|
|
18
23
|
|
|
19
24
|
// Snippet 相关
|
|
20
25
|
export { Snippet } from './snippet/Snippet.js';
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import { Lifecycle, isValidTransition, isCandidate as isLifecycleCandidate, inferKind, normalizeLifecycle } from './Lifecycle.js';
|
|
3
|
+
import { Content, Relations, Constraints, Reasoning, Quality, Stats } from './values/index.js';
|
|
4
|
+
|
|
5
|
+
/* ═══════════════════════════════════════════════════════════
|
|
6
|
+
* KnowledgeEntry — 统一知识实体
|
|
7
|
+
*
|
|
8
|
+
* lifecycle 状态决定其行为(3 状态简化版):
|
|
9
|
+
* pending → 待审核(新建条目初始状态)
|
|
10
|
+
* active → 已发布(被 Guard/Search/Export 消费)
|
|
11
|
+
* deprecated → 已废弃
|
|
12
|
+
* ═══════════════════════════════════════════════════════════ */
|
|
13
|
+
|
|
14
|
+
export class KnowledgeEntry {
|
|
15
|
+
/**
|
|
16
|
+
* @param {Object} props
|
|
17
|
+
*/
|
|
18
|
+
constructor(props = {}) {
|
|
19
|
+
// ── 标识 ──
|
|
20
|
+
this.id = props.id || uuidv4();
|
|
21
|
+
this.title = props.title || '';
|
|
22
|
+
this.description = props.description || '';
|
|
23
|
+
|
|
24
|
+
// ── 生命周期 ──
|
|
25
|
+
this.lifecycle = normalizeLifecycle(props.lifecycle || Lifecycle.PENDING);
|
|
26
|
+
this.lifecycleHistory = props.lifecycleHistory || [];
|
|
27
|
+
this.autoApprovable = props.autoApprovable ?? false;
|
|
28
|
+
|
|
29
|
+
// ── 语言与分类 ──
|
|
30
|
+
this.language = props.language || '';
|
|
31
|
+
this.category = props.category || '';
|
|
32
|
+
this.knowledgeType = props.knowledgeType || 'code-pattern';
|
|
33
|
+
this.kind = props.kind || inferKind(this.knowledgeType);
|
|
34
|
+
this.complexity = props.complexity || 'intermediate';
|
|
35
|
+
this.scope = props.scope || 'universal';
|
|
36
|
+
this.difficulty = props.difficulty || null;
|
|
37
|
+
this.tags = props.tags || [];
|
|
38
|
+
|
|
39
|
+
// ── Cursor 交付字段(AI 直接产出)──
|
|
40
|
+
this.trigger = props.trigger || '';
|
|
41
|
+
this.topicHint = props.topicHint || '';
|
|
42
|
+
this.whenClause = props.whenClause || '';
|
|
43
|
+
this.doClause = props.doClause || '';
|
|
44
|
+
this.dontClause = props.dontClause || '';
|
|
45
|
+
this.coreCode = props.coreCode || '';
|
|
46
|
+
|
|
47
|
+
// ── 值对象 ──
|
|
48
|
+
this.content = Content.from(props.content);
|
|
49
|
+
this.relations = Relations.from(props.relations);
|
|
50
|
+
this.constraints = Constraints.from(props.constraints);
|
|
51
|
+
this.reasoning = Reasoning.from(props.reasoning);
|
|
52
|
+
this.quality = Quality.from(props.quality);
|
|
53
|
+
this.stats = Stats.from(props.stats);
|
|
54
|
+
|
|
55
|
+
// ── 代码头文件 (ObjC/Swift) ──
|
|
56
|
+
this.headers = props.headers || [];
|
|
57
|
+
this.headerPaths = props.headerPaths || [];
|
|
58
|
+
this.moduleName = props.moduleName || '';
|
|
59
|
+
this.includeHeaders = props.includeHeaders ?? false;
|
|
60
|
+
|
|
61
|
+
// ── AI 润色 ──
|
|
62
|
+
this.agentNotes = props.agentNotes || null;
|
|
63
|
+
this.aiInsight = props.aiInsight || null;
|
|
64
|
+
|
|
65
|
+
// ── 审核 ──
|
|
66
|
+
this.reviewedBy = props.reviewedBy || null;
|
|
67
|
+
this.reviewedAt = props.reviewedAt || null;
|
|
68
|
+
this.rejectionReason = props.rejectionReason || null;
|
|
69
|
+
|
|
70
|
+
// ── 来源 ──
|
|
71
|
+
this.source = props.source || 'manual';
|
|
72
|
+
this.sourceFile = props.sourceFile || null;
|
|
73
|
+
this.sourceCandidateId = props.sourceCandidateId || null;
|
|
74
|
+
|
|
75
|
+
// ── 时间 ──
|
|
76
|
+
this.createdBy = props.createdBy || 'system';
|
|
77
|
+
this.createdAt = props.createdAt || Math.floor(Date.now() / 1000);
|
|
78
|
+
this.updatedAt = props.updatedAt || Math.floor(Date.now() / 1000);
|
|
79
|
+
this.publishedAt = props.publishedAt || null;
|
|
80
|
+
this.publishedBy = props.publishedBy || null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* ═══ 生命周期操作 ═══════════════════════════════════ */
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 发布 (pending → active)
|
|
87
|
+
* @param {string} publisher
|
|
88
|
+
* @returns {{ success: boolean, error?: string }}
|
|
89
|
+
*/
|
|
90
|
+
publish(publisher) {
|
|
91
|
+
if (!this.isValid()) {
|
|
92
|
+
return { success: false, error: '内容不完整,无法发布' };
|
|
93
|
+
}
|
|
94
|
+
const result = this._transition(Lifecycle.ACTIVE);
|
|
95
|
+
if (result.success) {
|
|
96
|
+
this.publishedAt = this._now();
|
|
97
|
+
this.publishedBy = publisher;
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 弃用 (pending|active → deprecated)
|
|
104
|
+
* @param {string} reason
|
|
105
|
+
* @returns {{ success: boolean, error?: string }}
|
|
106
|
+
*/
|
|
107
|
+
deprecate(reason) {
|
|
108
|
+
const result = this._transition(Lifecycle.DEPRECATED);
|
|
109
|
+
if (result.success) {
|
|
110
|
+
this.rejectionReason = reason;
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 重新激活 (deprecated → pending)
|
|
117
|
+
* @returns {{ success: boolean, error?: string }}
|
|
118
|
+
*/
|
|
119
|
+
reactivate() {
|
|
120
|
+
const result = this._transition(Lifecycle.PENDING);
|
|
121
|
+
if (result.success) {
|
|
122
|
+
this.rejectionReason = null;
|
|
123
|
+
}
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* ═══ 谓词 ═══════════════════════════════════════════ */
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 是否处于候选阶段
|
|
131
|
+
* @returns {boolean}
|
|
132
|
+
*/
|
|
133
|
+
isCandidate() {
|
|
134
|
+
return isLifecycleCandidate(this.lifecycle);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 是否可被 Guard/Search/Export 消费
|
|
139
|
+
* @returns {boolean}
|
|
140
|
+
*/
|
|
141
|
+
isActive() {
|
|
142
|
+
return this.lifecycle === Lifecycle.ACTIVE;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 是否为 Guard 规则类型
|
|
147
|
+
* @returns {boolean}
|
|
148
|
+
*/
|
|
149
|
+
isRule() {
|
|
150
|
+
return this.kind === 'rule';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 内容是否有效
|
|
155
|
+
* @returns {boolean}
|
|
156
|
+
*/
|
|
157
|
+
isValid() {
|
|
158
|
+
return !!(this.title?.trim() && this.content.hasContent());
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* ═══ Guard 消费 ═══════════════════════════════════ */
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 返回此 Entry 中可被 GuardCheckEngine 消费的规则列表
|
|
165
|
+
* @returns {Array<Object>}
|
|
166
|
+
*/
|
|
167
|
+
getGuardRules() {
|
|
168
|
+
if (!this.isActive() || !this.isRule()) return [];
|
|
169
|
+
|
|
170
|
+
const regexRules = this.constraints.getRegexGuards().map(g => ({
|
|
171
|
+
id: g.id || this.id,
|
|
172
|
+
type: 'regex',
|
|
173
|
+
name: g.message || this.title,
|
|
174
|
+
message: g.message || this.description || this.title,
|
|
175
|
+
pattern: g.pattern,
|
|
176
|
+
languages: this.language ? [this.language] : [],
|
|
177
|
+
severity: g.severity || 'warning',
|
|
178
|
+
source: 'knowledge_entry',
|
|
179
|
+
fixSuggestion: g.fix_suggestion || null,
|
|
180
|
+
}));
|
|
181
|
+
|
|
182
|
+
const astRules = this.constraints.getAstGuards().map(g => ({
|
|
183
|
+
id: g.id || `${this.id}:ast`,
|
|
184
|
+
type: 'ast',
|
|
185
|
+
name: g.message || this.title,
|
|
186
|
+
message: g.message,
|
|
187
|
+
astQuery: g.ast_query,
|
|
188
|
+
languages: g.ast_query?.language ? [g.ast_query.language] : [],
|
|
189
|
+
severity: g.severity || 'warning',
|
|
190
|
+
source: 'knowledge_entry',
|
|
191
|
+
fixSuggestion: g.fix_suggestion || null,
|
|
192
|
+
}));
|
|
193
|
+
|
|
194
|
+
return [...regexRules, ...astRules];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* ═══ 序列化 ═══════════════════════════════════════ */
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Domain → JSON (camelCase 直出,全链路统一)
|
|
201
|
+
*/
|
|
202
|
+
toJSON() {
|
|
203
|
+
return {
|
|
204
|
+
id: this.id,
|
|
205
|
+
title: this.title,
|
|
206
|
+
description: this.description,
|
|
207
|
+
lifecycle: this.lifecycle,
|
|
208
|
+
lifecycleHistory: this.lifecycleHistory,
|
|
209
|
+
autoApprovable: this.autoApprovable,
|
|
210
|
+
language: this.language,
|
|
211
|
+
category: this.category,
|
|
212
|
+
kind: this.kind,
|
|
213
|
+
knowledgeType: this.knowledgeType,
|
|
214
|
+
complexity: this.complexity,
|
|
215
|
+
scope: this.scope,
|
|
216
|
+
difficulty: this.difficulty,
|
|
217
|
+
tags: this.tags,
|
|
218
|
+
trigger: this.trigger,
|
|
219
|
+
topicHint: this.topicHint,
|
|
220
|
+
whenClause: this.whenClause,
|
|
221
|
+
doClause: this.doClause,
|
|
222
|
+
dontClause: this.dontClause,
|
|
223
|
+
coreCode: this.coreCode,
|
|
224
|
+
content: this.content.toJSON(),
|
|
225
|
+
relations: this.relations.toJSON(),
|
|
226
|
+
constraints: this.constraints.toJSON(),
|
|
227
|
+
reasoning: this.reasoning.toJSON(),
|
|
228
|
+
quality: this.quality.toJSON(),
|
|
229
|
+
stats: this.stats.toJSON(),
|
|
230
|
+
headers: this.headers,
|
|
231
|
+
headerPaths: this.headerPaths,
|
|
232
|
+
moduleName: this.moduleName,
|
|
233
|
+
includeHeaders: this.includeHeaders,
|
|
234
|
+
agentNotes: this.agentNotes,
|
|
235
|
+
aiInsight: this.aiInsight,
|
|
236
|
+
reviewedBy: this.reviewedBy,
|
|
237
|
+
reviewedAt: this.reviewedAt,
|
|
238
|
+
rejectionReason: this.rejectionReason,
|
|
239
|
+
source: this.source,
|
|
240
|
+
sourceFile: this.sourceFile,
|
|
241
|
+
sourceCandidateId: this.sourceCandidateId,
|
|
242
|
+
createdBy: this.createdBy,
|
|
243
|
+
createdAt: this.createdAt,
|
|
244
|
+
updatedAt: this.updatedAt,
|
|
245
|
+
publishedAt: this.publishedAt,
|
|
246
|
+
publishedBy: this.publishedBy,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* JSON → Domain (camelCase 直入)
|
|
252
|
+
* @param {Object} data
|
|
253
|
+
* @returns {KnowledgeEntry}
|
|
254
|
+
*/
|
|
255
|
+
static fromJSON(data) {
|
|
256
|
+
if (!data) return new KnowledgeEntry();
|
|
257
|
+
return new KnowledgeEntry(data);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* ═══ 私有 ═══════════════════════════════════════════ */
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @param {string} to
|
|
264
|
+
* @returns {{ success: boolean, error?: string }}
|
|
265
|
+
*/
|
|
266
|
+
_transition(to) {
|
|
267
|
+
if (!isValidTransition(this.lifecycle, to)) {
|
|
268
|
+
return {
|
|
269
|
+
success: false,
|
|
270
|
+
error: `Invalid lifecycle transition: ${this.lifecycle} → ${to}`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
this.lifecycleHistory.push({
|
|
274
|
+
from: this.lifecycle,
|
|
275
|
+
to,
|
|
276
|
+
at: this._now(),
|
|
277
|
+
});
|
|
278
|
+
this.lifecycle = to;
|
|
279
|
+
this.updatedAt = this._now();
|
|
280
|
+
return { success: true };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** @returns {number} */
|
|
284
|
+
_now() {
|
|
285
|
+
return Math.floor(Date.now() / 1000);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export default KnowledgeEntry;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KnowledgeRepository — 统一知识实体仓储接口
|
|
3
|
+
*
|
|
4
|
+
* 替代 CandidateRepository + RecipeRepository。
|
|
5
|
+
* 实现类见 lib/repository/knowledge/KnowledgeRepository.impl.js
|
|
6
|
+
*/
|
|
7
|
+
export class KnowledgeRepository {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 创建 KnowledgeEntry
|
|
11
|
+
* @param {import('./KnowledgeEntry.js').KnowledgeEntry} entry
|
|
12
|
+
* @returns {Promise<import('./KnowledgeEntry.js').KnowledgeEntry>}
|
|
13
|
+
*/
|
|
14
|
+
async create(entry) {
|
|
15
|
+
throw new Error('Not implemented');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 根据 ID 获取
|
|
20
|
+
* @param {string} id
|
|
21
|
+
* @returns {Promise<import('./KnowledgeEntry.js').KnowledgeEntry|null>}
|
|
22
|
+
*/
|
|
23
|
+
async findById(id) {
|
|
24
|
+
throw new Error('Not implemented');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 分页查询
|
|
29
|
+
* @param {Object} filters - { lifecycle, kind, language, category, knowledgeType, source }
|
|
30
|
+
* @param {Object} options - { page, pageSize, orderBy, order }
|
|
31
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
32
|
+
*/
|
|
33
|
+
async findWithPagination(filters = {}, options = {}) {
|
|
34
|
+
throw new Error('Not implemented');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 根据生命周期状态查询
|
|
39
|
+
* @param {string} lifecycle
|
|
40
|
+
* @param {Object} pagination
|
|
41
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
42
|
+
*/
|
|
43
|
+
async findByLifecycle(lifecycle, pagination = {}) {
|
|
44
|
+
throw new Error('Not implemented');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 根据 kind 查询
|
|
49
|
+
* @param {string} kind - 'rule' | 'pattern' | 'fact'
|
|
50
|
+
* @param {Object} options - { page, pageSize, lifecycle }
|
|
51
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
52
|
+
*/
|
|
53
|
+
async findByKind(kind, options = {}) {
|
|
54
|
+
throw new Error('Not implemented');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 查询所有 active 的 rule 类型(Guard 消费热路径)
|
|
59
|
+
* @returns {Promise<KnowledgeEntry[]>}
|
|
60
|
+
*/
|
|
61
|
+
async findActiveRules() {
|
|
62
|
+
throw new Error('Not implemented');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 根据语言查询
|
|
67
|
+
* @param {string} language
|
|
68
|
+
* @param {Object} pagination
|
|
69
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
70
|
+
*/
|
|
71
|
+
async findByLanguage(language, pagination = {}) {
|
|
72
|
+
throw new Error('Not implemented');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 根据分类查询
|
|
77
|
+
* @param {string} category
|
|
78
|
+
* @param {Object} pagination
|
|
79
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
80
|
+
*/
|
|
81
|
+
async findByCategory(category, pagination = {}) {
|
|
82
|
+
throw new Error('Not implemented');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 搜索 (标题/内容/触发词/标签)
|
|
87
|
+
* @param {string} keyword
|
|
88
|
+
* @param {Object} pagination
|
|
89
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
90
|
+
*/
|
|
91
|
+
async search(keyword, pagination = {}) {
|
|
92
|
+
throw new Error('Not implemented');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 更新
|
|
97
|
+
* @param {string} id
|
|
98
|
+
* @param {Object} updates - wire format 的部分字段
|
|
99
|
+
* @returns {Promise<import('./KnowledgeEntry.js').KnowledgeEntry>}
|
|
100
|
+
*/
|
|
101
|
+
async update(id, updates) {
|
|
102
|
+
throw new Error('Not implemented');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 删除
|
|
107
|
+
* @param {string} id
|
|
108
|
+
* @returns {Promise<boolean>}
|
|
109
|
+
*/
|
|
110
|
+
async delete(id) {
|
|
111
|
+
throw new Error('Not implemented');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 获取统计信息
|
|
116
|
+
* @returns {Promise<Object>}
|
|
117
|
+
*/
|
|
118
|
+
async getStats() {
|
|
119
|
+
throw new Error('Not implemented');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default KnowledgeRepository;
|