autosnippet 3.0.10 → 3.0.13
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/bin/cli.js +64 -1
- package/config/default.json +9 -0
- package/dashboard/dist/assets/{index-I2ySoCmF.js → index-Bnm26ulL.js} +47 -47
- package/dashboard/dist/index.html +1 -1
- package/lib/cli/SetupService.js +92 -5
- package/lib/cli/UpgradeService.js +14 -5
- package/lib/core/discovery/GenericDiscoverer.js +4 -28
- package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +246 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +80 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +275 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +600 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +125 -342
- package/lib/external/mcp/handlers/bootstrap/refine.js +362 -0
- package/lib/external/mcp/handlers/bootstrap.js +6 -590
- package/lib/external/mcp/handlers/browse.js +119 -9
- package/lib/external/mcp/handlers/guard.js +25 -6
- package/lib/external/mcp/handlers/search.js +56 -24
- package/lib/http/routes/guardRules.js +9 -17
- package/lib/injection/ServiceContainer.js +12 -3
- package/lib/platform/ios/xcode/XcodeImportResolver.js +434 -0
- package/lib/platform/ios/xcode/XcodeIntegration.js +40 -659
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +220 -0
- package/lib/service/chat/ChatAgent.js +39 -418
- package/lib/service/chat/ChatAgentPrompts.js +149 -0
- package/lib/service/chat/ChatAgentTasks.js +297 -0
- package/lib/service/chat/tools/_shared.js +61 -0
- package/lib/service/chat/tools/ai-analysis.js +284 -0
- package/lib/service/chat/tools/ast-graph.js +681 -0
- package/lib/service/chat/tools/composite.js +496 -0
- package/lib/service/chat/tools/guard.js +265 -0
- package/lib/service/chat/tools/index.js +250 -0
- package/lib/service/chat/tools/infrastructure.js +222 -0
- package/lib/service/chat/tools/knowledge-graph.js +234 -0
- package/lib/service/chat/tools/lifecycle.js +469 -0
- package/lib/service/chat/tools/project-access.js +923 -0
- package/lib/service/chat/tools/query.js +264 -0
- package/lib/service/chat/tools.js +14 -3994
- package/lib/service/cursor/AgentInstructionsGenerator.js +395 -0
- package/lib/service/cursor/CursorDeliveryPipeline.js +70 -11
- package/lib/service/cursor/FileProtection.js +116 -0
- package/lib/service/cursor/KnowledgeCompressor.js +61 -11
- package/lib/service/cursor/SkillsSyncer.js +5 -3
- package/lib/service/cursor/TopicClassifier.js +19 -3
- package/lib/service/guard/ExclusionManager.js +26 -2
- package/lib/service/guard/GuardCheckEngine.js +38 -370
- package/lib/service/guard/GuardCodeChecks.js +362 -0
- package/lib/service/guard/GuardCrossFileChecks.js +307 -0
- package/lib/service/guard/GuardPatternUtils.js +180 -0
- package/lib/service/guard/GuardService.js +80 -38
- package/lib/service/module/ModuleService.js +1 -0
- package/lib/service/search/SearchEngine.js +10 -2
- package/lib/service/wiki/WikiGenerator.js +226 -1532
- package/lib/service/wiki/WikiRenderers.js +1878 -0
- package/lib/service/wiki/WikiUtils.js +907 -0
- package/lib/shared/LanguageService.js +299 -0
- package/package.json +1 -1
|
@@ -1,16 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* KnowledgeCompressor — 知识条目压缩器(
|
|
2
|
+
* KnowledgeCompressor — 知识条目压缩器(v3 优化版)
|
|
3
3
|
*
|
|
4
4
|
* 将 KnowledgeEntry(含 AI 预计算字段)格式化为 Cursor 交付格式:
|
|
5
|
-
* - Channel A: compressToRuleLine() →
|
|
6
|
-
* - Channel B: compressToWhenDoDont() → When/Do/Don't + Template 格式
|
|
5
|
+
* - Channel A: compressToRuleLine() → 一行式强制规则(含可选 language 前缀)
|
|
6
|
+
* - Channel B: compressToWhenDoDont() → When/Do/Don't/Why + Template 格式
|
|
7
7
|
*
|
|
8
8
|
* 原则:只做格式化,无字段 = 不输出,不做启发式猜测。
|
|
9
|
+
*
|
|
10
|
+
* v3 变更:
|
|
11
|
+
* - Channel A: 多语言项目中增加 [language] 前缀
|
|
12
|
+
* - Channel B: 增加 Why 行(content.rationale 首句)
|
|
13
|
+
* - Channel B: coreCode 骨架化(去注释 + 截断 ≤15 行)
|
|
9
14
|
*/
|
|
10
15
|
|
|
16
|
+
/** 从 rationale 提取首句(≤120 字符),用于 Channel B 的 Why 行 */
|
|
17
|
+
function _extractFirstSentence(rationale) {
|
|
18
|
+
if (!rationale) return '';
|
|
19
|
+
// 优先按句号或换行分割
|
|
20
|
+
const first = rationale.split(/[.\n。!?!?]/)[0]?.trim();
|
|
21
|
+
if (!first) return '';
|
|
22
|
+
return first.length > 120 ? `${first.slice(0, 117)}...` : first;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 骨架化 coreCode:去注释 + 截断 ≤ maxLines 行 */
|
|
26
|
+
function _skeletonize(code, maxLines = 15) {
|
|
27
|
+
if (!code) return '';
|
|
28
|
+
const lines = code
|
|
29
|
+
.split('\n')
|
|
30
|
+
// 去掉纯注释行(// 或 /* 或 * (JSDoc续行) 或 # 开头)
|
|
31
|
+
.filter((l) => !/^\s*(\/\/|\/\*|\*\s|#\s)/.test(l))
|
|
32
|
+
// 去掉空行连续超过 1 行
|
|
33
|
+
.reduce((acc, line) => {
|
|
34
|
+
if (line.trim() === '' && acc.length > 0 && acc[acc.length - 1].trim() === '') {
|
|
35
|
+
return acc; // 跳过连续空行
|
|
36
|
+
}
|
|
37
|
+
acc.push(line);
|
|
38
|
+
return acc;
|
|
39
|
+
}, []);
|
|
40
|
+
if (lines.length <= maxLines) return lines.join('\n');
|
|
41
|
+
return `${lines.slice(0, maxLines).join('\n')}\n// ... (truncated)`;
|
|
42
|
+
}
|
|
43
|
+
|
|
11
44
|
export class KnowledgeCompressor {
|
|
12
45
|
/**
|
|
13
|
-
* Channel A —
|
|
46
|
+
* Channel A — 一行式规则(含可选 language 前缀)
|
|
47
|
+
*
|
|
48
|
+
* 多语言项目中增加 [language] 前缀,帮助 Agent 判断规则适用性。
|
|
49
|
+
* scope='universal' 或无 language 的规则不加前缀。
|
|
50
|
+
*
|
|
14
51
|
* @param {Array<Object>} entries - KnowledgeEntry 数组 (kind='rule')
|
|
15
52
|
* @returns {Array<string>}
|
|
16
53
|
*/
|
|
@@ -18,10 +55,14 @@ export class KnowledgeCompressor {
|
|
|
18
55
|
return entries
|
|
19
56
|
.filter((e) => e.doClause) // 无 doClause → 跳过,不猜
|
|
20
57
|
.map((e) => {
|
|
21
|
-
|
|
58
|
+
// 可选 language 前缀
|
|
59
|
+
const langPrefix =
|
|
60
|
+
e.language && e.scope !== 'universal' ? `[${e.language}] ` : '';
|
|
61
|
+
const doText = e.doClause.replace(/\.+$/, ''); // 去尾 .
|
|
62
|
+
let line = `${langPrefix}${doText}`;
|
|
22
63
|
if (e.dontClause) {
|
|
23
|
-
// AI 可能返回 "Don't ..." / "Do not ..."
|
|
24
|
-
const stripped = e.dontClause.replace(/^(Don't|Do not|Never)\s+/i, '');
|
|
64
|
+
// AI 可能返回 "Don't ..." / "Do not ..." / "Never ..." 开头,去掉冗余前缀后统一为 "Do NOT"
|
|
65
|
+
const stripped = e.dontClause.replace(/^(Don't|Do not|Never)\s+/i, '').replace(/\.+$/, '');
|
|
25
66
|
line += `. Do NOT ${stripped}`;
|
|
26
67
|
}
|
|
27
68
|
return `- ${line}.`;
|
|
@@ -29,9 +70,9 @@ export class KnowledgeCompressor {
|
|
|
29
70
|
}
|
|
30
71
|
|
|
31
72
|
/**
|
|
32
|
-
* Channel B — When/Do/Don't + Template
|
|
73
|
+
* Channel B — When/Do/Don't/Why + Template(骨架化)
|
|
33
74
|
* @param {Array<Object>} entries - KnowledgeEntry 数组 (kind='pattern')
|
|
34
|
-
* @returns {Array<{ trigger: string, when: string, do: string, dont: string, template: string }>}
|
|
75
|
+
* @returns {Array<{ trigger: string, when: string, do: string, dont: string, why: string, template: string }>}
|
|
35
76
|
*/
|
|
36
77
|
compressToWhenDoDont(entries) {
|
|
37
78
|
const seen = new Set();
|
|
@@ -48,18 +89,24 @@ export class KnowledgeCompressor {
|
|
|
48
89
|
trigger = `${trigger}-${i}`;
|
|
49
90
|
}
|
|
50
91
|
seen.add(trigger);
|
|
92
|
+
|
|
93
|
+
// 提取 rationale 首句作 Why 行
|
|
94
|
+
const rationale = e.content?.rationale || '';
|
|
95
|
+
const why = _extractFirstSentence(rationale);
|
|
96
|
+
|
|
51
97
|
return {
|
|
52
98
|
trigger,
|
|
53
99
|
when: e.whenClause,
|
|
54
100
|
do: e.doClause,
|
|
55
101
|
dont: e.dontClause || '',
|
|
56
|
-
|
|
102
|
+
why,
|
|
103
|
+
template: _skeletonize(e.coreCode),
|
|
57
104
|
};
|
|
58
105
|
});
|
|
59
106
|
}
|
|
60
107
|
|
|
61
108
|
/**
|
|
62
|
-
* 将 When/Do/Don't 结果格式化为 Markdown 字符串
|
|
109
|
+
* 将 When/Do/Don't/Why 结果格式化为 Markdown 字符串
|
|
63
110
|
* @param {Array<Object>} compressed - compressToWhenDoDont 输出
|
|
64
111
|
* @param {string} [language=''] - 代码围栏语言标识
|
|
65
112
|
* @returns {string}
|
|
@@ -75,6 +122,9 @@ export class KnowledgeCompressor {
|
|
|
75
122
|
const stripped = item.dont.replace(/^(Don't|Do not|Never)\s+/i, '');
|
|
76
123
|
lines.push(`- **Don't**: ${stripped}`);
|
|
77
124
|
}
|
|
125
|
+
if (item.why) {
|
|
126
|
+
lines.push(`- **Why**: ${item.why}`);
|
|
127
|
+
}
|
|
78
128
|
if (item.template) {
|
|
79
129
|
lines.push('');
|
|
80
130
|
lines.push(`\`\`\`${lang}`);
|
|
@@ -208,15 +208,17 @@ export class SkillsSyncer {
|
|
|
208
208
|
const lines = [`# ${this._capitalizeWords(dimensionLabel)} Recipes`, ''];
|
|
209
209
|
|
|
210
210
|
if (recipes.length > 0) {
|
|
211
|
-
lines.push('| Title | Trigger | Summary |');
|
|
212
|
-
lines.push('
|
|
211
|
+
lines.push('| Title | Trigger | Kind | Lang | Summary |');
|
|
212
|
+
lines.push('|---|---|---|---|---|');
|
|
213
213
|
for (const entry of recipes.slice(0, 20)) {
|
|
214
214
|
const title = (entry.title || '').replace(/\|/g, '/');
|
|
215
215
|
const trigger = entry.trigger || '-';
|
|
216
|
+
const kind = entry.kind || '-';
|
|
217
|
+
const lang = entry.language || '-';
|
|
216
218
|
const summary = (entry.summaryCn || entry.description || '')
|
|
217
219
|
.replace(/\|/g, '/')
|
|
218
220
|
.slice(0, 80);
|
|
219
|
-
lines.push(`| ${title} | ${trigger} | ${summary} |`);
|
|
221
|
+
lines.push(`| ${title} | ${trigger} | ${kind} | ${lang} | ${summary} |`);
|
|
220
222
|
}
|
|
221
223
|
} else {
|
|
222
224
|
lines.push('No recipes available yet. Run `asd bootstrap` to generate knowledge.');
|
|
@@ -78,6 +78,11 @@ export class TopicClassifier {
|
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
80
|
* 为主题构建 description — Agent 判断关联性的唯一依据
|
|
81
|
+
*
|
|
82
|
+
* v3: 动态丰富化 — 除硬编码 baseKeywords 外,还从 entries 的
|
|
83
|
+
* tags, whenClause, trigger 中提取高价值关键词,让 Cursor 的
|
|
84
|
+
* description 匹配更精准。
|
|
85
|
+
*
|
|
81
86
|
* @param {string} topic
|
|
82
87
|
* @param {Array<Object>} entries
|
|
83
88
|
* @returns {string}
|
|
@@ -91,10 +96,21 @@ export class TopicClassifier {
|
|
|
91
96
|
.filter(Boolean)
|
|
92
97
|
.join(', ');
|
|
93
98
|
|
|
94
|
-
// 从 entries
|
|
99
|
+
// 从 entries 提取动态关键词(tags + whenClause + title/description)
|
|
95
100
|
const entryKeywords = entries.flatMap((e) => this._extractKeywords(e)).filter(Boolean);
|
|
96
|
-
|
|
97
|
-
const
|
|
101
|
+
// 从 tags 直接提取(tags 本身就是高质量关键词)
|
|
102
|
+
const tagKeywords = entries
|
|
103
|
+
.flatMap((e) => e.tags || [])
|
|
104
|
+
.map((t) => t.toLowerCase())
|
|
105
|
+
.filter((t) => t.length >= 2 && !STOP_WORDS.has(t));
|
|
106
|
+
// 从 whenClause 提取(包含触发场景信息)
|
|
107
|
+
const whenKeywords = entries
|
|
108
|
+
.flatMap((e) => (e.whenClause || '').match(/[a-zA-Z]{3,}/g) || [])
|
|
109
|
+
.map((w) => w.toLowerCase())
|
|
110
|
+
.filter((w) => !STOP_WORDS.has(w));
|
|
111
|
+
|
|
112
|
+
const allExtra = [...new Set([...tagKeywords, ...whenKeywords, ...entryKeywords])].slice(0, 15);
|
|
113
|
+
const extra = allExtra.length > 0 ? `, ${allExtra.join(', ')}` : '';
|
|
98
114
|
|
|
99
115
|
return `${this._topicLabel(topic)} patterns for ${this.projectName} — ${baseKeywords}${extra}. Use when writing or reviewing ${this._topicLabel(topic).toLowerCase()}-related code.`;
|
|
100
116
|
}
|
|
@@ -197,13 +197,37 @@ export class ExclusionManager {
|
|
|
197
197
|
// ─── 私有方法 ─────────────────────────────────────────
|
|
198
198
|
|
|
199
199
|
#matchGlob(filePath, pattern) {
|
|
200
|
-
// 简易 glob 匹配: ** 表示任意路径, *
|
|
200
|
+
// 简易 glob 匹配: ** 表示任意路径, * 表示同级任意文件名, ? 表示单个字符
|
|
201
|
+
// 1. 精确正则匹配 (支持 glob 通配符)
|
|
201
202
|
const escaped = pattern
|
|
202
203
|
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
203
204
|
.replace(/\*\*/g, '<<<GLOB>>>')
|
|
204
205
|
.replace(/\*/g, '[^/]*')
|
|
206
|
+
.replace(/\?/g, '[^/]')
|
|
205
207
|
.replace(/<<<GLOB>>>/g, '.*');
|
|
206
|
-
|
|
208
|
+
if (new RegExp(`^${escaped}$`).test(filePath)) {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
// 1b. 不含 / 的通配符 pattern 按文件名匹配 (如 *.test.js 匹配任意路径下的文件名)
|
|
212
|
+
if (pattern.includes('*') && !pattern.includes('/')) {
|
|
213
|
+
const basename = filePath.split('/').pop() || '';
|
|
214
|
+
if (new RegExp(`^${escaped}$`).test(basename)) {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// 2. 路径段匹配 — pattern 不含通配符时,按完整路径段(/segment/)匹配
|
|
219
|
+
// 避免 "test" 匹配 "contest.js" 这类误报
|
|
220
|
+
if (!pattern.includes('*') && !pattern.includes('?')) {
|
|
221
|
+
const segments = filePath.split('/');
|
|
222
|
+
if (segments.includes(pattern)) {
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
// 后缀匹配: 支持 "src/foo.js" 匹配 "/project/src/foo.js"
|
|
226
|
+
if (pattern.includes('/') && filePath.endsWith(`/${pattern}`)) {
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
207
231
|
}
|
|
208
232
|
|
|
209
233
|
#load() {
|