autosnippet 3.3.7 → 3.3.8

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 (211) hide show
  1. package/README.md +1 -0
  2. package/dashboard/dist/assets/icons-BMNb0V6L.js +1 -0
  3. package/dashboard/dist/assets/index-DHJ1Dj7u.css +1 -0
  4. package/dashboard/dist/assets/index-DV8biUkH.js +112 -0
  5. package/dashboard/dist/index.html +3 -3
  6. package/dist/bin/cli.js +7 -4
  7. package/dist/lib/agent/core/ChatAgentPrompts.js +57 -21
  8. package/dist/lib/agent/core/LoopContext.d.ts +1 -0
  9. package/dist/lib/agent/core/ToolExecutionPipeline.js +13 -0
  10. package/dist/lib/agent/memory/ActiveContext.d.ts +0 -2
  11. package/dist/lib/agent/memory/ActiveContext.js +0 -2
  12. package/dist/lib/agent/memory/MemoryEmbeddingStore.d.ts +49 -0
  13. package/dist/lib/agent/memory/MemoryEmbeddingStore.js +159 -0
  14. package/dist/lib/agent/memory/MemoryRetriever.d.ts +2 -0
  15. package/dist/lib/agent/memory/MemoryRetriever.js +25 -11
  16. package/dist/lib/agent/memory/MemoryStore.d.ts +8 -41
  17. package/dist/lib/agent/memory/MemoryStore.js +196 -261
  18. package/dist/lib/agent/memory/PersistentMemory.d.ts +2 -0
  19. package/dist/lib/agent/memory/PersistentMemory.js +4 -5
  20. package/dist/lib/agent/memory/SessionStore.d.ts +0 -2
  21. package/dist/lib/agent/memory/SessionStore.js +0 -2
  22. package/dist/lib/agent/tools/ast-graph.js +21 -19
  23. package/dist/lib/agent/tools/infrastructure.js +3 -2
  24. package/dist/lib/agent/tools/project-access.d.ts +2 -2
  25. package/dist/lib/agent/tools/project-access.js +5 -4
  26. package/dist/lib/bootstrap.js +2 -1
  27. package/dist/lib/cli/AiScanService.js +4 -17
  28. package/dist/lib/cli/KnowledgeSyncService.d.ts +7 -37
  29. package/dist/lib/cli/KnowledgeSyncService.js +23 -51
  30. package/dist/lib/core/ast/ProjectGraph.js +5 -27
  31. package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +0 -2
  32. package/dist/lib/core/discovery/CustomConfigDiscoverer.js +0 -2
  33. package/dist/lib/domain/dimension/DimensionRegistry.d.ts +0 -2
  34. package/dist/lib/domain/dimension/DimensionRegistry.js +0 -2
  35. package/dist/lib/domain/dimension/DimensionSop.js +44 -33
  36. package/dist/lib/domain/dimension/UnifiedDimension.d.ts +0 -2
  37. package/dist/lib/domain/dimension/UnifiedDimension.js +0 -2
  38. package/dist/lib/domain/knowledge/Lifecycle.d.ts +26 -0
  39. package/dist/lib/domain/knowledge/Lifecycle.js +42 -0
  40. package/dist/lib/domain/knowledge/index.d.ts +2 -1
  41. package/dist/lib/domain/knowledge/index.js +1 -1
  42. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.d.ts +2 -1
  43. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +102 -153
  44. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +33 -16
  45. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +1 -1
  46. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +41 -37
  47. package/dist/lib/external/mcp/handlers/bootstrap-external.js +1 -1
  48. package/dist/lib/external/mcp/handlers/dimension-complete-external.js +7 -3
  49. package/dist/lib/external/mcp/handlers/evolve-external.d.ts +1 -0
  50. package/dist/lib/external/mcp/handlers/evolve-external.js +13 -16
  51. package/dist/lib/external/mcp/handlers/guard.js +15 -24
  52. package/dist/lib/external/mcp/handlers/panorama.js +9 -9
  53. package/dist/lib/external/mcp/handlers/rescan-external.js +7 -6
  54. package/dist/lib/external/mcp/handlers/rescan-internal.js +9 -5
  55. package/dist/lib/external/mcp/handlers/search.js +3 -1
  56. package/dist/lib/external/mcp/handlers/skill.js +4 -4
  57. package/dist/lib/external/mcp/handlers/structure.js +8 -12
  58. package/dist/lib/external/mcp/handlers/system.js +10 -34
  59. package/dist/lib/http/routes/ai.js +11 -13
  60. package/dist/lib/http/routes/guardReport.js +3 -5
  61. package/dist/lib/http/routes/panorama.js +12 -12
  62. package/dist/lib/http/routes/recipes.js +59 -8
  63. package/dist/lib/http/routes/remote.js +3 -13
  64. package/dist/lib/http/routes/search.js +11 -8
  65. package/dist/lib/infrastructure/audit/AuditLogger.d.ts +20 -3
  66. package/dist/lib/infrastructure/audit/AuditStore.d.ts +28 -29
  67. package/dist/lib/infrastructure/audit/AuditStore.js +81 -88
  68. package/dist/lib/infrastructure/database/drizzle/schema.d.ts +180 -2
  69. package/dist/lib/infrastructure/database/drizzle/schema.js +23 -3
  70. package/dist/lib/injection/ServiceContainer.js +7 -4
  71. package/dist/lib/injection/ServiceMap.d.ts +20 -0
  72. package/dist/lib/injection/modules/AppModule.js +2 -1
  73. package/dist/lib/injection/modules/GuardModule.js +5 -5
  74. package/dist/lib/injection/modules/InfraModule.js +60 -0
  75. package/dist/lib/injection/modules/KnowledgeModule.js +86 -51
  76. package/dist/lib/injection/modules/PanoramaModule.js +16 -10
  77. package/dist/lib/injection/modules/VectorModule.js +3 -0
  78. package/dist/lib/repository/audit/AuditRepository.d.ts +107 -0
  79. package/dist/lib/repository/audit/AuditRepository.js +272 -0
  80. package/dist/lib/repository/base/RepositoryBase.d.ts +46 -0
  81. package/dist/lib/repository/base/RepositoryBase.js +32 -0
  82. package/dist/lib/repository/bootstrap/BootstrapRepository.d.ts +94 -0
  83. package/dist/lib/repository/bootstrap/BootstrapRepository.js +246 -0
  84. package/dist/lib/repository/code/CodeEntityRepository.d.ts +91 -0
  85. package/dist/lib/repository/code/CodeEntityRepository.js +361 -0
  86. package/dist/lib/repository/delivery/DeliveryRepoAdapter.d.ts +39 -0
  87. package/dist/lib/repository/delivery/DeliveryRepoAdapter.js +23 -0
  88. package/dist/lib/repository/evolution/LifecycleEventRepository.d.ts +51 -0
  89. package/dist/lib/repository/evolution/LifecycleEventRepository.js +119 -0
  90. package/dist/lib/repository/evolution/ProposalRepository.d.ts +9 -12
  91. package/dist/lib/repository/evolution/ProposalRepository.js +114 -57
  92. package/dist/lib/repository/guard/GuardViolationRepository.d.ts +104 -0
  93. package/dist/lib/repository/guard/GuardViolationRepository.js +217 -0
  94. package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.d.ts +129 -0
  95. package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.js +475 -0
  96. package/dist/lib/repository/knowledge/KnowledgeFileStore.d.ts +39 -0
  97. package/dist/lib/repository/knowledge/KnowledgeFileStore.js +12 -0
  98. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +295 -11
  99. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +608 -13
  100. package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.d.ts +61 -0
  101. package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.js +156 -0
  102. package/dist/lib/repository/memory/MemoryRepository.d.ts +90 -0
  103. package/dist/lib/repository/memory/MemoryRepository.js +260 -0
  104. package/dist/lib/repository/search/SearchRepoAdapter.d.ts +92 -0
  105. package/dist/lib/repository/search/SearchRepoAdapter.js +124 -0
  106. package/dist/lib/repository/session/SessionRepository.d.ts +46 -0
  107. package/dist/lib/repository/session/SessionRepository.js +110 -0
  108. package/dist/lib/repository/sourceref/RecipeSourceRefRepository.d.ts +66 -0
  109. package/dist/lib/repository/sourceref/RecipeSourceRefRepository.js +182 -0
  110. package/dist/lib/repository/sync/SyncRepoAdapter.d.ts +58 -0
  111. package/dist/lib/repository/sync/SyncRepoAdapter.js +58 -0
  112. package/dist/lib/service/bootstrap/UiStartupTasks.js +5 -6
  113. package/dist/lib/service/bootstrap/bootstrap-event-types.d.ts +0 -1
  114. package/dist/lib/service/bootstrap/bootstrap-event-types.js +0 -1
  115. package/dist/lib/service/cleanup/CleanupService.js +8 -4
  116. package/dist/lib/service/delivery/CursorDeliveryPipeline.js +6 -8
  117. package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +4 -9
  118. package/dist/lib/service/evolution/ConsolidationAdvisor.js +34 -70
  119. package/dist/lib/service/evolution/ContentPatcher.d.ts +4 -12
  120. package/dist/lib/service/evolution/ContentPatcher.js +48 -19
  121. package/dist/lib/service/evolution/ContradictionDetector.d.ts +3 -7
  122. package/dist/lib/service/evolution/ContradictionDetector.js +17 -24
  123. package/dist/lib/service/evolution/DecayDetector.d.ts +10 -9
  124. package/dist/lib/service/evolution/DecayDetector.js +63 -57
  125. package/dist/lib/service/evolution/EnhancementSuggester.d.ts +3 -9
  126. package/dist/lib/service/evolution/EnhancementSuggester.js +42 -86
  127. package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +4 -4
  128. package/dist/lib/service/evolution/KnowledgeMetabolism.js +102 -71
  129. package/dist/lib/service/evolution/ProposalExecutor.d.ts +5 -12
  130. package/dist/lib/service/evolution/ProposalExecutor.js +64 -69
  131. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.d.ts +9 -14
  132. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.js +94 -155
  133. package/dist/lib/service/evolution/RecipeRelevanceAuditor.d.ts +4 -1
  134. package/dist/lib/service/evolution/RecipeRelevanceAuditor.js +50 -49
  135. package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +3 -7
  136. package/dist/lib/service/evolution/RedundancyAnalyzer.js +15 -22
  137. package/dist/lib/service/evolution/StagingManager.d.ts +6 -15
  138. package/dist/lib/service/evolution/StagingManager.js +37 -95
  139. package/dist/lib/service/evolution/createSupersedeProposal.d.ts +1 -1
  140. package/dist/lib/service/evolution/createSupersedeProposal.js +7 -8
  141. package/dist/lib/service/guard/CoverageAnalyzer.d.ts +3 -7
  142. package/dist/lib/service/guard/CoverageAnalyzer.js +9 -11
  143. package/dist/lib/service/guard/GuardCheckEngine.d.ts +3 -0
  144. package/dist/lib/service/guard/GuardCheckEngine.js +14 -22
  145. package/dist/lib/service/guard/ReverseGuard.d.ts +4 -7
  146. package/dist/lib/service/guard/ReverseGuard.js +21 -31
  147. package/dist/lib/service/guard/ViolationsStore.d.ts +15 -21
  148. package/dist/lib/service/guard/ViolationsStore.js +75 -69
  149. package/dist/lib/service/knowledge/CodeEntityGraph.d.ts +39 -63
  150. package/dist/lib/service/knowledge/CodeEntityGraph.js +418 -512
  151. package/dist/lib/service/knowledge/ConfidenceRouter.js +18 -9
  152. package/dist/lib/service/knowledge/KnowledgeFileWriter.d.ts +2 -1
  153. package/dist/lib/service/knowledge/KnowledgeGraphService.d.ts +18 -60
  154. package/dist/lib/service/knowledge/KnowledgeGraphService.js +58 -109
  155. package/dist/lib/service/knowledge/KnowledgeService.d.ts +15 -1
  156. package/dist/lib/service/knowledge/KnowledgeService.js +76 -38
  157. package/dist/lib/service/knowledge/RecipeProductionGateway.d.ts +0 -2
  158. package/dist/lib/service/knowledge/RecipeProductionGateway.js +0 -2
  159. package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +5 -13
  160. package/dist/lib/service/knowledge/SourceRefReconciler.js +58 -78
  161. package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +5 -3
  162. package/dist/lib/service/panorama/CouplingAnalyzer.js +102 -39
  163. package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +7 -4
  164. package/dist/lib/service/panorama/DimensionAnalyzer.js +72 -25
  165. package/dist/lib/service/panorama/LayerInferrer.js +1 -1
  166. package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +7 -6
  167. package/dist/lib/service/panorama/ModuleDiscoverer.js +174 -82
  168. package/dist/lib/service/panorama/PanoramaAggregator.d.ts +10 -3
  169. package/dist/lib/service/panorama/PanoramaAggregator.js +67 -79
  170. package/dist/lib/service/panorama/PanoramaScanner.d.ts +5 -1
  171. package/dist/lib/service/panorama/PanoramaScanner.js +32 -31
  172. package/dist/lib/service/panorama/PanoramaService.d.ts +11 -8
  173. package/dist/lib/service/panorama/PanoramaService.js +41 -66
  174. package/dist/lib/service/panorama/PanoramaTypes.d.ts +3 -0
  175. package/dist/lib/service/panorama/RoleRefiner.d.ts +8 -5
  176. package/dist/lib/service/panorama/RoleRefiner.js +52 -283
  177. package/dist/lib/service/panorama/TechStackProfiler.js +7 -119
  178. package/dist/lib/service/quality/QualityScorer.d.ts +45 -26
  179. package/dist/lib/service/quality/QualityScorer.js +157 -83
  180. package/dist/lib/service/search/SearchEngine.d.ts +1 -0
  181. package/dist/lib/service/search/SearchEngine.js +32 -37
  182. package/dist/lib/service/signal/HitRecorder.js +5 -5
  183. package/dist/lib/service/skills/RuleRecallStrategy.js +7 -3
  184. package/dist/lib/service/skills/SignalCollector.d.ts +5 -8
  185. package/dist/lib/service/skills/SignalCollector.js +28 -55
  186. package/dist/lib/service/skills/SkillAdvisor.d.ts +7 -13
  187. package/dist/lib/service/skills/SkillAdvisor.js +30 -79
  188. package/dist/lib/service/vector/SyncCoordinator.d.ts +3 -1
  189. package/dist/lib/service/vector/SyncCoordinator.js +25 -3
  190. package/dist/lib/service/vector/VectorService.d.ts +2 -0
  191. package/dist/lib/service/vector/VectorService.js +3 -0
  192. package/dist/lib/service/wiki/WikiGenerator.js +1 -1
  193. package/dist/lib/shared/LanguageProfiles.d.ts +109 -0
  194. package/dist/lib/shared/LanguageProfiles.js +939 -0
  195. package/dist/lib/shared/LanguageService.d.ts +6 -0
  196. package/dist/lib/shared/LanguageService.js +16 -0
  197. package/dist/lib/shared/constants.d.ts +19 -19
  198. package/dist/lib/shared/constants.js +10 -10
  199. package/dist/lib/shared/schemas/mcp-tools.d.ts +1 -1
  200. package/dist/lib/types/project-snapshot-builder.d.ts +0 -1
  201. package/dist/lib/types/project-snapshot-builder.js +0 -1
  202. package/dist/lib/types/project-snapshot.d.ts +0 -1
  203. package/dist/lib/types/project-snapshot.js +0 -1
  204. package/dist/lib/types/snapshot-views.d.ts +0 -2
  205. package/dist/lib/types/snapshot-views.js +0 -1
  206. package/package.json +2 -1
  207. package/dashboard/dist/assets/icons-FHns2ypa.js +0 -1
  208. package/dashboard/dist/assets/index-BRJv5Y3r.js +0 -135
  209. package/dashboard/dist/assets/index-DzoB7kxK.css +0 -1
  210. package/dist/lib/repository/base/BaseRepository.d.ts +0 -53
  211. package/dist/lib/repository/base/BaseRepository.js +0 -226
@@ -1,5 +1,5 @@
1
1
  /**
2
- * ProposalRepository — evolution_proposals 表 CRUD
2
+ * ProposalRepository — evolution_proposals 表 CRUD (Drizzle ORM)
3
3
  *
4
4
  * 操作 evolution_proposals 表,存储进化提案(merge/supersede/enhance/deprecate/
5
5
  * reorganize/contradiction/correction)。
@@ -8,8 +8,14 @@
8
8
  * - 去重:同 target + 同 type 不允许多个 observing 状态的 Proposal
9
9
  * - Rate Limit:同一 target 不允许同时存在多个相同类型的 observing Proposal
10
10
  * - JSON 字段(evidence/related_recipe_ids)序列化/反序列化
11
+ *
12
+ * Drizzle 迁移策略 (Phase 5a):
13
+ * - 全部 raw SQL → Drizzle 类型安全 API
14
+ * - 构造器接收 DrizzleDB(不再需要 raw Database)
11
15
  */
12
16
  import { randomBytes } from 'node:crypto';
17
+ import { and, count, desc, eq, inArray, lte } from 'drizzle-orm';
18
+ import { evolutionProposals } from '../../infrastructure/database/drizzle/schema.js';
13
19
  /* ────────────────────── Constants ────────────────────── */
14
20
  /** 默认观察窗口:7 天 */
15
21
  const DEFAULT_OBSERVATION_WINDOW = 7 * 24 * 60 * 60 * 1000;
@@ -35,9 +41,9 @@ const AUTO_OBSERVE_THRESHOLDS = {
35
41
  };
36
42
  /* ────────────────────── Class ────────────────────── */
37
43
  export class ProposalRepository {
38
- #db;
39
- constructor(db) {
40
- this.#db = db;
44
+ #drizzle;
45
+ constructor(drizzle) {
46
+ this.#drizzle = drizzle;
41
47
  }
42
48
  /* ═══════════════════ Create ═══════════════════ */
43
49
  /**
@@ -73,55 +79,66 @@ export class ProposalRepository {
73
79
  resolvedBy: null,
74
80
  resolution: null,
75
81
  };
76
- this.#db
77
- .prepare(`INSERT INTO evolution_proposals
78
- (id, type, target_recipe_id, related_recipe_ids, confidence, source, description, evidence, status, proposed_at, expires_at)
79
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
80
- .run(record.id, record.type, record.targetRecipeId, JSON.stringify(record.relatedRecipeIds), record.confidence, record.source, record.description, JSON.stringify(record.evidence), record.status, record.proposedAt, record.expiresAt);
82
+ this.#drizzle
83
+ .insert(evolutionProposals)
84
+ .values({
85
+ id: record.id,
86
+ type: record.type,
87
+ targetRecipeId: record.targetRecipeId,
88
+ relatedRecipeIds: JSON.stringify(record.relatedRecipeIds),
89
+ confidence: record.confidence,
90
+ source: record.source,
91
+ description: record.description,
92
+ evidence: JSON.stringify(record.evidence),
93
+ status: record.status,
94
+ proposedAt: record.proposedAt,
95
+ expiresAt: record.expiresAt,
96
+ })
97
+ .run();
81
98
  return record;
82
99
  }
83
100
  /* ═══════════════════ Read ═══════════════════ */
84
101
  /** 按 ID 查询 */
85
102
  findById(id) {
86
- const row = this.#db.prepare(`SELECT * FROM evolution_proposals WHERE id = ?`).get(id);
103
+ const row = this.#drizzle
104
+ .select()
105
+ .from(evolutionProposals)
106
+ .where(eq(evolutionProposals.id, id))
107
+ .limit(1)
108
+ .get();
87
109
  return row ? ProposalRepository.#mapRow(row) : null;
88
110
  }
89
111
  /** 按条件查询 */
90
112
  find(filter = {}) {
91
113
  const conditions = [];
92
- const params = [];
93
114
  if (filter.status) {
94
115
  if (Array.isArray(filter.status)) {
95
- const placeholders = filter.status.map(() => '?').join(', ');
96
- conditions.push(`status IN (${placeholders})`);
97
- params.push(...filter.status);
116
+ conditions.push(inArray(evolutionProposals.status, filter.status));
98
117
  }
99
118
  else {
100
- conditions.push('status = ?');
101
- params.push(filter.status);
119
+ conditions.push(eq(evolutionProposals.status, filter.status));
102
120
  }
103
121
  }
104
122
  if (filter.type) {
105
- conditions.push('type = ?');
106
- params.push(filter.type);
123
+ conditions.push(eq(evolutionProposals.type, filter.type));
107
124
  }
108
125
  if (filter.targetRecipeId) {
109
- conditions.push('target_recipe_id = ?');
110
- params.push(filter.targetRecipeId);
126
+ conditions.push(eq(evolutionProposals.targetRecipeId, filter.targetRecipeId));
111
127
  }
112
128
  if (filter.source) {
113
- conditions.push('source = ?');
114
- params.push(filter.source);
129
+ conditions.push(eq(evolutionProposals.source, filter.source));
115
130
  }
116
131
  if (filter.expiredBefore) {
117
- conditions.push('expires_at <= ?');
118
- params.push(filter.expiredBefore);
132
+ conditions.push(lte(evolutionProposals.expiresAt, filter.expiredBefore));
119
133
  }
120
- const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
121
- const rows = this.#db
122
- .prepare(`SELECT * FROM evolution_proposals ${where} ORDER BY proposed_at DESC`)
123
- .all(...params);
124
- return rows.map(ProposalRepository.#mapRow);
134
+ const condition = conditions.length > 0 ? and(...conditions) : undefined;
135
+ const rows = this.#drizzle
136
+ .select()
137
+ .from(evolutionProposals)
138
+ .where(condition)
139
+ .orderBy(desc(evolutionProposals.proposedAt))
140
+ .all();
141
+ return rows.map((r) => ProposalRepository.#mapRow(r));
125
142
  }
126
143
  /** 查询已到期的 observing 状态 Proposal */
127
144
  findExpiredObserving() {
@@ -152,44 +169,81 @@ export class ProposalRepository {
152
169
  return false;
153
170
  }
154
171
  const expiresAt = now + (OBSERVATION_WINDOWS[proposal.type] ?? DEFAULT_OBSERVATION_WINDOW);
155
- const result = this.#db
156
- .prepare(`UPDATE evolution_proposals SET status = 'observing', expires_at = ? WHERE id = ? AND status = 'pending'`)
157
- .run(expiresAt, id);
172
+ const result = this.#drizzle
173
+ .update(evolutionProposals)
174
+ .set({ status: 'observing', expiresAt })
175
+ .where(and(eq(evolutionProposals.id, id), eq(evolutionProposals.status, 'pending')))
176
+ .run();
158
177
  return result.changes > 0;
159
178
  }
160
179
  /** 标记 Proposal 为已执行 */
161
180
  markExecuted(id, resolution, resolvedBy = 'auto') {
162
- const result = this.#db
163
- .prepare(`UPDATE evolution_proposals SET status = 'executed', resolved_at = ?, resolved_by = ?, resolution = ? WHERE id = ? AND status = 'observing'`)
164
- .run(Date.now(), resolvedBy, resolution, id);
181
+ const result = this.#drizzle
182
+ .update(evolutionProposals)
183
+ .set({
184
+ status: 'executed',
185
+ resolvedAt: Date.now(),
186
+ resolvedBy,
187
+ resolution,
188
+ })
189
+ .where(and(eq(evolutionProposals.id, id), eq(evolutionProposals.status, 'observing')))
190
+ .run();
165
191
  return result.changes > 0;
166
192
  }
167
193
  /** 标记 Proposal 为已拒绝 */
168
194
  markRejected(id, resolution, resolvedBy = 'auto') {
169
- const result = this.#db
170
- .prepare(`UPDATE evolution_proposals SET status = 'rejected', resolved_at = ?, resolved_by = ?, resolution = ? WHERE id = ? AND status IN ('pending', 'observing')`)
171
- .run(Date.now(), resolvedBy, resolution, id);
195
+ const result = this.#drizzle
196
+ .update(evolutionProposals)
197
+ .set({
198
+ status: 'rejected',
199
+ resolvedAt: Date.now(),
200
+ resolvedBy,
201
+ resolution,
202
+ })
203
+ .where(and(eq(evolutionProposals.id, id), inArray(evolutionProposals.status, ['pending', 'observing'])))
204
+ .run();
172
205
  return result.changes > 0;
173
206
  }
174
207
  /** 标记 Proposal 为过期 */
175
208
  markExpired(id) {
176
- const result = this.#db
177
- .prepare(`UPDATE evolution_proposals SET status = 'expired', resolved_at = ? WHERE id = ? AND status IN ('pending', 'observing')`)
178
- .run(Date.now(), id);
209
+ const result = this.#drizzle
210
+ .update(evolutionProposals)
211
+ .set({
212
+ status: 'expired',
213
+ resolvedAt: Date.now(),
214
+ })
215
+ .where(and(eq(evolutionProposals.id, id), inArray(evolutionProposals.status, ['pending', 'observing'])))
216
+ .run();
179
217
  return result.changes > 0;
180
218
  }
181
219
  /** 更新 evidence(用于追加观察期指标快照) */
182
220
  updateEvidence(id, evidence) {
183
- const result = this.#db
184
- .prepare(`UPDATE evolution_proposals SET evidence = ? WHERE id = ?`)
185
- .run(JSON.stringify(evidence), id);
221
+ const result = this.#drizzle
222
+ .update(evolutionProposals)
223
+ .set({ evidence: JSON.stringify(evidence) })
224
+ .where(eq(evolutionProposals.id, id))
225
+ .run();
186
226
  return result.changes > 0;
187
227
  }
228
+ /* ═══════════════════ Delete ═══════════════════ */
229
+ /** 按 target Recipe ID 删除所有 Proposal(用于知识删除时清理关联提案) */
230
+ deleteByTargetRecipeId(targetRecipeId) {
231
+ const result = this.#drizzle
232
+ .delete(evolutionProposals)
233
+ .where(eq(evolutionProposals.targetRecipeId, targetRecipeId))
234
+ .run();
235
+ return result.changes;
236
+ }
188
237
  /* ═══════════════════ Stats ═══════════════════ */
189
238
  /** 统计各状态的 Proposal 数量 */
190
239
  stats() {
191
- const rows = this.#db
192
- .prepare(`SELECT status, COUNT(*) as count FROM evolution_proposals GROUP BY status`)
240
+ const rows = this.#drizzle
241
+ .select({
242
+ status: evolutionProposals.status,
243
+ count: count(),
244
+ })
245
+ .from(evolutionProposals)
246
+ .groupBy(evolutionProposals.status)
193
247
  .all();
194
248
  const result = {
195
249
  pending: 0,
@@ -206,9 +260,12 @@ export class ProposalRepository {
206
260
  /* ═══════════════════ Private ═══════════════════ */
207
261
  /** 去重检查:同 target + 同 type 是否已有 pending/observing Proposal */
208
262
  #hasDuplicate(targetRecipeId, type) {
209
- const row = this.#db
210
- .prepare(`SELECT 1 FROM evolution_proposals WHERE target_recipe_id = ? AND type = ? AND status IN ('pending', 'observing') LIMIT 1`)
211
- .get(targetRecipeId, type);
263
+ const row = this.#drizzle
264
+ .select({ id: evolutionProposals.id })
265
+ .from(evolutionProposals)
266
+ .where(and(eq(evolutionProposals.targetRecipeId, targetRecipeId), eq(evolutionProposals.type, type), inArray(evolutionProposals.status, ['pending', 'observing'])))
267
+ .limit(1)
268
+ .get();
212
269
  return row !== undefined;
213
270
  }
214
271
  /** 根据 type + confidence 判断初始状态 */
@@ -221,22 +278,22 @@ export class ProposalRepository {
221
278
  const rand = randomBytes(4).toString('hex');
222
279
  return `ep-${timestamp}-${rand}`;
223
280
  }
224
- /** DB → ProposalRecord */
281
+ /** Drizzle Row → ProposalRecord */
225
282
  static #mapRow(row) {
226
283
  return {
227
284
  id: row.id,
228
285
  type: row.type,
229
- targetRecipeId: row.target_recipe_id,
230
- relatedRecipeIds: safeJsonParse(row.related_recipe_ids, []),
286
+ targetRecipeId: row.targetRecipeId,
287
+ relatedRecipeIds: safeJsonParse(row.relatedRecipeIds, []),
231
288
  confidence: row.confidence,
232
289
  source: row.source,
233
- description: row.description,
290
+ description: row.description ?? '',
234
291
  evidence: safeJsonParse(row.evidence, []),
235
292
  status: row.status,
236
- proposedAt: row.proposed_at,
237
- expiresAt: row.expires_at,
238
- resolvedAt: row.resolved_at ?? null,
239
- resolvedBy: row.resolved_by ?? null,
293
+ proposedAt: row.proposedAt,
294
+ expiresAt: row.expiresAt,
295
+ resolvedAt: row.resolvedAt ?? null,
296
+ resolvedBy: row.resolvedBy ?? null,
240
297
  resolution: row.resolution ?? null,
241
298
  };
242
299
  }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * GuardViolationRepository — Guard 违反记录的仓储实现
3
+ *
4
+ * 从 ViolationsStore 提取的数据操作,
5
+ * 使用 Drizzle 类型安全 API 操作 guard_violations 表。
6
+ */
7
+ import { guardViolations } from '../../infrastructure/database/drizzle/schema.js';
8
+ import { RepositoryBase } from '../base/RepositoryBase.js';
9
+ export interface ViolationRecord {
10
+ ruleId?: string;
11
+ severity?: string;
12
+ message?: string;
13
+ line?: number;
14
+ [key: string]: unknown;
15
+ }
16
+ export interface GuardViolationEntity {
17
+ id: string;
18
+ filePath: string;
19
+ triggeredAt: string;
20
+ violationCount: number;
21
+ summary: string;
22
+ violations: ViolationRecord[];
23
+ createdAt: number;
24
+ }
25
+ export interface GuardViolationInsert {
26
+ id: string;
27
+ filePath: string;
28
+ triggeredAt: string;
29
+ violationCount: number;
30
+ summary?: string;
31
+ violations: ViolationRecord[];
32
+ createdAt: number;
33
+ }
34
+ export interface ViolationStats {
35
+ totalRuns: number;
36
+ totalViolations: number;
37
+ averageViolationsPerRun: string | number;
38
+ lastRunAt: string | null;
39
+ }
40
+ export interface ViolationStatByRule {
41
+ ruleId: string | null;
42
+ severity: string | null;
43
+ count: number;
44
+ }
45
+ export interface PaginatedViolations {
46
+ data: GuardViolationEntity[];
47
+ pagination: {
48
+ page: number;
49
+ limit: number;
50
+ total: number;
51
+ pages: number;
52
+ };
53
+ }
54
+ export declare class GuardViolationRepositoryImpl extends RepositoryBase<typeof guardViolations, GuardViolationEntity> {
55
+ #private;
56
+ /** 最大保留条数 */
57
+ static readonly MAX_RUNS = 200;
58
+ constructor(drizzle: ConstructorParameters<typeof RepositoryBase<typeof guardViolations, GuardViolationEntity>>[0]);
59
+ findById(id: string): Promise<GuardViolationEntity | null>;
60
+ create(data: GuardViolationInsert): Promise<GuardViolationEntity>;
61
+ delete(id: string): Promise<boolean>;
62
+ /** 获取指定文件的最近一条记录 (用于去重比较) */
63
+ getLastByFile(filePath: string): Promise<{
64
+ id: string;
65
+ violationsJson: string;
66
+ } | null>;
67
+ /** 刷新已有记录的时间戳 (去重命中时) */
68
+ refreshTimestamp(id: string): Promise<void>;
69
+ /** 获取所有运行记录 (最旧在前) */
70
+ getRuns(): Promise<GuardViolationEntity[]>;
71
+ /** 按文件路径查询 */
72
+ getRunsByFile(filePath: string): Promise<GuardViolationEntity[]>;
73
+ /** 获取最近 N 条记录 */
74
+ getRecentRuns(n?: number): Promise<GuardViolationEntity[]>;
75
+ /** 分页查询 */
76
+ list(filters?: {
77
+ file?: string;
78
+ }, options?: {
79
+ page?: number;
80
+ limit?: number;
81
+ }): Promise<PaginatedViolations>;
82
+ /** 获取统计汇总 */
83
+ getStats(): Promise<ViolationStats>;
84
+ /**
85
+ * 按规则 ID 聚合统计
86
+ * 利用 SQLite json_each 展开 violations_json 数组
87
+ *
88
+ * json_each 是 SQLite 专有函数,Drizzle 无 typed API (ORM limitation)
89
+ */
90
+ getStatsByRule(): Promise<ViolationStatByRule[]>;
91
+ /** 截断超限记录,保留最新 maxRuns 条 */
92
+ enforceCapacity(maxRuns?: number): Promise<number>;
93
+ /** 清空所有记录 */
94
+ clearAll(): Promise<void>;
95
+ /** 清除指定文件的记录 */
96
+ clearByFile(filePath: string): Promise<number>;
97
+ /**
98
+ * 最近的 violation JSON 列表 (CoverageAnalyzer.#getRecentViolations)
99
+ */
100
+ findRecentViolationsJson(limit: number): Array<{
101
+ filePath: string;
102
+ violationsJson: string;
103
+ }>;
104
+ }
@@ -0,0 +1,217 @@
1
+ /**
2
+ * GuardViolationRepository — Guard 违反记录的仓储实现
3
+ *
4
+ * 从 ViolationsStore 提取的数据操作,
5
+ * 使用 Drizzle 类型安全 API 操作 guard_violations 表。
6
+ */
7
+ import { asc, count, desc, eq, sql } from 'drizzle-orm';
8
+ import { guardViolations } from '../../infrastructure/database/drizzle/schema.js';
9
+ import { RepositoryBase } from '../base/RepositoryBase.js';
10
+ /* ═══ Repository 实现 ═══ */
11
+ export class GuardViolationRepositoryImpl extends RepositoryBase {
12
+ /** 最大保留条数 */
13
+ static MAX_RUNS = 200;
14
+ constructor(drizzle) {
15
+ super(drizzle, guardViolations);
16
+ }
17
+ /* ─── CRUD ─── */
18
+ async findById(id) {
19
+ const row = this.drizzle.select().from(this.table).where(eq(this.table.id, id)).limit(1).get();
20
+ return row ? this.#mapRow(row) : null;
21
+ }
22
+ async create(data) {
23
+ this.drizzle
24
+ .insert(this.table)
25
+ .values({
26
+ id: data.id,
27
+ filePath: data.filePath,
28
+ triggeredAt: data.triggeredAt,
29
+ violationCount: data.violationCount,
30
+ summary: data.summary ?? '',
31
+ violationsJson: JSON.stringify(data.violations),
32
+ createdAt: data.createdAt,
33
+ })
34
+ .run();
35
+ return (await this.findById(data.id));
36
+ }
37
+ async delete(id) {
38
+ const result = this.drizzle.delete(this.table).where(eq(this.table.id, id)).run();
39
+ return result.changes > 0;
40
+ }
41
+ /* ─── 去重查询 ─── */
42
+ /** 获取指定文件的最近一条记录 (用于去重比较) */
43
+ async getLastByFile(filePath) {
44
+ const row = this.drizzle
45
+ .select({
46
+ id: this.table.id,
47
+ violationsJson: this.table.violationsJson,
48
+ })
49
+ .from(this.table)
50
+ .where(eq(this.table.filePath, filePath))
51
+ .orderBy(desc(this.table.createdAt))
52
+ .limit(1)
53
+ .get();
54
+ return row ? { id: row.id, violationsJson: row.violationsJson ?? '[]' } : null;
55
+ }
56
+ /** 刷新已有记录的时间戳 (去重命中时) */
57
+ async refreshTimestamp(id) {
58
+ this.drizzle
59
+ .update(this.table)
60
+ .set({
61
+ triggeredAt: new Date().toISOString(),
62
+ createdAt: Math.floor(Date.now() / 1000),
63
+ })
64
+ .where(eq(this.table.id, id))
65
+ .run();
66
+ }
67
+ /* ─── 查询 ─── */
68
+ /** 获取所有运行记录 (最旧在前) */
69
+ async getRuns() {
70
+ const rows = this.drizzle.select().from(this.table).orderBy(asc(this.table.createdAt)).all();
71
+ return rows.map((r) => this.#mapRow(r));
72
+ }
73
+ /** 按文件路径查询 */
74
+ async getRunsByFile(filePath) {
75
+ const rows = this.drizzle
76
+ .select()
77
+ .from(this.table)
78
+ .where(eq(this.table.filePath, filePath))
79
+ .orderBy(asc(this.table.createdAt))
80
+ .all();
81
+ return rows.map((r) => this.#mapRow(r));
82
+ }
83
+ /** 获取最近 N 条记录 */
84
+ async getRecentRuns(n = 20) {
85
+ const rows = this.drizzle
86
+ .select()
87
+ .from(this.table)
88
+ .orderBy(desc(this.table.createdAt), sql `rowid DESC`)
89
+ .limit(n)
90
+ .all();
91
+ return rows.reverse().map((r) => this.#mapRow(r));
92
+ }
93
+ /** 分页查询 */
94
+ async list(filters = {}, options = {}) {
95
+ const { page = 1, limit = 20 } = options;
96
+ const offset = (page - 1) * limit;
97
+ const condition = filters.file ? eq(this.table.filePath, filters.file) : undefined;
98
+ const rows = this.drizzle
99
+ .select()
100
+ .from(this.table)
101
+ .where(condition)
102
+ .orderBy(desc(this.table.createdAt))
103
+ .limit(limit)
104
+ .offset(offset)
105
+ .all();
106
+ const [totalRow] = this.drizzle
107
+ .select({ cnt: count() })
108
+ .from(this.table)
109
+ .where(condition)
110
+ .all();
111
+ const total = totalRow?.cnt ?? 0;
112
+ return {
113
+ data: rows.map((r) => this.#mapRow(r)),
114
+ pagination: { page, limit, total, pages: Math.ceil(total / limit) },
115
+ };
116
+ }
117
+ /* ─── 统计 ─── */
118
+ /** 获取统计汇总 */
119
+ async getStats() {
120
+ const [row] = this.drizzle
121
+ .select({
122
+ totalRuns: count(),
123
+ totalViolations: sql `COALESCE(SUM(${this.table.violationCount}), 0)`,
124
+ lastRunAt: sql `MAX(${this.table.triggeredAt})`,
125
+ })
126
+ .from(this.table)
127
+ .all();
128
+ const totalRuns = row?.totalRuns ?? 0;
129
+ const totalViolations = row?.totalViolations ?? 0;
130
+ return {
131
+ totalRuns,
132
+ totalViolations,
133
+ averageViolationsPerRun: totalRuns > 0 ? (totalViolations / totalRuns).toFixed(2) : 0,
134
+ lastRunAt: row?.lastRunAt ?? null,
135
+ };
136
+ }
137
+ /**
138
+ * 按规则 ID 聚合统计
139
+ * 利用 SQLite json_each 展开 violations_json 数组
140
+ *
141
+ * json_each 是 SQLite 专有函数,Drizzle 无 typed API (ORM limitation)
142
+ */
143
+ async getStatsByRule() {
144
+ try {
145
+ const rows = this.drizzle.all(sql `
146
+ SELECT
147
+ json_extract(j.value, '$.ruleId') AS ruleId,
148
+ json_extract(j.value, '$.severity') AS severity,
149
+ COUNT(*) AS count
150
+ FROM ${this.table} gv, json_each(gv.violations_json) j
151
+ WHERE json_extract(j.value, '$.ruleId') IS NOT NULL
152
+ GROUP BY ruleId, severity
153
+ ORDER BY count DESC
154
+ `);
155
+ return rows;
156
+ }
157
+ catch {
158
+ return [];
159
+ }
160
+ }
161
+ /* ─── 容量控制 ─── */
162
+ /** 截断超限记录,保留最新 maxRuns 条 */
163
+ async enforceCapacity(maxRuns = GuardViolationRepositoryImpl.MAX_RUNS) {
164
+ const result = this.drizzle
165
+ .delete(this.table)
166
+ .where(sql `${this.table.id} NOT IN (
167
+ SELECT ${this.table.id} FROM ${this.table}
168
+ ORDER BY ${this.table.createdAt} DESC
169
+ LIMIT ${maxRuns}
170
+ )`)
171
+ .run();
172
+ return result.changes;
173
+ }
174
+ /** 清空所有记录 */
175
+ async clearAll() {
176
+ this.drizzle.delete(this.table).run();
177
+ }
178
+ /** 清除指定文件的记录 */
179
+ async clearByFile(filePath) {
180
+ const result = this.drizzle.delete(this.table).where(eq(this.table.filePath, filePath)).run();
181
+ return result.changes;
182
+ }
183
+ /**
184
+ * 最近的 violation JSON 列表 (CoverageAnalyzer.#getRecentViolations)
185
+ */
186
+ findRecentViolationsJson(limit) {
187
+ return this.drizzle
188
+ .select({
189
+ filePath: this.table.filePath,
190
+ violationsJson: this.table.violationsJson,
191
+ })
192
+ .from(this.table)
193
+ .orderBy(desc(this.table.createdAt))
194
+ .limit(limit)
195
+ .all();
196
+ }
197
+ /* ─── 内部辅助 ─── */
198
+ #mapRow(row) {
199
+ return {
200
+ id: row.id,
201
+ filePath: row.filePath,
202
+ triggeredAt: row.triggeredAt,
203
+ violationCount: row.violationCount ?? 0,
204
+ summary: row.summary ?? '',
205
+ violations: safeParseJSON(row.violationsJson, []),
206
+ createdAt: row.createdAt,
207
+ };
208
+ }
209
+ }
210
+ function safeParseJSON(str, fallback) {
211
+ try {
212
+ return str ? JSON.parse(str) : fallback;
213
+ }
214
+ catch {
215
+ return fallback;
216
+ }
217
+ }