autosnippet 3.3.2 → 3.3.3

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 (51) hide show
  1. package/dist/bin/cli.js +27 -1
  2. package/dist/lib/cli/KnowledgeSyncService.d.ts +26 -0
  3. package/dist/lib/cli/KnowledgeSyncService.js +33 -1
  4. package/dist/lib/external/mcp/handlers/browse.d.ts +1 -0
  5. package/dist/lib/external/mcp/handlers/browse.js +2 -1
  6. package/dist/lib/external/mcp/handlers/consolidated.d.ts +1 -0
  7. package/dist/lib/external/mcp/handlers/panorama.d.ts +11 -11
  8. package/dist/lib/external/mcp/handlers/panorama.js +20 -20
  9. package/dist/lib/external/mcp/handlers/system.d.ts +1 -1
  10. package/dist/lib/external/mcp/handlers/task.js +2 -1
  11. package/dist/lib/external/mcp/tools.d.ts +12 -12
  12. package/dist/lib/external/mcp/tools.js +120 -118
  13. package/dist/lib/http/middleware/validate.js +7 -3
  14. package/dist/lib/infrastructure/database/drizzle/schema.d.ts +100 -0
  15. package/dist/lib/infrastructure/database/drizzle/schema.js +10 -0
  16. package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.d.ts +9 -0
  17. package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.js +24 -0
  18. package/dist/lib/infrastructure/vector/HnswVectorAdapter.js +18 -2
  19. package/dist/lib/injection/ServiceContainer.js +2 -0
  20. package/dist/lib/injection/modules/KnowledgeModule.d.ts +5 -0
  21. package/dist/lib/injection/modules/KnowledgeModule.js +80 -0
  22. package/dist/lib/service/bootstrap/UiStartupTasks.d.ts +45 -0
  23. package/dist/lib/service/bootstrap/UiStartupTasks.js +101 -0
  24. package/dist/lib/service/evolution/ConsolidationAdvisor.js +9 -9
  25. package/dist/lib/service/evolution/ContradictionDetector.js +2 -2
  26. package/dist/lib/service/evolution/RedundancyAnalyzer.js +2 -2
  27. package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +68 -0
  28. package/dist/lib/service/knowledge/SourceRefReconciler.js +309 -0
  29. package/dist/lib/service/panorama/PanoramaService.d.ts +18 -1
  30. package/dist/lib/service/panorama/PanoramaService.js +148 -5
  31. package/dist/lib/service/search/BM25Scorer.d.ts +2 -2
  32. package/dist/lib/service/search/CoarseRanker.d.ts +7 -6
  33. package/dist/lib/service/search/CoarseRanker.js +11 -10
  34. package/dist/lib/service/search/FieldWeightedScorer.d.ts +81 -0
  35. package/dist/lib/service/search/FieldWeightedScorer.js +318 -0
  36. package/dist/lib/service/search/MultiSignalRanker.d.ts +2 -2
  37. package/dist/lib/service/search/MultiSignalRanker.js +1 -1
  38. package/dist/lib/service/search/SearchEngine.d.ts +8 -7
  39. package/dist/lib/service/search/SearchEngine.js +59 -10
  40. package/dist/lib/service/search/SearchTypes.d.ts +23 -3
  41. package/dist/lib/service/search/SearchTypes.js +6 -1
  42. package/dist/lib/service/task/IntentExtractor.d.ts +8 -0
  43. package/dist/lib/service/task/IntentExtractor.js +115 -1
  44. package/dist/lib/service/task/PrimeSearchPipeline.js +39 -24
  45. package/dist/lib/service/vector/VectorService.d.ts +3 -0
  46. package/dist/lib/service/vector/VectorService.js +38 -4
  47. package/package.json +1 -1
  48. package/skills/autosnippet-create/SKILL.md +98 -89
  49. package/skills/autosnippet-devdocs/SKILL.md +55 -60
  50. package/templates/guard-ci.yml +2 -2
  51. package/templates/recipes-setup/_template.md +39 -39
@@ -6,6 +6,7 @@
6
6
  *
7
7
  * @module service/task/IntentExtractor
8
8
  */
9
+ import { tokenize } from '#service/search/tokenizer.js';
9
10
  // ── Universal Patterns (language-agnostic) ──────────
10
11
  const UNIVERSAL_PATTERNS = [
11
12
  /\b[A-Z][a-z]+(?:[A-Z][a-z]+)+\b/g, // CamelCase
@@ -29,6 +30,74 @@ const LANG_MAP = {
29
30
  java: 'java',
30
31
  kt: 'kotlin',
31
32
  };
33
+ // ── Cross-Language Synonym Groups ───────────────────
34
+ // Each group contains EN morphological variants + CN equivalents.
35
+ // Used to expand queries so English terms match Chinese recipe fields (and vice versa).
36
+ const SYNONYM_GROUPS = [
37
+ // Design patterns & DI
38
+ ['inject', 'injection', '注入'],
39
+ ['construct', 'constructor', '构造器', '构造函数'],
40
+ ['depend', 'dependency', 'dependencies', '依赖'],
41
+ ['protocol', '协议'],
42
+ ['interface', '接口'],
43
+ ['pattern', '模式'],
44
+ ['factory', '工厂'],
45
+ ['singleton', '单例'],
46
+ ['delegate', '代理', '委托'],
47
+ ['observe', 'observer', '观察者'],
48
+ ['subscribe', 'subscription', '订阅'],
49
+ ['repository', 'repo', '仓库'],
50
+ // Architecture
51
+ ['module', '模块'],
52
+ ['architect', 'architecture', '架构'],
53
+ ['route', 'router', 'routing', '路由'],
54
+ ['middleware', '中间件'],
55
+ ['component', '组件'],
56
+ ['lifecycle', '生命周期'],
57
+ ['layer', '分层', '层'],
58
+ // Language features
59
+ ['generic', 'generics', '泛型'],
60
+ ['closure', '闭包'],
61
+ ['callback', '回调'],
62
+ ['extend', 'extension', '扩展'],
63
+ ['inherit', 'inheritance', '继承'],
64
+ ['abstract', 'abstraction', '抽象'],
65
+ ['encapsulate', 'encapsulation', '封装'],
66
+ ['polymorph', 'polymorphism', '多态'],
67
+ ['implement', 'implementation', '实现'],
68
+ // Concurrency
69
+ ['async', 'asynchronous', '异步'],
70
+ ['sync', 'synchronous', '同步'],
71
+ ['thread', 'threading', '线程'],
72
+ ['concur', 'concurrency', '并发'],
73
+ // Common concepts
74
+ ['network', '网络'],
75
+ ['cache', 'caching', '缓存'],
76
+ ['persist', 'persistence', '持久化'],
77
+ ['serialize', 'serialization', '序列化'],
78
+ ['validate', 'validation', '校验', '验证'],
79
+ ['authenticate', 'authentication', '认证'],
80
+ ['authorize', 'authorization', '授权'],
81
+ ['config', 'configuration', '配置'],
82
+ ['navigate', 'navigation', '导航'],
83
+ ['animate', 'animation', '动画'],
84
+ ['layout', '布局'],
85
+ ['render', 'rendering', '渲染'],
86
+ ['responsive', '响应式'],
87
+ ['state', '状态'],
88
+ ['toast', '提示'],
89
+ ['error', '错误'],
90
+ ['handle', 'handler', '处理'],
91
+ ['service', '服务'],
92
+ ['test', 'testing', '测试'],
93
+ ];
94
+ /** Lookup: lowercased term → synonym expansions (excluding the term itself) */
95
+ const SYNONYM_LOOKUP = new Map();
96
+ for (const group of SYNONYM_GROUPS) {
97
+ for (const term of group) {
98
+ SYNONYM_LOOKUP.set(term.toLowerCase(), group.filter((t) => t !== term));
99
+ }
100
+ }
32
101
  // ── Public API ──────────────────────────────────────
33
102
  /**
34
103
  * Extract intent signals from user query and active file.
@@ -36,11 +105,13 @@ const LANG_MAP = {
36
105
  */
37
106
  export function extract(userQuery, activeFile, language, termOpts) {
38
107
  const queries = buildQueries(userQuery, activeFile, termOpts);
108
+ const keywordQueries = buildKeywordQueries(userQuery);
39
109
  const inferredLang = language || (activeFile ? inferLanguage(activeFile) : null);
40
110
  const module = activeFile ? inferFileContext(activeFile) : null;
41
111
  const scenario = classifyScenario(userQuery);
42
112
  return {
43
113
  queries,
114
+ keywordQueries,
44
115
  language: inferredLang,
45
116
  module,
46
117
  scenario,
@@ -50,9 +121,13 @@ export function extract(userQuery, activeFile, language, termOpts) {
50
121
  /**
51
122
  * Build multi-query set from user query + active file.
52
123
  * Q1: raw query, Q2: extracted tech terms, Q3: file context.
124
+ * Q1 is enriched with cross-language synonyms to bridge EN↔CJK matching.
53
125
  */
54
126
  export function buildQueries(userQuery, activeFile, termOpts) {
55
- const queries = [userQuery];
127
+ // Enrich raw query with cross-language synonyms
128
+ const synonyms = expandWithSynonyms(userQuery);
129
+ const enrichedQuery = synonyms ? `${userQuery} ${synonyms}` : userQuery;
130
+ const queries = [enrichedQuery];
56
131
  const terms = extractTechTerms(userQuery, termOpts);
57
132
  if (terms.length > 0) {
58
133
  queries.push(terms.join(' '));
@@ -65,6 +140,14 @@ export function buildQueries(userQuery, activeFile, termOpts) {
65
140
  }
66
141
  return queries;
67
142
  }
143
+ /**
144
+ * Build keyword-mode queries for cross-language synonym matching.
145
+ * Uses keyword mode to preserve raw FWS scores without CoarseRanker semantic normalization.
146
+ */
147
+ export function buildKeywordQueries(userQuery) {
148
+ const expanded = expandWithSynonyms(userQuery);
149
+ return expanded ? [expanded] : [];
150
+ }
68
151
  /**
69
152
  * Extract tech terms from query using universal patterns + dynamic project prefixes.
70
153
  */
@@ -132,6 +215,37 @@ export function classifyScenario(userQuery) {
132
215
  return 'search';
133
216
  }
134
217
  // ── Internal Helpers ────────────────────────────────
218
+ /**
219
+ * Expand query tokens with cross-language synonyms.
220
+ * Tokenizes query, looks up each token in the synonym table,
221
+ * returns a query string of synonym expansions for cross-language matching.
222
+ *
223
+ * Strategy: return only cross-script synonyms (EN→CJK or CJK→EN).
224
+ * This keeps the expansion focused — the original script tokens are already in Q1.
225
+ */
226
+ function expandWithSynonyms(query) {
227
+ const tokens = tokenize(query);
228
+ const crossScriptTerms = new Set();
229
+ // Detect query script: does it contain CJK?
230
+ const hasCJK = /[\u4e00-\u9fff\u3400-\u4dbf]/.test(query);
231
+ for (const token of tokens) {
232
+ const synonyms = SYNONYM_LOOKUP.get(token.toLowerCase());
233
+ if (!synonyms) {
234
+ continue;
235
+ }
236
+ for (const syn of synonyms) {
237
+ const synIsCJK = /[\u4e00-\u9fff\u3400-\u4dbf]/.test(syn);
238
+ // Cross-script: EN query → add CJK synonyms; CJK query → add EN synonyms
239
+ if (hasCJK !== synIsCJK) {
240
+ crossScriptTerms.add(syn);
241
+ }
242
+ }
243
+ }
244
+ if (crossScriptTerms.size === 0) {
245
+ return null;
246
+ }
247
+ return [...crossScriptTerms].slice(0, 12).join(' ');
248
+ }
135
249
  function buildPrefixPattern(prefixes) {
136
250
  if (prefixes.length === 0) {
137
251
  return null;
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { slimSearchResult } from '#service/search/SearchTypes.js';
10
10
  // ── Constants ───────────────────────────────────────
11
- const RELEVANCE_THRESHOLD = 0.44;
11
+ const RELEVANCE_THRESHOLD = 0.01;
12
12
  // ── PrimeSearchPipeline ─────────────────────────────
13
13
  export class PrimeSearchPipeline {
14
14
  #search;
@@ -29,8 +29,8 @@ export class PrimeSearchPipeline {
29
29
  intent: intent.scenario,
30
30
  sessionHistory: this.#buildSessionHistory(),
31
31
  };
32
- // Multi-query parallel search
33
- const allResults = await this.#multiQuerySearch(intent.queries, context);
32
+ // Multi-query parallel search (auto mode + keyword mode for cross-language)
33
+ const allResults = await this.#multiQuerySearch(intent.queries, intent.keywordQueries ?? [], context);
34
34
  // Threshold filter
35
35
  const filtered = allResults.filter((r) => (r.score ?? 0) >= RELEVANCE_THRESHOLD);
36
36
  if (filtered.length === 0) {
@@ -62,32 +62,47 @@ export class PrimeSearchPipeline {
62
62
  }
63
63
  // ── Private ───────────────────────────────────────
64
64
  /**
65
- * Multi-query parallel search + de-dup by ID (keep highest score).
65
+ * Multi-query parallel search with Reciprocal Rank Fusion (RRF).
66
+ * Auto-mode queries use CoarseRanker; keyword queries use raw FWS scores.
67
+ * Results are fused by rank position, not absolute scores — robust across heterogeneous scorers.
66
68
  */
67
- async #multiQuerySearch(queries, context) {
68
- const promises = queries.map((q) => this.#search
69
- .search(q, {
70
- mode: 'auto',
71
- limit: 8,
72
- rank: true,
73
- context,
74
- })
69
+ async #multiQuerySearch(autoQueries, keywordQueries, context) {
70
+ // Auto-mode searches (full CoarseRanker pipeline)
71
+ const autoPromises = autoQueries.map((q) => this.#search
72
+ .search(q, { mode: 'auto', limit: 8, rank: true, context })
75
73
  .catch(() => ({ items: [] })));
76
- const responses = await Promise.all(promises);
77
- // Merge by ID, keep highest score
78
- const bestById = new Map();
79
- for (const resp of responses) {
80
- const items = resp.items || [];
81
- for (const raw of items) {
82
- const item = slimSearchResult(raw);
83
- const existing = bestById.get(item.id);
84
- if (!existing || item.score > existing.score) {
85
- bestById.set(item.id, item);
74
+ // Keyword-mode searches (raw FWS scores — for cross-language synonym matching)
75
+ const kwPromises = keywordQueries.map((q) => this.#search
76
+ .search(q, { mode: 'keyword', limit: 8, rank: false })
77
+ .catch(() => ({ items: [] })));
78
+ const [autoResponses, kwResponses] = await Promise.all([
79
+ Promise.all(autoPromises),
80
+ Promise.all(kwPromises),
81
+ ]);
82
+ const allResponses = [...autoResponses, ...kwResponses];
83
+ // Reciprocal Rank Fusion: RRF(d) = Σ 1/(k + rank)
84
+ const RRF_K = 60;
85
+ const rrfScores = new Map();
86
+ const itemById = new Map();
87
+ for (const resp of allResponses) {
88
+ const items = (resp.items || []);
89
+ for (let rank = 0; rank < items.length; rank++) {
90
+ const item = slimSearchResult(items[rank]);
91
+ rrfScores.set(item.id, (rrfScores.get(item.id) ?? 0) + 1 / (RRF_K + rank));
92
+ // Keep the richest metadata version
93
+ if (!itemById.has(item.id)) {
94
+ itemById.set(item.id, item);
86
95
  }
87
96
  }
88
97
  }
89
- // Sort by score descending
90
- return [...bestById.values()].sort((a, b) => b.score - a.score);
98
+ // Assign fused scores and sort
99
+ const results = [];
100
+ for (const [id, rrfScore] of rrfScores) {
101
+ const item = itemById.get(id);
102
+ item.score = rrfScore;
103
+ results.push(item);
104
+ }
105
+ return results.sort((a, b) => b.score - a.score);
91
106
  }
92
107
  /**
93
108
  * Build sessionHistory for contextBoost (last 5 queries).
@@ -109,6 +109,9 @@ export declare class VectorService {
109
109
  /**
110
110
  * 混合搜索 (Dense + Sparse RRF 融合)
111
111
  * 通过 HybridRetriever 执行向量 + BM25 关键词并行检索
112
+ *
113
+ * Embed 失败时优雅降级: 跳过 Dense 路, 仅用 Sparse 结果进行 RRF 融合,
114
+ * 避免因网络问题导致整个搜索返回空结果。
112
115
  */
113
116
  hybridSearch(query: string, opts?: {
114
117
  topK?: number;
@@ -26,6 +26,11 @@ export class VectorService {
26
26
  #syncDebounceMs;
27
27
  #logger = Logger.getInstance();
28
28
  #initialized = false;
29
+ // ── Embed circuit breaker ──
30
+ #embedConsecutiveFailures = 0;
31
+ #embedCircuitOpenUntil = 0;
32
+ static #EMBED_CIRCUIT_THRESHOLD = 3;
33
+ static #EMBED_CIRCUIT_COOLDOWN_MS = 60_000;
29
34
  constructor(config) {
30
35
  this.#vectorStore = config.vectorStore;
31
36
  this.#indexingPipeline = config.indexingPipeline;
@@ -211,6 +216,9 @@ export class VectorService {
211
216
  /**
212
217
  * 混合搜索 (Dense + Sparse RRF 融合)
213
218
  * 通过 HybridRetriever 执行向量 + BM25 关键词并行检索
219
+ *
220
+ * Embed 失败时优雅降级: 跳过 Dense 路, 仅用 Sparse 结果进行 RRF 融合,
221
+ * 避免因网络问题导致整个搜索返回空结果。
214
222
  */
215
223
  async hybridSearch(query, opts = {}) {
216
224
  if (!this.#embedProvider) {
@@ -226,11 +234,37 @@ export class VectorService {
226
234
  }));
227
235
  }
228
236
  const { topK = 10, alpha = 0.5, sparseSearchFn = null } = opts;
237
+ // Embed query — circuit breaker skips embed after repeated failures
238
+ let queryVector = null;
239
+ const circuitOpen = Date.now() < this.#embedCircuitOpenUntil;
240
+ if (circuitOpen) {
241
+ this.#logger.debug('[VectorService] embed circuit open, skipping embed');
242
+ }
243
+ else {
244
+ try {
245
+ const embedResult = await this.#embedProvider.embed(query);
246
+ queryVector = Array.isArray(embedResult[0])
247
+ ? embedResult[0]
248
+ : embedResult;
249
+ this.#embedConsecutiveFailures = 0;
250
+ }
251
+ catch (err) {
252
+ this.#embedConsecutiveFailures++;
253
+ if (this.#embedConsecutiveFailures >= VectorService.#EMBED_CIRCUIT_THRESHOLD) {
254
+ this.#embedCircuitOpenUntil = Date.now() + VectorService.#EMBED_CIRCUIT_COOLDOWN_MS;
255
+ this.#logger.warn('[VectorService] embed circuit OPEN — skipping embed for 60s', {
256
+ consecutiveFailures: this.#embedConsecutiveFailures,
257
+ });
258
+ }
259
+ else {
260
+ this.#logger.warn('[VectorService] embed failed, degrading to sparse-only', {
261
+ error: err instanceof Error ? err.message : String(err),
262
+ failCount: this.#embedConsecutiveFailures,
263
+ });
264
+ }
265
+ }
266
+ }
229
267
  try {
230
- const embedResult = await this.#embedProvider.embed(query);
231
- const queryVector = Array.isArray(embedResult[0])
232
- ? embedResult[0]
233
- : embedResult;
234
268
  const fused = await this.#hybridRetriever.search(query, queryVector, {
235
269
  topK,
236
270
  alpha,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autosnippet",
3
- "version": "3.3.2",
3
+ "version": "3.3.3",
4
4
  "description": "Extract code patterns into a knowledge base for AI coding assistants",
5
5
  "type": "module",
6
6
  "main": "dist/lib/bootstrap.js",
@@ -1,97 +1,94 @@
1
1
  ---
2
2
  name: autosnippet-create
3
- description: Submit knowledge to AutoSnippet. Covers single/batch MCP submission, V3 field requirements, quality validation, and lifecycle. Use when user says "提交知识/加入知识库/create recipe" or agent needs to persist code patterns, rules, or facts.
3
+ description: Submit knowledge to AutoSnippet. Covers single/batch MCP submission, V3 field requirements, quality validation, and lifecycle. Use when user says "submit knowledge / add to KB / create recipe" or agent needs to persist code patterns, rules, or facts.
4
4
  ---
5
5
 
6
- # AutoSnippet Create — 知识提交
6
+ # AutoSnippet Create — Knowledge Submission
7
7
 
8
- > 前置:MCP 工具返回统一 JSON Envelope `{ success, errorCode?, message?, data?, meta }`。操作前调用 `autosnippet_health` 确认服务可用。
8
+ > Prerequisite: MCP tools return a unified JSON Envelope `{ success, errorCode?, message?, data?, meta }`. Call `autosnippet_health` before operations to confirm service availability.
9
9
 
10
- Skill 指导 Agent 将代码模式、规则、事实提交到 AutoSnippet 知识库。提交后的条目进入 **Candidates**(pending 状态),用户在 Dashboard 审核后发布。
10
+ This Skill guides the Agent to submit code patterns, rules, and facts to the AutoSnippet knowledge base. Submitted entries enter **Candidates** (pending status); users review and publish them via the Dashboard.
11
11
 
12
- 关联 Skill:**autosnippet-recipes**(检索已有知识)。
12
+ Related Skill: **autosnippet-recipes** (search existing knowledge).
13
13
 
14
14
  ---
15
15
 
16
- ## 提交路径
16
+ ## Submission Paths
17
17
 
18
- | 路径 | 工具 | 适用场景 |
18
+ | Path | Tool | Use Case |
19
19
  |------|------|----------|
20
- | **单条提交** | `autosnippet_submit_knowledge` | Agent 精心构造一条完整知识 |
21
- | **批量提交** | `autosnippet_submit_knowledge_batch` | 冷启动维度分析、批量扫描 |
22
- | **Dashboard** | 浏览器 `http://localhost:3000` | 用户手动粘贴/扫描文件 |
20
+ | **Single** | `autosnippet_submit_knowledge` | Agent carefully constructs one complete entry |
21
+ | **Batch** | `autosnippet_submit_knowledge` (items array) | Cold-start dimension analysis, batch scans |
22
+ | **Dashboard** | Browser `http://localhost:3000` | User manual paste/file scan |
23
23
 
24
- **Agent 首选 MCP 提交**,无需浏览器。
24
+ **Agent prefers MCP submission** — no browser needed.
25
25
 
26
26
  ---
27
27
 
28
- ## 单条提交 — autosnippet_submit_knowledge
28
+ ## Single Submission — autosnippet_submit_knowledge
29
29
 
30
- 一次提交一条完整的 V3 知识条目。即使部分字段校验未通过也会入库,返回中附带 `recipeReadyHints` 提示缺失字段。
30
+ Submit one complete V3 knowledge entry at a time. Even if some fields fail validation, the entry is still stored; the response includes `recipeReadyHints` indicating missing fields.
31
31
 
32
- ### V3 必填字段(16 个)
32
+ ### V3 Required Fields (16)
33
33
 
34
- | 字段 | 类型 | 说明 |
35
- |------|------|------|
36
- | `title` | string | 知识标题,简洁明确 |
37
- | `description` | string | 一句话描述用途 |
38
- | `trigger` | string | 触发关键词,如 `@NetworkMonitor` |
39
- | `language` | string | 编程语言,如 `typescript`、`swift` |
40
- | `kind` | enum | `rule`(规范)/ `pattern`(模式)/ `fact`(事实) |
34
+ | Field | Type | Description |
35
+ |-------|------|-------------|
36
+ | `title` | string | Knowledge title, concise and clear |
37
+ | `description` | string | One-line purpose description |
38
+ | `trigger` | string | Trigger keyword, e.g. `@NetworkMonitor` |
39
+ | `language` | string | Programming language, e.g. `typescript`, `swift` |
40
+ | `kind` | enum | `rule` (constraint) / `pattern` (reusable) / `fact` (project fact) |
41
41
  | `category` | string | `View`/`Service`/`Tool`/`Model`/`Network`/`Storage`/`UI`/`Utility` |
42
- | `knowledgeType` | string | 知识类型标识 |
43
- | `doClause` | string | ✅ 应该做什么(Channel A+B 硬依赖) |
44
- | `dontClause` | string | ❌ 不应该做什么 |
45
- | `whenClause` | string | 何时适用(Channel B 硬依赖) |
46
- | `coreCode` | string | 核心代码片段 |
47
- | `headers` | string[] | 完整 import 语句列表 |
48
- | `usageGuide` | string | 使用指南(Markdown,见下方格式要求) |
49
- | `content` | object | `{ markdown: string, rationale: string }` 至少提供 markdown |
42
+ | `knowledgeType` | string | Knowledge type identifier |
43
+ | `doClause` | string | ✅ What to do (Channel A+B hard dependency) |
44
+ | `dontClause` | string | ❌ What not to do |
45
+ | `whenClause` | string | When to apply (Channel B hard dependency) |
46
+ | `coreCode` | string | Core code snippet |
47
+ | `headers` | string[] | Complete import statement list |
48
+ | `usageGuide` | string | Usage guide (Markdown, see format below) |
49
+ | `content` | object | `{ markdown: string, rationale: string }` at minimum provide markdown |
50
50
  | `reasoning` | object | `{ whyStandard: string, sources: string[], confidence: number }` |
51
51
 
52
- ### 可选字段
52
+ ### Optional Fields
53
53
 
54
- `topicHint`、`complexity`(beginner/intermediate/advanced)、`scope`(universal/project-specific/target-specific)、`tags`(string[])、`constraints`、`relations`、`skipDuplicateCheck`(默认 false
54
+ `topicHint`, `complexity` (beginner/intermediate/advanced), `scope` (universal/project-specific/target-specific), `tags` (string[]), `constraints`, `relations`, `skipDuplicateCheck` (default false)
55
55
 
56
- ### usageGuide 格式要求
56
+ ### usageGuide Format Requirements
57
57
 
58
- **必须**使用 Markdown 分节,禁止写成一行长文本。
58
+ **Must** use Markdown sections. Never write as a single long line.
59
59
 
60
60
  ```markdown
61
- ### 何时用
62
- - 场景 A
63
- - 场景 B
61
+ ### When to Use
62
+ - Scenario A
63
+ - Scenario B
64
64
 
65
- ### 何时不用
66
- - 排除场景
65
+ ### When Not to Use
66
+ - Exclusion scenario
67
67
 
68
- ### 使用步骤
69
- 1. 第一步
70
- 2. 第二步
68
+ ### Steps
69
+ 1. First step
70
+ 2. Second step
71
71
 
72
- ### 关键点
73
- - 注意事项 A
74
- - 注意事项 B
72
+ ### Key Points
73
+ - Note A
74
+ - Note B
75
75
  ```
76
76
 
77
- 可选章节:依赖与前置条件、错误处理、性能与资源、安全与合规、常见误用、替代方案、相关知识。
77
+ Optional sections: Dependencies & Prerequisites, Error Handling, Performance & Resources, Security & Compliance, Common Misuse, Alternatives, Related Knowledge.
78
78
 
79
79
  ---
80
80
 
81
- ## 批量提交autosnippet_submit_knowledge_batch
81
+ ## Batch Submission autosnippet_submit_knowledge (items array)
82
82
 
83
- 一次提交多条知识。每条单独校验,不通过的拒绝但不阻塞其他。
83
+ Submit multiple entries at once. Each is validated independently; failures are rejected without blocking others.
84
84
 
85
- ### 参数
85
+ ### Parameters
86
86
 
87
- | 字段 | 必填 | 类型 | 说明 |
88
- |------|------|------|------|
89
- | `target_name` | ✅ | string | 批量来源标识(如 `network-module-scan`) |
90
- | `items` | ✅ | object[] | 知识条目数组,每条结构同单条提交的字段 |
91
- | `source` | | string | 来源标记,默认 `cursor-scan` |
92
- | `deduplicate` | | boolean | 基于 title 去重,默认 `true` |
87
+ | Field | Required | Type | Description |
88
+ |-------|----------|------|-------------|
89
+ | `items` | ✅ | object[] | Array of knowledge entries, each following the same field structure as single submission |
93
90
 
94
- ### 返回值
91
+ ### Response
95
92
 
96
93
  ```json
97
94
  {
@@ -104,75 +101,87 @@ description: Submit knowledge to AutoSnippet. Covers single/batch MCP submission
104
101
  }
105
102
  ```
106
103
 
107
- **批量提交校验更严格**:单条提交校验不通过仍入库(附 hints),**批量提交校验不通过直接拒绝**。
104
+ **Batch validation is stricter**: single submission stores entries even with validation warnings (with hints), **batch submission rejects entries that fail validation**.
108
105
 
109
106
  ---
110
107
 
111
- ## 提交工作流
108
+ ## Submission Workflow
112
109
 
113
- ### 标准流程(Agent 通过 MCP
110
+ ### Standard Flow (Agent via MCP)
114
111
 
115
112
  ```
116
- 1. 分析代码构造 V3 字段
117
- 2. autosnippet_submit_knowledge / _batch 入库为 pending
118
- 3. 检查返回值:
119
- - 成功告知用户"已提交,请在 Dashboard Candidates 审核"
120
- - rejectedItems → 根据 rejectedSummary.commonMissingFields 补全后重试
121
- 4. [可选] autosnippet_enrich_candidates → 诊断候选字段完整性
113
+ 1. Analyze code construct V3 fields
114
+ 2. autosnippet_submit_knowledge stored as pending
115
+ 3. Check response:
116
+ - Successinform user "Submitted. Review in Dashboard Candidates."
117
+ - Has rejectedItems → fill in missing fields per rejectedSummary.commonMissingFields, retry
118
+ 4. [Optional] autosnippet_enrich_candidates → diagnose candidate field completeness
122
119
  ```
123
120
 
124
- ### 一条知识一个场景
121
+ ### One Entry Per Scenario
125
122
 
126
- 拆分原则:不同使用场景、不同 API 入口、不同配置方式→各自一条知识。禁止将多个模式合并为一条。
123
+ Splitting principle: different use cases, different API endpoints, different configurations → separate entries each. Never merge multiple patterns into one.
124
+
125
+ ### Batch Anti-Redundancy Rules (⚠️ MANDATORY)
126
+
127
+ **Items in the array must NOT be cross-redundant**:
128
+ - No highly overlapping doClause / coreCode / trigger entries within the same batch
129
+ - If two entries share 80%+ content, **merge into one** or split into **primary + extends supplementary** entries
130
+ - Primary entry contains complete core content; supplementary entry contains only the differences, referencing the primary trigger in `_relations.extends`
131
+ - The system only detects fusion between "candidates vs existing DB entries" — **it does NOT check intra-batch redundancy** — Agent must self-enforce
132
+
133
+ **Example**: Two routing knowledge entries (registration flow + dispatch supplement) should be structured as:
134
+ 1. Primary: Complete route registration pattern (register + open + doc sync)
135
+ 2. Supplementary: Only deepLink/Modal/Tab stack differences, `_relations.extends → primary trigger`
127
136
 
128
137
  ---
129
138
 
130
- ## 提交后管理
139
+ ## Post-Submission Management
131
140
 
132
- | 需求 | 工具 |
141
+ | Need | Tool |
133
142
  |------|------|
134
- | 查看候选状态 | `autosnippet_knowledge(operation=list)` |
135
- | 诊断缺失字段 | `autosnippet_enrich_candidates` |
136
- | 审核/发布 | `autosnippet_knowledge_lifecycle(operation=approve/publish/fast_track)` |
137
- | 搜索已有知识避免重复 | `autosnippet_search(mode=context, query=...)` |
143
+ | Check candidate status | `autosnippet_knowledge(operation=list)` |
144
+ | Diagnose missing fields | `autosnippet_enrich_candidates` |
145
+ | Review/publish | `autosnippet_knowledge_lifecycle(operation=approve/publish/fast_track)` |
146
+ | Search existing knowledge to avoid duplicates | `autosnippet_search(mode=context, query=...)` |
138
147
 
139
148
  ---
140
149
 
141
- ## kind 路由与管线影响
150
+ ## Kind Routing & Pipeline Impact
142
151
 
143
- | kind | 用途 | 管线产出 |
144
- |------|------|----------|
145
- | `rule` | 编码规范、约束 | → Channel A(.mdc 规则文件) |
146
- | `pattern` | 代码模式、用法 | → Channel B(.mdc 模式文件 + Snippet |
147
- | `fact` | 项目事实、架构决策 | → 搜索/Guard 上下文,不直接产出文件 |
152
+ | kind | Purpose | Pipeline Output |
153
+ |------|---------|-----------------|
154
+ | `rule` | Coding conventions, constraints | → Channel A (.mdc rule files) |
155
+ | `pattern` | Code patterns, usage | → Channel B (.mdc pattern files + Snippet) |
156
+ | `fact` | Project facts, architecture decisions | → Search/Guard context, no direct file output |
148
157
 
149
- `doClause` Channel A+B 的**硬依赖**——缺少此字段则完全无法生成 .mdc 文件。
158
+ `doClause` is a **hard dependency** for Channel A+B missing this field means .mdc files cannot be generated at all.
150
159
 
151
160
  ---
152
161
 
153
- ## 示例:提交一条知识
162
+ ## Example: Submit One Entry
154
163
 
155
164
  ```json
156
165
  {
157
- "title": "Network Monitor — 网络状态监听",
158
- "description": "使用 NWPathMonitor 监听网络连通性变化",
166
+ "title": "Network Monitor — Connectivity Listener",
167
+ "description": "Monitor network connectivity changes using NWPathMonitor",
159
168
  "trigger": "@NetworkMonitor",
160
169
  "language": "swift",
161
170
  "kind": "pattern",
162
171
  "category": "Network",
163
172
  "knowledgeType": "api-usage",
164
- "doClause": "使用 NWPathMonitor 监听网络状态变化,在主队列回调更新 UI",
165
- "dontClause": "不要用 Reachability 旧库,不要在后台线程直接更新 UI",
166
- "whenClause": "需要实时感知网络连通性变化时",
173
+ "doClause": "Use NWPathMonitor to observe network status changes; dispatch UI updates to the main queue",
174
+ "dontClause": "Do not use the deprecated Reachability library; do not update UI directly on background threads",
175
+ "whenClause": "When the app needs real-time network connectivity awareness",
167
176
  "coreCode": "let monitor = NWPathMonitor()\nmonitor.pathUpdateHandler = { path in\n DispatchQueue.main.async {\n self.isConnected = path.status == .satisfied\n }\n}\nmonitor.start(queue: DispatchQueue.global())",
168
177
  "headers": ["import Network"],
169
- "usageGuide": "### 何时用\n- App 需要实时网络状态\n- 启动时初始化一次\n\n### 关键点\n- 单例模式访问 sharedMonitor\n- start() 开始监听,cancel() 停止\n- 回调在 global queue,更新 UI 需切主线程",
178
+ "usageGuide": "### When to Use\n- App needs real-time network status\n- Initialize once at launch\n\n### Key Points\n- Access via singleton sharedMonitor\n- start() begins monitoring, cancel() stops\n- Callback runs on global queue; switch to main thread for UI updates",
170
179
  "content": {
171
- "markdown": "NWPathMonitor iOS 12+ 推荐的网络状态监听方案,替代废弃的 Reachability",
172
- "rationale": "Apple 官方推荐,线程安全,支持蜂窝/WiFi/有线判断。"
180
+ "markdown": "NWPathMonitor is the recommended network status monitoring API for iOS 12+, replacing deprecated Reachability.",
181
+ "rationale": "Apple-recommended, thread-safe, supports cellular/WiFi/wired detection."
173
182
  },
174
183
  "reasoning": {
175
- "whyStandard": "Apple Developer Documentation 推荐方案,替代 SCNetworkReachability",
184
+ "whyStandard": "Apple Developer Documentation recommended approach, replacing SCNetworkReachability",
176
185
  "sources": ["Apple Developer Documentation - NWPathMonitor"],
177
186
  "confidence": 0.95
178
187
  }