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.
- package/dist/bin/cli.js +27 -1
- package/dist/lib/cli/KnowledgeSyncService.d.ts +26 -0
- package/dist/lib/cli/KnowledgeSyncService.js +33 -1
- package/dist/lib/external/mcp/handlers/browse.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/browse.js +2 -1
- package/dist/lib/external/mcp/handlers/consolidated.d.ts +1 -0
- package/dist/lib/external/mcp/handlers/panorama.d.ts +11 -11
- package/dist/lib/external/mcp/handlers/panorama.js +20 -20
- package/dist/lib/external/mcp/handlers/system.d.ts +1 -1
- package/dist/lib/external/mcp/handlers/task.js +2 -1
- package/dist/lib/external/mcp/tools.d.ts +12 -12
- package/dist/lib/external/mcp/tools.js +120 -118
- package/dist/lib/http/middleware/validate.js +7 -3
- package/dist/lib/infrastructure/database/drizzle/schema.d.ts +100 -0
- package/dist/lib/infrastructure/database/drizzle/schema.js +10 -0
- package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.d.ts +9 -0
- package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.js +24 -0
- package/dist/lib/infrastructure/vector/HnswVectorAdapter.js +18 -2
- package/dist/lib/injection/ServiceContainer.js +2 -0
- package/dist/lib/injection/modules/KnowledgeModule.d.ts +5 -0
- package/dist/lib/injection/modules/KnowledgeModule.js +80 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.d.ts +45 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.js +101 -0
- package/dist/lib/service/evolution/ConsolidationAdvisor.js +9 -9
- package/dist/lib/service/evolution/ContradictionDetector.js +2 -2
- package/dist/lib/service/evolution/RedundancyAnalyzer.js +2 -2
- package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +68 -0
- package/dist/lib/service/knowledge/SourceRefReconciler.js +309 -0
- package/dist/lib/service/panorama/PanoramaService.d.ts +18 -1
- package/dist/lib/service/panorama/PanoramaService.js +148 -5
- package/dist/lib/service/search/BM25Scorer.d.ts +2 -2
- package/dist/lib/service/search/CoarseRanker.d.ts +7 -6
- package/dist/lib/service/search/CoarseRanker.js +11 -10
- package/dist/lib/service/search/FieldWeightedScorer.d.ts +81 -0
- package/dist/lib/service/search/FieldWeightedScorer.js +318 -0
- package/dist/lib/service/search/MultiSignalRanker.d.ts +2 -2
- package/dist/lib/service/search/MultiSignalRanker.js +1 -1
- package/dist/lib/service/search/SearchEngine.d.ts +8 -7
- package/dist/lib/service/search/SearchEngine.js +59 -10
- package/dist/lib/service/search/SearchTypes.d.ts +23 -3
- package/dist/lib/service/search/SearchTypes.js +6 -1
- package/dist/lib/service/task/IntentExtractor.d.ts +8 -0
- package/dist/lib/service/task/IntentExtractor.js +115 -1
- package/dist/lib/service/task/PrimeSearchPipeline.js +39 -24
- package/dist/lib/service/vector/VectorService.d.ts +3 -0
- package/dist/lib/service/vector/VectorService.js +38 -4
- package/package.json +1 -1
- package/skills/autosnippet-create/SKILL.md +98 -89
- package/skills/autosnippet-devdocs/SKILL.md +55 -60
- package/templates/guard-ci.yml +2 -2
- 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
|
-
|
|
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.
|
|
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
|
|
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(
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
//
|
|
90
|
-
|
|
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,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 "
|
|
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
|
-
>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
|
21
|
-
|
|
|
22
|
-
| **Dashboard** |
|
|
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
|
|
24
|
+
**Agent prefers MCP submission** — no browser needed.
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## Single Submission — autosnippet_submit_knowledge
|
|
29
29
|
|
|
30
|
-
|
|
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
|
|
32
|
+
### V3 Required Fields (16)
|
|
33
33
|
|
|
34
|
-
|
|
|
35
|
-
|
|
36
|
-
| `title` | string |
|
|
37
|
-
| `description` | string |
|
|
38
|
-
| `trigger` | string |
|
|
39
|
-
| `language` | string |
|
|
40
|
-
| `kind` | enum | `rule
|
|
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 | ✅
|
|
44
|
-
| `dontClause` | string | ❌
|
|
45
|
-
| `whenClause` | string |
|
|
46
|
-
| `coreCode` | string |
|
|
47
|
-
| `headers` | string[] |
|
|
48
|
-
| `usageGuide` | string |
|
|
49
|
-
| `content` | object | `{ markdown: string, rationale: string }`
|
|
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
|
|
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
|
-
|
|
58
|
+
**Must** use Markdown sections. Never write as a single long line.
|
|
59
59
|
|
|
60
60
|
```markdown
|
|
61
|
-
###
|
|
62
|
-
-
|
|
63
|
-
-
|
|
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
|
-
-
|
|
74
|
-
-
|
|
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
|
-
##
|
|
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
|
-
| `
|
|
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
|
-
|
|
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
|
-
###
|
|
110
|
+
### Standard Flow (Agent via MCP)
|
|
114
111
|
|
|
115
112
|
```
|
|
116
|
-
1.
|
|
117
|
-
2. autosnippet_submit_knowledge
|
|
118
|
-
3.
|
|
119
|
-
-
|
|
120
|
-
-
|
|
121
|
-
4. [
|
|
113
|
+
1. Analyze code → construct V3 fields
|
|
114
|
+
2. autosnippet_submit_knowledge → stored as pending
|
|
115
|
+
3. Check response:
|
|
116
|
+
- Success → inform 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
|
-
|
|
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
|
-
|
|
|
135
|
-
|
|
|
136
|
-
|
|
|
137
|
-
|
|
|
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
|
-
##
|
|
150
|
+
## Kind Routing & Pipeline Impact
|
|
142
151
|
|
|
143
|
-
| kind |
|
|
144
|
-
|
|
145
|
-
| `rule` |
|
|
146
|
-
| `pattern` |
|
|
147
|
-
| `fact` |
|
|
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`
|
|
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": "
|
|
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": "
|
|
165
|
-
"dontClause": "
|
|
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": "###
|
|
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
|
|
172
|
-
"rationale": "Apple
|
|
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
|
|
184
|
+
"whyStandard": "Apple Developer Documentation recommended approach, replacing SCNetworkReachability",
|
|
176
185
|
"sources": ["Apple Developer Documentation - NWPathMonitor"],
|
|
177
186
|
"confidence": 0.95
|
|
178
187
|
}
|