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.
Files changed (110) hide show
  1. package/README.md +5 -5
  2. package/bin/cli.js +5 -33
  3. package/config/constitution.yaml +9 -2
  4. package/dashboard/dist/assets/{icons-B_Xg4B-s.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 +9 -27
  11. package/lib/core/ast/ProjectGraph.js +160 -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 +351 -0
  15. package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
  16. package/lib/domain/knowledge/Lifecycle.js +109 -0
  17. package/lib/domain/knowledge/index.js +27 -0
  18. package/lib/domain/knowledge/values/Constraints.js +125 -0
  19. package/lib/domain/knowledge/values/Content.js +86 -0
  20. package/lib/domain/knowledge/values/Quality.js +93 -0
  21. package/lib/domain/knowledge/values/Reasoning.js +69 -0
  22. package/lib/domain/knowledge/values/Relations.js +168 -0
  23. package/lib/domain/knowledge/values/Stats.js +87 -0
  24. package/lib/domain/knowledge/values/index.js +9 -0
  25. package/lib/external/ai/AiProvider.js +48 -0
  26. package/lib/external/ai/providers/GoogleGeminiProvider.js +12 -3
  27. package/lib/external/mcp/McpServer.js +7 -5
  28. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +3 -2
  29. package/lib/external/mcp/handlers/bootstrap.js +121 -12
  30. package/lib/external/mcp/handlers/browse.js +77 -73
  31. package/lib/external/mcp/handlers/candidate.js +29 -276
  32. package/lib/external/mcp/handlers/guard.js +2 -0
  33. package/lib/external/mcp/handlers/knowledge.js +205 -0
  34. package/lib/external/mcp/handlers/skill.js +4 -2
  35. package/lib/external/mcp/handlers/structure.js +25 -23
  36. package/lib/external/mcp/handlers/system.js +10 -12
  37. package/lib/external/mcp/tools.js +125 -138
  38. package/lib/http/HttpServer.js +4 -8
  39. package/lib/http/middleware/requestLogger.js +3 -3
  40. package/lib/http/routes/ai.js +17 -1
  41. package/lib/http/routes/extract.js +48 -4
  42. package/lib/http/routes/knowledge.js +246 -0
  43. package/lib/http/routes/search.js +12 -17
  44. package/lib/http/routes/skills.js +44 -1
  45. package/lib/infrastructure/cache/GraphCache.js +143 -0
  46. package/lib/infrastructure/database/migrations/015_create_token_usage.js +27 -0
  47. package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
  48. package/lib/infrastructure/external/XcodeAutomation.js +187 -103
  49. package/lib/infrastructure/realtime/RealtimeService.js +14 -2
  50. package/lib/injection/ServiceContainer.js +164 -63
  51. package/lib/repository/knowledge/KnowledgeRepository.impl.js +373 -0
  52. package/lib/repository/token/TokenUsageStore.js +162 -0
  53. package/lib/service/automation/DirectiveDetector.js +2 -3
  54. package/lib/service/automation/FileWatcher.js +67 -28
  55. package/lib/service/automation/XcodeIntegration.js +931 -156
  56. package/lib/service/automation/handlers/AlinkHandler.js +6 -4
  57. package/lib/service/automation/handlers/CreateHandler.js +53 -18
  58. package/lib/service/automation/handlers/GuardHandler.js +183 -20
  59. package/lib/service/automation/handlers/SearchHandler.js +35 -17
  60. package/lib/service/chat/AnalystAgent.js +25 -14
  61. package/lib/service/chat/CandidateGuardrail.js +1 -1
  62. package/lib/service/chat/ChatAgent.js +280 -48
  63. package/lib/service/chat/ContextWindow.js +92 -8
  64. package/lib/service/chat/HandoffProtocol.js +26 -1
  65. package/lib/service/chat/ProducerAgent.js +11 -9
  66. package/lib/service/chat/tools.js +298 -194
  67. package/lib/service/guard/GuardCheckEngine.js +114 -10
  68. package/lib/service/guard/GuardService.js +59 -48
  69. package/lib/service/knowledge/ConfidenceRouter.js +159 -0
  70. package/lib/service/knowledge/KnowledgeFileWriter.js +602 -0
  71. package/lib/service/knowledge/KnowledgeService.js +725 -0
  72. package/lib/service/search/SearchEngine.js +92 -19
  73. package/lib/service/skills/SignalCollector.js +15 -9
  74. package/lib/service/skills/SkillAdvisor.js +13 -11
  75. package/lib/service/snippet/SnippetFactory.js +5 -5
  76. package/lib/service/spm/SpmService.js +119 -18
  77. package/package.json +1 -1
  78. package/scripts/install-cursor-skill.js +0 -6
  79. package/scripts/migrate-md-to-knowledge.mjs +364 -0
  80. package/skills/autosnippet-analysis/SKILL.md +15 -7
  81. package/skills/autosnippet-candidates/SKILL.md +6 -6
  82. package/skills/autosnippet-coldstart/SKILL.md +7 -3
  83. package/skills/autosnippet-concepts/SKILL.md +7 -6
  84. package/skills/autosnippet-create/SKILL.md +13 -13
  85. package/skills/autosnippet-intent/SKILL.md +3 -2
  86. package/skills/autosnippet-lifecycle/SKILL.md +5 -5
  87. package/skills/autosnippet-recipes/SKILL.md +16 -4
  88. package/templates/constitution.yaml +1 -1
  89. package/templates/copilot-instructions.md +6 -6
  90. package/templates/recipes-setup/README.md +3 -3
  91. package/dashboard/dist/assets/index-CkIih2CC.css +0 -1
  92. package/dashboard/dist/assets/index-Duc8Qk-c.js +0 -197
  93. package/lib/cli/CandidateSyncService.js +0 -261
  94. package/lib/cli/SyncService.js +0 -356
  95. package/lib/domain/candidate/Candidate.js +0 -196
  96. package/lib/domain/candidate/CandidateRepository.js +0 -107
  97. package/lib/domain/candidate/Reasoning.js +0 -52
  98. package/lib/domain/recipe/Recipe.js +0 -421
  99. package/lib/domain/recipe/RecipeRepository.js +0 -54
  100. package/lib/domain/types/CandidateStatus.js +0 -52
  101. package/lib/http/routes/candidates.js +0 -559
  102. package/lib/http/routes/recipes.js +0 -397
  103. package/lib/repository/candidate/CandidateRepository.impl.js +0 -230
  104. package/lib/repository/recipe/RecipeRepository.impl.js +0 -498
  105. package/lib/service/candidate/CandidateAggregator.js +0 -52
  106. package/lib/service/candidate/CandidateFileWriter.js +0 -383
  107. package/lib/service/candidate/CandidateService.js +0 -973
  108. package/lib/service/recipe/RecipeFileWriter.js +0 -514
  109. package/lib/service/recipe/RecipeService.js +0 -786
  110. package/lib/service/recipe/RecipeStatsTracker.js +0 -148
@@ -0,0 +1,205 @@
1
+ /**
2
+ * MCP Handlers — V3 知识条目提交 & 生命周期
3
+ * submitKnowledge, submitKnowledgeBatch, knowledgeLifecycle
4
+ */
5
+
6
+ import { envelope } from '../envelope.js';
7
+ import { checkRecipeReadiness } from '../../../shared/RecipeReadinessChecker.js';
8
+
9
+ // ─── 限流 ──────────────────────────────────────────────────
10
+
11
+ async function _checkRateLimit(toolName, clientId) {
12
+ const { checkRecipeSave } = await import('../../../http/middleware/RateLimiter.js');
13
+ const projectRoot = process.cwd();
14
+ const limitCheck = checkRecipeSave(projectRoot, clientId || process.env.USER || 'mcp-client');
15
+ if (!limitCheck.allowed) {
16
+ return envelope({
17
+ success: false,
18
+ message: `提交过于频繁,请 ${limitCheck.retryAfter}s 后再试。`,
19
+ errorCode: 'RATE_LIMIT',
20
+ meta: { tool: toolName },
21
+ });
22
+ }
23
+ return null;
24
+ }
25
+
26
+ // ─── V3 wire format → KnowledgeService.create() 直通 ────────
27
+
28
+ /**
29
+ * 单条知识提交 (autosnippet_submit_knowledge)
30
+ *
31
+ * args 就是 wire format — 直接交给 knowledgeService.create()。
32
+ * 无需字段映射,fromJSON() 处理一切。
33
+ */
34
+ export async function submitKnowledge(ctx, args) {
35
+ // 限流
36
+ const blocked = await _checkRateLimit('autosnippet_submit_knowledge', args.client_id);
37
+ if (blocked) return blocked;
38
+
39
+ const service = ctx.container.get('knowledgeService');
40
+
41
+ // MCP 参数直接是 wire format
42
+ const entry = await service.create(args, { userId: 'mcp' });
43
+
44
+ // Recipe-Ready 诊断(兼容旧格式)
45
+ const readinessInput = _toReadinessInput(args);
46
+ const readiness = checkRecipeReadiness(readinessInput);
47
+
48
+ const data = {
49
+ id: entry.id,
50
+ lifecycle: entry.lifecycle,
51
+ title: entry.title,
52
+ kind: entry.kind,
53
+ };
54
+
55
+ if (!readiness.ready) {
56
+ data.recipeReadyHints = {
57
+ ready: false,
58
+ missingFields: readiness.missing,
59
+ suggestions: readiness.suggestions,
60
+ hint: '请补全以上字段后重新提交,或调用 autosnippet_enrich_candidates 进行完整性诊断',
61
+ };
62
+ }
63
+
64
+ return envelope({
65
+ success: true,
66
+ data,
67
+ meta: { tool: 'autosnippet_submit_knowledge' },
68
+ });
69
+ }
70
+
71
+ /**
72
+ * 批量知识提交 (autosnippet_submit_knowledge_batch)
73
+ */
74
+ export async function submitKnowledgeBatch(ctx, args) {
75
+ if (!args.target_name || !Array.isArray(args.items) || args.items.length === 0) {
76
+ throw new Error('需要 target_name 与 items(非空数组)');
77
+ }
78
+
79
+ // 限流
80
+ const blocked = await _checkRateLimit('autosnippet_submit_knowledge_batch', args.client_id);
81
+ if (blocked) return blocked;
82
+
83
+ // 去重(可选)
84
+ let items = args.items;
85
+ if (args.deduplicate !== false) {
86
+ try {
87
+ const { aggregateCandidates } = await import('../../../service/candidate/CandidateAggregator.js');
88
+ // 对 title 字段做去重
89
+ const readinessItems = items.map(it => ({
90
+ ...it,
91
+ code: it.content?.pattern || it.code || '',
92
+ }));
93
+ const result = aggregateCandidates(readinessItems);
94
+ // 保留原始 items 顺序中去重后的
95
+ if (result.items && result.items.length < items.length) {
96
+ const titles = new Set(result.items.map(it => it.title));
97
+ items = items.filter(it => titles.has(it.title));
98
+ }
99
+ } catch {
100
+ // CandidateAggregator 加载失败时降级:不去重
101
+ }
102
+ }
103
+
104
+ const service = ctx.container.get('knowledgeService');
105
+ const source = args.source || 'cursor-scan';
106
+ let count = 0;
107
+ const itemErrors = [];
108
+
109
+ for (let i = 0; i < items.length; i++) {
110
+ try {
111
+ const itemData = { ...items[i], source };
112
+ await service.create(itemData, { userId: 'mcp' });
113
+ count++;
114
+ } catch (err) {
115
+ itemErrors.push({ index: i, title: items[i].title || '(untitled)', error: err.message });
116
+ }
117
+ }
118
+
119
+ const data = { count, total: items.length, targetName: args.target_name };
120
+ if (itemErrors.length > 0) data.errors = itemErrors;
121
+
122
+ // Recipe-Ready 统计
123
+ const notReady = items.filter(it => !checkRecipeReadiness(_toReadinessInput(it)).ready);
124
+ if (notReady.length > 0) {
125
+ const allMissing = [...new Set(notReady.flatMap(it => checkRecipeReadiness(_toReadinessInput(it)).missing))];
126
+ data.recipeReadyHints = {
127
+ notReadyCount: notReady.length,
128
+ totalCount: items.length,
129
+ commonMissingFields: allMissing,
130
+ hint: `${notReady.length}/${items.length} 条知识条目缺少必要字段(${allMissing.join(', ')}),请补全后重新提交`,
131
+ };
132
+ }
133
+
134
+ return envelope({
135
+ success: true,
136
+ data,
137
+ message: `已提交 ${count}/${items.length} 条知识条目。`,
138
+ meta: { tool: 'autosnippet_submit_knowledge_batch' },
139
+ });
140
+ }
141
+
142
+ /**
143
+ * 知识条目生命周期操作 (autosnippet_knowledge_lifecycle)
144
+ *
145
+ * 简化为 3 状态: pending / active / deprecated
146
+ * 外部 Agent 允许 reactivate(废弃 → 待审核);发布/废弃由开发者在 Dashboard 操作
147
+ * 外部 Agent 也可以通过 submitKnowledge / submitKnowledgeBatch 提交新条目(→ pending)
148
+ */
149
+ const MCP_ALLOWED_LIFECYCLE_ACTIONS = new Set(['reactivate']);
150
+
151
+ export async function knowledgeLifecycle(ctx, args) {
152
+ const { id, action } = args;
153
+ if (!id || !action) {
154
+ throw new Error('需要 id 和 action');
155
+ }
156
+
157
+ if (!MCP_ALLOWED_LIFECYCLE_ACTIONS.has(action)) {
158
+ throw new Error(
159
+ `[PERMISSION_DENIED] 外部 Agent 不允许执行 "${action}" 操作,仅支持: reactivate。发布、废弃等操作请在 Dashboard 中完成。提交新知识请使用 autosnippet_submit_knowledge 工具。`
160
+ );
161
+ }
162
+
163
+ const service = ctx.container.get('knowledgeService');
164
+ const context = { userId: 'mcp' };
165
+
166
+ const entry = await service.reactivate(id, context);
167
+
168
+ return envelope({
169
+ success: true,
170
+ data: {
171
+ id: entry.id,
172
+ lifecycle: entry.lifecycle,
173
+ title: entry.title,
174
+ action,
175
+ },
176
+ meta: { tool: 'autosnippet_knowledge_lifecycle' },
177
+ });
178
+ }
179
+
180
+ // ─── 内部辅助 ──────────────────────────────────────────────
181
+
182
+ /**
183
+ * V3 wire format → RecipeReadinessChecker 兼容格式
184
+ * RecipeReadinessChecker 期望旧字段名 (code, summary_cn, ...)
185
+ */
186
+ function _toReadinessInput(args) {
187
+ return {
188
+ title: args.title,
189
+ code: args.content?.pattern || args.code || '',
190
+ language: args.language,
191
+ category: args.category,
192
+ trigger: args.trigger,
193
+ summary_cn: args.summary_cn,
194
+ summary_en: args.summary_en,
195
+ headers: args.headers,
196
+ reasoning: args.reasoning ? {
197
+ whyStandard: args.reasoning.why_standard || args.reasoning.whyStandard,
198
+ sources: args.reasoning.sources,
199
+ confidence: args.reasoning.confidence,
200
+ } : undefined,
201
+ knowledgeType: args.knowledge_type || args.knowledgeType,
202
+ complexity: args.complexity,
203
+ usageGuide: args.usage_guide_cn || args.usageGuide,
204
+ };
205
+ }
@@ -613,8 +613,10 @@ export function updateSkill(_ctx, args) {
613
613
  export async function suggestSkills(ctx) {
614
614
  try {
615
615
  const { SkillAdvisor } = await import('../../../service/skills/SkillAdvisor.js');
616
- const database = ctx?.container?.get?.('database') || null;
617
- const advisor = new SkillAdvisor(PROJECT_ROOT, { database });
616
+ const dbConn = ctx?.container?.get?.('database') || null;
617
+ const database = dbConn?.getDb?.() || dbConn || null;
618
+ const projectRoot = process.env.ASD_PROJECT_DIR || process.cwd();
619
+ const advisor = new SkillAdvisor(projectRoot, { database });
618
620
  const result = advisor.suggest();
619
621
 
620
622
  return JSON.stringify({
@@ -262,40 +262,41 @@ export async function graphImpact(ctx, args) {
262
262
  }
263
263
 
264
264
  /**
265
- * 降级:从 Recipe.relations_json 提取关系(不依赖 knowledge_edges 表)
265
+ * 降级:从 knowledge_entries.relations 提取关系(不依赖 knowledge_edges 表)
266
266
  */
267
267
  async function _fallbackRelationsFromRecipe(ctx, nodeId, relation, direction) {
268
- const recipeService = ctx.container.get('recipeService');
269
268
  try {
270
- const recipe = await recipeService.getRecipe(nodeId);
271
- if (!recipe) return { outgoing: [], incoming: [] };
269
+ const knowledgeService = ctx.container.get('knowledgeService');
270
+ const entry = await knowledgeService.get(nodeId);
271
+ if (!entry) return { outgoing: [], incoming: [] };
272
272
 
273
+ const relJson = typeof entry.relations?.toJSON === 'function' ? entry.relations.toJSON() : (entry.relations || {});
273
274
  const outgoing = [];
274
275
  if (direction === 'both' || direction === 'out') {
275
- for (const [relType, targets] of Object.entries(recipe.relations || {})) {
276
+ for (const [relType, targets] of Object.entries(relJson)) {
276
277
  if (relation && relType !== relation) continue;
277
278
  for (const t of (Array.isArray(targets) ? targets : [])) {
278
- outgoing.push({ fromId: nodeId, fromType: 'recipe', toId: t.target || t.id || t, toType: 'recipe', relation: relType });
279
+ outgoing.push({ fromId: nodeId, fromType: 'knowledge', toId: t.target || t.id || t, toType: 'knowledge', relation: relType });
279
280
  }
280
281
  }
281
282
  }
282
283
 
283
- // 反向查找:其他 Recipe 中 relations_json 包含当前 nodeId
284
+ // 反向查找:其他条目中 relations 包含当前 nodeId
284
285
  const incoming = [];
285
286
  if (direction === 'both' || direction === 'in') {
286
- const recipeRepo = ctx.container.get('recipeRepository');
287
- const reverseRows = recipeRepo.db.prepare(
288
- `SELECT id, relations_json FROM recipes WHERE relations_json LIKE ? AND id != ?`
287
+ const knowledgeRepo = ctx.container.get('knowledgeRepository');
288
+ const reverseRows = knowledgeRepo.db.prepare(
289
+ `SELECT id, relations FROM knowledge_entries WHERE relations LIKE ? AND id != ?`
289
290
  ).all(`%${nodeId}%`, nodeId);
290
291
  for (const row of reverseRows) {
291
292
  try {
292
- const rels = JSON.parse(row.relations_json || '{}');
293
+ const rels = JSON.parse(row.relations || '{}');
293
294
  for (const [relType, targets] of Object.entries(rels)) {
294
295
  if (relation && relType !== relation) continue;
295
296
  for (const t of (Array.isArray(targets) ? targets : [])) {
296
297
  const targetId = t.target || t.id || t;
297
298
  if (targetId === nodeId) {
298
- incoming.push({ fromId: row.id, fromType: 'recipe', toId: nodeId, toType: 'recipe', relation: relType });
299
+ incoming.push({ fromId: row.id, fromType: 'knowledge', toId: nodeId, toType: 'knowledge', relation: relType });
299
300
  }
300
301
  }
301
302
  }
@@ -308,23 +309,23 @@ async function _fallbackRelationsFromRecipe(ctx, nodeId, relation, direction) {
308
309
  }
309
310
 
310
311
  /**
311
- * 降级:从 relations_json 反查受影响的 Recipe
312
+ * 降级:从 knowledge_entries.relations 反查受影响的条目
312
313
  */
313
314
  async function _fallbackImpactFromRecipe(ctx, nodeId) {
314
315
  try {
315
- const recipeRepo = ctx.container.get('recipeRepository');
316
- const rows = recipeRepo.db.prepare(
317
- `SELECT id, title, relations_json FROM recipes WHERE relations_json LIKE ? AND id != ?`
316
+ const knowledgeRepo = ctx.container.get('knowledgeRepository');
317
+ const rows = knowledgeRepo.db.prepare(
318
+ `SELECT id, title, relations FROM knowledge_entries WHERE relations LIKE ? AND id != ?`
318
319
  ).all(`%${nodeId}%`, nodeId);
319
320
 
320
321
  const impacted = [];
321
322
  for (const row of rows) {
322
323
  try {
323
- const rels = JSON.parse(row.relations_json || '{}');
324
+ const rels = JSON.parse(row.relations || '{}');
324
325
  for (const [relType, targets] of Object.entries(rels)) {
325
326
  for (const t of (Array.isArray(targets) ? targets : [])) {
326
327
  if ((t.target || t.id || t) === nodeId) {
327
- impacted.push({ id: row.id, title: row.title, type: 'recipe', relation: relType, depth: 1 });
328
+ impacted.push({ id: row.id, title: row.title, type: 'knowledge', relation: relType, depth: 1 });
328
329
  }
329
330
  }
330
331
  }
@@ -364,17 +365,18 @@ export async function graphPath(ctx, args) {
364
365
  */
365
366
  async function _fallbackPathFromRecipe(ctx, fromId, toId) {
366
367
  try {
367
- const recipeService = ctx.container.get('recipeService');
368
- const recipe = await recipeService.getRecipe(fromId);
369
- if (!recipe) return { found: false, path: [], depth: -1 };
368
+ const knowledgeService = ctx.container.get('knowledgeService');
369
+ const entry = await knowledgeService.get(fromId);
370
+ if (!entry) return { found: false, path: [], depth: -1 };
370
371
 
371
- for (const [relType, targets] of Object.entries(recipe.relations || {})) {
372
+ const relJson = typeof entry.relations?.toJSON === 'function' ? entry.relations.toJSON() : (entry.relations || {});
373
+ for (const [relType, targets] of Object.entries(relJson)) {
372
374
  for (const t of (Array.isArray(targets) ? targets : [])) {
373
375
  const targetId = t.target || t.id || t;
374
376
  if (targetId === toId) {
375
377
  return {
376
378
  found: true,
377
- path: [{ from: { id: fromId, type: 'recipe' }, to: { id: toId, type: 'recipe' }, relation: relType }],
379
+ path: [{ from: { id: fromId, type: 'knowledge' }, to: { id: toId, type: 'knowledge' }, relation: relType }],
378
380
  depth: 1,
379
381
  };
380
382
  }
@@ -30,22 +30,23 @@ export async function health(ctx) {
30
30
  checks.database = true;
31
31
  // 知识库统计(轻量聚合查询)
32
32
  try {
33
+ // V3: knowledge_entries 统一表(lifecycle 替代 status)
33
34
  const rStats = db.prepare(`
34
35
  SELECT COUNT(*) as total,
35
- SUM(CASE WHEN status='active' THEN 1 ELSE 0 END) as active,
36
+ SUM(CASE WHEN lifecycle='active' THEN 1 ELSE 0 END) as active,
36
37
  SUM(CASE WHEN kind='rule' THEN 1 ELSE 0 END) as rules,
37
38
  SUM(CASE WHEN kind='pattern' THEN 1 ELSE 0 END) as patterns,
38
39
  SUM(CASE WHEN kind='fact' THEN 1 ELSE 0 END) as facts
39
- FROM recipes
40
+ FROM knowledge_entries
40
41
  `).get();
41
- const cStats = db.prepare(`
42
+ const cPending = db.prepare(`
42
43
  SELECT COUNT(*) as total,
43
- SUM(CASE WHEN status='pending' THEN 1 ELSE 0 END) as pending
44
- FROM candidates
44
+ SUM(CASE WHEN lifecycle='pending' THEN 1 ELSE 0 END) as pending
45
+ FROM knowledge_entries
45
46
  `).get();
46
47
  knowledgeBase = {
47
48
  recipes: { total: rStats.total, active: rStats.active, rules: rStats.rules, patterns: rStats.patterns, facts: rStats.facts },
48
- candidates: { total: cStats.total, pending: cStats.pending },
49
+ candidates: { total: cPending.total, pending: cPending.pending },
49
50
  };
50
51
  } catch { /* 统计查询失败不影响 health */ }
51
52
  }
@@ -132,9 +133,6 @@ export function capabilities() {
132
133
  autosnippet_get_target_metadata: 'structure',
133
134
  autosnippet_validate_candidate: 'validate',
134
135
  autosnippet_check_duplicate: 'validate',
135
- autosnippet_submit_candidate: 'submit',
136
- autosnippet_submit_candidates: 'submit',
137
- autosnippet_submit_draft_recipes: 'submit',
138
136
  autosnippet_guard_check: 'guard',
139
137
  autosnippet_guard_audit_files: 'guard',
140
138
  autosnippet_scan_project: 'scan',
@@ -191,9 +189,9 @@ export function capabilities() {
191
189
  tools,
192
190
  workflows: [
193
191
  { name: '知识查询', steps: ['search(推荐首选,auto mode 融合)', 'get_recipe', 'confirm_usage'], tips: '精确匹配用 keyword_search,需意图+会话上下文用 context_search' },
194
- { name: '单文件候选提交', steps: ['check_duplicate', 'validate_candidate', 'submit_candidate'] },
195
- { name: '批量 Target 扫描', steps: ['get_targets', 'get_target_files', '(Agent 分析)', 'submit_candidates'] },
196
- { name: '全项目深度扫描', steps: ['scan_project', '(Agent 语义分析)', 'submit_candidates', 'enrich_candidates'] },
192
+ { name: '单条知识提交', steps: ['check_duplicate', 'validate_candidate', 'submit_knowledge'] },
193
+ { name: '批量 Target 扫描', steps: ['get_targets', 'get_target_files', '(Agent 分析)', 'submit_knowledge_batch'] },
194
+ { name: '全项目深度扫描', steps: ['scan_project', '(Agent 语义分析)', 'submit_knowledge_batch', 'enrich_candidates'] },
197
195
  { name: '候选就绪检查', steps: ['enrich_candidates', 'validate_candidate', 'check_duplicate'] },
198
196
  ],
199
197
  },