autosnippet 2.13.0 → 2.14.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 +2 -1
- package/bin/cli.js +1 -0
- package/lib/cli/UpgradeService.js +2 -1
- package/lib/domain/knowledge/Lifecycle.js +1 -0
- package/lib/external/mcp/McpServer.js +1 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +2 -1
- package/lib/external/mcp/handlers/knowledge.js +74 -0
- package/lib/external/mcp/tools.js +23 -0
- package/lib/service/chat/tools.js +63 -0
- package/lib/service/cursor/CursorDeliveryPipeline.js +116 -10
- package/package.json +1 -1
- package/skills/autosnippet-candidates/SKILL.md +1 -1
- package/skills/autosnippet-create/SKILL.md +1 -0
- package/skills/autosnippet-devdocs/SKILL.md +90 -0
- package/skills/autosnippet-recipes/SKILL.md +1 -0
package/README.md
CHANGED
|
@@ -240,7 +240,7 @@ asd install:vscode-copilot # 配置 MCP 和 Copilot 指令
|
|
|
240
240
|
|
|
241
241
|
## MCP 工具一览
|
|
242
242
|
|
|
243
|
-
|
|
243
|
+
39 个 MCP 工具按功能分组(省略了 **autosnippet_** 前缀):
|
|
244
244
|
|
|
245
245
|
| 分类 | 工具 |
|
|
246
246
|
|------|------|
|
|
@@ -248,6 +248,7 @@ asd install:vscode-copilot # 配置 MCP 和 Copilot 指令
|
|
|
248
248
|
| **搜索** | `search`(统合入口)、`context_search`(4 层漏斗)、`keyword_search`、`semantic_search` |
|
|
249
249
|
| **Recipe 浏览** | `list_recipes`、`get_recipe`、`list_rules`、`patterns`、`list_facts`、`recipe_insights`、`confirm_usage` |
|
|
250
250
|
| **候选管理** | `validate_candidate`、`check_duplicate`、`submit_knowledge`、`submit_knowledge_batch`、`enrich_candidates` |
|
|
251
|
+
| **开发文档** | `save_document` |
|
|
251
252
|
| **知识图谱** | `graph_query`、`graph_impact`、`graph_path`、`graph_stats` |
|
|
252
253
|
| **项目结构** | `get_targets`、`get_target_files`、`get_target_metadata` |
|
|
253
254
|
| **Guard** | `guard_check`、`guard_audit_files`、`scan_project` |
|
package/bin/cli.js
CHANGED
|
@@ -658,6 +658,7 @@ program
|
|
|
658
658
|
console.log(` Channel A (Always-On Rules): ${result.channelA.rulesCount} 条规则 (${result.channelA.tokensUsed} tokens)`);
|
|
659
659
|
console.log(` Channel B (Smart Rules): ${result.channelB.topicCount} 个主题, ${result.channelB.patternsCount} 个模式 (${result.channelB.totalTokens} tokens)`);
|
|
660
660
|
console.log(` Channel C (Agent Skills): ${result.channelC.synced} 个 Skills 已同步`);
|
|
661
|
+
console.log(` Channel D (Dev Documents): ${result.channelD?.documentsCount || 0} 篇文档`);
|
|
661
662
|
if (result.channelC.errors > 0) {
|
|
662
663
|
console.log(` ⚠️ ${result.channelC.errors} 个错误`);
|
|
663
664
|
}
|
|
@@ -181,7 +181,8 @@ export class UpgradeService {
|
|
|
181
181
|
pipeline.deliver()
|
|
182
182
|
.then(result => {
|
|
183
183
|
console.log(` ✅ Cursor Delivery: ${result.channelA.rulesCount} rules, ` +
|
|
184
|
-
`${result.channelB.topicCount} topics, ${result.channelC.synced} skills`
|
|
184
|
+
`${result.channelB.topicCount} topics, ${result.channelC.synced} skills, ` +
|
|
185
|
+
`${result.channelD?.documentsCount || 0} documents`);
|
|
185
186
|
})
|
|
186
187
|
.catch(err => {
|
|
187
188
|
console.log(` ⚠️ Cursor Delivery 跳过: ${err.message}`);
|
|
@@ -163,6 +163,7 @@ export class McpServer {
|
|
|
163
163
|
case 'autosnippet_submit_knowledge': return knowledgeHandlers.submitKnowledge(ctx, args);
|
|
164
164
|
case 'autosnippet_submit_knowledge_batch': return knowledgeHandlers.submitKnowledgeBatch(ctx, args);
|
|
165
165
|
case 'autosnippet_knowledge_lifecycle': return knowledgeHandlers.knowledgeLifecycle(ctx, args);
|
|
166
|
+
case 'autosnippet_save_document': return knowledgeHandlers.saveDocument(ctx, args);
|
|
166
167
|
default: throw new Error(`Unknown tool: ${name}`);
|
|
167
168
|
}
|
|
168
169
|
}
|
|
@@ -1108,7 +1108,8 @@ export async function fillDimensionsV3(fillContext) {
|
|
|
1108
1108
|
logger.info(`[Bootstrap-v3] 🚀 Cursor Delivery complete — ` +
|
|
1109
1109
|
`A: ${deliveryResult.channelA.rulesCount} rules, ` +
|
|
1110
1110
|
`B: ${deliveryResult.channelB.topicCount} topics, ` +
|
|
1111
|
-
`C: ${deliveryResult.channelC.synced} skills`
|
|
1111
|
+
`C: ${deliveryResult.channelC.synced} skills, ` +
|
|
1112
|
+
`D: ${deliveryResult.channelD?.documentsCount || 0} documents`);
|
|
1112
1113
|
}
|
|
1113
1114
|
} catch (deliveryErr) {
|
|
1114
1115
|
logger.warn(`[Bootstrap-v3] Cursor Delivery failed (non-blocking): ${deliveryErr.message}`);
|
|
@@ -237,6 +237,80 @@ export async function knowledgeLifecycle(ctx, args) {
|
|
|
237
237
|
|
|
238
238
|
// ─── 内部辅助 ──────────────────────────────────────────────
|
|
239
239
|
|
|
240
|
+
/**
|
|
241
|
+
* 保存开发文档 (autosnippet_save_document)
|
|
242
|
+
*
|
|
243
|
+
* 精简入口:仅需 title + markdown。
|
|
244
|
+
* 自动设置 knowledgeType='dev-document', kind='fact', source='agent'。
|
|
245
|
+
* 不走 RecipeReadiness 检查(文档无需 doClause/trigger)。
|
|
246
|
+
* 支持 autoApprove — 文档直接进入 active 状态。
|
|
247
|
+
*/
|
|
248
|
+
export async function saveDocument(ctx, args) {
|
|
249
|
+
if (!args.title || !args.title.trim()) {
|
|
250
|
+
throw new Error('title 必填');
|
|
251
|
+
}
|
|
252
|
+
if (!args.markdown || !args.markdown.trim()) {
|
|
253
|
+
throw new Error('markdown 必填');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// 限流
|
|
257
|
+
const blocked = await _checkRateLimit('autosnippet_save_document', args.client_id);
|
|
258
|
+
if (blocked) return blocked;
|
|
259
|
+
|
|
260
|
+
const service = ctx.container.get('knowledgeService');
|
|
261
|
+
|
|
262
|
+
const data = {
|
|
263
|
+
title: args.title.trim(),
|
|
264
|
+
description: args.description || '',
|
|
265
|
+
knowledgeType: 'dev-document',
|
|
266
|
+
kind: 'fact',
|
|
267
|
+
source: args.source || 'agent',
|
|
268
|
+
scope: args.scope || 'project-specific',
|
|
269
|
+
tags: args.tags || [],
|
|
270
|
+
content: {
|
|
271
|
+
markdown: args.markdown,
|
|
272
|
+
pattern: '',
|
|
273
|
+
},
|
|
274
|
+
// 文档不需要 Cursor Delivery 字段
|
|
275
|
+
trigger: '',
|
|
276
|
+
doClause: '',
|
|
277
|
+
dontClause: '',
|
|
278
|
+
whenClause: '',
|
|
279
|
+
topicHint: '',
|
|
280
|
+
coreCode: '',
|
|
281
|
+
// 基础推理
|
|
282
|
+
reasoning: {
|
|
283
|
+
whyStandard: 'Agent development document — preserved for team knowledge',
|
|
284
|
+
sources: ['agent'],
|
|
285
|
+
confidence: 0.8,
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const entry = await service.create(data, { userId: 'mcp' });
|
|
290
|
+
|
|
291
|
+
// 自动发布(dev-document 不需要人工审核)
|
|
292
|
+
try {
|
|
293
|
+
await service.publish(entry.id, { userId: 'mcp' });
|
|
294
|
+
} catch {
|
|
295
|
+
// 发布失败保持 pending — 非阻塞
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return envelope({
|
|
299
|
+
success: true,
|
|
300
|
+
data: {
|
|
301
|
+
id: entry.id,
|
|
302
|
+
lifecycle: 'active',
|
|
303
|
+
title: entry.title,
|
|
304
|
+
kind: 'fact',
|
|
305
|
+
knowledgeType: 'dev-document',
|
|
306
|
+
},
|
|
307
|
+
message: `文档「${entry.title}」已保存到知识库。`,
|
|
308
|
+
meta: { tool: 'autosnippet_save_document' },
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ─── 内部辅助 ──────────────────────────────────────────────
|
|
313
|
+
|
|
240
314
|
/**
|
|
241
315
|
* V3 wire format → RecipeReadinessChecker 兼容格式
|
|
242
316
|
*/
|
|
@@ -23,6 +23,7 @@ export const TOOL_GATEWAY_MAP = {
|
|
|
23
23
|
autosnippet_submit_knowledge: { action: 'knowledge:create', resource: 'knowledge' },
|
|
24
24
|
autosnippet_submit_knowledge_batch: { action: 'knowledge:create', resource: 'knowledge' },
|
|
25
25
|
autosnippet_knowledge_lifecycle: { action: 'knowledge:update', resource: 'knowledge' },
|
|
26
|
+
autosnippet_save_document: { action: 'knowledge:create', resource: 'knowledge' },
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
export const TOOLS = [
|
|
@@ -751,4 +752,26 @@ export const TOOLS = [
|
|
|
751
752
|
required: ['id', 'action'],
|
|
752
753
|
},
|
|
753
754
|
},
|
|
755
|
+
// 39. 保存开发文档
|
|
756
|
+
{
|
|
757
|
+
name: 'autosnippet_save_document',
|
|
758
|
+
description:
|
|
759
|
+
'保存开发文档到知识库(架构设计、排查报告、决策记录、调研笔记等)。\n' +
|
|
760
|
+
'仅需 title + markdown,无需填写 kind/doClause/trigger 等 Cursor Delivery 字段。\n' +
|
|
761
|
+
'文档自动以 dev-document 类型存储,不参与 Cursor Rules 压缩,保持原文完整性。\n' +
|
|
762
|
+
'交付路径: Channel D → .cursor/skills/autosnippet-devdocs/references/*.md。\n' +
|
|
763
|
+
'Agent 可通过 autosnippet_search 全文检索已保存的文档。',
|
|
764
|
+
inputSchema: {
|
|
765
|
+
type: 'object',
|
|
766
|
+
properties: {
|
|
767
|
+
title: { type: 'string', description: '文档标题(中英文皆可)' },
|
|
768
|
+
markdown: { type: 'string', description: '文档 Markdown 全文' },
|
|
769
|
+
description: { type: 'string', description: '一句话摘要(可选)' },
|
|
770
|
+
tags: { type: 'array', items: { type: 'string' }, description: '标签列表,如 ["adr", "debug-report", "design-doc", "research"]' },
|
|
771
|
+
scope: { type: 'string', enum: ['universal', 'project-specific'], default: 'project-specific', description: '适用范围' },
|
|
772
|
+
source: { type: 'string', description: '来源标识(默认 agent)' },
|
|
773
|
+
},
|
|
774
|
+
required: ['title', 'markdown'],
|
|
775
|
+
},
|
|
776
|
+
},
|
|
754
777
|
];
|
|
@@ -1844,6 +1844,68 @@ const submitCandidate = {
|
|
|
1844
1844
|
},
|
|
1845
1845
|
};
|
|
1846
1846
|
|
|
1847
|
+
// ────────────────────────────────────────────────────────────
|
|
1848
|
+
// 16b. save_document — 保存开发文档到知识库
|
|
1849
|
+
// ────────────────────────────────────────────────────────────
|
|
1850
|
+
const saveDocument = {
|
|
1851
|
+
name: 'save_document',
|
|
1852
|
+
description: '保存开发文档到知识库(架构设计、排查报告、决策记录、调研笔记等)。仅需 title + markdown,无需 Cursor Delivery 字段。文档自动发布,可通过 autosnippet_search 检索。',
|
|
1853
|
+
parameters: {
|
|
1854
|
+
type: 'object',
|
|
1855
|
+
properties: {
|
|
1856
|
+
title: { type: 'string', description: '文档标题' },
|
|
1857
|
+
markdown: { type: 'string', description: '文档 Markdown 全文' },
|
|
1858
|
+
description: { type: 'string', description: '一句话摘要(可选)' },
|
|
1859
|
+
tags: { type: 'array', items: { type: 'string' }, description: '标签: adr, debug-report, design-doc, research, performance 等' },
|
|
1860
|
+
scope: { type: 'string', enum: ['universal', 'project-specific'], description: '适用范围(默认 project-specific)' },
|
|
1861
|
+
},
|
|
1862
|
+
required: ['title', 'markdown'],
|
|
1863
|
+
},
|
|
1864
|
+
handler: async (params, ctx) => {
|
|
1865
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
1866
|
+
|
|
1867
|
+
const data = {
|
|
1868
|
+
title: params.title.trim(),
|
|
1869
|
+
description: params.description || '',
|
|
1870
|
+
knowledgeType: 'dev-document',
|
|
1871
|
+
kind: 'fact',
|
|
1872
|
+
source: 'agent',
|
|
1873
|
+
scope: params.scope || 'project-specific',
|
|
1874
|
+
tags: params.tags || [],
|
|
1875
|
+
content: {
|
|
1876
|
+
markdown: params.markdown,
|
|
1877
|
+
pattern: '',
|
|
1878
|
+
},
|
|
1879
|
+
trigger: '',
|
|
1880
|
+
doClause: '',
|
|
1881
|
+
dontClause: '',
|
|
1882
|
+
whenClause: '',
|
|
1883
|
+
topicHint: '',
|
|
1884
|
+
coreCode: '',
|
|
1885
|
+
reasoning: {
|
|
1886
|
+
whyStandard: 'Agent development document',
|
|
1887
|
+
sources: ['agent'],
|
|
1888
|
+
confidence: 0.8,
|
|
1889
|
+
},
|
|
1890
|
+
};
|
|
1891
|
+
|
|
1892
|
+
const saved = await knowledgeService.create(data, { userId: 'agent' });
|
|
1893
|
+
|
|
1894
|
+
// 自动发布(文档不需要人工审核)
|
|
1895
|
+
try {
|
|
1896
|
+
await knowledgeService.publish(saved.id, { userId: 'agent' });
|
|
1897
|
+
} catch { /* best effort */ }
|
|
1898
|
+
|
|
1899
|
+
return {
|
|
1900
|
+
id: saved.id,
|
|
1901
|
+
title: saved.title,
|
|
1902
|
+
lifecycle: 'active',
|
|
1903
|
+
knowledgeType: 'dev-document',
|
|
1904
|
+
message: `文档「${saved.title}」已保存到知识库`,
|
|
1905
|
+
};
|
|
1906
|
+
},
|
|
1907
|
+
};
|
|
1908
|
+
|
|
1847
1909
|
// ────────────────────────────────────────────────────────────
|
|
1848
1910
|
// 17. approve_candidate
|
|
1849
1911
|
// ────────────────────────────────────────────────────────────
|
|
@@ -3273,6 +3335,7 @@ export const ALL_TOOLS = [
|
|
|
3273
3335
|
generateGuardRule,
|
|
3274
3336
|
// 生命周期操作类 (7)
|
|
3275
3337
|
submitCandidate,
|
|
3338
|
+
saveDocument,
|
|
3276
3339
|
approveCandidate,
|
|
3277
3340
|
rejectCandidate,
|
|
3278
3341
|
publishRecipe,
|
|
@@ -16,6 +16,7 @@ import { RulesGenerator } from './RulesGenerator.js';
|
|
|
16
16
|
import { SkillsSyncer } from './SkillsSyncer.js';
|
|
17
17
|
import { estimateTokens, BUDGET } from './TokenBudget.js';
|
|
18
18
|
import path from 'node:path';
|
|
19
|
+
import fs from 'node:fs';
|
|
19
20
|
|
|
20
21
|
export class CursorDeliveryPipeline {
|
|
21
22
|
/**
|
|
@@ -48,6 +49,7 @@ export class CursorDeliveryPipeline {
|
|
|
48
49
|
channelA: { rulesCount: 0, tokensUsed: 0 },
|
|
49
50
|
channelB: { topicCount: 0, patternsCount: 0, totalTokens: 0 },
|
|
50
51
|
channelC: { synced: 0, skipped: 0, errors: 0 },
|
|
52
|
+
channelD: { documentsCount: 0, filesWritten: 0 },
|
|
51
53
|
totalTokensUsed: 0,
|
|
52
54
|
duration: 0,
|
|
53
55
|
};
|
|
@@ -57,9 +59,9 @@ export class CursorDeliveryPipeline {
|
|
|
57
59
|
const entries = await this._loadEntries();
|
|
58
60
|
this.logger.info?.(`[CursorDelivery] Loaded ${entries.length} knowledge entries`);
|
|
59
61
|
|
|
60
|
-
// 2. 分类:rules vs patterns vs facts
|
|
61
|
-
const { rules, patterns } = this._classify(entries);
|
|
62
|
-
this.logger.info?.(`[CursorDelivery] Classified: ${rules.length} rules, ${patterns.length} patterns`);
|
|
62
|
+
// 2. 分类:rules vs patterns vs facts vs documents
|
|
63
|
+
const { rules, patterns, documents } = this._classify(entries);
|
|
64
|
+
this.logger.info?.(`[CursorDelivery] Classified: ${rules.length} rules, ${patterns.length} patterns, ${documents.length} documents`);
|
|
63
65
|
|
|
64
66
|
// 3. 清理旧的动态生成文件
|
|
65
67
|
this.rulesGenerator.cleanDynamicFiles();
|
|
@@ -76,6 +78,10 @@ export class CursorDeliveryPipeline {
|
|
|
76
78
|
const channelC = await this._generateChannelC();
|
|
77
79
|
stats.channelC = channelC;
|
|
78
80
|
|
|
81
|
+
// ── Channel D: Dev Documents → references ──
|
|
82
|
+
const channelD = this._generateChannelD(documents);
|
|
83
|
+
stats.channelD = channelD;
|
|
84
|
+
|
|
79
85
|
// 统计
|
|
80
86
|
stats.totalTokensUsed = channelA.tokensUsed + channelB.totalTokens;
|
|
81
87
|
stats.duration = Date.now() - startTime;
|
|
@@ -83,9 +89,10 @@ export class CursorDeliveryPipeline {
|
|
|
83
89
|
this.logger.info?.(`[CursorDelivery] Done in ${stats.duration}ms — ` +
|
|
84
90
|
`A: ${channelA.rulesCount} rules (${channelA.tokensUsed} tokens), ` +
|
|
85
91
|
`B: ${channelB.topicCount} topics (${channelB.totalTokens} tokens), ` +
|
|
86
|
-
`C: ${channelC.synced} skills synced`
|
|
92
|
+
`C: ${channelC.synced} skills synced, ` +
|
|
93
|
+
`D: ${channelD.documentsCount} documents`);
|
|
87
94
|
|
|
88
|
-
return { channelA, channelB, channelC, stats };
|
|
95
|
+
return { channelA, channelB, channelC, channelD, stats };
|
|
89
96
|
} catch (error) {
|
|
90
97
|
this.logger.error?.(`[CursorDelivery] Error: ${error.message}`);
|
|
91
98
|
throw error;
|
|
@@ -146,16 +153,23 @@ export class CursorDeliveryPipeline {
|
|
|
146
153
|
|
|
147
154
|
/**
|
|
148
155
|
* 按 kind 分类知识条目
|
|
156
|
+
* dev-document 类型单独分流,不进入 Channel A/B 压缩
|
|
149
157
|
* @private
|
|
150
158
|
*/
|
|
151
159
|
_classify(entries) {
|
|
152
|
-
const rules = [], patterns = [], facts = [];
|
|
160
|
+
const rules = [], patterns = [], facts = [], documents = [];
|
|
153
161
|
for (const entry of entries) {
|
|
154
|
-
if (entry.
|
|
155
|
-
|
|
156
|
-
else
|
|
162
|
+
if (entry.knowledgeType === 'dev-document') {
|
|
163
|
+
documents.push(entry);
|
|
164
|
+
} else if (entry.kind === 'rule') {
|
|
165
|
+
rules.push(entry);
|
|
166
|
+
} else if (entry.kind === 'fact') {
|
|
167
|
+
facts.push(entry);
|
|
168
|
+
} else {
|
|
169
|
+
patterns.push(entry); // 无 kind 或 kind='pattern' → pattern
|
|
170
|
+
}
|
|
157
171
|
}
|
|
158
|
-
return { rules, patterns, facts };
|
|
172
|
+
return { rules, patterns, facts, documents };
|
|
159
173
|
}
|
|
160
174
|
|
|
161
175
|
/**
|
|
@@ -267,6 +281,98 @@ export class CursorDeliveryPipeline {
|
|
|
267
281
|
}
|
|
268
282
|
}
|
|
269
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Channel D — Dev Documents 生成
|
|
286
|
+
* 将 knowledgeType='dev-document' 的条目以原始 MD 写入
|
|
287
|
+
* .cursor/skills/autosnippet-devdocs/references/ 目录
|
|
288
|
+
* @private
|
|
289
|
+
*/
|
|
290
|
+
_generateChannelD(documents) {
|
|
291
|
+
const result = { documentsCount: 0, filesWritten: 0, filePaths: [] };
|
|
292
|
+
if (!documents || documents.length === 0) {
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const devdocsDir = path.join(this.projectRoot, '.cursor', 'skills', 'autosnippet-devdocs');
|
|
297
|
+
const refsDir = path.join(devdocsDir, 'references');
|
|
298
|
+
fs.mkdirSync(refsDir, { recursive: true });
|
|
299
|
+
|
|
300
|
+
// 生成 SKILL.md(索引页)
|
|
301
|
+
const skillLines = [
|
|
302
|
+
'---',
|
|
303
|
+
'name: autosnippet-devdocs',
|
|
304
|
+
`description: "Development documents and knowledge artifacts for ${this.projectName}. Use when looking up architecture decisions, debug reports, design docs, or analysis notes."`,
|
|
305
|
+
'---',
|
|
306
|
+
'',
|
|
307
|
+
`# Dev Documents — ${this.projectName}`,
|
|
308
|
+
'',
|
|
309
|
+
'Use this skill when:',
|
|
310
|
+
'- Looking up architecture decisions or design rationale',
|
|
311
|
+
'- Reviewing debug reports or performance analysis',
|
|
312
|
+
'- Finding previous research or investigation notes',
|
|
313
|
+
'- Understanding project-specific decisions and trade-offs',
|
|
314
|
+
'',
|
|
315
|
+
'## Document Index',
|
|
316
|
+
'',
|
|
317
|
+
'| Title | Tags | Updated |',
|
|
318
|
+
'|-------|------|---------|',
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
for (const doc of documents) {
|
|
322
|
+
const tags = (doc.tags || []).join(', ') || '-';
|
|
323
|
+
const updated = doc.updatedAt
|
|
324
|
+
? new Date(doc.updatedAt * 1000).toISOString().split('T')[0]
|
|
325
|
+
: '-';
|
|
326
|
+
const slug = this._slugify(doc.title || doc.id);
|
|
327
|
+
skillLines.push(`| [${doc.title}](references/${slug}.md) | ${tags} | ${updated} |`);
|
|
328
|
+
|
|
329
|
+
// 写入单个文档 MD
|
|
330
|
+
const markdown = doc.content?.markdown || doc.description || '';
|
|
331
|
+
const docContent = [
|
|
332
|
+
`# ${doc.title || 'Untitled'}`,
|
|
333
|
+
'',
|
|
334
|
+
doc.description ? `> ${doc.description}` : '',
|
|
335
|
+
'',
|
|
336
|
+
`**Tags:** ${tags} `,
|
|
337
|
+
`**Scope:** ${doc.scope || 'universal'} `,
|
|
338
|
+
`**Created:** ${doc.createdAt ? new Date(doc.createdAt * 1000).toISOString().split('T')[0] : '-'}`,
|
|
339
|
+
'',
|
|
340
|
+
'---',
|
|
341
|
+
'',
|
|
342
|
+
markdown,
|
|
343
|
+
].filter(Boolean).join('\n');
|
|
344
|
+
|
|
345
|
+
const docPath = path.join(refsDir, `${slug}.md`);
|
|
346
|
+
fs.writeFileSync(docPath, docContent, 'utf8');
|
|
347
|
+
result.filePaths.push(docPath);
|
|
348
|
+
result.filesWritten++;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
skillLines.push('');
|
|
352
|
+
skillLines.push('## Deeper Knowledge');
|
|
353
|
+
skillLines.push('');
|
|
354
|
+
skillLines.push('For full-text search across all documents:');
|
|
355
|
+
skillLines.push('- `autosnippet_search("your query")`');
|
|
356
|
+
|
|
357
|
+
fs.writeFileSync(path.join(devdocsDir, 'SKILL.md'), skillLines.join('\n') + '\n', 'utf8');
|
|
358
|
+
result.documentsCount = documents.length;
|
|
359
|
+
|
|
360
|
+
this.logger.info?.(`[CursorDelivery] Channel D: ${result.documentsCount} documents → ${refsDir}`);
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* 文件名安全 slug 化
|
|
366
|
+
* @private
|
|
367
|
+
*/
|
|
368
|
+
_slugify(text) {
|
|
369
|
+
return text
|
|
370
|
+
.toLowerCase()
|
|
371
|
+
.replace(/[^a-z0-9\u4e00-\u9fff]+/g, '-')
|
|
372
|
+
.replace(/^-+|-+$/g, '')
|
|
373
|
+
.substring(0, 80) || 'untitled';
|
|
374
|
+
}
|
|
375
|
+
|
|
270
376
|
/**
|
|
271
377
|
* 从项目路径推断项目名称
|
|
272
378
|
* @private
|
package/package.json
CHANGED
|
@@ -82,7 +82,7 @@ Every candidate submitted via `submit_candidate` or `submit_candidates` supports
|
|
|
82
82
|
| Field | Type | Values / Example |
|
|
83
83
|
|-------|------|-----------------|
|
|
84
84
|
| **category** | string | View / Service / Tool / Model / Network / Storage / UI / Utility |
|
|
85
|
-
| **knowledgeType** | string | `code-pattern` \| `architecture` \| `best-practice` \| `code-standard` \| `code-relation` \| `inheritance` \| `call-chain` \| `data-flow` \| `module-dependency` \| `boundary-constraint` \| `code-style` \| `solution` |
|
|
85
|
+
| **knowledgeType** | string | `code-pattern` \| `architecture` \| `best-practice` \| `code-standard` \| `code-relation` \| `inheritance` \| `call-chain` \| `data-flow` \| `module-dependency` \| `boundary-constraint` \| `code-style` \| `solution` \| `dev-document` |
|
|
86
86
|
| **complexity** | string | `beginner` \| `intermediate` \| `advanced` |
|
|
87
87
|
| **scope** | string | `universal` (通用) \| `project-specific` (本项目) \| `target-specific` (特定 Target) |
|
|
88
88
|
| **tags** | string[] | `["networking", "async-await", "error-handling"]` |
|
|
@@ -165,6 +165,7 @@ Watch parses all such blocks and adds each as a separate candidate; prompt may s
|
|
|
165
165
|
| `autosnippet_submit_knowledge_batch` | Submit draft .md as candidates: prefer draft folder + multiple files (not one big file); supports intro-only docs (no code—no Snippet). Pass `filePaths`, optional `targetName`, `deleteAfterSubmit`. **Delete the draft folder after submit** (e.g. `deleteAfterSubmit: true` or `rm -rf .autosnippet-drafts`). |
|
|
166
166
|
| `autosnippet_submit_knowledge_batch` | Submit structured items (title, summary, trigger, language, code, usageGuide) for batch scan, etc. |
|
|
167
167
|
| `autosnippet_submit_knowledge` | Submit single structured candidate with full V3 fields. |
|
|
168
|
+
| `autosnippet_save_document` | Save a development document (design doc, debug report, ADR) — only needs title + markdown. See **autosnippet-devdocs** skill. |
|
|
168
169
|
| `autosnippet_validate_candidate` | Pre-validate candidate quality before submission. |
|
|
169
170
|
| `autosnippet_check_duplicate` | Check for duplicate Recipes before submission. |
|
|
170
171
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: autosnippet-devdocs
|
|
3
|
+
description: Teaches the agent how to save development documents (architecture decisions, debug reports, design docs, research notes) to the AutoSnippet knowledge base using autosnippet_save_document. Use when the agent finishes analysis, debugging, design, or research and wants to persist the findings.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AutoSnippet — Save Development Documents
|
|
7
|
+
|
|
8
|
+
This skill tells the agent how to **save development documents** (architecture decisions, debug reports, design docs, research notes) to the AutoSnippet knowledge base, so they can be retrieved by future sessions.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- After finishing a **debug/troubleshooting session** — save the root cause analysis
|
|
13
|
+
- After making an **architecture or design decision** — save the ADR (Architecture Decision Record)
|
|
14
|
+
- After completing a **research or investigation** — save findings and conclusions
|
|
15
|
+
- After a **performance analysis** — save the benchmark results and optimization notes
|
|
16
|
+
- When the user says "保存这个分析" / "记录一下" / "save this to KB"
|
|
17
|
+
|
|
18
|
+
## MCP Tool
|
|
19
|
+
|
|
20
|
+
| Tool | Description |
|
|
21
|
+
|------|-------------|
|
|
22
|
+
| `autosnippet_save_document` | Save a development document to KB. Only needs `title` + `markdown`. Auto-published, no review needed. |
|
|
23
|
+
| `autosnippet_search` | Search saved documents by keyword or semantic query. |
|
|
24
|
+
|
|
25
|
+
## How to save a document
|
|
26
|
+
|
|
27
|
+
Call `autosnippet_save_document` with:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"title": "BiliDemo 冷启动性能分析",
|
|
32
|
+
"markdown": "## 问题背景\n\n冷启动耗时 8s...\n\n## 根因分析\n\n...\n\n## 解决方案\n\n...",
|
|
33
|
+
"description": "冷启动耗时 8s 的根因分析和优化方案",
|
|
34
|
+
"tags": ["debug-report", "performance"],
|
|
35
|
+
"scope": "project-specific"
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Required fields
|
|
40
|
+
|
|
41
|
+
| Field | Description |
|
|
42
|
+
|-------|-------------|
|
|
43
|
+
| `title` | Document title (Chinese or English) |
|
|
44
|
+
| `markdown` | Full Markdown content |
|
|
45
|
+
|
|
46
|
+
### Optional fields
|
|
47
|
+
|
|
48
|
+
| Field | Default | Description |
|
|
49
|
+
|-------|---------|-------------|
|
|
50
|
+
| `description` | `""` | One-line summary |
|
|
51
|
+
| `tags` | `[]` | Labels for filtering: `adr`, `debug-report`, `design-doc`, `research`, `performance`, `refactoring` |
|
|
52
|
+
| `scope` | `project-specific` | `universal` or `project-specific` |
|
|
53
|
+
|
|
54
|
+
## Recommended tags
|
|
55
|
+
|
|
56
|
+
| Tag | Use case |
|
|
57
|
+
|-----|----------|
|
|
58
|
+
| `adr` | Architecture Decision Record |
|
|
59
|
+
| `debug-report` | Bug investigation / root cause analysis |
|
|
60
|
+
| `design-doc` | Module/feature design document |
|
|
61
|
+
| `research` | Technology research or investigation |
|
|
62
|
+
| `performance` | Benchmark results, profiling analysis |
|
|
63
|
+
| `refactoring` | Refactoring plan or post-mortem |
|
|
64
|
+
| `migration` | Migration guide or plan |
|
|
65
|
+
| `meeting-notes` | Technical meeting summary |
|
|
66
|
+
|
|
67
|
+
## How documents are delivered to Cursor
|
|
68
|
+
|
|
69
|
+
Documents are stored as `knowledgeType: 'dev-document'` in the knowledge DB. They follow a dedicated delivery path:
|
|
70
|
+
|
|
71
|
+
1. **NOT compressed** into Cursor Rules (Channel A/B skip dev-documents)
|
|
72
|
+
2. **Full text** written to `.cursor/skills/autosnippet-devdocs/references/*.md` (Channel D)
|
|
73
|
+
3. **Searchable** via `autosnippet_search("your query")` — full-text search hits documents
|
|
74
|
+
|
|
75
|
+
## Document format tips
|
|
76
|
+
|
|
77
|
+
- Use **clear headings** (`##`, `###`) — helps search and scanning
|
|
78
|
+
- Include a **summary section** at the top
|
|
79
|
+
- Reference **file paths** and **class names** concretely — improves search relevance
|
|
80
|
+
- For ADRs, use the structure: Context → Decision → Consequences
|
|
81
|
+
- For debug reports: Symptom → Investigation → Root Cause → Fix
|
|
82
|
+
|
|
83
|
+
## Relation to other skills
|
|
84
|
+
|
|
85
|
+
| Skill | When to use |
|
|
86
|
+
|-------|-------------|
|
|
87
|
+
| `autosnippet-create` | Saving **code patterns/recipes** (needs trigger, doClause, etc.) |
|
|
88
|
+
| `autosnippet-devdocs` (this) | Saving **prose documents** (only needs title + markdown) |
|
|
89
|
+
| `autosnippet-recipes` | Looking up existing knowledge |
|
|
90
|
+
| `autosnippet-concepts` | Understanding AutoSnippet concepts |
|
|
@@ -112,6 +112,7 @@ Recipe is the core knowledge unit. V3 uses a unified structured model:
|
|
|
112
112
|
| autosnippet_submit_knowledge | Submit single candidate (supports structured content) |
|
|
113
113
|
| autosnippet_submit_knowledge_batch | Batch submit candidates |
|
|
114
114
|
| autosnippet_submit_knowledge_batch | Submit draft .md files as candidates |
|
|
115
|
+
| autosnippet_save_document | Save development document (design doc, debug report, ADR) — title + markdown only |
|
|
115
116
|
| autosnippet_validate_candidate | Validate candidate quality |
|
|
116
117
|
| autosnippet_check_duplicate | Dedup check |
|
|
117
118
|
| autosnippet_enrich_candidates | AI semantic field enrichment for candidates |
|