autosnippet 2.6.0 → 2.7.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/bin/cli.js +1 -1
- package/dashboard/dist/assets/{icons-rnn04CvH.js → icons-Cq4-iQhP.js} +148 -88
- package/dashboard/dist/assets/index-DBxH7pVn.css +1 -0
- package/dashboard/dist/assets/index-Dw2F6qAS.js +197 -0
- package/dashboard/dist/assets/{react-markdown-CWxUbOf4.js → react-markdown-BA6FB2NP.js} +1 -1
- package/dashboard/dist/assets/{syntax-highlighter-CJ2drQQb.js → syntax-highlighter-CVLHn9O5.js} +1 -1
- package/dashboard/dist/assets/{vendor-f83ah6cm.js → vendor-BotF760a.js} +61 -61
- package/dashboard/dist/index.html +6 -6
- package/lib/bootstrap.js +1 -1
- package/lib/cli/SetupService.js +33 -8
- package/lib/cli/UpgradeService.js +139 -2
- package/lib/core/ast/ProjectGraph.js +599 -0
- package/lib/core/gateway/GatewayActionRegistry.js +2 -2
- package/lib/domain/recipe/Recipe.js +3 -0
- package/lib/external/ai/AiProvider.js +83 -20
- package/lib/external/ai/providers/ClaudeProvider.js +197 -0
- package/lib/external/ai/providers/GoogleGeminiProvider.js +235 -1
- package/lib/external/ai/providers/OpenAiProvider.js +131 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +216 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +468 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +162 -0
- package/lib/external/mcp/handlers/bootstrap/skills.js +225 -0
- package/lib/external/mcp/handlers/bootstrap.js +151 -1634
- package/lib/external/mcp/handlers/browse.js +1 -1
- package/lib/external/mcp/handlers/candidate.js +1 -33
- package/lib/external/mcp/handlers/skill.js +54 -17
- package/lib/external/mcp/tools.js +4 -3
- package/lib/http/middleware/requestLogger.js +23 -4
- package/lib/http/routes/ai.js +3 -1
- package/lib/http/routes/auth.js +3 -2
- package/lib/http/routes/candidates.js +49 -25
- package/lib/http/routes/commands.js +0 -8
- package/lib/http/routes/guardRules.js +1 -16
- package/lib/http/routes/recipes.js +4 -17
- package/lib/http/routes/search.js +11 -19
- package/lib/http/routes/skills.js +2 -0
- package/lib/http/routes/snippets.js +0 -33
- package/lib/http/routes/spm.js +37 -63
- package/lib/http/utils/routeHelpers.js +31 -0
- package/lib/infrastructure/config/Paths.js +9 -0
- package/lib/infrastructure/logging/Logger.js +86 -3
- package/lib/infrastructure/realtime/RealtimeService.js +2 -5
- package/lib/infrastructure/vector/JsonVectorAdapter.js +24 -1
- package/lib/injection/ServiceContainer.js +55 -2
- package/lib/service/bootstrap/BootstrapTaskManager.js +400 -0
- package/lib/service/candidate/CandidateFileWriter.js +68 -27
- package/lib/service/candidate/CandidateService.js +156 -10
- package/lib/service/chat/AnalystAgent.js +216 -0
- package/lib/service/chat/CandidateGuardrail.js +134 -0
- package/lib/service/chat/ChatAgent.js +1036 -167
- package/lib/service/chat/ContextWindow.js +730 -0
- package/lib/service/chat/HandoffProtocol.js +180 -0
- package/lib/service/chat/ProducerAgent.js +240 -0
- package/lib/service/chat/ToolRegistry.js +149 -5
- package/lib/service/chat/tools.js +1397 -61
- package/lib/service/recipe/RecipeFileWriter.js +12 -1
- package/lib/service/skills/SignalCollector.js +31 -6
- package/lib/service/skills/SkillAdvisor.js +2 -1
- package/lib/service/skills/SkillHooks.js +13 -5
- package/lib/service/spm/SpmService.js +2 -2
- package/package.json +1 -1
- package/templates/copilot-instructions.md +20 -3
- package/templates/cursor-rules/autosnippet-conventions.mdc +21 -4
- package/templates/cursor-rules/autosnippet-skills.mdc +45 -0
- package/dashboard/dist/assets/index-BBKa3Dgi.js +0 -195
- package/dashboard/dist/assets/index-DLsECfzW.css +0 -1
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap — Skill 加载与维度增强
|
|
3
|
+
*
|
|
4
|
+
* 负责加载 coldstart + language-reference Skills,
|
|
5
|
+
* 从中提取维度增强指引注入 baseDimensions。
|
|
6
|
+
*
|
|
7
|
+
* ChatAgent + MCP 外部 Agent 共享此模块。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const SKILLS_DIR = path.resolve(__dirname, '../../../../../skills');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 语言 → Skills 映射(Skills 必须位于内置 skills/ 目录)
|
|
19
|
+
*/
|
|
20
|
+
const LANG_SKILL_MAP = {
|
|
21
|
+
objectivec: ['autosnippet-coldstart', 'reference-objc'],
|
|
22
|
+
swift: ['autosnippet-coldstart', 'reference-swift'],
|
|
23
|
+
// 未来扩展: kotlin, java, python, typescript ...
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 加载 Bootstrap 相关 Skills
|
|
28
|
+
*
|
|
29
|
+
* @param {string} primaryLanguage 主语言
|
|
30
|
+
* @param {object} logger
|
|
31
|
+
* @returns {{ coldstartSkill: string|null, languageSkill: string|null, loaded: string[] }}
|
|
32
|
+
*/
|
|
33
|
+
export function loadBootstrapSkills(primaryLanguage, logger) {
|
|
34
|
+
const result = { coldstartSkill: null, languageSkill: null, loaded: [] };
|
|
35
|
+
const skillNames = LANG_SKILL_MAP[primaryLanguage] || LANG_SKILL_MAP.swift;
|
|
36
|
+
|
|
37
|
+
for (const skillName of skillNames) {
|
|
38
|
+
const skillPath = path.join(SKILLS_DIR, skillName, 'SKILL.md');
|
|
39
|
+
try {
|
|
40
|
+
if (fs.existsSync(skillPath)) {
|
|
41
|
+
const content = fs.readFileSync(skillPath, 'utf8');
|
|
42
|
+
if (skillName.startsWith('autosnippet-coldstart')) {
|
|
43
|
+
result.coldstartSkill = content;
|
|
44
|
+
} else if (skillName.startsWith('reference-')) {
|
|
45
|
+
result.languageSkill = content;
|
|
46
|
+
}
|
|
47
|
+
result.loaded.push(skillName);
|
|
48
|
+
logger?.info?.(`[Bootstrap] Loaded skill: ${skillName}`);
|
|
49
|
+
} else {
|
|
50
|
+
logger?.debug?.(`[Bootstrap] Skill not found: ${skillPath}`);
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {
|
|
53
|
+
logger?.warn?.(`[Bootstrap] Failed to load skill ${skillName}: ${e.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Skill 中提取维度增强指引
|
|
62
|
+
*
|
|
63
|
+
* 1) coldstart SKILL.md 中的 "Per-Dimension Industry Reference Templates" 是**格式示例**(Swift 代码),
|
|
64
|
+
* 仅当无语言 Skill 时才用作 fallback;
|
|
65
|
+
* 2) 语言 Skill(如 reference-objc)包含真正的业界最佳实践内容,优先使用;
|
|
66
|
+
* 3) 返回 per-section 结构以便后续 per-candidate 精准匹配。
|
|
67
|
+
*
|
|
68
|
+
* @param {object} skillContext — 由 loadBootstrapSkills 返回
|
|
69
|
+
* @returns {{ guides: Record<string, string>, sectionMap: Record<string, Array<{title: string, content: string, keywords: string[]}>> }}
|
|
70
|
+
*/
|
|
71
|
+
export function extractSkillDimensionGuides(skillContext) {
|
|
72
|
+
const guides = {}; // dimId → summary guide text
|
|
73
|
+
const sectionMap = {}; // dimId → [{title, content, keywords}]
|
|
74
|
+
const hasLanguageSkill = !!skillContext.languageSkill;
|
|
75
|
+
|
|
76
|
+
// ── coldstart 模板: 仅在无语言 Skill 时用作 fallback ──
|
|
77
|
+
// coldstart 中的 rationale/whyStandard 是 Swift 示例,不适合直接注入其它语言项目
|
|
78
|
+
if (skillContext.coldstartSkill && !hasLanguageSkill) {
|
|
79
|
+
const content = skillContext.coldstartSkill;
|
|
80
|
+
const dimBlocks = content.matchAll(/###\s+维度\s*\d+\s*[::]\s*(.+?)\s*\(([^)]+)\)\s*[—–-]\s*参考模板\s*\n([\s\S]*?)(?=\n###\s|\n##\s)/g);
|
|
81
|
+
for (const match of dimBlocks) {
|
|
82
|
+
let dimId = match[2].trim();
|
|
83
|
+
if (/solution|antiPattern|bug/i.test(dimId)) dimId = 'anti-pattern';
|
|
84
|
+
dimId = dimId.replace(/\s+/g, '-');
|
|
85
|
+
const block = match[3];
|
|
86
|
+
const rationaleMatch = block.match(/"rationale"\s*:\s*"([^"]{20,300})"/);
|
|
87
|
+
const whyMatch = block.match(/"whyStandard"\s*:\s*"([^"]{20,200})"/);
|
|
88
|
+
const extraGuide = [rationaleMatch?.[1], whyMatch?.[1]].filter(Boolean).join('。');
|
|
89
|
+
if (extraGuide) {
|
|
90
|
+
guides[dimId] = extraGuide;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── 语言 Skill: 逐 section 提取丰富内容作为业界参考(PRIMARY) ──
|
|
96
|
+
if (skillContext.languageSkill) {
|
|
97
|
+
const content = skillContext.languageSkill;
|
|
98
|
+
|
|
99
|
+
// heading → dimension(s) + 子主题匹配关键词
|
|
100
|
+
const HEADING_DIM_MAP = [
|
|
101
|
+
{ pattern: /命名|naming|前缀|prefix/i, dims: ['code-standard', 'code-pattern'], keywords: ['naming', 'prefix', '命名', '前缀', 'category'] },
|
|
102
|
+
{ pattern: /属性|propert/i, dims: ['code-standard', 'best-practice', 'anti-pattern'], keywords: ['property', '属性', 'copy', 'weak', 'strong', 'memory', 'retain', 'cycle', 'leak'] },
|
|
103
|
+
{ pattern: /delegate|委托/i, dims: ['event-and-data-flow', 'code-pattern'], keywords: ['delegate', 'protocol', '委托', '协议'] },
|
|
104
|
+
{ pattern: /初始化|initializ/i, dims: ['code-pattern'], keywords: ['init', 'initializer', '初始化', 'factory'] },
|
|
105
|
+
{ pattern: /null|可选/i, dims: ['code-standard'], keywords: ['nullable', 'nonnull', 'nullability'] },
|
|
106
|
+
{ pattern: /错误处理|error/i, dims: ['best-practice'], keywords: ['error', 'NSError', '错误', 'error-handling'] },
|
|
107
|
+
{ pattern: /bool|陷阱/i, dims: ['anti-pattern'], keywords: ['BOOL', 'bool', '陷阱', 'trap'] },
|
|
108
|
+
{ pattern: /gcd|线程|thread|并发|concurrent/i, dims: ['best-practice', 'anti-pattern'], keywords: ['GCD', 'dispatch', 'thread', '线程', 'main', 'concurrency', 'main-thread'] },
|
|
109
|
+
{ pattern: /泛型|generic/i, dims: ['code-standard'], keywords: ['generics', '泛型', 'generic'] },
|
|
110
|
+
{ pattern: /import|导入/i, dims: ['code-standard'], keywords: ['import', '#import', '导入', 'file-organization'] },
|
|
111
|
+
{ pattern: /特有维度|extra.?dim/i, dims: ['agent-guidelines'], keywords: ['agent', '注意', '维度', 'extra'] },
|
|
112
|
+
{ pattern: /category|扩展(?!.*特有)/i, dims: ['code-pattern'], keywords: ['category', 'extension', '扩展'] },
|
|
113
|
+
{ pattern: /singleton|单例/i, dims: ['code-pattern'], keywords: ['singleton', '单例', 'dispatch_once'] },
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
// 用 --- 分割section,更可靠地提取完整 section body
|
|
117
|
+
const sectionParts = content.split(/\n---\n/);
|
|
118
|
+
for (const part of sectionParts) {
|
|
119
|
+
const headingMatch = part.match(/^##\s+\d+\.\s+(.+?)(?:\s*\(.+\))?\s*$/m);
|
|
120
|
+
if (!headingMatch) continue;
|
|
121
|
+
|
|
122
|
+
const heading = headingMatch[1].trim();
|
|
123
|
+
const bodyStart = part.indexOf(headingMatch[0]) + headingMatch[0].length;
|
|
124
|
+
const body = part.substring(bodyStart);
|
|
125
|
+
|
|
126
|
+
// 查找匹配的 dimension(s)
|
|
127
|
+
let matchedDims = [];
|
|
128
|
+
let matchedKeywords = [];
|
|
129
|
+
for (const mapping of HEADING_DIM_MAP) {
|
|
130
|
+
if (mapping.pattern.test(heading)) {
|
|
131
|
+
matchedDims.push(...mapping.dims);
|
|
132
|
+
matchedKeywords.push(...mapping.keywords);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
matchedDims = [...new Set(matchedDims)];
|
|
136
|
+
if (matchedDims.length === 0) continue;
|
|
137
|
+
|
|
138
|
+
// 提取有意义的摘要内容
|
|
139
|
+
let summary = extractSectionSummary(body);
|
|
140
|
+
// 如果 section body 主要是代码块导致摘要太短,用 heading 本身作为摘要前缀
|
|
141
|
+
if (summary.length < 20) {
|
|
142
|
+
summary = `${heading}:${summary}`;
|
|
143
|
+
}
|
|
144
|
+
if (summary.length < 10) continue;
|
|
145
|
+
|
|
146
|
+
const sectionData = {
|
|
147
|
+
title: heading,
|
|
148
|
+
content: summary.substring(0, 500),
|
|
149
|
+
keywords: [...new Set(matchedKeywords)],
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
for (const dimId of matchedDims) {
|
|
153
|
+
if (!sectionMap[dimId]) sectionMap[dimId] = [];
|
|
154
|
+
sectionMap[dimId].push(sectionData);
|
|
155
|
+
|
|
156
|
+
const shortContent = summary.substring(0, 120);
|
|
157
|
+
if (!guides[dimId]) {
|
|
158
|
+
guides[dimId] = `[${heading}] ${shortContent}`;
|
|
159
|
+
} else if (guides[dimId].length < 500) {
|
|
160
|
+
guides[dimId] += `; [${heading}] ${shortContent}`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { guides, sectionMap };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 从 Skill section body 中提取有意义的摘要内容
|
|
171
|
+
* 跳过 JSON 模板、代码块(保留关键注释),保留表格和文字描述
|
|
172
|
+
*/
|
|
173
|
+
export function extractSectionSummary(body) {
|
|
174
|
+
const lines = body.split('\n');
|
|
175
|
+
const parts = [];
|
|
176
|
+
let inCodeBlock = false;
|
|
177
|
+
let inJsonBlock = false;
|
|
178
|
+
|
|
179
|
+
for (const line of lines) {
|
|
180
|
+
const trimmed = line.trim();
|
|
181
|
+
|
|
182
|
+
// 跳过 JSON 模板块(候选格式示例)
|
|
183
|
+
if (trimmed.startsWith('```json')) { inJsonBlock = true; continue; }
|
|
184
|
+
if (inJsonBlock) { if (trimmed === '```') inJsonBlock = false; continue; }
|
|
185
|
+
|
|
186
|
+
// 追踪代码块 — 保留 ✅/❌ 关键注释
|
|
187
|
+
if (trimmed.startsWith('```')) { inCodeBlock = !inCodeBlock; continue; }
|
|
188
|
+
if (inCodeBlock) {
|
|
189
|
+
if (/[✅❌]/.test(trimmed) && parts.length < 12) parts.push(trimmed);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!trimmed) continue;
|
|
194
|
+
if (trimmed.startsWith('###') || trimmed.startsWith('####')) continue;
|
|
195
|
+
if (/^\|[-\s|:]+\|$/.test(trimmed)) continue; // 表格分隔线
|
|
196
|
+
|
|
197
|
+
parts.push(trimmed);
|
|
198
|
+
if (parts.length >= 10) break;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return parts.join('; ').replace(/\s{2,}/g, ' ').trim();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* 增强 9 维度定义 — 将 Skill 提供的参考指引注入 dimensions[].guide
|
|
206
|
+
*
|
|
207
|
+
* @param {Array} dimensions — 原始维度数组
|
|
208
|
+
* @param {Record<string, string>} skillGuides — guides 部分
|
|
209
|
+
* @param {Record<string, Array>} skillSections — sectionMap 部分(per-candidate 匹配用)
|
|
210
|
+
* @returns {Array} 增强后的维度数组(原数组不变,返回新数组)
|
|
211
|
+
*/
|
|
212
|
+
export function enhanceDimensions(dimensions, skillGuides, skillSections) {
|
|
213
|
+
if (!skillGuides || Object.keys(skillGuides).length === 0) return dimensions;
|
|
214
|
+
|
|
215
|
+
return dimensions.map(dim => {
|
|
216
|
+
const extra = skillGuides[dim.id];
|
|
217
|
+
if (!extra) return dim;
|
|
218
|
+
return {
|
|
219
|
+
...dim,
|
|
220
|
+
guide: `${dim.guide}。[Skill 参考] ${extra}`,
|
|
221
|
+
_skillEnhanced: true,
|
|
222
|
+
_skillSections: skillSections?.[dim.id] || [],
|
|
223
|
+
};
|
|
224
|
+
});
|
|
225
|
+
}
|