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
@@ -1,7 +1,9 @@
1
1
  /**
2
- * MCP 工具定义(38 个)+ Gateway 映射
2
+ * MCP 工具定义(35 个)+ Gateway 映射
3
3
  *
4
4
  * 只包含 JSON Schema 级别的声明,不含任何业务逻辑。
5
+ * V3: 旧 submit_candidate / submit_candidates / submit_draft_recipes 已移除,
6
+ * 统一使用 submit_knowledge / submit_knowledge_batch / knowledge_lifecycle。
5
7
  */
6
8
 
7
9
  /**
@@ -9,9 +11,6 @@
9
11
  * 只读工具不在此映射中,跳过 Gateway 以保持性能
10
12
  */
11
13
  export const TOOL_GATEWAY_MAP = {
12
- autosnippet_submit_candidate: { action: 'candidate:create', resource: 'candidates' },
13
- autosnippet_submit_candidates: { action: 'candidate:create', resource: 'candidates' },
14
- autosnippet_submit_draft_recipes: { action: 'candidate:create', resource: 'candidates' },
15
14
  autosnippet_guard_audit_files: { action: 'guard_rule:check_code', resource: 'guard_rules' },
16
15
  autosnippet_scan_project: { action: 'guard_rule:check_code', resource: 'guard_rules' },
17
16
  autosnippet_enrich_candidates: { action: 'candidate:update', resource: 'candidates' },
@@ -20,6 +19,10 @@ export const TOOL_GATEWAY_MAP = {
20
19
  autosnippet_create_skill: { action: 'create:skills', resource: 'skills' },
21
20
  autosnippet_delete_skill: { action: 'delete:skills', resource: 'skills' },
22
21
  autosnippet_update_skill: { action: 'update:skills', resource: 'skills' },
22
+ // V3 知识条目
23
+ autosnippet_submit_knowledge: { action: 'knowledge:create', resource: 'knowledge' },
24
+ autosnippet_submit_knowledge_batch: { action: 'knowledge:create', resource: 'knowledge' },
25
+ autosnippet_knowledge_lifecycle: { action: 'knowledge:update', resource: 'knowledge' },
23
26
  };
24
27
 
25
28
  export const TOOLS = [
@@ -283,132 +286,8 @@ export const TOOLS = [
283
286
  required: ['candidate'],
284
287
  },
285
288
  },
286
- // 18. 单条候选提交(支持结构化 content)
287
- {
288
- name: 'autosnippet_submit_candidate',
289
- description:
290
- '提交单条代码片段候选供审核。支持 V2 结构化全字段。含限流保护。Agent 必须提供 reasoning(推理依据)。\n' +
291
- '⚠️ Recipe-Ready 要求:为使候选直接审核通过为 Recipe,请尽量填写以下字段:\n' +
292
- ' 必填: title, code, language, category, trigger(@开头), summary_cn, headers(完整import语句)\n' +
293
- ' 强烈建议: summary_en, usageGuide(Markdown ### 章节), reasoning, knowledgeType, complexity\n' +
294
- ' 推荐: usageGuide_en, rationale, steps, constraints, relations\n' +
295
- '如字段不全,返回值中 recipeReadyHints 会提示缺失字段,Agent 应据此补全后重新提交或调用 enrich_candidates 查漏。',
296
- inputSchema: {
297
- type: 'object',
298
- properties: {
299
- // ── 核心(必填)──
300
- title: { type: 'string', description: '中文简短标题(≤20字)' },
301
- code: { type: 'string', description: '代码片段(映射到 content.pattern),使用 Xcode 占位符 <#name#>' },
302
- language: { type: 'string', description: '编程语言:swift / objectivec(必须小写)' },
303
- // ── 分类 ──
304
- category: { type: 'string', description: '分类(必填):View/Service/Tool/Model/Network/Storage/UI/Utility' },
305
- knowledgeType: { type: 'string', description: '知识维度:code-pattern|architecture|best-practice|code-standard|code-relation|inheritance|call-chain|data-flow|module-dependency|boundary-constraint|code-style|solution' },
306
- complexity: { type: 'string', enum: ['beginner', 'intermediate', 'advanced'] },
307
- scope: { type: 'string', enum: ['universal', 'project-specific', 'target-specific'], description: '适用范围' },
308
- tags: { type: 'array', items: { type: 'string' }, description: '可搜索标签' },
309
- // ── 描述与文档(双语) ──
310
- description: { type: 'string', description: '一句话功能描述' },
311
- summary: { type: 'string', description: '中文详细摘要(等同 summary_cn,Markdown)' },
312
- summary_cn: { type: 'string', description: '中文摘要(≤100字)。与 summary 二选一' },
313
- summary_en: { type: 'string', description: '英文摘要(≤100 words)。强烈建议提供,提升检索与 AI 理解' },
314
- trigger: { type: 'string', description: '触发关键词(必填,@开头,小写,如 @video-cover-cell)' },
315
- usageGuide: { type: 'string', description: '中文使用指南(等同 usageGuide_cn,Markdown ### 章节格式)' },
316
- usageGuide_cn: { type: 'string', description: '中文使用指南。与 usageGuide 二选一' },
317
- usageGuide_en: { type: 'string', description: '英文使用指南(Markdown ### 章节格式)。推荐提供' },
318
- // ── 结构化内容 ──
319
- rationale: { type: 'string', description: '设计原理/为什么这样做' },
320
- steps: { type: 'array', items: { type: 'object' }, description: '实施步骤 [{title, description, code}]' },
321
- codeChanges: { type: 'array', items: { type: 'object' }, description: '代码变更 [{file, before, after, explanation}]' },
322
- verification: { type: 'object', description: '验证方式 {method, expectedResult, testCode}' },
323
- headers: { type: 'array', items: { type: 'string' }, description: '完整 import/include 语句(必填)如 ["#import <Module/Header.h>"] 或 ["import Foundation"]' },
324
- // ── 约束与关系 ──
325
- constraints: { type: 'object', description: '约束 {boundaries[], preconditions[], sideEffects[], guards[{pattern,severity,message}]}' },
326
- relations: { type: 'object', description: '关系 {dependsOn[], extends[], conflicts[], related[], inherits[], implements[], calls[], dataFlow[]},每项 {target, description}' },
327
- // ── 质量 & 来源 ──
328
- quality: { type: 'object', description: '质量评分 {codeCompleteness, projectAdaptation, documentationClarity} (0-1)' },
329
- sourceFile: { type: 'string', description: '来源文件路径' },
330
- // ── 推理依据(Reasoning)— 必填 ──
331
- reasoning: {
332
- type: 'object',
333
- description: '推理依据:为什么提取这段代码。Agent 必须填写。',
334
- properties: {
335
- whyStandard: { type: 'string', description: '为什么这段代码值得沉淀为知识——如"该模式在项目中被反复使用且新人容易写错"' },
336
- sources: { type: 'array', items: { type: 'string' }, description: '来源列表:文件路径、文档链接、上下文引用等' },
337
- confidence: { type: 'number', description: '置信度 0-1,表示 Agent 对这条候选质量的确信程度' },
338
- qualitySignals: { type: 'object', description: '质量信号 {clarity, reusability, importance} 等自由 KV' },
339
- alternatives: { type: 'array', items: { type: 'string' }, description: '备选方案描述(如果有替代实现)' },
340
- },
341
- required: ['whyStandard', 'sources', 'confidence'],
342
- },
343
- source: { type: 'string', description: '来源标识(默认 mcp)' },
344
- clientId: { type: 'string', description: '客户端标识(用于限流)' },
345
- },
346
- required: ['title', 'code', 'language'],
347
- },
348
- },
349
- // 19. 批量候选提交
350
- {
351
- name: 'autosnippet_submit_candidates',
352
- description:
353
- '批量提交候选到 Candidates,支持去重与限流。每个 item 支持 V2 全字段。返回逐条结果与 recipeReadyHints。\n' +
354
- '⚠️ Recipe-Ready:请尽量每条填写 category, trigger(@开头), summary_cn, summary_en, headers, reasoning。缺失字段会在 recipeReadyHints 中提示。',
355
- inputSchema: {
356
- type: 'object',
357
- properties: {
358
- targetName: { type: 'string', description: 'Target 名称' },
359
- items: {
360
- type: 'array',
361
- description: '候选数组,每项字段详见 submit_candidate。尽量填充全部 Recipe-Ready 字段。',
362
- items: {
363
- type: 'object',
364
- properties: {
365
- title: { type: 'string' }, code: { type: 'string' }, language: { type: 'string' },
366
- category: { type: 'string' }, knowledgeType: { type: 'string' },
367
- complexity: { type: 'string' }, scope: { type: 'string' },
368
- tags: { type: 'array', items: { type: 'string' } },
369
- description: { type: 'string' },
370
- summary: { type: 'string', description: '中文摘要(等同 summary_cn)' },
371
- summary_cn: { type: 'string', description: '中文摘要' },
372
- summary_en: { type: 'string', description: '英文摘要(强烈建议)' },
373
- trigger: { type: 'string', description: '触发词(@开头)' },
374
- usageGuide: { type: 'string', description: '中文使用指南(等同 usageGuide_cn)' },
375
- usageGuide_cn: { type: 'string', description: '中文使用指南' },
376
- usageGuide_en: { type: 'string', description: '英文使用指南' },
377
- rationale: { type: 'string' },
378
- steps: { type: 'array', items: { type: 'object' } },
379
- codeChanges: { type: 'array', items: { type: 'object' } },
380
- verification: { type: 'object' },
381
- headers: { type: 'array', items: { type: 'string' }, description: '完整 import 语句' },
382
- constraints: { type: 'object' }, relations: { type: 'object' },
383
- quality: { type: 'object' }, sourceFile: { type: 'string' },
384
- reasoning: { type: 'object', description: '{whyStandard, sources[], confidence}(必填)' },
385
- },
386
- required: ['title', 'code', 'language'],
387
- },
388
- },
389
- source: { type: 'string', default: 'cursor-scan' },
390
- deduplicate: { type: 'boolean', default: true },
391
- clientId: { type: 'string' },
392
- },
393
- required: ['targetName', 'items'],
394
- },
395
- },
396
- // 20. 草稿 Recipe 提交
397
- {
398
- name: 'autosnippet_submit_draft_recipes',
399
- description: '解析草稿 Markdown 文件为 Recipe 候选并提交。支持完整 Recipe 和纯介绍。保留解析出的全部结构化字段(trigger/usageGuide/headers 等)。自动生成默认 reasoning。',
400
- inputSchema: {
401
- type: 'object',
402
- properties: {
403
- filePaths: { description: '草稿文件路径(字符串或数组)' },
404
- targetName: { type: 'string', default: '_draft' },
405
- source: { type: 'string', default: 'copilot-draft' },
406
- deleteAfterSubmit: { type: 'boolean', default: false },
407
- clientId: { type: 'string' },
408
- },
409
- required: ['filePaths'],
410
- },
411
- },
289
+ // 18-20: [已移除] 旧 submit_candidate / submit_candidates / submit_draft_recipes
290
+ // 统一使用 V3 submit_knowledge / submit_knowledge_batch / knowledge_lifecycle
412
291
  // 21. 能力声明
413
292
  {
414
293
  name: 'autosnippet_capabilities',
@@ -574,7 +453,7 @@ export const TOOLS = [
574
453
  '⚠️ 产出为启发式初稿,必须执行后续步骤提升质量:\n' +
575
454
  ' Step 1: autosnippet_enrich_candidates — 诊断字段缺失,逐条补全必填字段\n' +
576
455
  ' Step 2: autosnippet_bootstrap_refine — AI 润色 summary/insight/relations/confidence\n' +
577
- ' Step 3: 逐 Target 深入分析,补充更细粒度候选(autosnippet_submit_candidates)\n' +
456
+ ' Step 3: 逐 Target 深入分析,补充更细粒度知识条目(autosnippet_submit_knowledge_batch)\n' +
578
457
  ' Step 4: 对新候选重复 Step 1-2\n' +
579
458
  '\n' +
580
459
  '质量标准:每条必须包含 title/code/language/category/trigger/summary_cn/headers/reasoning。',
@@ -589,7 +468,7 @@ export const TOOLS = [
589
468
  required: [],
590
469
  },
591
470
  },
592
- // 33. Skills 发现:列出所有可用 Agent Skill 及其适用场景
471
+ // 32. Skills 发现:列出所有可用 Agent Skill 及其适用场景
593
472
  {
594
473
  name: 'autosnippet_list_skills',
595
474
  description:
@@ -603,7 +482,7 @@ export const TOOLS = [
603
482
  ' • 执行具体任务前,加载对应的 Skill 获取操作指南和最佳实践',
604
483
  inputSchema: { type: 'object', properties: {}, required: [] },
605
484
  },
606
- // 34. Skills 加载:按需获取指定 Skill 的完整操作指南
485
+ // 33. Skills 加载:按需获取指定 Skill 的完整操作指南
607
486
  {
608
487
  name: 'autosnippet_load_skill',
609
488
  description:
@@ -627,7 +506,7 @@ export const TOOLS = [
627
506
  required: ['skillName'],
628
507
  },
629
508
  },
630
- // 35. 创建项目级 Skill
509
+ // 34. 创建项目级 Skill
631
510
  {
632
511
  name: 'autosnippet_create_skill',
633
512
  description:
@@ -671,7 +550,7 @@ export const TOOLS = [
671
550
  required: ['name', 'description', 'content'],
672
551
  },
673
552
  },
674
- // 36. Skill 推荐:基于使用模式分析,推荐创建 Skill
553
+ // 35. Skill 推荐:基于使用模式分析,推荐创建 Skill
675
554
  {
676
555
  name: 'autosnippet_suggest_skills',
677
556
  description:
@@ -686,7 +565,7 @@ export const TOOLS = [
686
565
  ' • 候选被大量驳回时',
687
566
  inputSchema: { type: 'object', properties: {}, required: [] },
688
567
  },
689
- // 37. 删除项目级 Skill
568
+ // 36. 删除项目级 Skill
690
569
  {
691
570
  name: 'autosnippet_delete_skill',
692
571
  description:
@@ -707,7 +586,7 @@ export const TOOLS = [
707
586
  required: ['name'],
708
587
  },
709
588
  },
710
- // 38. 更新项目级 Skill
589
+ // 37. 更新项目级 Skill
711
590
  {
712
591
  name: 'autosnippet_update_skill',
713
592
  description:
@@ -737,7 +616,7 @@ export const TOOLS = [
737
616
  required: ['name'],
738
617
  },
739
618
  },
740
- // 36. ② 内容润色:Bootstrap 候选 AI 精炼(Phase 6)
619
+ // 38. ② 内容润色:Bootstrap 候选 AI 精炼(Phase 6)
741
620
  {
742
621
  name: 'autosnippet_bootstrap_refine',
743
622
  description:
@@ -757,4 +636,112 @@ export const TOOLS = [
757
636
  required: [],
758
637
  },
759
638
  },
639
+ // ═══ V3 知识条目(统一实体) ═══════════════════════════════
640
+ // 36. 单条知识提交(V3 wire format 直通)
641
+ {
642
+ name: 'autosnippet_submit_knowledge',
643
+ description:
644
+ '提交单条知识条目到 AutoSnippet 知识库(V3 统一实体)。\n' +
645
+ '参数即 wire format — 直接构造 KnowledgeEntry,无需字段映射。\n' +
646
+ '根据 ConfidenceRouter 自动路由:高置信度自动入库(active),低置信度待审核(pending),极低置信度自动驳回(rejected)。\n' +
647
+ '⚠️ content 对象必须有 pattern 或 markdown。',
648
+ inputSchema: {
649
+ type: 'object',
650
+ properties: {
651
+ // ── 必填 ──
652
+ title: { type: 'string', description: '中文标题(≤20字)' },
653
+ language: { type: 'string', description: '编程语言(swift/objectivec,小写)' },
654
+ content: {
655
+ type: 'object',
656
+ description: '内容值对象(必须有 pattern 或 markdown)',
657
+ properties: {
658
+ pattern: { type: 'string', description: '代码片段(等同旧 code 字段)' },
659
+ markdown: { type: 'string', description: 'Markdown 全文' },
660
+ rationale: { type: 'string', description: '设计原理' },
661
+ steps: { type: 'array', items: { type: 'object', properties: {
662
+ title: { type: 'string' }, description: { type: 'string' }, code: { type: 'string' },
663
+ }}},
664
+ code_changes: { type: 'array', items: { type: 'object' } },
665
+ verification: { type: 'object' },
666
+ },
667
+ },
668
+ // ── 分类 ──
669
+ category: { type: 'string', description: '分类: View/Service/Tool/Model/Network/Storage/UI/Utility' },
670
+ knowledge_type: { type: 'string', description: '知识维度: code-pattern|architecture|best-practice|boundary-constraint|...' },
671
+ complexity: { type: 'string', enum: ['beginner', 'intermediate', 'advanced'] },
672
+ scope: { type: 'string', enum: ['universal', 'project-specific', 'target-specific'] },
673
+ difficulty: { type: 'string' },
674
+ tags: { type: 'array', items: { type: 'string' } },
675
+ // ── 描述双语 ──
676
+ trigger: { type: 'string', description: '触发关键词(@开头,小写)' },
677
+ description: { type: 'string' },
678
+ summary_cn: { type: 'string', description: '中文摘要' },
679
+ summary_en: { type: 'string', description: '英文摘要' },
680
+ usage_guide_cn: { type: 'string', description: '中文使用指南(Markdown)' },
681
+ usage_guide_en: { type: 'string', description: '英文使用指南(Markdown)' },
682
+ // ── 约束与关系 ──
683
+ constraints: { type: 'object', description: '约束 {guards[], boundaries[], preconditions[], side_effects[]}' },
684
+ relations: { type: 'object', description: '关系分桶 {inherits[], extends[], depends_on[], conflicts[], related[], ...}' },
685
+ // ── 推理 ──
686
+ reasoning: {
687
+ type: 'object',
688
+ description: '推理依据(必须提供)',
689
+ properties: {
690
+ why_standard: { type: 'string', description: '为什么值得沉淀为知识' },
691
+ sources: { type: 'array', items: { type: 'string' }, description: '来源列表' },
692
+ confidence: { type: 'number', description: '置信度 0-1' },
693
+ quality_signals: { type: 'object' },
694
+ alternatives: { type: 'array', items: { type: 'string' } },
695
+ },
696
+ required: ['why_standard', 'sources'],
697
+ },
698
+ // ── 头文件 ──
699
+ headers: { type: 'array', items: { type: 'string' }, description: '完整 import 语句' },
700
+ header_paths: { type: 'array', items: { type: 'string' } },
701
+ module_name: { type: 'string' },
702
+ include_headers: { type: 'boolean' },
703
+ // ── 控制 ──
704
+ source: { type: 'string', description: '来源标识(默认 mcp)' },
705
+ client_id: { type: 'string', description: '客户端标识(用于限流)' },
706
+ },
707
+ required: ['title', 'language', 'content'],
708
+ },
709
+ },
710
+ // 37. 批量知识提交
711
+ {
712
+ name: 'autosnippet_submit_knowledge_batch',
713
+ description:
714
+ '批量提交知识条目到知识库(V3 统一实体)。\n' +
715
+ '每条 item 使用与 autosnippet_submit_knowledge 相同的 wire format。\n' +
716
+ '支持去重。返回逐条结果与 recipeReadyHints。',
717
+ inputSchema: {
718
+ type: 'object',
719
+ properties: {
720
+ target_name: { type: 'string', description: 'Target 名称' },
721
+ items: { type: 'array', description: '知识条目数组,每项字段同 submit_knowledge', items: { type: 'object' } },
722
+ source: { type: 'string', default: 'cursor-scan', description: '来源标识' },
723
+ deduplicate: { type: 'boolean', default: true, description: '是否去重' },
724
+ client_id: { type: 'string', description: '客户端标识(用于限流)' },
725
+ },
726
+ required: ['target_name', 'items'],
727
+ },
728
+ },
729
+ // 38. 知识条目生命周期操作
730
+ {
731
+ name: 'autosnippet_knowledge_lifecycle',
732
+ description:
733
+ '知识条目生命周期操作。\n' +
734
+ '可用操作: submit(draft→pending), approve(pending→approved), reject(pending→rejected),\n' +
735
+ 'publish(approved→active), deprecate(active→deprecated), reactivate(deprecated→active),\n' +
736
+ 'to_draft(rejected→draft), fast_track(draft→active 一键发布)。',
737
+ inputSchema: {
738
+ type: 'object',
739
+ properties: {
740
+ id: { type: 'string', description: '知识条目 ID' },
741
+ action: { type: 'string', enum: ['submit', 'approve', 'reject', 'publish', 'deprecate', 'reactivate', 'to_draft', 'fast_track'], description: '生命周期操作' },
742
+ reason: { type: 'string', description: 'reject/deprecate 时必须提供原因' },
743
+ },
744
+ required: ['id', 'action'],
745
+ },
746
+ },
760
747
  ];
@@ -13,8 +13,6 @@ import { requestLogger } from './middleware/requestLogger.js';
13
13
  import { gatewayMiddleware } from './middleware/gatewayMiddleware.js';
14
14
  import { roleResolverMiddleware } from './middleware/roleResolver.js';
15
15
  import { CapabilityProbe } from '../core/capability/CapabilityProbe.js';
16
- import candidateRouter from './routes/candidates.js';
17
- import recipeRouter from './routes/recipes.js';
18
16
  import guardRuleRouter from './routes/guardRules.js';
19
17
  import searchRouter from './routes/search.js';
20
18
  import healthRouter from './routes/health.js';
@@ -27,6 +25,7 @@ import spmRouter from './routes/spm.js';
27
25
  import violationsRouter from './routes/violations.js';
28
26
  import authRouter from './routes/auth.js';
29
27
  import skillsRouter from './routes/skills.js';
28
+ import knowledgeRouter from './routes/knowledge.js';
30
29
  import apiSpec from './api-spec.js';
31
30
  import { initRedisService } from '../infrastructure/cache/RedisService.js';
32
31
  import { initCacheAdapter } from '../infrastructure/cache/UnifiedCacheAdapter.js';
@@ -235,12 +234,6 @@ export class HttpServer {
235
234
  this.app.use(`${apiPrefix}/monitoring`, monitoringRouter);
236
235
  }
237
236
 
238
- // 候选项路由
239
- this.app.use(`${apiPrefix}/candidates`, candidateRouter);
240
-
241
- // 配方路由
242
- this.app.use(`${apiPrefix}/recipes`, recipeRouter);
243
-
244
237
  // 守护规则路由
245
238
  this.app.use(`${apiPrefix}/rules`, guardRuleRouter);
246
239
 
@@ -268,6 +261,9 @@ export class HttpServer {
268
261
  // 违规记录路由
269
262
  this.app.use(`${apiPrefix}/violations`, violationsRouter);
270
263
 
264
+ // 知识条目路由 (V3)
265
+ this.app.use(`${apiPrefix}/knowledge`, knowledgeRouter);
266
+
271
267
  // 404 处理
272
268
  this.app.use('*', (req, res) => {
273
269
  res.status(404).json({
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  // 轮询/心跳路径 — 完全静默
11
- const SILENT_PATHS = ['/api/health', '/api/realtime/events', '/api/sse'];
11
+ const SILENT_PATHS = ['/api/health', '/api/realtime/events', '/api/sse', '/socket.io'];
12
12
 
13
13
  export function requestLogger(logger) {
14
14
  return (req, res, next) => {
@@ -27,8 +27,8 @@ export function requestLogger(logger) {
27
27
  duration: `${duration}ms`,
28
28
  };
29
29
 
30
- // 非 GET / 非 2xx / 慢请求 → info; 其余 → debug
31
- const isNoisy = req.method === 'GET' && res.statusCode >= 200 && res.statusCode < 300 && duration < 2000;
30
+ // 非 GET / 非 2xx / 慢请求 → info; GET + 2xx/304 → debug(304 是缓存命中,与 200 同级)
31
+ const isNoisy = req.method === 'GET' && (res.statusCode >= 200 && res.statusCode < 300 || res.statusCode === 304) && duration < 2000;
32
32
  const isSlow = duration >= 1000;
33
33
  if (isSlow) {
34
34
  logger.warn(`🐌慢请求: ${req.method} ${req.path} - ${duration}ms`, logData);
@@ -27,7 +27,7 @@ function getChatAgent() {
27
27
  */
28
28
  router.get('/providers', asyncHandler(async (req, res) => {
29
29
  const providers = [
30
- { id: 'google', label: 'Google Gemini', defaultModel: 'gemini-2.0-flash' },
30
+ { id: 'google', label: 'Google Gemini', defaultModel: 'gemini-3-flash-preview' },
31
31
  { id: 'openai', label: 'OpenAI', defaultModel: 'gpt-4o' },
32
32
  { id: 'deepseek', label: 'DeepSeek', defaultModel: 'deepseek-chat' },
33
33
  { id: 'claude', label: 'Claude', defaultModel: 'claude-3-5-sonnet-20240620' },
@@ -398,4 +398,20 @@ router.post('/env-config', asyncHandler(async (req, res) => {
398
398
  res.json({ success: true, data: result });
399
399
  }));
400
400
 
401
+ /**
402
+ * GET /api/v1/ai/token-usage
403
+ * 近 7 日 Token 消耗报告(按日 + 按来源 + 总计)
404
+ */
405
+ router.get('/token-usage', asyncHandler(async (req, res) => {
406
+ const container = getServiceContainer();
407
+ let tokenStore;
408
+ try {
409
+ tokenStore = container.get('tokenUsageStore');
410
+ } catch {
411
+ return res.json({ success: true, data: { daily: [], bySource: [], summary: { input_tokens: 0, output_tokens: 0, total_tokens: 0, call_count: 0, avg_per_call: 0 } } });
412
+ }
413
+ const report = tokenStore.getLast7DaysReport();
414
+ res.json({ success: true, data: report });
415
+ }));
416
+
401
417
  export default router;
@@ -52,6 +52,7 @@ router.post('/path', asyncHandler(async (req, res) => {
52
52
  const aiResult = await chatAgent.executeTool('extract_recipes', {
53
53
  targetName: fileName,
54
54
  files: [{ name: fileName, content: file.code || '' }],
55
+ comprehensive: true,
55
56
  });
56
57
 
57
58
  if (aiResult && !aiResult.error && Array.isArray(aiResult.recipes) && aiResult.recipes.length > 0) {
@@ -82,6 +83,7 @@ router.post('/path', asyncHandler(async (req, res) => {
82
83
  /**
83
84
  * POST /api/v1/extract/text
84
85
  * 从文本内容提取代码片段(剪贴板等)
86
+ * 管线: RecipeParser(MD解析) → AI 提取(ChatAgent) → 基础兜底
85
87
  */
86
88
  router.post('/text', asyncHandler(async (req, res) => {
87
89
  const { text, language, relativePath, projectRoot: bodyRoot } = req.body;
@@ -94,21 +96,63 @@ router.post('/text', asyncHandler(async (req, res) => {
94
96
  const recipeParser = container.get('recipeParser');
95
97
  const projectRoot = bodyRoot || container.singletons?._projectRoot || process.cwd();
96
98
 
97
- // 先尝试解析为 Recipe Markdown 格式
99
+ // 1. 先尝试解析为 Recipe Markdown 格式
98
100
  let result;
99
101
  try {
100
102
  result = await recipeParser.parseFromText(text, {
101
103
  language,
102
104
  relativePath,
103
105
  });
106
+ // 解析成功,直接返回
107
+ return res.json({
108
+ success: true,
109
+ data: {
110
+ result: Array.isArray(result) ? result : [result],
111
+ source: 'text',
112
+ },
113
+ });
104
114
  } catch (error) {
105
- logger.debug('Recipe MD parse failed, falling back to AI extraction', {
115
+ logger.debug('Recipe MD parse failed, trying AI extraction', {
106
116
  error: error.message,
107
117
  });
108
- // 回退到 AI 提取
109
- result = await recipeParser.extractFromText(text, { language });
110
118
  }
111
119
 
120
+ // 2. Recipe MD 解析失败 → 尝试 ChatAgent AI 提取
121
+ try {
122
+ const chatAgent = container.get('chatAgent');
123
+ if (chatAgent) {
124
+ const lang = language || (relativePath && /\.swift$/i.test(relativePath) ? 'swift' : 'objc');
125
+ const fileName = relativePath ? basename(relativePath) : `clipboard.${lang === 'swift' ? 'swift' : 'm'}`;
126
+ const aiResult = await chatAgent.executeTool('extract_recipes', {
127
+ targetName: fileName,
128
+ files: [{ name: fileName, content: text }],
129
+ comprehensive: true,
130
+ });
131
+
132
+ if (aiResult && !aiResult.error && Array.isArray(aiResult.recipes) && aiResult.recipes.length > 0) {
133
+ logger.info('extract/text: AI extraction succeeded', { count: aiResult.recipes.length });
134
+
135
+ // 多条 Recipe 时在第一条上标记总数(供前端提示)
136
+ if (aiResult.recipes.length > 1) {
137
+ aiResult.recipes[0]._multipleCount = aiResult.recipes.length;
138
+ }
139
+
140
+ return res.json({
141
+ success: true,
142
+ data: {
143
+ result: aiResult.recipes,
144
+ source: 'text',
145
+ },
146
+ });
147
+ }
148
+ }
149
+ } catch (err) {
150
+ logger.debug('extract/text: AI extraction failed, using basic fallback', { error: err.message });
151
+ }
152
+
153
+ // 3. AI 也失败 → 基础代码块提取兜底
154
+ result = await recipeParser.extractFromText(text, { language });
155
+
112
156
  res.json({
113
157
  success: true,
114
158
  data: {