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
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP 工具定义(
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
];
|
package/lib/http/HttpServer.js
CHANGED
|
@@ -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;
|
|
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);
|
package/lib/http/routes/ai.js
CHANGED
|
@@ -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-
|
|
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,
|
|
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: {
|