autosnippet 2.8.3 → 2.10.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 +5 -5
- package/bin/cli.js +5 -33
- package/config/constitution.yaml +9 -2
- package/dashboard/dist/assets/{icons-B_Xg4B-s.js → icons-BkT3XrKf.js} +105 -100
- package/dashboard/dist/assets/index-BsB7DzW4.css +1 -0
- package/dashboard/dist/assets/index-DdmQMrJJ.js +155 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/cli/AiScanService.js +13 -11
- package/lib/cli/KnowledgeSyncService.js +343 -0
- package/lib/cli/SetupService.js +9 -27
- package/lib/core/ast/ProjectGraph.js +160 -0
- package/lib/core/gateway/GatewayActionRegistry.js +48 -58
- package/lib/domain/index.js +16 -11
- package/lib/domain/knowledge/KnowledgeEntry.js +351 -0
- package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
- package/lib/domain/knowledge/Lifecycle.js +109 -0
- package/lib/domain/knowledge/index.js +27 -0
- package/lib/domain/knowledge/values/Constraints.js +125 -0
- package/lib/domain/knowledge/values/Content.js +86 -0
- package/lib/domain/knowledge/values/Quality.js +93 -0
- package/lib/domain/knowledge/values/Reasoning.js +69 -0
- package/lib/domain/knowledge/values/Relations.js +168 -0
- package/lib/domain/knowledge/values/Stats.js +87 -0
- package/lib/domain/knowledge/values/index.js +9 -0
- package/lib/external/ai/AiProvider.js +48 -0
- package/lib/external/ai/providers/GoogleGeminiProvider.js +12 -3
- package/lib/external/mcp/McpServer.js +7 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +3 -2
- package/lib/external/mcp/handlers/bootstrap.js +121 -12
- package/lib/external/mcp/handlers/browse.js +77 -73
- package/lib/external/mcp/handlers/candidate.js +29 -276
- package/lib/external/mcp/handlers/guard.js +2 -0
- package/lib/external/mcp/handlers/knowledge.js +205 -0
- package/lib/external/mcp/handlers/skill.js +4 -2
- package/lib/external/mcp/handlers/structure.js +25 -23
- package/lib/external/mcp/handlers/system.js +10 -12
- package/lib/external/mcp/tools.js +125 -138
- package/lib/http/HttpServer.js +4 -8
- package/lib/http/middleware/requestLogger.js +3 -3
- package/lib/http/routes/ai.js +17 -1
- 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/http/routes/skills.js +44 -1
- package/lib/infrastructure/cache/GraphCache.js +143 -0
- package/lib/infrastructure/database/migrations/015_create_token_usage.js +27 -0
- package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
- package/lib/infrastructure/external/XcodeAutomation.js +187 -103
- package/lib/infrastructure/realtime/RealtimeService.js +14 -2
- package/lib/injection/ServiceContainer.js +164 -63
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +373 -0
- package/lib/repository/token/TokenUsageStore.js +162 -0
- package/lib/service/automation/DirectiveDetector.js +2 -3
- package/lib/service/automation/FileWatcher.js +67 -28
- package/lib/service/automation/XcodeIntegration.js +931 -156
- package/lib/service/automation/handlers/AlinkHandler.js +6 -4
- package/lib/service/automation/handlers/CreateHandler.js +53 -18
- package/lib/service/automation/handlers/GuardHandler.js +183 -20
- package/lib/service/automation/handlers/SearchHandler.js +35 -17
- package/lib/service/chat/AnalystAgent.js +25 -14
- package/lib/service/chat/CandidateGuardrail.js +1 -1
- package/lib/service/chat/ChatAgent.js +280 -48
- package/lib/service/chat/ContextWindow.js +92 -8
- package/lib/service/chat/HandoffProtocol.js +26 -1
- package/lib/service/chat/ProducerAgent.js +11 -9
- package/lib/service/chat/tools.js +298 -194
- package/lib/service/guard/GuardCheckEngine.js +114 -10
- package/lib/service/guard/GuardService.js +59 -48
- package/lib/service/knowledge/ConfidenceRouter.js +159 -0
- package/lib/service/knowledge/KnowledgeFileWriter.js +602 -0
- package/lib/service/knowledge/KnowledgeService.js +725 -0
- package/lib/service/search/SearchEngine.js +92 -19
- package/lib/service/skills/SignalCollector.js +15 -9
- package/lib/service/skills/SkillAdvisor.js +13 -11
- package/lib/service/snippet/SnippetFactory.js +5 -5
- package/lib/service/spm/SpmService.js +119 -18
- 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 +6 -6
- package/skills/autosnippet-coldstart/SKILL.md +7 -3
- 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 +16 -4
- 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-CkIih2CC.css +0 -1
- package/dashboard/dist/assets/index-Duc8Qk-c.js +0 -197
- 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 -973
- package/lib/service/recipe/RecipeFileWriter.js +0 -514
- package/lib/service/recipe/RecipeService.js +0 -786
- package/lib/service/recipe/RecipeStatsTracker.js +0 -148
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration 016: Create knowledge_entries unified table
|
|
3
|
+
*
|
|
4
|
+
* 合并 candidates + recipes 为单表 knowledge_entries。
|
|
5
|
+
* 所有字段统一命名(snake_case),消除 metadata_json 袋子模式。
|
|
6
|
+
*
|
|
7
|
+
* 数据迁移策略:
|
|
8
|
+
* 1. 创建 knowledge_entries 表
|
|
9
|
+
* 2. 从 recipes 迁移数据 → lifecycle = active/deprecated/draft
|
|
10
|
+
* 3. 从 candidates 迁移数据 → lifecycle = 原 status 映射
|
|
11
|
+
* 4. 更新 knowledge_edges 的 from_type/to_type
|
|
12
|
+
* 5. 旧表重命名为 _legacy_* (保留回滚能力)
|
|
13
|
+
*/
|
|
14
|
+
export default function migrate(db) {
|
|
15
|
+
// 1. 检查是否已存在
|
|
16
|
+
const existing = db.prepare(
|
|
17
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='knowledge_entries'"
|
|
18
|
+
).get();
|
|
19
|
+
|
|
20
|
+
if (existing) {
|
|
21
|
+
process.stderr.write(' ℹ️ 016: knowledge_entries already exists, skipping\n');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// 2. 建表
|
|
26
|
+
db.exec(`
|
|
27
|
+
CREATE TABLE knowledge_entries (
|
|
28
|
+
id TEXT PRIMARY KEY,
|
|
29
|
+
title TEXT NOT NULL DEFAULT '',
|
|
30
|
+
trigger_key TEXT DEFAULT '',
|
|
31
|
+
description TEXT DEFAULT '',
|
|
32
|
+
|
|
33
|
+
lifecycle TEXT NOT NULL DEFAULT 'draft',
|
|
34
|
+
lifecycle_history TEXT DEFAULT '[]',
|
|
35
|
+
probation INTEGER DEFAULT 0,
|
|
36
|
+
|
|
37
|
+
language TEXT NOT NULL DEFAULT '',
|
|
38
|
+
category TEXT NOT NULL DEFAULT '',
|
|
39
|
+
kind TEXT DEFAULT 'pattern',
|
|
40
|
+
knowledge_type TEXT DEFAULT 'code-pattern',
|
|
41
|
+
complexity TEXT DEFAULT 'intermediate',
|
|
42
|
+
scope TEXT DEFAULT 'universal',
|
|
43
|
+
difficulty TEXT,
|
|
44
|
+
tags TEXT DEFAULT '[]',
|
|
45
|
+
|
|
46
|
+
summary_cn TEXT DEFAULT '',
|
|
47
|
+
summary_en TEXT DEFAULT '',
|
|
48
|
+
usage_guide_cn TEXT DEFAULT '',
|
|
49
|
+
usage_guide_en TEXT DEFAULT '',
|
|
50
|
+
|
|
51
|
+
content TEXT DEFAULT '{}',
|
|
52
|
+
relations TEXT DEFAULT '{}',
|
|
53
|
+
constraints TEXT DEFAULT '{}',
|
|
54
|
+
reasoning TEXT DEFAULT '{}',
|
|
55
|
+
quality TEXT DEFAULT '{}',
|
|
56
|
+
stats TEXT DEFAULT '{}',
|
|
57
|
+
|
|
58
|
+
headers TEXT DEFAULT '[]',
|
|
59
|
+
header_paths TEXT DEFAULT '[]',
|
|
60
|
+
module_name TEXT DEFAULT '',
|
|
61
|
+
include_headers INTEGER DEFAULT 0,
|
|
62
|
+
|
|
63
|
+
agent_notes TEXT,
|
|
64
|
+
ai_insight TEXT,
|
|
65
|
+
|
|
66
|
+
reviewed_by TEXT,
|
|
67
|
+
reviewed_at INTEGER,
|
|
68
|
+
rejection_reason TEXT,
|
|
69
|
+
|
|
70
|
+
source TEXT DEFAULT 'manual',
|
|
71
|
+
source_file TEXT,
|
|
72
|
+
source_candidate_id TEXT,
|
|
73
|
+
|
|
74
|
+
created_by TEXT DEFAULT 'system',
|
|
75
|
+
created_at INTEGER NOT NULL,
|
|
76
|
+
updated_at INTEGER NOT NULL,
|
|
77
|
+
published_at INTEGER,
|
|
78
|
+
published_by TEXT,
|
|
79
|
+
|
|
80
|
+
content_hash TEXT
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
CREATE INDEX idx_ke2_lifecycle ON knowledge_entries(lifecycle);
|
|
84
|
+
CREATE INDEX idx_ke2_language ON knowledge_entries(language);
|
|
85
|
+
CREATE INDEX idx_ke2_category ON knowledge_entries(category);
|
|
86
|
+
CREATE INDEX idx_ke2_kind ON knowledge_entries(kind);
|
|
87
|
+
CREATE INDEX idx_ke2_knowledge_type ON knowledge_entries(knowledge_type);
|
|
88
|
+
CREATE INDEX idx_ke2_created_at ON knowledge_entries(created_at);
|
|
89
|
+
CREATE INDEX idx_ke2_trigger ON knowledge_entries(trigger_key);
|
|
90
|
+
CREATE INDEX idx_ke2_title ON knowledge_entries(title);
|
|
91
|
+
CREATE INDEX idx_ke2_source ON knowledge_entries(source);
|
|
92
|
+
CREATE INDEX idx_ke2_guard_active ON knowledge_entries(kind, lifecycle);
|
|
93
|
+
`);
|
|
94
|
+
|
|
95
|
+
const now = Math.floor(Date.now() / 1000);
|
|
96
|
+
let recipeCount = 0;
|
|
97
|
+
let candidateCount = 0;
|
|
98
|
+
|
|
99
|
+
// 3. 迁移 recipes → knowledge_entries
|
|
100
|
+
try {
|
|
101
|
+
const hasRecipes = db.prepare(
|
|
102
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='recipes'"
|
|
103
|
+
).get();
|
|
104
|
+
|
|
105
|
+
if (hasRecipes) {
|
|
106
|
+
const recipes = db.prepare('SELECT * FROM recipes').all();
|
|
107
|
+
|
|
108
|
+
const insertStmt = db.prepare(`
|
|
109
|
+
INSERT OR IGNORE INTO knowledge_entries (
|
|
110
|
+
id, title, trigger_key, description,
|
|
111
|
+
lifecycle, lifecycle_history, probation,
|
|
112
|
+
language, category, kind, knowledge_type, complexity, scope, difficulty, tags,
|
|
113
|
+
summary_cn, summary_en, usage_guide_cn, usage_guide_en,
|
|
114
|
+
content, relations, constraints, reasoning, quality, stats,
|
|
115
|
+
headers, header_paths, module_name, include_headers,
|
|
116
|
+
source, source_file, source_candidate_id,
|
|
117
|
+
created_by, created_at, updated_at, published_at, published_by,
|
|
118
|
+
reviewed_by, reviewed_at
|
|
119
|
+
) VALUES (
|
|
120
|
+
?, ?, ?, ?,
|
|
121
|
+
?, ?, ?,
|
|
122
|
+
?, ?, ?, ?, ?, ?, ?, ?,
|
|
123
|
+
?, ?, ?, ?,
|
|
124
|
+
?, ?, ?, ?, ?, ?,
|
|
125
|
+
?, ?, ?, ?,
|
|
126
|
+
?, ?, ?,
|
|
127
|
+
?, ?, ?, ?, ?,
|
|
128
|
+
?, ?
|
|
129
|
+
)
|
|
130
|
+
`);
|
|
131
|
+
|
|
132
|
+
for (const r of recipes) {
|
|
133
|
+
const lifecycle = r.status === 'active' ? 'active'
|
|
134
|
+
: r.status === 'deprecated' ? 'deprecated'
|
|
135
|
+
: 'draft';
|
|
136
|
+
|
|
137
|
+
const dims = _json(r.dimensions_json, {});
|
|
138
|
+
const quality = JSON.stringify({
|
|
139
|
+
completeness: r.quality_code_completeness || 0,
|
|
140
|
+
adaptation: r.quality_project_adaptation || 0,
|
|
141
|
+
documentation: r.quality_documentation_clarity || 0,
|
|
142
|
+
overall: r.quality_overall || 0,
|
|
143
|
+
grade: _calcGrade(r.quality_overall || 0),
|
|
144
|
+
});
|
|
145
|
+
const stats = JSON.stringify({
|
|
146
|
+
views: r.view_count || 0,
|
|
147
|
+
adoptions: r.adoption_count || 0,
|
|
148
|
+
applications: r.application_count || 0,
|
|
149
|
+
guard_hits: r.guard_hit_count || 0,
|
|
150
|
+
search_hits: 0,
|
|
151
|
+
authority: Math.min((r.quality_overall || 0) * 5, 5),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
insertStmt.run(
|
|
155
|
+
r.id,
|
|
156
|
+
r.title || '',
|
|
157
|
+
r.trigger || '',
|
|
158
|
+
r.description || '',
|
|
159
|
+
lifecycle,
|
|
160
|
+
'[]',
|
|
161
|
+
0,
|
|
162
|
+
r.language || '',
|
|
163
|
+
r.category || '',
|
|
164
|
+
r.kind || _inferKind(r.knowledge_type),
|
|
165
|
+
r.knowledge_type || 'code-pattern',
|
|
166
|
+
r.complexity || 'intermediate',
|
|
167
|
+
r.scope || 'universal',
|
|
168
|
+
dims.difficulty || null,
|
|
169
|
+
r.tags_json || '[]',
|
|
170
|
+
r.summary_cn || '',
|
|
171
|
+
r.summary_en || '',
|
|
172
|
+
r.usage_guide_cn || '',
|
|
173
|
+
r.usage_guide_en || '',
|
|
174
|
+
r.content_json || '{}',
|
|
175
|
+
r.relations_json || '{}',
|
|
176
|
+
r.constraints_json || '{}',
|
|
177
|
+
'{}', // reasoning (recipes 没有)
|
|
178
|
+
quality,
|
|
179
|
+
stats,
|
|
180
|
+
JSON.stringify(dims.headers || []),
|
|
181
|
+
'[]',
|
|
182
|
+
'',
|
|
183
|
+
0,
|
|
184
|
+
'migration',
|
|
185
|
+
r.source_file || null,
|
|
186
|
+
r.source_candidate_id || null,
|
|
187
|
+
r.created_by || 'system',
|
|
188
|
+
r.created_at || now,
|
|
189
|
+
r.updated_at || now,
|
|
190
|
+
r.published_at || null,
|
|
191
|
+
r.published_by || null,
|
|
192
|
+
null, // reviewed_by
|
|
193
|
+
null, // reviewed_at
|
|
194
|
+
);
|
|
195
|
+
recipeCount++;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} catch (err) {
|
|
199
|
+
process.stderr.write(` ⚠️ 016: Recipe migration error: ${err.message}\n`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 4. 迁移 candidates → knowledge_entries
|
|
203
|
+
try {
|
|
204
|
+
const hasCandidates = db.prepare(
|
|
205
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='candidates'"
|
|
206
|
+
).get();
|
|
207
|
+
|
|
208
|
+
if (hasCandidates) {
|
|
209
|
+
const candidates = db.prepare('SELECT * FROM candidates').all();
|
|
210
|
+
|
|
211
|
+
const insertStmt = db.prepare(`
|
|
212
|
+
INSERT OR IGNORE INTO knowledge_entries (
|
|
213
|
+
id, title, trigger_key, description,
|
|
214
|
+
lifecycle, lifecycle_history, probation,
|
|
215
|
+
language, category, kind, knowledge_type, complexity, scope, difficulty, tags,
|
|
216
|
+
summary_cn, summary_en, usage_guide_cn, usage_guide_en,
|
|
217
|
+
content, relations, constraints, reasoning, quality, stats,
|
|
218
|
+
headers, header_paths, module_name, include_headers,
|
|
219
|
+
source, source_file, source_candidate_id,
|
|
220
|
+
created_by, created_at, updated_at,
|
|
221
|
+
reviewed_by, reviewed_at, rejection_reason
|
|
222
|
+
) VALUES (
|
|
223
|
+
?, ?, ?, ?,
|
|
224
|
+
?, ?, ?,
|
|
225
|
+
?, ?, ?, ?, ?, ?, ?, ?,
|
|
226
|
+
?, ?, ?, ?,
|
|
227
|
+
?, ?, ?, ?, ?, ?,
|
|
228
|
+
?, ?, ?, ?,
|
|
229
|
+
?, ?, ?,
|
|
230
|
+
?, ?, ?,
|
|
231
|
+
?, ?, ?
|
|
232
|
+
)
|
|
233
|
+
`);
|
|
234
|
+
|
|
235
|
+
for (const c of candidates) {
|
|
236
|
+
const meta = _json(c.metadata_json, {});
|
|
237
|
+
const reasoning = _json(c.reasoning_json, {});
|
|
238
|
+
|
|
239
|
+
// 状态映射: applied → active, 其余 1:1
|
|
240
|
+
const lifecycle = c.status === 'applied' ? 'active' : (c.status || 'pending');
|
|
241
|
+
|
|
242
|
+
// 判断内容类型
|
|
243
|
+
const code = c.code || '';
|
|
244
|
+
const isMarkdown = code && (
|
|
245
|
+
code.includes('— 项目特写') || /^#{1,3}\s/.test(code.trimStart())
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
const content = JSON.stringify({
|
|
249
|
+
pattern: isMarkdown ? '' : code,
|
|
250
|
+
markdown: isMarkdown ? code : '',
|
|
251
|
+
rationale: meta.rationale || reasoning.whyStandard || '',
|
|
252
|
+
steps: meta.steps || [],
|
|
253
|
+
code_changes: meta.codeChanges || [],
|
|
254
|
+
verification: meta.verification || null,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// relations: 旧 candidate 可能是扁平数组
|
|
258
|
+
let relations = '{}';
|
|
259
|
+
if (meta.relations) {
|
|
260
|
+
if (Array.isArray(meta.relations)) {
|
|
261
|
+
const buckets = {};
|
|
262
|
+
for (const rel of meta.relations) {
|
|
263
|
+
const bucket = rel.type || 'related';
|
|
264
|
+
if (!buckets[bucket]) buckets[bucket] = [];
|
|
265
|
+
buckets[bucket].push({
|
|
266
|
+
target: rel.target || '',
|
|
267
|
+
description: rel.description || '',
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
relations = JSON.stringify(buckets);
|
|
271
|
+
} else {
|
|
272
|
+
relations = JSON.stringify(meta.relations);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const reasoningJson = JSON.stringify({
|
|
277
|
+
why_standard: reasoning.whyStandard || '',
|
|
278
|
+
sources: reasoning.sources || [],
|
|
279
|
+
confidence: reasoning.confidence ?? 0.7,
|
|
280
|
+
quality_signals: reasoning.qualitySignals || {},
|
|
281
|
+
alternatives: reasoning.alternatives || [],
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
insertStmt.run(
|
|
285
|
+
c.id,
|
|
286
|
+
meta.title || code.substring(0, 60) || '',
|
|
287
|
+
meta.trigger || '',
|
|
288
|
+
meta.description || '',
|
|
289
|
+
lifecycle,
|
|
290
|
+
c.status_history_json || '[]',
|
|
291
|
+
0,
|
|
292
|
+
c.language || '',
|
|
293
|
+
meta.category || c.category || 'general',
|
|
294
|
+
_inferKind(meta.knowledgeType),
|
|
295
|
+
meta.knowledgeType || 'code-pattern',
|
|
296
|
+
meta.complexity || 'intermediate',
|
|
297
|
+
meta.scope || 'universal',
|
|
298
|
+
null, // difficulty
|
|
299
|
+
JSON.stringify(meta.tags || []),
|
|
300
|
+
meta.summary || meta.summary_cn || '',
|
|
301
|
+
meta.summary_en || '',
|
|
302
|
+
meta.usageGuide || meta.usageGuide_cn || '',
|
|
303
|
+
meta.usageGuide_en || '',
|
|
304
|
+
content,
|
|
305
|
+
relations,
|
|
306
|
+
JSON.stringify(meta.constraints || {}),
|
|
307
|
+
reasoningJson,
|
|
308
|
+
JSON.stringify(meta.quality || {}),
|
|
309
|
+
'{}', // stats
|
|
310
|
+
JSON.stringify(meta.headers || []),
|
|
311
|
+
'[]',
|
|
312
|
+
'',
|
|
313
|
+
0,
|
|
314
|
+
c.source || 'manual',
|
|
315
|
+
meta.sourceFile || null,
|
|
316
|
+
null,
|
|
317
|
+
c.created_by || 'system',
|
|
318
|
+
c.created_at || now,
|
|
319
|
+
c.updated_at || now,
|
|
320
|
+
c.approved_by || c.rejected_by || null,
|
|
321
|
+
c.approved_at || null,
|
|
322
|
+
c.rejection_reason || null,
|
|
323
|
+
);
|
|
324
|
+
candidateCount++;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} catch (err) {
|
|
328
|
+
process.stderr.write(` ⚠️ 016: Candidate migration error: ${err.message}\n`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 5. 更新 knowledge_edges 的 type 字段
|
|
332
|
+
try {
|
|
333
|
+
const hasEdges = db.prepare(
|
|
334
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='knowledge_edges'"
|
|
335
|
+
).get();
|
|
336
|
+
if (hasEdges) {
|
|
337
|
+
db.exec(`
|
|
338
|
+
UPDATE knowledge_edges SET from_type = 'knowledge_entry' WHERE from_type = 'recipe';
|
|
339
|
+
UPDATE knowledge_edges SET to_type = 'knowledge_entry' WHERE to_type = 'recipe';
|
|
340
|
+
`);
|
|
341
|
+
}
|
|
342
|
+
} catch (err) {
|
|
343
|
+
process.stderr.write(` ⚠️ 016: knowledge_edges update error: ${err.message}\n`);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 6. 重命名旧表(不删除以保留回滚能力)
|
|
347
|
+
try {
|
|
348
|
+
const hasRecipes = db.prepare(
|
|
349
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='recipes'"
|
|
350
|
+
).get();
|
|
351
|
+
if (hasRecipes) {
|
|
352
|
+
db.exec(`ALTER TABLE recipes RENAME TO _legacy_recipes`);
|
|
353
|
+
}
|
|
354
|
+
} catch { /* already renamed or doesn't exist */ }
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
const hasCandidates = db.prepare(
|
|
358
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='candidates'"
|
|
359
|
+
).get();
|
|
360
|
+
if (hasCandidates) {
|
|
361
|
+
db.exec(`ALTER TABLE candidates RENAME TO _legacy_candidates`);
|
|
362
|
+
}
|
|
363
|
+
} catch { /* already renamed or doesn't exist */ }
|
|
364
|
+
|
|
365
|
+
process.stderr.write(
|
|
366
|
+
` ✅ 016_unified_knowledge_entries: Created table, migrated ${recipeCount} recipes + ${candidateCount} candidates\n`
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/* ── 迁移辅助函数 ── */
|
|
371
|
+
|
|
372
|
+
function _json(str, fallback) {
|
|
373
|
+
if (!str) return fallback;
|
|
374
|
+
try { return JSON.parse(str); } catch { return fallback; }
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function _inferKind(knowledgeType) {
|
|
378
|
+
const map = {
|
|
379
|
+
'code-standard': 'rule', 'code-style': 'rule', 'best-practice': 'rule',
|
|
380
|
+
'boundary-constraint': 'rule',
|
|
381
|
+
'code-pattern': 'pattern', 'architecture': 'pattern', 'solution': 'pattern',
|
|
382
|
+
'anti-pattern': 'pattern',
|
|
383
|
+
'code-relation': 'fact', 'inheritance': 'fact', 'call-chain': 'fact',
|
|
384
|
+
'data-flow': 'fact', 'module-dependency': 'fact',
|
|
385
|
+
};
|
|
386
|
+
return map[knowledgeType] || 'pattern';
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function _calcGrade(score) {
|
|
390
|
+
if (score >= 0.9) return 'A';
|
|
391
|
+
if (score >= 0.75) return 'B';
|
|
392
|
+
if (score >= 0.6) return 'C';
|
|
393
|
+
if (score >= 0.4) return 'D';
|
|
394
|
+
return 'F';
|
|
395
|
+
}
|