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.
Files changed (78) hide show
  1. package/README.md +137 -65
  2. package/bin/api-server.js +5 -0
  3. package/bin/cli.js +6 -1
  4. package/dashboard/dist/assets/{icons-rnn04CvH.js → icons-B_Xg4B-s.js} +148 -88
  5. package/dashboard/dist/assets/index-BjfUm8p9.js +197 -0
  6. package/dashboard/dist/assets/index-CkIih2CC.css +1 -0
  7. package/dashboard/dist/assets/{react-markdown-CWxUbOf4.js → react-markdown-BA6FB2NP.js} +1 -1
  8. package/dashboard/dist/assets/{syntax-highlighter-CJ2drQQb.js → syntax-highlighter-CVLHn9O5.js} +1 -1
  9. package/dashboard/dist/assets/{vendor-f83ah6cm.js → vendor-BotF760a.js} +61 -61
  10. package/dashboard/dist/index.html +6 -6
  11. package/lib/bootstrap.js +18 -1
  12. package/lib/cli/SetupService.js +86 -8
  13. package/lib/cli/UpgradeService.js +139 -2
  14. package/lib/core/ast/ProjectGraph.js +599 -0
  15. package/lib/core/gateway/GatewayActionRegistry.js +2 -2
  16. package/lib/domain/recipe/Recipe.js +3 -0
  17. package/lib/external/ai/AiProvider.js +83 -20
  18. package/lib/external/ai/providers/ClaudeProvider.js +208 -0
  19. package/lib/external/ai/providers/GoogleGeminiProvider.js +247 -1
  20. package/lib/external/ai/providers/OpenAiProvider.js +141 -0
  21. package/lib/external/mcp/McpServer.js +6 -1
  22. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +216 -0
  23. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +657 -0
  24. package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +160 -0
  25. package/lib/external/mcp/handlers/bootstrap/skills.js +225 -0
  26. package/lib/external/mcp/handlers/bootstrap.js +159 -1634
  27. package/lib/external/mcp/handlers/browse.js +1 -1
  28. package/lib/external/mcp/handlers/candidate.js +1 -33
  29. package/lib/external/mcp/handlers/skill.js +58 -17
  30. package/lib/external/mcp/tools.js +4 -3
  31. package/lib/http/middleware/requestLogger.js +23 -4
  32. package/lib/http/routes/ai.js +158 -2
  33. package/lib/http/routes/auth.js +3 -2
  34. package/lib/http/routes/candidates.js +49 -25
  35. package/lib/http/routes/commands.js +0 -8
  36. package/lib/http/routes/guardRules.js +1 -16
  37. package/lib/http/routes/recipes.js +4 -17
  38. package/lib/http/routes/search.js +11 -19
  39. package/lib/http/routes/skills.js +2 -0
  40. package/lib/http/routes/snippets.js +0 -33
  41. package/lib/http/routes/spm.js +37 -63
  42. package/lib/http/utils/routeHelpers.js +31 -0
  43. package/lib/infrastructure/config/Paths.js +12 -0
  44. package/lib/infrastructure/database/DatabaseConnection.js +6 -1
  45. package/lib/infrastructure/logging/Logger.js +86 -3
  46. package/lib/infrastructure/realtime/RealtimeService.js +2 -5
  47. package/lib/infrastructure/vector/JsonVectorAdapter.js +26 -1
  48. package/lib/injection/ServiceContainer.js +55 -2
  49. package/lib/service/bootstrap/BootstrapTaskManager.js +400 -0
  50. package/lib/service/candidate/CandidateFileWriter.js +72 -27
  51. package/lib/service/candidate/CandidateService.js +156 -10
  52. package/lib/service/chat/AnalystAgent.js +245 -0
  53. package/lib/service/chat/CandidateGuardrail.js +134 -0
  54. package/lib/service/chat/ChatAgent.js +1055 -167
  55. package/lib/service/chat/ContextWindow.js +730 -0
  56. package/lib/service/chat/ConversationStore.js +3 -0
  57. package/lib/service/chat/HandoffProtocol.js +181 -0
  58. package/lib/service/chat/Memory.js +3 -0
  59. package/lib/service/chat/ProducerAgent.js +293 -0
  60. package/lib/service/chat/ToolRegistry.js +149 -5
  61. package/lib/service/chat/tools.js +1404 -61
  62. package/lib/service/guard/ExclusionManager.js +2 -0
  63. package/lib/service/guard/RuleLearner.js +2 -0
  64. package/lib/service/quality/FeedbackCollector.js +2 -0
  65. package/lib/service/recipe/RecipeFileWriter.js +16 -1
  66. package/lib/service/recipe/RecipeStatsTracker.js +2 -0
  67. package/lib/service/skills/SignalCollector.js +33 -6
  68. package/lib/service/skills/SkillAdvisor.js +2 -1
  69. package/lib/service/skills/SkillHooks.js +13 -5
  70. package/lib/service/spm/SpmService.js +2 -2
  71. package/lib/shared/PathGuard.js +314 -0
  72. package/package.json +1 -1
  73. package/resources/native-ui/combined-window.swift +494 -0
  74. package/templates/copilot-instructions.md +20 -3
  75. package/templates/cursor-rules/autosnippet-conventions.mdc +21 -4
  76. package/templates/cursor-rules/autosnippet-skills.mdc +45 -0
  77. package/dashboard/dist/assets/index-BBKa3Dgi.js +0 -195
  78. 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
- this.#logger.debug(`Tool execute: ${name}`, { params: Object.keys(params) });
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(params, context);
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
  */