autosnippet 2.6.0 → 2.7.1
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 +137 -65
- package/bin/api-server.js +5 -0
- package/bin/cli.js +6 -1
- package/dashboard/dist/assets/{icons-rnn04CvH.js → icons-B_Xg4B-s.js} +148 -88
- package/dashboard/dist/assets/index-BjfUm8p9.js +197 -0
- package/dashboard/dist/assets/index-CkIih2CC.css +1 -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 +18 -1
- package/lib/cli/SetupService.js +86 -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 +208 -0
- package/lib/external/ai/providers/GoogleGeminiProvider.js +247 -1
- package/lib/external/ai/providers/OpenAiProvider.js +141 -0
- package/lib/external/mcp/McpServer.js +6 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +216 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +657 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +160 -0
- package/lib/external/mcp/handlers/bootstrap/skills.js +225 -0
- package/lib/external/mcp/handlers/bootstrap.js +159 -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 +58 -17
- package/lib/external/mcp/tools.js +4 -3
- package/lib/http/middleware/requestLogger.js +23 -4
- package/lib/http/routes/ai.js +158 -2
- 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 +12 -0
- package/lib/infrastructure/database/DatabaseConnection.js +6 -1
- package/lib/infrastructure/logging/Logger.js +86 -3
- package/lib/infrastructure/realtime/RealtimeService.js +2 -5
- package/lib/infrastructure/vector/JsonVectorAdapter.js +26 -1
- package/lib/injection/ServiceContainer.js +55 -2
- package/lib/service/bootstrap/BootstrapTaskManager.js +400 -0
- package/lib/service/candidate/CandidateFileWriter.js +72 -27
- package/lib/service/candidate/CandidateService.js +156 -10
- package/lib/service/chat/AnalystAgent.js +245 -0
- package/lib/service/chat/CandidateGuardrail.js +134 -0
- package/lib/service/chat/ChatAgent.js +1055 -167
- package/lib/service/chat/ContextWindow.js +730 -0
- package/lib/service/chat/ConversationStore.js +3 -0
- package/lib/service/chat/HandoffProtocol.js +181 -0
- package/lib/service/chat/Memory.js +3 -0
- package/lib/service/chat/ProducerAgent.js +293 -0
- package/lib/service/chat/ToolRegistry.js +149 -5
- package/lib/service/chat/tools.js +1404 -61
- package/lib/service/guard/ExclusionManager.js +2 -0
- package/lib/service/guard/RuleLearner.js +2 -0
- package/lib/service/quality/FeedbackCollector.js +2 -0
- package/lib/service/recipe/RecipeFileWriter.js +16 -1
- package/lib/service/recipe/RecipeStatsTracker.js +2 -0
- package/lib/service/skills/SignalCollector.js +33 -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/lib/shared/PathGuard.js +314 -0
- package/package.json +1 -1
- package/resources/native-ui/combined-window.swift +494 -0
- 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
|
@@ -12,6 +12,38 @@
|
|
|
12
12
|
|
|
13
13
|
import Logger from '../../infrastructure/logging/Logger.js';
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* AI 模型常见的参数命名变体 → schema 标准名映射
|
|
17
|
+
* 覆盖 Gemini / GPT / DeepSeek / Claude 常见偏好
|
|
18
|
+
*/
|
|
19
|
+
const PARAM_ALIASES = {
|
|
20
|
+
// read_project_file 变体
|
|
21
|
+
file: 'filePath',
|
|
22
|
+
filename: 'filePath',
|
|
23
|
+
file_name: 'filePath',
|
|
24
|
+
filepath: 'filePath',
|
|
25
|
+
file_path: 'filePath',
|
|
26
|
+
path: 'filePath',
|
|
27
|
+
// search_project_code 变体
|
|
28
|
+
query: 'pattern',
|
|
29
|
+
search: 'pattern',
|
|
30
|
+
keyword: 'pattern',
|
|
31
|
+
search_query: 'pattern',
|
|
32
|
+
search_text: 'pattern',
|
|
33
|
+
regex: 'pattern',
|
|
34
|
+
// 通用变体
|
|
35
|
+
is_regex: 'isRegex',
|
|
36
|
+
file_filter: 'fileFilter',
|
|
37
|
+
context_lines: 'contextLines',
|
|
38
|
+
max_results: 'maxResults',
|
|
39
|
+
start_line: 'startLine',
|
|
40
|
+
end_line: 'endLine',
|
|
41
|
+
max_lines: 'maxLines',
|
|
42
|
+
candidate_id: 'candidateId',
|
|
43
|
+
recipe_id: 'recipeId',
|
|
44
|
+
skill_name: 'skillName',
|
|
45
|
+
};
|
|
46
|
+
|
|
15
47
|
export class ToolRegistry {
|
|
16
48
|
#tools = new Map();
|
|
17
49
|
#logger;
|
|
@@ -32,7 +64,6 @@ export class ToolRegistry {
|
|
|
32
64
|
const { name, description, handler, parameters = {} } = toolDef;
|
|
33
65
|
if (!name || !handler) throw new Error('Tool must have name and handler');
|
|
34
66
|
this.#tools.set(name, { name, description, parameters, handler });
|
|
35
|
-
this.#logger.debug(`Tool registered: ${name}`);
|
|
36
67
|
}
|
|
37
68
|
|
|
38
69
|
/**
|
|
@@ -41,14 +72,18 @@ export class ToolRegistry {
|
|
|
41
72
|
*/
|
|
42
73
|
registerAll(defs) {
|
|
43
74
|
for (const def of defs) this.register(def);
|
|
75
|
+
this.#logger.info(`[ToolRegistry] ${defs.length} tools registered`);
|
|
44
76
|
}
|
|
45
77
|
|
|
46
78
|
/**
|
|
47
79
|
* 获取工具定义(不含 handler,给 LLM prompt 使用)
|
|
80
|
+
* @param {string[]} [allowedTools] — 限制返回的工具列表(不传则返回全部)
|
|
81
|
+
* @returns {Array<{name: string, description: string, parameters: object}>}
|
|
48
82
|
*/
|
|
49
|
-
getToolSchemas() {
|
|
83
|
+
getToolSchemas(allowedTools) {
|
|
50
84
|
const schemas = [];
|
|
51
|
-
for (const [, tool] of this.#tools) {
|
|
85
|
+
for (const [name, tool] of this.#tools) {
|
|
86
|
+
if (allowedTools && !allowedTools.includes(name)) continue;
|
|
52
87
|
schemas.push({
|
|
53
88
|
name: tool.name,
|
|
54
89
|
description: tool.description,
|
|
@@ -69,9 +104,13 @@ export class ToolRegistry {
|
|
|
69
104
|
const tool = this.#tools.get(name);
|
|
70
105
|
if (!tool) throw new Error(`Tool '${name}' not found`);
|
|
71
106
|
|
|
72
|
-
|
|
107
|
+
// 参数归一化: AI 可能用 snake_case / 不同命名传参,
|
|
108
|
+
// 将其映射到 tool schema 中定义的 camelCase 参数名
|
|
109
|
+
const normalized = this.#normalizeParams(params, tool.parameters);
|
|
110
|
+
|
|
111
|
+
this.#logger.debug(`Tool execute: ${name}`, { params: Object.keys(normalized) });
|
|
73
112
|
try {
|
|
74
|
-
const result = await tool.handler(
|
|
113
|
+
const result = await tool.handler(normalized, context);
|
|
75
114
|
return result;
|
|
76
115
|
} catch (err) {
|
|
77
116
|
this.#logger.error(`Tool '${name}' failed`, { error: err.message });
|
|
@@ -79,6 +118,59 @@ export class ToolRegistry {
|
|
|
79
118
|
}
|
|
80
119
|
}
|
|
81
120
|
|
|
121
|
+
/**
|
|
122
|
+
* 参数归一化 — 将 AI 传来的 snake_case / 变体参数名映射到 schema 定义名
|
|
123
|
+
*
|
|
124
|
+
* 例: AI 传 { file_path: "x.m" } → schema 定义 filePath → 归一化为 { filePath: "x.m" }
|
|
125
|
+
* AI 传 { file: "x.m" } → schema 定义 filePath → 通过别名表匹配
|
|
126
|
+
*
|
|
127
|
+
* 策略:
|
|
128
|
+
* 1. schema 中已有的 key → 保留不动
|
|
129
|
+
* 2. snake_case → camelCase 自动转换
|
|
130
|
+
* 3. 常用别名表兜底
|
|
131
|
+
*/
|
|
132
|
+
#normalizeParams(params, schema) {
|
|
133
|
+
if (!params || typeof params !== 'object') return params || {};
|
|
134
|
+
const properties = schema?.properties || {};
|
|
135
|
+
const schemaKeys = new Set(Object.keys(properties));
|
|
136
|
+
if (schemaKeys.size === 0) return params;
|
|
137
|
+
|
|
138
|
+
const result = {};
|
|
139
|
+
const unmatched = [];
|
|
140
|
+
|
|
141
|
+
for (const [key, value] of Object.entries(params)) {
|
|
142
|
+
// 1. 精确匹配 — 已在 schema 中
|
|
143
|
+
if (schemaKeys.has(key)) {
|
|
144
|
+
result[key] = value;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 2. snake_case → camelCase 转换
|
|
149
|
+
const camelKey = key.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
150
|
+
if (schemaKeys.has(camelKey)) {
|
|
151
|
+
result[camelKey] = value;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 3. 常用别名映射
|
|
156
|
+
const aliased = PARAM_ALIASES[key];
|
|
157
|
+
if (aliased && schemaKeys.has(aliased)) {
|
|
158
|
+
result[aliased] = value;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 4. 无匹配 — 保留原样(handler 可能有自定义处理)
|
|
163
|
+
result[key] = value;
|
|
164
|
+
unmatched.push(key);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (unmatched.length > 0) {
|
|
168
|
+
this.#logger.debug(`[ToolRegistry] param normalization: unmatched keys [${unmatched.join(', ')}]`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
|
|
82
174
|
/**
|
|
83
175
|
* 检查工具是否存在
|
|
84
176
|
*/
|
|
@@ -86,6 +178,58 @@ export class ToolRegistry {
|
|
|
86
178
|
return this.#tools.has(name);
|
|
87
179
|
}
|
|
88
180
|
|
|
181
|
+
/**
|
|
182
|
+
* 转换为 Gemini functionDeclarations 格式
|
|
183
|
+
* 供 GoogleGeminiProvider.chatWithTools() 使用
|
|
184
|
+
*
|
|
185
|
+
* @param {string[]} [allowedTools] — 限制可用工具列表(不传则返回全部)
|
|
186
|
+
* @returns {Array<{name: string, description: string, parameters: object}>}
|
|
187
|
+
*/
|
|
188
|
+
toFunctionDeclarations(allowedTools) {
|
|
189
|
+
const result = [];
|
|
190
|
+
for (const [name, tool] of this.#tools) {
|
|
191
|
+
if (allowedTools && !allowedTools.includes(name)) continue;
|
|
192
|
+
result.push({
|
|
193
|
+
name: tool.name,
|
|
194
|
+
description: tool.description || '',
|
|
195
|
+
parameters: this.#sanitizeSchemaForGemini(tool.parameters),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 清理 JSON Schema 使之兼容 Gemini API 的 OpenAPI 子集
|
|
203
|
+
* Gemini API 不支持某些 JSON Schema 扩展语法
|
|
204
|
+
*/
|
|
205
|
+
#sanitizeSchemaForGemini(schema) {
|
|
206
|
+
if (!schema || typeof schema !== 'object') {
|
|
207
|
+
return { type: 'object', properties: {} };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const cleaned = { ...schema };
|
|
211
|
+
|
|
212
|
+
// 确保 type 存在
|
|
213
|
+
if (!cleaned.type) cleaned.type = 'object';
|
|
214
|
+
|
|
215
|
+
// 递归清理 properties
|
|
216
|
+
if (cleaned.properties) {
|
|
217
|
+
const props = {};
|
|
218
|
+
for (const [key, val] of Object.entries(cleaned.properties)) {
|
|
219
|
+
const prop = { ...val };
|
|
220
|
+
// 移除 Gemini 不支持的字段
|
|
221
|
+
delete prop.default;
|
|
222
|
+
delete prop.examples;
|
|
223
|
+
// 确保 type 存在
|
|
224
|
+
if (!prop.type) prop.type = 'string';
|
|
225
|
+
props[key] = prop;
|
|
226
|
+
}
|
|
227
|
+
cleaned.properties = props;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return cleaned;
|
|
231
|
+
}
|
|
232
|
+
|
|
89
233
|
/**
|
|
90
234
|
* 获取所有工具名
|
|
91
235
|
*/
|