autosnippet 2.9.0 → 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.
Files changed (97) hide show
  1. package/README.md +4 -4
  2. package/bin/cli.js +5 -33
  3. package/config/constitution.yaml +9 -2
  4. package/dashboard/dist/assets/{icons-CH-H9x0E.js → icons-BkT3XrKf.js} +105 -100
  5. package/dashboard/dist/assets/index-BsB7DzW4.css +1 -0
  6. package/dashboard/dist/assets/index-DdmQMrJJ.js +155 -0
  7. package/dashboard/dist/index.html +3 -3
  8. package/lib/cli/AiScanService.js +13 -11
  9. package/lib/cli/KnowledgeSyncService.js +343 -0
  10. package/lib/cli/SetupService.js +8 -26
  11. package/lib/core/gateway/GatewayActionRegistry.js +48 -58
  12. package/lib/domain/index.js +16 -11
  13. package/lib/domain/knowledge/KnowledgeEntry.js +351 -0
  14. package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
  15. package/lib/domain/knowledge/Lifecycle.js +109 -0
  16. package/lib/domain/knowledge/index.js +27 -0
  17. package/lib/domain/knowledge/values/Constraints.js +125 -0
  18. package/lib/domain/knowledge/values/Content.js +86 -0
  19. package/lib/domain/knowledge/values/Quality.js +93 -0
  20. package/lib/domain/knowledge/values/Reasoning.js +69 -0
  21. package/lib/domain/knowledge/values/Relations.js +168 -0
  22. package/lib/domain/knowledge/values/Stats.js +87 -0
  23. package/lib/domain/knowledge/values/index.js +9 -0
  24. package/lib/external/ai/AiProvider.js +48 -0
  25. package/lib/external/mcp/McpServer.js +7 -5
  26. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +2 -2
  27. package/lib/external/mcp/handlers/bootstrap.js +116 -11
  28. package/lib/external/mcp/handlers/browse.js +77 -73
  29. package/lib/external/mcp/handlers/candidate.js +29 -276
  30. package/lib/external/mcp/handlers/guard.js +2 -0
  31. package/lib/external/mcp/handlers/knowledge.js +205 -0
  32. package/lib/external/mcp/handlers/structure.js +25 -23
  33. package/lib/external/mcp/handlers/system.js +10 -12
  34. package/lib/external/mcp/tools.js +125 -138
  35. package/lib/http/HttpServer.js +4 -8
  36. package/lib/http/routes/extract.js +48 -4
  37. package/lib/http/routes/knowledge.js +246 -0
  38. package/lib/http/routes/search.js +12 -17
  39. package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
  40. package/lib/infrastructure/external/XcodeAutomation.js +187 -103
  41. package/lib/injection/ServiceContainer.js +49 -60
  42. package/lib/repository/knowledge/KnowledgeRepository.impl.js +373 -0
  43. package/lib/service/automation/DirectiveDetector.js +2 -3
  44. package/lib/service/automation/FileWatcher.js +67 -28
  45. package/lib/service/automation/XcodeIntegration.js +931 -156
  46. package/lib/service/automation/handlers/AlinkHandler.js +6 -4
  47. package/lib/service/automation/handlers/CreateHandler.js +53 -18
  48. package/lib/service/automation/handlers/GuardHandler.js +183 -20
  49. package/lib/service/automation/handlers/SearchHandler.js +35 -17
  50. package/lib/service/chat/CandidateGuardrail.js +1 -1
  51. package/lib/service/chat/ChatAgent.js +46 -45
  52. package/lib/service/chat/ContextWindow.js +5 -5
  53. package/lib/service/chat/ProducerAgent.js +7 -7
  54. package/lib/service/chat/tools.js +130 -123
  55. package/lib/service/guard/GuardCheckEngine.js +114 -10
  56. package/lib/service/guard/GuardService.js +59 -48
  57. package/lib/service/knowledge/ConfidenceRouter.js +159 -0
  58. package/lib/service/knowledge/KnowledgeFileWriter.js +602 -0
  59. package/lib/service/knowledge/KnowledgeService.js +725 -0
  60. package/lib/service/search/SearchEngine.js +92 -19
  61. package/lib/service/skills/SignalCollector.js +12 -7
  62. package/lib/service/skills/SkillAdvisor.js +13 -11
  63. package/lib/service/snippet/SnippetFactory.js +5 -5
  64. package/package.json +1 -1
  65. package/scripts/install-cursor-skill.js +0 -6
  66. package/scripts/migrate-md-to-knowledge.mjs +364 -0
  67. package/skills/autosnippet-analysis/SKILL.md +15 -7
  68. package/skills/autosnippet-candidates/SKILL.md +6 -6
  69. package/skills/autosnippet-coldstart/SKILL.md +7 -3
  70. package/skills/autosnippet-concepts/SKILL.md +7 -6
  71. package/skills/autosnippet-create/SKILL.md +13 -13
  72. package/skills/autosnippet-intent/SKILL.md +3 -2
  73. package/skills/autosnippet-lifecycle/SKILL.md +5 -5
  74. package/skills/autosnippet-recipes/SKILL.md +16 -4
  75. package/templates/constitution.yaml +1 -1
  76. package/templates/copilot-instructions.md +6 -6
  77. package/templates/recipes-setup/README.md +3 -3
  78. package/dashboard/dist/assets/index-CqJRvYRL.js +0 -197
  79. package/dashboard/dist/assets/index-DICm9PNa.css +0 -1
  80. package/lib/cli/CandidateSyncService.js +0 -261
  81. package/lib/cli/SyncService.js +0 -356
  82. package/lib/domain/candidate/Candidate.js +0 -196
  83. package/lib/domain/candidate/CandidateRepository.js +0 -107
  84. package/lib/domain/candidate/Reasoning.js +0 -52
  85. package/lib/domain/recipe/Recipe.js +0 -421
  86. package/lib/domain/recipe/RecipeRepository.js +0 -54
  87. package/lib/domain/types/CandidateStatus.js +0 -52
  88. package/lib/http/routes/candidates.js +0 -559
  89. package/lib/http/routes/recipes.js +0 -397
  90. package/lib/repository/candidate/CandidateRepository.impl.js +0 -230
  91. package/lib/repository/recipe/RecipeRepository.impl.js +0 -498
  92. package/lib/service/candidate/CandidateAggregator.js +0 -52
  93. package/lib/service/candidate/CandidateFileWriter.js +0 -383
  94. package/lib/service/candidate/CandidateService.js +0 -1001
  95. package/lib/service/recipe/RecipeFileWriter.js +0 -514
  96. package/lib/service/recipe/RecipeService.js +0 -786
  97. package/lib/service/recipe/RecipeStatsTracker.js +0 -148
@@ -1,18 +1,50 @@
1
1
  /**
2
- * XcodeAutomation - Xcode AppleScript 自动化
2
+ * XcodeAutomation Xcode AppleScript 自动化
3
3
  *
4
- * 提供 Xcode 行操作:跳转行、选中行、剪切行、粘贴等。
5
- * 所有操作都带超时保护,Xcode 未运行时默认跳过。
4
+ * 通过 osascript 向 Xcode 发送键盘事件,实现行级操作:
5
+ * 跳转行、选中行内容、剪切行、粘贴、在行首插入、删除行内容、保存文档。
6
6
  *
7
- * V2 ESM 版本,对应 V1 SearchHandler/CreateHandler 中的散落 AppleScript 逻辑。
7
+ * 所有操作都带超时保护(OSASCRIPT_TIMEOUT),Xcode 未运行时安全跳过。
8
+ * 仅支持 macOS。
8
9
  */
9
10
 
10
11
  import { execSync, spawnSync } from 'node:child_process';
11
12
 
12
13
  const OSASCRIPT_TIMEOUT = 5000;
13
14
 
15
+ // ─────────────────────────────────────────────
16
+ // 内部辅助
17
+ // ─────────────────────────────────────────────
18
+
19
+ /**
20
+ * 将行号限制为有效正整数(最小值 1)
21
+ * @param {number} n 原始行号
22
+ * @returns {number} 安全的 1-based 行号
23
+ */
24
+ function _safeLine(n) {
25
+ return Number.isFinite(n) && n > 0 ? n : 1;
26
+ }
27
+
28
+ /**
29
+ * 执行 osascript 并返回是否成功
30
+ * @param {string[]} args osascript 参数数组(每对 `-e`, `script`)
31
+ * @returns {boolean}
32
+ */
33
+ function _run(args) {
34
+ try {
35
+ const res = spawnSync('osascript', args, { stdio: 'ignore', timeout: OSASCRIPT_TIMEOUT });
36
+ return res.status === 0;
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ // ─────────────────────────────────────────────
43
+ // 状态查询
44
+ // ─────────────────────────────────────────────
45
+
14
46
  /**
15
- * 检查 Xcode 是否正在运行(不启动 Xcode)
47
+ * 检查 Xcode 是否正在运行(不会启动 Xcode)
16
48
  */
17
49
  export function isXcodeRunning() {
18
50
  if (process.platform !== 'darwin') return false;
@@ -29,7 +61,7 @@ export function isXcodeRunning() {
29
61
  }
30
62
 
31
63
  /**
32
- * 检查 Xcode 是否是当前焦点应用
64
+ * 检查 Xcode 是否为当前前台应用
33
65
  */
34
66
  export function isXcodeFrontmost() {
35
67
  if (!isXcodeRunning()) return false;
@@ -44,132 +76,184 @@ export function isXcodeFrontmost() {
44
76
  }
45
77
  }
46
78
 
79
+ // ─────────────────────────────────────────────
80
+ // 行操作
81
+ // ─────────────────────────────────────────────
82
+
47
83
  /**
48
- * 在 Xcode 中剪切指定行内容(不含换行符,与 V1 一致)
49
- * Cmd+L 跳转 → Cmd+← 行首 → Cmd+Shift+→ 选中行内容 → Cmd+X 剪切
84
+ * 跳转到指定行
85
+ *
86
+ * 按键序列:Cmd+L → 输入行号 → Return
87
+ *
88
+ * @param {number} lineNumber 1-based 行号
89
+ * @returns {boolean} 是否成功
90
+ */
91
+ export function jumpToLineInXcode(lineNumber) {
92
+ if (!isXcodeRunning()) return false;
93
+ const n = _safeLine(lineNumber);
94
+ return _run([
95
+ '-e', 'tell application "Xcode" to activate',
96
+ '-e', 'delay 0.2',
97
+ '-e', 'tell application "System Events"',
98
+ '-e', ' keystroke "l" using command down',
99
+ '-e', ' delay 0.2',
100
+ '-e', ` keystroke "${String(n)}"`,
101
+ '-e', ' delay 0.2',
102
+ '-e', ' key code 36',
103
+ '-e', 'end tell',
104
+ ]);
105
+ }
106
+
107
+ /**
108
+ * 剪切指定行的文本内容(不含换行符)
109
+ *
110
+ * 按键序列:Cmd+L 跳转 → Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Cmd+X 剪切
111
+ *
50
112
  * @param {number} lineNumber 1-based 行号
51
113
  * @returns {boolean} 是否成功
52
114
  */
53
115
  export function cutLineInXcode(lineNumber) {
54
116
  if (!isXcodeRunning()) return false;
55
- const safeLineNumber = Number.isFinite(lineNumber) && lineNumber > 0 ? lineNumber : 1;
56
- try {
57
- const args = [
58
- '-e', 'tell application "Xcode" to activate',
59
- '-e', 'delay 0.5',
60
- '-e', 'tell application "System Events"',
61
- '-e', ' keystroke "l" using command down', // Cmd+L: Go to Line
62
- '-e', ' delay 0.5',
63
- '-e', ` keystroke "${String(safeLineNumber)}"`, // 输入行号
64
- '-e', ' delay 0.5',
65
- '-e', ' key code 36', // Return
66
- '-e', ' delay 0.5',
67
- '-e', ' key code 123 using command down', // Cmd+← 行首
68
- '-e', ' delay 0.5',
69
- '-e', ' key code 124 using {command down, shift down}', // Cmd+Shift+→ 选到行尾(不含换行)
70
- '-e', ' delay 0.5',
71
- '-e', ' keystroke "x" using command down', // Cmd+X
72
- '-e', 'end tell',
73
- ];
74
- const res = spawnSync('osascript', args, { stdio: 'ignore', timeout: OSASCRIPT_TIMEOUT });
75
- return res.status === 0;
76
- } catch {
77
- return false;
78
- }
117
+ const n = _safeLine(lineNumber);
118
+ return _run([
119
+ '-e', 'tell application "Xcode" to activate',
120
+ '-e', 'delay 0.5',
121
+ '-e', 'tell application "System Events"',
122
+ '-e', ' keystroke "l" using command down', // Cmd+L: Go to Line
123
+ '-e', ' delay 0.5',
124
+ '-e', ` keystroke "${String(n)}"`, // 输入行号
125
+ '-e', ' delay 0.5',
126
+ '-e', ' key code 36', // Return
127
+ '-e', ' delay 0.5',
128
+ '-e', ' key code 123 using command down', // Cmd+← 行首
129
+ '-e', ' delay 0.5',
130
+ '-e', ' key code 124 using {command down, shift down}', // Cmd+Shift+→ 选到行尾
131
+ '-e', ' delay 0.5',
132
+ '-e', ' keystroke "x" using command down', // Cmd+X
133
+ '-e', 'end tell',
134
+ ]);
79
135
  }
80
136
 
81
137
  /**
82
- * 在 Xcode 中跳转到指定行
138
+ * 删除指定行的文本内容(保留空行,不删除行本身)
139
+ *
140
+ * 按键序列:Cmd+L 跳转 → Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Delete
141
+ *
83
142
  * @param {number} lineNumber 1-based 行号
84
- * @returns {boolean}
143
+ * @returns {boolean} 是否成功
85
144
  */
86
- export function jumpToLineInXcode(lineNumber) {
145
+ export function deleteLineContentInXcode(lineNumber) {
87
146
  if (!isXcodeRunning()) return false;
88
- const safeLine = Number.isFinite(lineNumber) && lineNumber > 0 ? lineNumber : 1;
89
- try {
90
- const args = [
91
- '-e', 'tell application "Xcode" to activate',
92
- '-e', 'delay 0.2',
93
- '-e', 'tell application "System Events"',
94
- '-e', ' keystroke "l" using command down',
95
- '-e', ' delay 0.2',
96
- '-e', ` keystroke "${String(safeLine)}"`,
97
- '-e', ' delay 0.2',
98
- '-e', ' key code 36',
99
- '-e', 'end tell',
100
- ];
101
- const res = spawnSync('osascript', args, { stdio: 'ignore', timeout: OSASCRIPT_TIMEOUT });
102
- return res.status === 0;
103
- } catch {
104
- return false;
105
- }
147
+ const n = _safeLine(lineNumber);
148
+ return _run([
149
+ '-e', 'tell application "Xcode" to activate',
150
+ '-e', 'delay 0.3',
151
+ '-e', 'tell application "System Events"',
152
+ '-e', ' keystroke "l" using command down',
153
+ '-e', ' delay 0.3',
154
+ '-e', ` keystroke "${String(n)}"`,
155
+ '-e', ' delay 0.3',
156
+ '-e', ' key code 36',
157
+ '-e', ' delay 0.3',
158
+ '-e', ' key code 123 using command down', // Cmd+← 行首
159
+ '-e', ' delay 0.2',
160
+ '-e', ' key code 124 using {command down, shift down}', // Cmd+Shift+→ 选到行尾
161
+ '-e', ' delay 0.2',
162
+ '-e', ' key code 51', // Delete 键
163
+ '-e', ' delay 0.3',
164
+ '-e', 'end tell',
165
+ ]);
106
166
  }
107
167
 
168
+ // ─────────────────────────────────────────────
169
+ // 粘贴操作
170
+ // ─────────────────────────────────────────────
171
+
108
172
  /**
109
- * 在 Xcode 中执行粘贴(Cmd+V)
110
- * @returns {boolean}
173
+ * 执行粘贴(Cmd+V)
174
+ *
175
+ * 调用前须确保剪贴板已写入目标内容。
176
+ * @returns {boolean} 是否成功
111
177
  */
112
178
  export function pasteInXcode() {
113
179
  if (!isXcodeRunning()) return false;
114
- try {
115
- const args = [
116
- '-e', 'tell application "Xcode" to activate',
117
- '-e', 'delay 0.2',
118
- '-e', 'tell application "System Events"',
119
- '-e', ' keystroke "v" using command down',
120
- '-e', 'end tell',
121
- ];
122
- const res = spawnSync('osascript', args, { stdio: 'ignore', timeout: OSASCRIPT_TIMEOUT });
123
- return res.status === 0;
124
- } catch {
125
- return false;
126
- }
180
+ return _run([
181
+ '-e', 'tell application "Xcode" to activate',
182
+ '-e', 'delay 0.2',
183
+ '-e', 'tell application "System Events"',
184
+ '-e', ' keystroke "v" using command down',
185
+ '-e', 'end tell',
186
+ ]);
127
187
  }
128
188
 
129
189
  /**
130
- * 在 Xcode 中选中当前行内容后粘贴替换(V1 _tryAutoPasteXcode 逻辑)
131
- * 光标已在目标行(由 jumpToLineInXcode 定位)
132
- * Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Cmd+V 粘贴替换
133
- * @returns {boolean}
190
+ * 选中当前行内容后粘贴替换
191
+ *
192
+ * 假设光标已在目标行(通常由 jumpToLineInXcode 定位后调用)。
193
+ * 按键序列:Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Cmd+V 粘贴替换
194
+ *
195
+ * @returns {boolean} 是否成功
134
196
  */
135
197
  export function selectAndPasteInXcode() {
136
198
  if (!isXcodeRunning()) return false;
137
- try {
138
- const args = [
139
- '-e', 'tell application "Xcode" to activate',
140
- '-e', 'delay 0.5',
141
- '-e', 'tell application "System Events"',
142
- '-e', ' key code 123 using command down', // Cmd+← 行首
143
- '-e', ' delay 0.1',
144
- '-e', ' key code 124 using {command down, shift down}', // Cmd+Shift+→ 选到行尾
145
- '-e', ' delay 0.2',
146
- '-e', ' keystroke "v" using command down', // Cmd+V 粘贴替换选中内容
147
- '-e', 'end tell',
148
- ];
149
- const res = spawnSync('osascript', args, { stdio: 'ignore', timeout: OSASCRIPT_TIMEOUT });
150
- return res.status === 0;
151
- } catch {
152
- return false;
153
- }
199
+ return _run([
200
+ '-e', 'tell application "Xcode" to activate',
201
+ '-e', 'delay 0.5',
202
+ '-e', 'tell application "System Events"',
203
+ '-e', ' key code 123 using command down', // Cmd+← 行首
204
+ '-e', ' delay 0.1',
205
+ '-e', ' key code 124 using {command down, shift down}', // Cmd+Shift+→ 选到行尾
206
+ '-e', ' delay 0.2',
207
+ '-e', ' keystroke "v" using command down', // Cmd+V 粘贴替换
208
+ '-e', 'end tell',
209
+ ]);
154
210
  }
155
211
 
212
+ /**
213
+ * 跳转到指定行行首并粘贴剪贴板内容
214
+ *
215
+ * 用于在 import 区域插入新行。
216
+ * 按键序列:Cmd+L → 输入行号 → Return → Cmd+← 行首 → Cmd+V 粘贴
217
+ *
218
+ * @param {number} lineNumber 1-based 行号
219
+ * @returns {boolean} 是否成功
220
+ */
221
+ export function insertAtLineStartInXcode(lineNumber) {
222
+ if (!isXcodeRunning()) return false;
223
+ const n = _safeLine(lineNumber);
224
+ return _run([
225
+ '-e', 'tell application "Xcode" to activate',
226
+ '-e', 'delay 0.3',
227
+ '-e', 'tell application "System Events"',
228
+ '-e', ' keystroke "l" using command down', // Cmd+L: Go to Line
229
+ '-e', ' delay 0.3',
230
+ '-e', ` keystroke "${String(n)}"`, // 输入行号
231
+ '-e', ' delay 0.3',
232
+ '-e', ' key code 36', // Return
233
+ '-e', ' delay 0.3',
234
+ '-e', ' key code 123 using command down', // Cmd+← 行首
235
+ '-e', ' delay 0.2',
236
+ '-e', ' keystroke "v" using command down', // Cmd+V 粘贴
237
+ '-e', ' delay 0.3',
238
+ '-e', 'end tell',
239
+ ]);
240
+ }
241
+
242
+ // ─────────────────────────────────────────────
243
+ // 文档操作
244
+ // ─────────────────────────────────────────────
245
+
156
246
  /**
157
247
  * 保存 Xcode 当前活动文档(Cmd+S)
158
- * @returns {boolean}
248
+ * @returns {boolean} 是否成功
159
249
  */
160
250
  export function saveActiveDocumentInXcode() {
161
251
  if (!isXcodeRunning()) return false;
162
- try {
163
- const args = [
164
- '-e', 'tell application "Xcode" to activate',
165
- '-e', 'delay 0.1',
166
- '-e', 'tell application "System Events"',
167
- '-e', ' keystroke "s" using command down',
168
- '-e', 'end tell',
169
- ];
170
- const res = spawnSync('osascript', args, { stdio: 'ignore', timeout: OSASCRIPT_TIMEOUT });
171
- return res.status === 0;
172
- } catch {
173
- return false;
174
- }
252
+ return _run([
253
+ '-e', 'tell application "Xcode" to activate',
254
+ '-e', 'delay 0.1',
255
+ '-e', 'tell application "System Events"',
256
+ '-e', ' keystroke "s" using command down',
257
+ '-e', 'end tell',
258
+ ]);
175
259
  }
@@ -6,10 +6,12 @@ import Gateway from '../core/gateway/Gateway.js';
6
6
  import { readdirSync, statSync } from 'node:fs';
7
7
  import { join as pathJoin, relative as pathRelative, extname as pathExtname } from 'node:path';
8
8
 
9
- import { CandidateRepositoryImpl } from '../repository/candidate/CandidateRepository.impl.js';
10
- import { RecipeRepositoryImpl } from '../repository/recipe/RecipeRepository.impl.js';
9
+ import { KnowledgeRepositoryImpl } from '../repository/knowledge/KnowledgeRepository.impl.js';
10
+ import { KnowledgeFileWriter } from '../service/knowledge/KnowledgeFileWriter.js';
11
+ import { KnowledgeSyncService } from '../cli/KnowledgeSyncService.js';
12
+ import { KnowledgeService } from '../service/knowledge/KnowledgeService.js';
13
+ import { ConfidenceRouter } from '../service/knowledge/ConfidenceRouter.js';
11
14
  import { SnippetFactory } from '../service/snippet/SnippetFactory.js';
12
- import { RecipeService } from '../service/recipe/RecipeService.js';
13
15
  import { GuardService } from '../service/guard/GuardService.js';
14
16
  import { KnowledgeGraphService } from '../service/knowledge/KnowledgeGraphService.js';
15
17
  import { SearchEngine } from '../service/search/SearchEngine.js';
@@ -22,13 +24,9 @@ import { RetrievalFunnel } from '../service/search/RetrievalFunnel.js';
22
24
  import { JsonVectorAdapter } from '../infrastructure/vector/JsonVectorAdapter.js';
23
25
  import { IndexingPipeline } from '../infrastructure/vector/IndexingPipeline.js';
24
26
 
25
- // ─── P1: Injection / Snippet / Recipe ─────────────────
27
+ // ─── P1: Injection / Snippet ─────────────────
26
28
  import { RecipeParser } from '../service/recipe/RecipeParser.js';
27
- import { RecipeStatsTracker } from '../service/recipe/RecipeStatsTracker.js';
28
29
  import { RecipeCandidateValidator } from '../service/recipe/RecipeCandidateValidator.js';
29
- import { RecipeFileWriter } from '../service/recipe/RecipeFileWriter.js';
30
- import { CandidateFileWriter } from '../service/candidate/CandidateFileWriter.js';
31
- import { CandidateService } from '../service/candidate/CandidateService.js';
32
30
  import { SnippetInstaller } from '../service/snippet/SnippetInstaller.js';
33
31
 
34
32
  // ─── P1: Guard Advanced ──────────────────────────────
@@ -238,22 +236,31 @@ export class ServiceContainer {
238
236
  * 注册仓储
239
237
  */
240
238
  _registerRepositories() {
241
- // CandidateRepository
242
- this.register('candidateRepository', () => {
243
- if (!this.singletons.candidateRepository) {
239
+ // KnowledgeRepository (统一知识实体)
240
+ this.register('knowledgeRepository', () => {
241
+ if (!this.singletons.knowledgeRepository) {
244
242
  const database = this.get('database');
245
- this.singletons.candidateRepository = new CandidateRepositoryImpl(database);
243
+ this.singletons.knowledgeRepository = new KnowledgeRepositoryImpl(database);
246
244
  }
247
- return this.singletons.candidateRepository;
245
+ return this.singletons.knowledgeRepository;
248
246
  });
249
247
 
250
- // RecipeRepository
251
- this.register('recipeRepository', () => {
252
- if (!this.singletons.recipeRepository) {
253
- const database = this.get('database');
254
- this.singletons.recipeRepository = new RecipeRepositoryImpl(database);
248
+ // KnowledgeFileWriter (统一 .md 序列化/落盘)
249
+ this.register('knowledgeFileWriter', () => {
250
+ if (!this.singletons.knowledgeFileWriter) {
251
+ const projectRoot = this.singletons._projectRoot || process.cwd();
252
+ this.singletons.knowledgeFileWriter = new KnowledgeFileWriter(projectRoot);
255
253
  }
256
- return this.singletons.recipeRepository;
254
+ return this.singletons.knowledgeFileWriter;
255
+ });
256
+
257
+ // KnowledgeSyncService (统一 .md ↔ DB 同步)
258
+ this.register('knowledgeSyncService', () => {
259
+ if (!this.singletons.knowledgeSyncService) {
260
+ const projectRoot = this.singletons._projectRoot || process.cwd();
261
+ this.singletons.knowledgeSyncService = new KnowledgeSyncService(projectRoot);
262
+ }
263
+ return this.singletons.knowledgeSyncService;
257
264
  });
258
265
  }
259
266
 
@@ -261,54 +268,45 @@ export class ServiceContainer {
261
268
  * 注册服务
262
269
  */
263
270
  _registerServices() {
264
- // CandidateService
265
- this.register('candidateService', () => {
266
- if (!this.singletons.candidateService) {
267
- const candidateRepository = this.get('candidateRepository');
268
- const auditLogger = this.get('auditLogger');
269
- const gateway = this.get('gateway');
270
- const projectRoot = this.singletons._projectRoot || process.cwd();
271
- const candidateFileWriter = new CandidateFileWriter(projectRoot);
272
- const skillHooks = this.get('skillHooks');
273
- this.singletons.candidateService = new CandidateService(
274
- candidateRepository,
275
- auditLogger,
276
- gateway,
277
- { fileWriter: candidateFileWriter, skillHooks }
278
- );
271
+ // ConfidenceRouter (V3: 知识条目自动审核路由)
272
+ this.register('confidenceRouter', () => {
273
+ if (!this.singletons.confidenceRouter) {
274
+ const qualityScorer = this.get('qualityScorer');
275
+ this.singletons.confidenceRouter = new ConfidenceRouter({}, qualityScorer);
279
276
  }
280
- return this.singletons.candidateService;
277
+ return this.singletons.confidenceRouter;
281
278
  });
282
279
 
283
- // RecipeService
284
- this.register('recipeService', () => {
285
- if (!this.singletons.recipeService) {
286
- const recipeRepository = this.get('recipeRepository');
280
+ // KnowledgeService (V3: 统一知识服务)
281
+ this.register('knowledgeService', () => {
282
+ if (!this.singletons.knowledgeService) {
283
+ const knowledgeRepository = this.get('knowledgeRepository');
287
284
  const auditLogger = this.get('auditLogger');
288
285
  const gateway = this.get('gateway');
289
286
  const knowledgeGraphService = this.get('knowledgeGraphService');
290
- const projectRoot = this.singletons._projectRoot || process.cwd();
291
- const fileWriter = new RecipeFileWriter(projectRoot);
287
+ const fileWriter = this.get('knowledgeFileWriter');
292
288
  const skillHooks = this.get('skillHooks');
293
- this.singletons.recipeService = new RecipeService(
294
- recipeRepository,
289
+ const confidenceRouter = this.get('confidenceRouter');
290
+ const qualityScorer = this.get('qualityScorer');
291
+ this.singletons.knowledgeService = new KnowledgeService(
292
+ knowledgeRepository,
295
293
  auditLogger,
296
294
  gateway,
297
295
  knowledgeGraphService,
298
- { fileWriter, skillHooks }
296
+ { fileWriter, skillHooks, confidenceRouter, qualityScorer }
299
297
  );
300
298
  }
301
- return this.singletons.recipeService;
299
+ return this.singletons.knowledgeService;
302
300
  });
303
301
 
304
- // GuardService (now uses recipeRepository)
302
+ // GuardService (V3: uses knowledgeRepository)
305
303
  this.register('guardService', () => {
306
304
  if (!this.singletons.guardService) {
307
- const recipeRepository = this.get('recipeRepository');
305
+ const knowledgeRepository = this.get('knowledgeRepository');
308
306
  const auditLogger = this.get('auditLogger');
309
307
  const gateway = this.get('gateway');
310
308
  this.singletons.guardService = new GuardService(
311
- recipeRepository,
309
+ knowledgeRepository,
312
310
  auditLogger,
313
311
  gateway
314
312
  );
@@ -394,15 +392,6 @@ export class ServiceContainer {
394
392
  return this.singletons.recipeParser;
395
393
  });
396
394
 
397
- // RecipeStatsTracker
398
- this.register('recipeStatsTracker', () => {
399
- if (!this.singletons.recipeStatsTracker) {
400
- const projectRoot = this.singletons._projectRoot || process.cwd();
401
- this.singletons.recipeStatsTracker = new RecipeStatsTracker(projectRoot);
402
- }
403
- return this.singletons.recipeStatsTracker;
404
- });
405
-
406
395
  // RecipeCandidateValidator
407
396
  this.register('recipeCandidateValidator', () => {
408
397
  if (!this.singletons.recipeCandidateValidator) {
@@ -411,11 +400,11 @@ export class ServiceContainer {
411
400
  return this.singletons.recipeCandidateValidator;
412
401
  });
413
402
 
414
- // SnippetFactory (no DB — generates from recipes on-the-fly)
403
+ // SnippetFactory (V3: uses knowledgeRepository)
415
404
  this.register('snippetFactory', () => {
416
405
  if (!this.singletons.snippetFactory) {
417
- const recipeRepo = this.get('recipeRepository');
418
- this.singletons.snippetFactory = new SnippetFactory(recipeRepo);
406
+ const knowledgeRepo = this.get('knowledgeRepository');
407
+ this.singletons.snippetFactory = new SnippetFactory(knowledgeRepo);
419
408
  }
420
409
  return this.singletons.snippetFactory;
421
410
  });