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.
Files changed (115) hide show
  1. package/README.md +12 -12
  2. package/bin/cli.js +53 -40
  3. package/config/constitution.yaml +9 -2
  4. package/dashboard/dist/assets/{icons-CH-H9x0E.js → icons-D4IWpDIk.js} +105 -100
  5. package/dashboard/dist/assets/index-CWBNcF9z.css +1 -0
  6. package/dashboard/dist/assets/index-DHtzhbuG.js +120 -0
  7. package/dashboard/dist/index.html +3 -3
  8. package/lib/cli/AiScanService.js +35 -36
  9. package/lib/cli/KnowledgeSyncService.js +345 -0
  10. package/lib/cli/SetupService.js +8 -26
  11. package/lib/cli/UpgradeService.js +28 -0
  12. package/lib/core/gateway/GatewayActionRegistry.js +48 -58
  13. package/lib/domain/index.js +16 -11
  14. package/lib/domain/knowledge/KnowledgeEntry.js +289 -0
  15. package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
  16. package/lib/domain/knowledge/Lifecycle.js +99 -0
  17. package/lib/domain/knowledge/index.js +27 -0
  18. package/lib/domain/knowledge/values/Constraints.js +128 -0
  19. package/lib/domain/knowledge/values/Content.js +69 -0
  20. package/lib/domain/knowledge/values/Quality.js +81 -0
  21. package/lib/domain/knowledge/values/Reasoning.js +70 -0
  22. package/lib/domain/knowledge/values/Relations.js +142 -0
  23. package/lib/domain/knowledge/values/Stats.js +72 -0
  24. package/lib/domain/knowledge/values/index.js +9 -0
  25. package/lib/external/ai/AiProvider.js +85 -11
  26. package/lib/external/mcp/McpServer.js +7 -5
  27. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +18 -2
  28. package/lib/external/mcp/handlers/bootstrap.js +116 -11
  29. package/lib/external/mcp/handlers/browse.js +76 -73
  30. package/lib/external/mcp/handlers/candidate.js +26 -275
  31. package/lib/external/mcp/handlers/guard.js +2 -0
  32. package/lib/external/mcp/handlers/knowledge.js +267 -0
  33. package/lib/external/mcp/handlers/structure.js +25 -23
  34. package/lib/external/mcp/handlers/system.js +10 -12
  35. package/lib/external/mcp/tools.js +134 -140
  36. package/lib/http/HttpServer.js +14 -8
  37. package/lib/http/routes/ai.js +4 -3
  38. package/lib/http/routes/extract.js +48 -4
  39. package/lib/http/routes/knowledge.js +246 -0
  40. package/lib/http/routes/search.js +12 -17
  41. package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
  42. package/lib/infrastructure/database/migrations/017_camelcase_knowledge_entries.js +107 -0
  43. package/lib/infrastructure/external/XcodeAutomation.js +187 -103
  44. package/lib/injection/ServiceContainer.js +69 -60
  45. package/lib/repository/knowledge/KnowledgeRepository.impl.js +338 -0
  46. package/lib/service/automation/DirectiveDetector.js +2 -3
  47. package/lib/service/automation/FileWatcher.js +59 -28
  48. package/lib/service/automation/XcodeIntegration.js +931 -156
  49. package/lib/service/automation/handlers/AlinkHandler.js +5 -4
  50. package/lib/service/automation/handlers/CreateHandler.js +53 -19
  51. package/lib/service/automation/handlers/DraftHandler.js +1 -1
  52. package/lib/service/automation/handlers/GuardHandler.js +183 -20
  53. package/lib/service/automation/handlers/SearchHandler.js +25 -22
  54. package/lib/service/candidate/SimilarityService.js +2 -2
  55. package/lib/service/chat/AnalystAgent.js +9 -0
  56. package/lib/service/chat/CandidateGuardrail.js +22 -11
  57. package/lib/service/chat/ChatAgent.js +132 -54
  58. package/lib/service/chat/ContextWindow.js +5 -5
  59. package/lib/service/chat/HandoffProtocol.js +1 -0
  60. package/lib/service/chat/ProducerAgent.js +40 -13
  61. package/lib/service/chat/ReasoningLayer.js +854 -0
  62. package/lib/service/chat/ReasoningTrace.js +329 -0
  63. package/lib/service/chat/tools.js +308 -205
  64. package/lib/service/cursor/CursorDeliveryPipeline.js +279 -0
  65. package/lib/service/cursor/KnowledgeCompressor.js +87 -0
  66. package/lib/service/cursor/RulesGenerator.js +168 -0
  67. package/lib/service/cursor/SkillsSyncer.js +268 -0
  68. package/lib/service/cursor/TokenBudget.js +58 -0
  69. package/lib/service/cursor/TopicClassifier.js +141 -0
  70. package/lib/service/guard/GuardCheckEngine.js +99 -10
  71. package/lib/service/guard/GuardService.js +57 -46
  72. package/lib/service/knowledge/ConfidenceRouter.js +159 -0
  73. package/lib/service/knowledge/KnowledgeFileWriter.js +595 -0
  74. package/lib/service/knowledge/KnowledgeService.js +802 -0
  75. package/lib/service/recipe/RecipeParser.js +3 -12
  76. package/lib/service/search/SearchEngine.js +67 -22
  77. package/lib/service/skills/SignalCollector.js +14 -9
  78. package/lib/service/skills/SkillAdvisor.js +13 -11
  79. package/lib/service/snippet/SnippetFactory.js +5 -5
  80. package/lib/service/spm/SpmService.js +15 -48
  81. package/lib/shared/RecipeReadinessChecker.js +6 -11
  82. package/package.json +1 -1
  83. package/scripts/install-cursor-skill.js +0 -6
  84. package/scripts/migrate-md-to-knowledge.mjs +364 -0
  85. package/skills/autosnippet-analysis/SKILL.md +15 -7
  86. package/skills/autosnippet-candidates/SKILL.md +8 -8
  87. package/skills/autosnippet-coldstart/SKILL.md +8 -4
  88. package/skills/autosnippet-concepts/SKILL.md +7 -6
  89. package/skills/autosnippet-create/SKILL.md +13 -13
  90. package/skills/autosnippet-intent/SKILL.md +3 -2
  91. package/skills/autosnippet-lifecycle/SKILL.md +5 -5
  92. package/skills/autosnippet-recipes/SKILL.md +18 -6
  93. package/templates/constitution.yaml +1 -1
  94. package/templates/copilot-instructions.md +6 -6
  95. package/templates/recipes-setup/README.md +3 -3
  96. package/dashboard/dist/assets/index-CqJRvYRL.js +0 -197
  97. package/dashboard/dist/assets/index-DICm9PNa.css +0 -1
  98. package/lib/cli/CandidateSyncService.js +0 -261
  99. package/lib/cli/SyncService.js +0 -356
  100. package/lib/domain/candidate/Candidate.js +0 -196
  101. package/lib/domain/candidate/CandidateRepository.js +0 -107
  102. package/lib/domain/candidate/Reasoning.js +0 -52
  103. package/lib/domain/recipe/Recipe.js +0 -421
  104. package/lib/domain/recipe/RecipeRepository.js +0 -54
  105. package/lib/domain/types/CandidateStatus.js +0 -52
  106. package/lib/http/routes/candidates.js +0 -559
  107. package/lib/http/routes/recipes.js +0 -397
  108. package/lib/repository/candidate/CandidateRepository.impl.js +0 -230
  109. package/lib/repository/recipe/RecipeRepository.impl.js +0 -498
  110. package/lib/service/candidate/CandidateAggregator.js +0 -52
  111. package/lib/service/candidate/CandidateFileWriter.js +0 -383
  112. package/lib/service/candidate/CandidateService.js +0 -1001
  113. package/lib/service/recipe/RecipeFileWriter.js +0 -514
  114. package/lib/service/recipe/RecipeService.js +0 -786
  115. 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
- // ========== Candidate Actions ==========
20
+ // ========== Knowledge Actions (V3: replaces Candidate + Recipe) ==========
21
21
 
22
22
  gateway.register('candidate:create', async (ctx) => {
23
- const service = container.get('candidateService');
24
- return service.createCandidate(ctx.data, {
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('candidateService');
33
- return service.approveCandidate(ctx.data.candidateId, {
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('candidateService');
40
- return service.rejectCandidate(ctx.data.candidateId, ctx.data.reason, {
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('candidateService');
47
- return service.applyToRecipe(ctx.data.candidateId, ctx.data.recipeId, {
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('candidateService');
54
- return service.listCandidates(ctx.data.filters, ctx.data.pagination);
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('candidateService');
59
- return service.searchCandidates(ctx.data.keyword, ctx.data.pagination);
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('candidateService');
64
- return service.getCandidateStats();
59
+ const service = container.get('knowledgeService');
60
+ return service.getStats();
65
61
  });
66
62
 
67
63
  gateway.register('candidate:get', async (ctx) => {
68
- const repo = container.get('candidateRepository');
69
- return repo.findById(ctx.data.id);
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('candidateService');
74
- return service.deleteCandidate(ctx.data.candidateId, { userId: ctx.actor });
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('recipeService');
81
- return service.createRecipe(ctx.data, {
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('recipeService');
90
- return service.publishRecipe(ctx.data.recipeId, {
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('recipeService');
97
- return service.deprecateRecipe(ctx.data.recipeId, ctx.data.reason, {
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('recipeService');
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('recipeService');
111
- return service.incrementAdoption(ctx.data.recipeId);
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('recipeService');
116
- return service.incrementApplication(ctx.data.recipeId);
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('recipeService');
121
- return service.listRecipes(ctx.data.filters, ctx.data.pagination);
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('recipeService');
126
- return service.searchRecipes(ctx.data.keyword, ctx.data.pagination);
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('recipeService');
131
- return service.getRecipeStats();
122
+ const service = container.get('knowledgeService');
123
+ return service.getStats();
132
124
  });
133
125
 
134
126
  gateway.register('recipe:get', async (ctx) => {
135
- const repo = container.get('recipeRepository');
136
- return repo.findById(ctx.data.id);
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('recipeService');
141
- return service.getRecommendations(ctx.data.limit);
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('recipeService');
146
- return service.deleteRecipe(ctx.data.recipeId, {
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('recipeRepository');
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
- // ========== Candidate Update (enrich/refine) ==========
201
+ // ========== Knowledge Update (enrich/refine) ==========
210
202
 
211
203
  gateway.register('candidate:update', async (ctx) => {
212
- const service = container.get('candidateService');
213
- return service.updateCandidate
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 ==========
@@ -3,18 +3,23 @@
3
3
  * 导出所有实体、值对象和仓储接口
4
4
  */
5
5
 
6
- // Candidate 相关
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
- Recipe, RecipeStatus, KnowledgeType, Complexity,
15
- RelationType, Kind, inferKind,
16
- } from './recipe/Recipe.js';
17
- export { default as RecipeRepository } from './recipe/RecipeRepository.js';
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;