autosnippet 3.0.11 → 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.
Files changed (56) hide show
  1. package/bin/cli.js +64 -1
  2. package/config/default.json +9 -0
  3. package/dashboard/dist/assets/{index-I2ySoCmF.js → index-Bnm26ulL.js} +47 -47
  4. package/dashboard/dist/index.html +1 -1
  5. package/lib/cli/SetupService.js +92 -5
  6. package/lib/cli/UpgradeService.js +14 -5
  7. package/lib/core/discovery/GenericDiscoverer.js +4 -28
  8. package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +246 -0
  9. package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +80 -0
  10. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +275 -0
  11. package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +600 -0
  12. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +125 -342
  13. package/lib/external/mcp/handlers/bootstrap/refine.js +362 -0
  14. package/lib/external/mcp/handlers/bootstrap.js +6 -590
  15. package/lib/external/mcp/handlers/browse.js +119 -9
  16. package/lib/external/mcp/handlers/guard.js +25 -6
  17. package/lib/external/mcp/handlers/search.js +56 -24
  18. package/lib/http/routes/guardRules.js +9 -17
  19. package/lib/injection/ServiceContainer.js +12 -3
  20. package/lib/platform/ios/xcode/XcodeImportResolver.js +434 -0
  21. package/lib/platform/ios/xcode/XcodeIntegration.js +40 -659
  22. package/lib/platform/ios/xcode/XcodeWriteUtils.js +220 -0
  23. package/lib/service/chat/ChatAgent.js +39 -418
  24. package/lib/service/chat/ChatAgentPrompts.js +149 -0
  25. package/lib/service/chat/ChatAgentTasks.js +297 -0
  26. package/lib/service/chat/tools/_shared.js +61 -0
  27. package/lib/service/chat/tools/ai-analysis.js +284 -0
  28. package/lib/service/chat/tools/ast-graph.js +681 -0
  29. package/lib/service/chat/tools/composite.js +496 -0
  30. package/lib/service/chat/tools/guard.js +265 -0
  31. package/lib/service/chat/tools/index.js +250 -0
  32. package/lib/service/chat/tools/infrastructure.js +222 -0
  33. package/lib/service/chat/tools/knowledge-graph.js +234 -0
  34. package/lib/service/chat/tools/lifecycle.js +469 -0
  35. package/lib/service/chat/tools/project-access.js +923 -0
  36. package/lib/service/chat/tools/query.js +264 -0
  37. package/lib/service/chat/tools.js +14 -3994
  38. package/lib/service/cursor/AgentInstructionsGenerator.js +395 -0
  39. package/lib/service/cursor/CursorDeliveryPipeline.js +70 -11
  40. package/lib/service/cursor/FileProtection.js +116 -0
  41. package/lib/service/cursor/KnowledgeCompressor.js +61 -11
  42. package/lib/service/cursor/SkillsSyncer.js +5 -3
  43. package/lib/service/cursor/TopicClassifier.js +19 -3
  44. package/lib/service/guard/ExclusionManager.js +26 -2
  45. package/lib/service/guard/GuardCheckEngine.js +38 -370
  46. package/lib/service/guard/GuardCodeChecks.js +362 -0
  47. package/lib/service/guard/GuardCrossFileChecks.js +307 -0
  48. package/lib/service/guard/GuardPatternUtils.js +180 -0
  49. package/lib/service/guard/GuardService.js +80 -38
  50. package/lib/service/module/ModuleService.js +1 -0
  51. package/lib/service/search/SearchEngine.js +10 -2
  52. package/lib/service/wiki/WikiGenerator.js +226 -1532
  53. package/lib/service/wiki/WikiRenderers.js +1878 -0
  54. package/lib/service/wiki/WikiUtils.js +907 -0
  55. package/lib/shared/LanguageService.js +299 -0
  56. package/package.json +1 -1
@@ -1,16 +1,53 @@
1
1
  /**
2
- * KnowledgeCompressor — 知识条目压缩器(v2 无降级版)
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
- let line = e.doClause;
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
- template: e.coreCode || '',
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
- const unique = [...new Set(entryKeywords)].slice(0, 10);
97
- const extra = unique.length > 0 ? `, ${unique.join(', ')}` : '';
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
- return new RegExp(`^${escaped}$`).test(filePath) || filePath.includes(pattern);
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() {