autosnippet 3.4.0 → 3.4.2

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 (49) hide show
  1. package/README.md +43 -18
  2. package/dashboard/dist/assets/{index-8b1Gf3Bb.js → index-BX6r2fiy.js} +40 -40
  3. package/dashboard/dist/assets/index-BvZcGN02.css +1 -0
  4. package/dashboard/dist/index.html +2 -2
  5. package/dist/lib/core/AstAnalyzer.js +0 -1
  6. package/dist/lib/core/ast/lang-dart.js +118 -8
  7. package/dist/lib/core/ast/lang-go.js +0 -1
  8. package/dist/lib/core/ast/lang-java.js +25 -11
  9. package/dist/lib/core/ast/lang-javascript.js +103 -17
  10. package/dist/lib/core/ast/lang-objc.d.ts +1 -1
  11. package/dist/lib/core/ast/lang-objc.js +80 -4
  12. package/dist/lib/core/ast/lang-python.js +0 -1
  13. package/dist/lib/core/ast/lang-rust.js +0 -1
  14. package/dist/lib/core/ast/lang-swift.d.ts +1 -1
  15. package/dist/lib/core/ast/lang-swift.js +184 -7
  16. package/dist/lib/core/ast/lang-typescript.js +0 -1
  17. package/dist/lib/external/ai/AiFactory.d.ts +14 -0
  18. package/dist/lib/external/ai/AiFactory.js +33 -1
  19. package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +7 -3
  20. package/dist/lib/external/ai/providers/OpenAiProvider.js +1 -1
  21. package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.d.ts +33 -1
  22. package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +392 -19
  23. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.d.ts +1 -0
  24. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +2 -1
  25. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +2 -0
  26. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +4 -0
  27. package/dist/lib/external/mcp/handlers/guard.js +11 -6
  28. package/dist/lib/http/routes/ai.js +18 -1
  29. package/dist/lib/infrastructure/vector/IndexingPipeline.js +6 -1
  30. package/dist/lib/injection/modules/AiModule.js +22 -1
  31. package/dist/lib/service/bootstrap/BootstrapTaskManager.d.ts +7 -0
  32. package/dist/lib/service/bootstrap/BootstrapTaskManager.js +17 -0
  33. package/dist/lib/service/guard/ComplianceReporter.js +5 -1
  34. package/dist/lib/service/guard/GuardCheckEngine.d.ts +12 -1
  35. package/dist/lib/service/guard/GuardCheckEngine.js +36 -4
  36. package/dist/lib/service/guard/GuardCodeChecks.js +27 -9
  37. package/dist/lib/service/guard/SourceFileCollector.d.ts +3 -2
  38. package/dist/lib/service/guard/SourceFileCollector.js +3 -3
  39. package/dist/lib/service/search/SearchEngine.js +165 -61
  40. package/dist/lib/service/task/PrimeSearchPipeline.js +17 -2
  41. package/dist/lib/service/vector/VectorService.js +10 -1
  42. package/dist/lib/shared/LanguageService.d.ts +12 -0
  43. package/dist/lib/shared/LanguageService.js +85 -0
  44. package/dist/lib/shared/schemas/http-requests.d.ts +4 -0
  45. package/dist/lib/shared/schemas/http-requests.js +4 -0
  46. package/dist/lib/types/project-snapshot.d.ts +1 -0
  47. package/package.json +1 -1
  48. package/resources/grammars/tree-sitter-dart.wasm +0 -0
  49. package/dashboard/dist/assets/index-DHJ1Dj7u.css +0 -1
@@ -172,10 +172,8 @@ function _parseJavaClass(node) {
172
172
  }
173
173
  }
174
174
  }
175
- // 提取注解
176
- const annotations = node.namedChildren
177
- .filter((c) => c.type === 'marker_annotation' || c.type === 'annotation')
178
- .map((a) => a.text);
175
+ // 提取注解(tree-sitter-java: 注解在 modifiers 子节点内)
176
+ const annotations = _extractAnnotations(node);
179
177
  // 修饰符
180
178
  const modifiers = node.namedChildren.find((c) => c.type === 'modifiers');
181
179
  const isAbstract = modifiers?.text?.includes('abstract') || false;
@@ -216,9 +214,7 @@ function _parseJavaMethod(node, className) {
216
214
  const bodyLines = body ? body.endPosition.row - body.startPosition.row + 1 : 0;
217
215
  const complexity = body ? _estimateComplexity(body) : 1;
218
216
  const nestingDepth = body ? _maxNesting(body, 0) : 0;
219
- const annotations = node.namedChildren
220
- .filter((c) => c.type === 'marker_annotation' || c.type === 'annotation')
221
- .map((a) => a.text);
217
+ const annotations = _extractAnnotations(node);
222
218
  return {
223
219
  name,
224
220
  className,
@@ -241,9 +237,7 @@ function _parseJavaField(node, className) {
241
237
  const isStatic = modifiers?.text?.includes('static') || false;
242
238
  const isFinal = modifiers?.text?.includes('final') || false;
243
239
  const isPrivate = modifiers?.text?.includes('private') || false;
244
- const annotations = node.namedChildren
245
- .filter((c) => c.type === 'marker_annotation' || c.type === 'annotation')
246
- .map((a) => a.text);
240
+ const annotations = _extractAnnotations(node);
247
241
  // Phase 5.3: Extract field type for DI resolution
248
242
  // field_declaration: [modifiers] type_identifier variable_declarator
249
243
  let typeAnnotation = null;
@@ -267,6 +261,27 @@ function _parseJavaField(node, className) {
267
261
  line: node.startPosition.row + 1,
268
262
  };
269
263
  }
264
+ /**
265
+ * tree-sitter-java 中注解位于 modifiers 子节点内,而非声明节点的直接子节点。
266
+ * 此辅助函数同时搜索两层:node.namedChildren + modifiers.namedChildren。
267
+ */
268
+ function _extractAnnotations(node) {
269
+ // 1. 直接子节点(兼容其他可能的 AST 结构)
270
+ const direct = node.namedChildren
271
+ .filter((c) => c.type === 'marker_annotation' || c.type === 'annotation')
272
+ .map((a) => a.text);
273
+ if (direct.length > 0) {
274
+ return direct;
275
+ }
276
+ // 2. modifiers 子节点
277
+ const modifiers = node.namedChildren.find((c) => c.type === 'modifiers');
278
+ if (modifiers) {
279
+ return modifiers.namedChildren
280
+ .filter((c) => c.type === 'marker_annotation' || c.type === 'annotation')
281
+ .map((a) => a.text);
282
+ }
283
+ return [];
284
+ }
270
285
  // ── Java 模式检测 ──
271
286
  function detectJavaPatterns(root, lang, methods, properties, classes) {
272
287
  const patterns = [];
@@ -387,7 +402,6 @@ function _maxNesting(node, depth) {
387
402
  'enhanced_for_statement',
388
403
  'while_statement',
389
404
  'switch_expression',
390
- 'block',
391
405
  'try_statement',
392
406
  ]);
393
407
  let max = depth;
@@ -75,9 +75,69 @@ function _walkJSClassBody(body, ctx, className) {
75
75
  else if (child.type === 'field_definition' || child.type === 'public_field_definition') {
76
76
  const name = child.namedChildren.find((c) => c.type === 'property_identifier')?.text;
77
77
  if (name) {
78
- ctx.properties.push({ name, className, line: child.startPosition.row + 1 });
78
+ const isStatic = child.text.trimStart().startsWith('static');
79
+ ctx.properties.push({
80
+ name,
81
+ className,
82
+ isStatic,
83
+ isConstant: false,
84
+ line: child.startPosition.row + 1,
85
+ });
86
+ }
87
+ }
88
+ }
89
+ // 从 constructor 中提取 this.xxx = ... 赋值属性
90
+ _extractConstructorProperties(body, ctx, className);
91
+ }
92
+ function _extractConstructorProperties(body, ctx, className) {
93
+ if (!body) {
94
+ return;
95
+ }
96
+ for (let i = 0; i < body.namedChildCount; i++) {
97
+ const child = body.namedChild(i);
98
+ if (child.type !== 'method_definition') {
99
+ continue;
100
+ }
101
+ const nameNode = child.namedChildren.find((c) => c.type === 'property_identifier' || c.type === 'identifier');
102
+ if (nameNode?.text !== 'constructor') {
103
+ continue;
104
+ }
105
+ const stmtBlock = child.namedChildren.find((c) => c.type === 'statement_block');
106
+ if (!stmtBlock) {
107
+ continue;
108
+ }
109
+ const seen = new Set(ctx.properties.filter((p) => p.className === className).map((p) => p.name));
110
+ _walkForThisAssignments(stmtBlock, ctx, className, seen);
111
+ }
112
+ }
113
+ function _walkForThisAssignments(node, ctx, className, seen) {
114
+ for (let i = 0; i < node.childCount; i++) {
115
+ const child = node.child(i);
116
+ if (child.type === 'expression_statement') {
117
+ const expr = child.namedChildren.find((c) => c.type === 'assignment_expression');
118
+ if (expr) {
119
+ const left = expr.namedChildren[0];
120
+ if (left?.type === 'member_expression') {
121
+ const obj = left.namedChildren.find((c) => c.type === 'this');
122
+ const prop = left.namedChildren.find((c) => c.type === 'property_identifier');
123
+ if (obj && prop && !seen.has(prop.text)) {
124
+ seen.add(prop.text);
125
+ ctx.properties.push({
126
+ name: prop.text,
127
+ className,
128
+ isStatic: false,
129
+ isConstant: false,
130
+ line: child.startPosition.row + 1,
131
+ });
132
+ }
133
+ }
79
134
  }
80
135
  }
136
+ else if (child.namedChildCount > 0 &&
137
+ child.type !== 'function' &&
138
+ child.type !== 'arrow_function') {
139
+ _walkForThisAssignments(child, ctx, className, seen);
140
+ }
81
141
  }
82
142
  }
83
143
  function _parseJSClass(node) {
@@ -85,12 +145,10 @@ function _parseJSClass(node) {
85
145
  let superclass = null;
86
146
  for (const child of node.namedChildren) {
87
147
  if (child.type === 'class_heritage') {
88
- const ext = child.namedChildren.find((c) => c.type === 'extends_clause');
89
- if (ext) {
90
- const typeNode = ext.namedChildren.find((c) => c.type === 'identifier' || c.type === 'member_expression');
91
- if (typeNode) {
92
- superclass = typeNode.text;
93
- }
148
+ // tree-sitter-javascript: class_heritage identifier (直接子节点, extends_clause 包装)
149
+ const typeNode = child.namedChildren.find((c) => c.type === 'identifier' || c.type === 'member_expression');
150
+ if (typeNode) {
151
+ superclass = typeNode.text;
94
152
  }
95
153
  }
96
154
  }
@@ -142,6 +200,44 @@ function _parseJSVariableDecl(node, ctx, parentClassName) {
142
200
  }
143
201
  function detectJSPatterns(root, lang, methods, properties, classes) {
144
202
  const patterns = [];
203
+ // 按 className 分组
204
+ const methodsByClass = new Map();
205
+ const propsByClass = new Map();
206
+ for (const m of methods) {
207
+ const k = m.className || '';
208
+ if (!methodsByClass.has(k)) {
209
+ methodsByClass.set(k, []);
210
+ }
211
+ methodsByClass.get(k).push(m);
212
+ }
213
+ for (const p of properties) {
214
+ const k = p.className || '';
215
+ if (!propsByClass.has(k)) {
216
+ propsByClass.set(k, []);
217
+ }
218
+ propsByClass.get(k).push(p);
219
+ }
220
+ for (const cls of classes) {
221
+ const clsMethods = methodsByClass.get(cls.name) || [];
222
+ const clsProps = propsByClass.get(cls.name) || [];
223
+ // ── Singleton: static getInstance() / static instance ──
224
+ const hasGetInstance = clsMethods.some((m) => m.isClassMethod && /^getInstance$|^shared$/.test(m.name));
225
+ const hasStaticInstance = clsProps.some((p) => p.isStatic && /^instance$|^shared$|^default$/.test(p.name));
226
+ if (hasGetInstance || hasStaticInstance) {
227
+ patterns.push({ type: 'singleton', className: cls.name, line: cls.line, confidence: 0.9 });
228
+ }
229
+ // ── Observer / EventEmitter ──
230
+ const emitMethods = clsMethods.filter((m) => /^on$|^emit$|^addEventListener$|^removeEventListener$|^subscribe$|^addListener$/.test(m.name));
231
+ if (emitMethods.length >= 2) {
232
+ patterns.push({ type: 'observer', className: cls.name, line: cls.line, confidence: 0.85 });
233
+ }
234
+ // ── Middleware ──
235
+ if (/middleware|interceptor/i.test(cls.name) ||
236
+ clsMethods.some((m) => /^use$|^handle$/.test(m.name) && m.isClassMethod === false)) {
237
+ patterns.push({ type: 'middleware', className: cls.name, line: cls.line, confidence: 0.8 });
238
+ }
239
+ }
240
+ // 自由函数级别的模式
145
241
  for (const m of methods) {
146
242
  if (/^use[A-Z]/.test(m.name) && !m.className) {
147
243
  patterns.push({ type: 'react-hook', methodName: m.name, line: m.line, confidence: 0.9 });
@@ -155,15 +251,6 @@ function detectJSPatterns(root, lang, methods, properties, classes) {
155
251
  confidence: 0.8,
156
252
  });
157
253
  }
158
- if (/^on[A-Z]|^emit$|^addEventListener$|^subscribe$/.test(m.name)) {
159
- patterns.push({
160
- type: 'observer',
161
- className: m.className,
162
- methodName: m.name,
163
- line: m.line,
164
- confidence: 0.7,
165
- });
166
- }
167
254
  }
168
255
  return patterns;
169
256
  }
@@ -204,7 +291,6 @@ function _maxNesting(node, depth) {
204
291
  'for_in_statement',
205
292
  'while_statement',
206
293
  'switch_statement',
207
- 'statement_block',
208
294
  ]);
209
295
  let max = depth;
210
296
  const nextDepth = NESTING_TYPES.has(node.type) ? depth + 1 : depth;
@@ -3,7 +3,7 @@
3
3
  * @description ObjC AST Walker 插件 - 从 AstAnalyzer.js 迁移
4
4
  */
5
5
  declare function walkObjC(root: any, ctx: any): void;
6
- declare function detectObjCPatterns(root: any, lang: any, methods: any, properties: any, classes: any): never[];
6
+ declare function detectObjCPatterns(root: any, lang: any, methods: any, properties: any, classes: any): any[];
7
7
  declare function getGrammar(): any;
8
8
  export declare function setGrammar(grammar: any): void;
9
9
  export declare const plugin: {
@@ -257,9 +257,86 @@ function _parseObjCProperty(node, className) {
257
257
  }
258
258
  // ── ObjC 模式检测 ──
259
259
  function detectObjCPatterns(root, lang, methods, properties, classes) {
260
- // ObjC 特有的模式检测交由 AstAnalyzer 内的通用 _detectPatterns 处理
261
- // 此函数可扩展 ObjC 特有模式(如 Category 模式)
262
- return [];
260
+ const patterns = [];
261
+ // className 分组
262
+ const methodsByClass = new Map();
263
+ const propsByClass = new Map();
264
+ for (const m of methods) {
265
+ const k = m.className || '';
266
+ if (!methodsByClass.has(k)) {
267
+ methodsByClass.set(k, []);
268
+ }
269
+ methodsByClass.get(k).push(m);
270
+ }
271
+ for (const p of properties) {
272
+ const k = p.className || '';
273
+ if (!propsByClass.has(k)) {
274
+ propsByClass.set(k, []);
275
+ }
276
+ propsByClass.get(k).push(p);
277
+ }
278
+ for (const cls of classes) {
279
+ const clsMethods = methodsByClass.get(cls.name) || [];
280
+ const clsProps = propsByClass.get(cls.name) || [];
281
+ // ── Singleton: +sharedInstance / +shared / +defaultManager ──
282
+ const singletonMethod = clsMethods.find((m) => m.isClassMethod && /^shared|^default|^current|^instance$/.test(m.name));
283
+ if (singletonMethod) {
284
+ patterns.push({
285
+ type: 'singleton',
286
+ className: cls.name,
287
+ methodName: singletonMethod.name,
288
+ line: singletonMethod.line,
289
+ confidence: 0.9,
290
+ });
291
+ }
292
+ // ── Delegate: @property (weak) id<XXXDelegate> delegate ──
293
+ for (const p of clsProps) {
294
+ if (/delegate/i.test(p.name)) {
295
+ const isWeak = (p.attributes || []).some((a) => a === 'weak');
296
+ patterns.push({
297
+ type: 'delegate',
298
+ className: cls.name,
299
+ propertyName: p.name,
300
+ isWeakRef: isWeak,
301
+ line: p.line,
302
+ confidence: 0.95,
303
+ });
304
+ }
305
+ if (/dataSource/i.test(p.name)) {
306
+ patterns.push({
307
+ type: 'delegate',
308
+ className: cls.name,
309
+ propertyName: p.name,
310
+ isWeakRef: true,
311
+ line: p.line,
312
+ confidence: 0.85,
313
+ });
314
+ }
315
+ }
316
+ // ── Factory: +classWithXxx / +xxxWithYyy (class factory methods) ──
317
+ for (const m of clsMethods) {
318
+ if (m.isClassMethod && /With[A-Z]/.test(m.name)) {
319
+ patterns.push({
320
+ type: 'factory',
321
+ className: cls.name,
322
+ methodName: m.name,
323
+ line: m.line,
324
+ confidence: 0.8,
325
+ });
326
+ }
327
+ }
328
+ // ── KVO Observer: observeValueForKeyPath / addObserver ──
329
+ const hasKVO = clsMethods.some((m) => /^observeValueForKeyPath$|^addObserver$|^removeObserver$/.test(m.name));
330
+ if (hasKVO) {
331
+ patterns.push({ type: 'observer', className: cls.name, line: cls.line, confidence: 0.85 });
332
+ }
333
+ // ── Notification Observer: NSNotificationCenter pattern ──
334
+ const hasNSNotif = clsMethods.some((m) => /notification|handleNotification|didReceiveNotification/i.test(m.name));
335
+ if (hasNSNotif) {
336
+ patterns.push({ type: 'observer', className: cls.name, line: cls.line, confidence: 0.7 });
337
+ }
338
+ }
339
+ return patterns;
263
340
  }
264
341
  // ── 工具函数 ──
265
342
  function _findIdentifier(node) {
@@ -312,7 +389,6 @@ function _maxNesting(node, depth) {
312
389
  'for_in_statement',
313
390
  'while_statement',
314
391
  'switch_statement',
315
- 'compound_statement',
316
392
  ]);
317
393
  let max = depth;
318
394
  const nextDepth = NESTING_TYPES.has(node.type) ? depth + 1 : depth;
@@ -330,7 +330,6 @@ function _maxNesting(node, depth) {
330
330
  'while_statement',
331
331
  'with_statement',
332
332
  'try_statement',
333
- 'block',
334
333
  ]);
335
334
  let max = depth;
336
335
  const nextDepth = NESTING_TYPES.has(node.type) ? depth + 1 : depth;
@@ -641,7 +641,6 @@ function _maxNesting(node, depth) {
641
641
  'while_let_expression',
642
642
  'loop_expression',
643
643
  'match_expression',
644
- 'block',
645
644
  ]);
646
645
  let max = depth;
647
646
  const nextDepth = NESTING_TYPES.has(node.type) ? depth + 1 : depth;
@@ -5,7 +5,7 @@
5
5
  * Phase 5: 新增 ImportRecord 结构化导入 + extractCallSites 调用点提取
6
6
  */
7
7
  declare function walkSwift(root: any, ctx: any): void;
8
- declare function detectSwiftPatterns(root: any, lang: any, methods: any, properties: any, classes: any): never[];
8
+ declare function detectSwiftPatterns(root: any, _lang: any, methods: any, properties: any, classes: any): any[];
9
9
  /**
10
10
  * 从 Swift AST root 提取所有调用点
11
11
  * 遍历 function_declaration 中的 function_body → call_expression
@@ -23,9 +23,29 @@ function _walkSwiftNode(node, ctx, parentClassName) {
23
23
  case 'class_declaration':
24
24
  case 'struct_declaration':
25
25
  case 'enum_declaration': {
26
+ // tree-sitter-swift maps protocol/extension to class_declaration too.
27
+ // Detect the actual keyword child and dispatch accordingly.
28
+ const keyword = _findKeywordChild(child);
29
+ if (keyword === 'protocol') {
30
+ const protoInfo = _parseSwiftProtocol(child);
31
+ ctx.protocols.push(protoInfo);
32
+ break;
33
+ }
34
+ if (keyword === 'extension') {
35
+ const extInfo = _parseSwiftExtension(child);
36
+ ctx.categories.push(extInfo);
37
+ const extBody = child.namedChildren.find((c) => c.type === 'class_body' || c.type === 'extension_body');
38
+ if (extBody) {
39
+ _walkSwiftNode(extBody, ctx, extInfo.className);
40
+ }
41
+ break;
42
+ }
26
43
  const classInfo = _parseSwiftTypeDecl(child);
27
44
  ctx.classes.push(classInfo);
28
- const body = child.namedChildren.find((c) => c.type === 'class_body' || c.type === 'struct_body' || c.type === 'enum_body');
45
+ const body = child.namedChildren.find((c) => c.type === 'class_body' ||
46
+ c.type === 'struct_body' ||
47
+ c.type === 'enum_body' ||
48
+ c.type === 'enum_class_body');
29
49
  if (body) {
30
50
  _walkSwiftNode(body, ctx, classInfo.name);
31
51
  }
@@ -66,9 +86,22 @@ function _walkSwiftNode(node, ctx, parentClassName) {
66
86
  }
67
87
  }
68
88
  }
89
+ /** Detect the actual Swift keyword from a class_declaration node's children. */
90
+ const _swiftKeywords = ['struct', 'class', 'enum', 'actor', 'protocol', 'extension'];
91
+ function _findKeywordChild(node) {
92
+ for (let i = 0; i < node.childCount; i++) {
93
+ const childType = node.child(i).type;
94
+ if (_swiftKeywords.includes(childType)) {
95
+ return childType;
96
+ }
97
+ }
98
+ return 'class';
99
+ }
69
100
  function _parseSwiftTypeDecl(node) {
70
101
  const name = node.namedChildren.find((c) => c.type === 'type_identifier' || c.type === 'simple_identifier')?.text || 'Unknown';
71
- const kind = node.type.replace('_declaration', '');
102
+ // tree-sitter-swift maps struct/class/enum/actor all to `class_declaration`.
103
+ // Detect the actual kind from the keyword child node (e.g. struct="struct").
104
+ const kind = _findKeywordChild(node);
72
105
  const _superclass = null;
73
106
  const protocols = [];
74
107
  for (const child of node.namedChildren) {
@@ -132,7 +165,7 @@ function _parseSwiftExtension(node) {
132
165
  }
133
166
  }
134
167
  const methods = [];
135
- const body = node.namedChildren.find((c) => c.type === 'extension_body');
168
+ const body = node.namedChildren.find((c) => c.type === 'extension_body' || c.type === 'class_body');
136
169
  if (body) {
137
170
  for (const child of body.namedChildren) {
138
171
  if (child.type === 'function_declaration') {
@@ -196,8 +229,150 @@ function _parseSwiftProperty(node, className) {
196
229
  };
197
230
  }
198
231
  // ── Swift 模式检测 ──
199
- function detectSwiftPatterns(root, lang, methods, properties, classes) {
200
- return [];
232
+ function detectSwiftPatterns(root, _lang, methods, properties, classes) {
233
+ const patterns = [];
234
+ // Index properties by className for quick lookup
235
+ const propsByClass = new Map();
236
+ for (const p of properties) {
237
+ if (p.className) {
238
+ const arr = propsByClass.get(p.className) || [];
239
+ arr.push(p);
240
+ propsByClass.set(p.className, arr);
241
+ }
242
+ }
243
+ // Index methods by className
244
+ const methodsByClass = new Map();
245
+ for (const m of methods) {
246
+ if (m.className && m.kind === 'definition') {
247
+ const arr = methodsByClass.get(m.className) || [];
248
+ arr.push(m);
249
+ methodsByClass.set(m.className, arr);
250
+ }
251
+ }
252
+ for (const cls of classes) {
253
+ const kind = cls.kind;
254
+ const className = cls.name;
255
+ const clsProps = propsByClass.get(className) || [];
256
+ const clsMethods = methodsByClass.get(className) || [];
257
+ // ── 1. Singleton: `static let shared = ...` 属性 ──
258
+ const sharedProp = clsProps.find((p) => p.isStatic &&
259
+ (p.isConstant || p.isConst) &&
260
+ /^shared$|^default$|^instance$|^current$/.test(p.name));
261
+ if (sharedProp) {
262
+ patterns.push({
263
+ type: 'singleton',
264
+ className,
265
+ propertyName: sharedProp.name,
266
+ line: sharedProp.line,
267
+ confidence: 0.95,
268
+ });
269
+ }
270
+ // ── 2. Delegate: `weak var delegate` 属性或 protocol 命名以 Delegate/DataSource 结尾 ──
271
+ for (const p of clsProps) {
272
+ if (/delegate/i.test(p.name) && !p.name.startsWith('_')) {
273
+ patterns.push({
274
+ type: 'delegate',
275
+ className,
276
+ propertyName: p.name,
277
+ isWeakRef: /weak/.test(`${(p.attributes || []).join(' ')} ${p.modifiers || ''}`),
278
+ line: p.line,
279
+ confidence: 0.95,
280
+ });
281
+ }
282
+ }
283
+ // ── 3. Factory: `static func make/create/from/build` ──
284
+ for (const m of clsMethods) {
285
+ if (m.isClassMethod && /^make|^create|^from|^build/.test(m.name)) {
286
+ patterns.push({
287
+ type: 'factory',
288
+ className,
289
+ methodName: m.name,
290
+ line: m.line,
291
+ confidence: 0.85,
292
+ });
293
+ }
294
+ }
295
+ // ── 4. Actor: Swift concurrency primitive ──
296
+ if (kind === 'actor') {
297
+ patterns.push({
298
+ type: 'actor',
299
+ className,
300
+ line: cls.line,
301
+ confidence: 0.95,
302
+ });
303
+ }
304
+ // ── 5. Value type (struct): immutable-by-default semantics ──
305
+ if (kind === 'struct' && !className.endsWith('Error')) {
306
+ // Only flag structs with methods (not pure data containers)
307
+ if (clsMethods.length > 0) {
308
+ patterns.push({
309
+ type: 'value-type',
310
+ className,
311
+ methodCount: clsMethods.length,
312
+ line: cls.line,
313
+ confidence: 0.9,
314
+ });
315
+ }
316
+ }
317
+ // ── 6. Protocol-oriented default implementation ──
318
+ // (detected via categories/extensions that match a protocol name)
319
+ // Handled below after the class loop.
320
+ // ── 7. ViewModel: class name ends with ViewModel ──
321
+ if (/ViewModel$/.test(className)) {
322
+ patterns.push({
323
+ type: 'viewmodel',
324
+ className,
325
+ line: cls.line,
326
+ confidence: 0.9,
327
+ });
328
+ }
329
+ // ── 8. Coordinator pattern ──
330
+ if (/Coordinator$/.test(className)) {
331
+ patterns.push({
332
+ type: 'coordinator',
333
+ className,
334
+ line: cls.line,
335
+ confidence: 0.85,
336
+ });
337
+ }
338
+ // ── 9. Error type: enum conforming to Error ──
339
+ if (kind === 'enum') {
340
+ const conformsError = (cls.protocols || []).some((p) => p === 'Error' || p === 'LocalizedError');
341
+ if (conformsError || className.endsWith('Error')) {
342
+ patterns.push({
343
+ type: 'error-type',
344
+ className,
345
+ line: cls.line,
346
+ confidence: 0.9,
347
+ });
348
+ }
349
+ }
350
+ // ── 10. Middleware / Interceptor pattern ──
351
+ if (/Middleware$|Interceptor$/.test(className)) {
352
+ patterns.push({
353
+ type: 'middleware',
354
+ className,
355
+ line: cls.line,
356
+ confidence: 0.85,
357
+ });
358
+ }
359
+ }
360
+ // ── 11. Observer: methods matching didChange/willChange/observe/subscribe ──
361
+ for (const m of methods) {
362
+ if (m.kind !== 'definition') {
363
+ continue;
364
+ }
365
+ if (/^didChange|^willChange|^observe|^addObserver|^subscribe|^on[A-Z]/.test(m.name)) {
366
+ patterns.push({
367
+ type: 'observer',
368
+ className: m.className,
369
+ methodName: m.name,
370
+ line: m.line,
371
+ confidence: 0.7,
372
+ });
373
+ }
374
+ }
375
+ return patterns;
201
376
  }
202
377
  // ── 工具函数 ──
203
378
  function _findIdentifier(node) {
@@ -249,7 +424,6 @@ function _maxNesting(node, depth) {
249
424
  'for_in_statement',
250
425
  'while_statement',
251
426
  'switch_statement',
252
- 'compound_statement',
253
427
  ]);
254
428
  let max = depth;
255
429
  const nextDepth = NESTING_TYPES.has(node.type) ? depth + 1 : depth;
@@ -282,7 +456,10 @@ function _collectSwiftScopes(root) {
282
456
  child.type === 'struct_declaration' ||
283
457
  child.type === 'enum_declaration') {
284
458
  const name = child.namedChildren.find((c) => c.type === 'type_identifier' || c.type === 'simple_identifier')?.text;
285
- const body = child.namedChildren.find((c) => c.type === 'class_body' || c.type === 'struct_body' || c.type === 'enum_body');
459
+ const body = child.namedChildren.find((c) => c.type === 'class_body' ||
460
+ c.type === 'struct_body' ||
461
+ c.type === 'enum_body' ||
462
+ c.type === 'enum_class_body');
286
463
  if (body) {
287
464
  visit(body, name || className);
288
465
  }
@@ -663,7 +663,6 @@ function _maxNesting(node, depth) {
663
663
  'for_in_statement',
664
664
  'while_statement',
665
665
  'switch_statement',
666
- 'statement_block',
667
666
  ]);
668
667
  let max = depth;
669
668
  const nextDepth = NESTING_TYPES.has(node.type) ? depth + 1 : depth;
@@ -27,10 +27,23 @@ export declare function isGeoOrProviderError(err: unknown): boolean;
27
27
  * 当主 provider 调用失败(地理限制等)时自动切换到备选 provider
28
28
  */
29
29
  export declare function getProviderWithFallback(): Promise<ClaudeProvider | GoogleGeminiProvider | MockProvider | OpenAiProvider | null>;
30
+ /**
31
+ * 创建独立的 Embedding Provider
32
+ *
33
+ * 当 ASD_EMBED_PROVIDER 被设置时,创建一个专用于 embedding 的 provider 实例,
34
+ * 使 embedding 和 LLM 生成可以使用不同的提供商/模型。
35
+ *
36
+ * 典型场景:LLM 用 Google Gemini,Embedding 用本地 Ollama + qwen3-embedding
37
+ *
38
+ * @returns 独立的 embed provider,或 null(未配置时)
39
+ */
40
+ export declare function createEmbedProvider(): ReturnType<typeof createProvider> | null;
30
41
  /** 获取当前 AI 配置信息(同步,用于 UI 展示) */
31
42
  export declare function getAiConfigInfo(): {
32
43
  provider: string;
33
44
  model: string;
45
+ embedProvider: string;
46
+ embedModel: string;
34
47
  hasKey: boolean;
35
48
  keys: {
36
49
  google: boolean;
@@ -46,6 +59,7 @@ export { MockProvider } from './providers/MockProvider.js';
46
59
  export { OpenAiProvider } from './providers/OpenAiProvider.js';
47
60
  declare const _default: {
48
61
  createProvider: typeof createProvider;
62
+ createEmbedProvider: typeof createEmbedProvider;
49
63
  autoDetectProvider: typeof autoDetectProvider;
50
64
  getAiConfigInfo: typeof getAiConfigInfo;
51
65
  getProviderWithFallback: typeof getProviderWithFallback;