autosnippet 3.0.13 → 3.1.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/bin/api-server.js +2 -0
- package/bin/cli.js +24 -19
- package/config/default.json +1 -1
- package/lib/bootstrap.js +4 -4
- package/lib/cli/SetupService.js +29 -29
- package/lib/cli/UpgradeService.js +3 -2
- package/lib/core/AstAnalyzer.js +1 -1
- package/lib/core/ast/ensure-grammars.js +1 -1
- package/lib/core/ast/index.js +62 -11
- package/lib/core/ast/lang-dart.js +27 -21
- package/lib/core/ast/lang-go.js +6 -20
- package/lib/core/ast/lang-rust.js +53 -28
- package/lib/core/ast/parser-init.js +9 -5
- package/lib/core/discovery/DartDiscoverer.js +4 -10
- package/lib/core/discovery/GoDiscoverer.js +45 -25
- package/lib/core/discovery/NodeDiscoverer.js +1 -3
- package/lib/core/discovery/PythonDiscoverer.js +7 -1
- package/lib/core/discovery/RustDiscoverer.js +111 -38
- package/lib/core/discovery/index.js +2 -2
- package/lib/core/enhancement/django-enhancement.js +10 -4
- package/lib/core/enhancement/fastapi-enhancement.js +16 -9
- package/lib/core/enhancement/go-grpc-enhancement.js +2 -1
- package/lib/core/enhancement/go-web-enhancement.js +3 -6
- package/lib/core/enhancement/ml-enhancement.js +6 -3
- package/lib/core/enhancement/nextjs-enhancement.js +17 -7
- package/lib/core/enhancement/node-server-enhancement.js +4 -2
- package/lib/core/enhancement/react-enhancement.js +6 -3
- package/lib/core/enhancement/rust-tokio-enhancement.js +6 -2
- package/lib/core/enhancement/rust-web-enhancement.js +13 -7
- package/lib/core/enhancement/vue-enhancement.js +10 -5
- package/lib/external/ai/AiFactory.js +3 -1
- package/lib/external/ai/AiProvider.js +3 -1
- package/lib/external/mcp/McpServer.js +2 -0
- package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +1 -2
- package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +7 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +55 -26
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +8 -8
- package/lib/external/mcp/handlers/bootstrap/refine.js +3 -1
- package/lib/external/mcp/handlers/bootstrap.js +4 -10
- package/lib/external/mcp/handlers/browse.js +6 -2
- package/lib/external/mcp/handlers/guard.js +6 -2
- package/lib/external/mcp/handlers/skill.js +6 -2
- package/lib/http/HttpServer.js +1 -1
- package/lib/http/routes/candidates.js +3 -1
- package/lib/http/routes/extract.js +4 -5
- package/lib/http/routes/guardRules.js +1 -1
- package/lib/http/routes/modules.js +9 -3
- package/lib/http/routes/skills.js +54 -6
- package/lib/http/routes/violations.js +4 -3
- package/lib/infrastructure/external/ClipboardManager.js +24 -7
- package/lib/infrastructure/external/NativeUi.js +3 -1
- package/lib/infrastructure/external/OpenBrowser.js +1 -0
- package/lib/infrastructure/external/XcodeAutomation.js +5 -5
- package/lib/infrastructure/vector/IndexingPipeline.js +14 -5
- package/lib/injection/ServiceContainer.js +34 -11
- package/lib/platform/ios/index.js +20 -25
- package/lib/platform/ios/routes/spm.js +6 -3
- package/lib/platform/ios/snippet/PlaceholderConverter.js +6 -2
- package/lib/platform/ios/snippet/XcodeCodec.js +4 -2
- package/lib/platform/ios/spm/SpmDiscoverer.js +1 -1
- package/lib/platform/ios/spm/SpmService.js +3 -1
- package/lib/platform/ios/xcode/XcodeIntegration.js +10 -12
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +6 -1
- package/lib/service/automation/FileWatcher.js +1 -3
- package/lib/service/automation/handlers/CreateHandler.js +3 -5
- package/lib/service/automation/handlers/GuardHandler.js +11 -32
- package/lib/service/automation/handlers/SearchHandler.js +9 -9
- package/lib/service/chat/CandidateGuardrail.js +11 -6
- package/lib/service/chat/ChatAgent.js +31 -22
- package/lib/service/chat/HandoffProtocol.js +5 -2
- package/lib/service/chat/tools/composite.js +3 -2
- package/lib/service/chat/tools/index.js +60 -71
- package/lib/service/chat/tools/infrastructure.js +9 -4
- package/lib/service/chat/tools/lifecycle.js +22 -5
- package/lib/service/chat/tools/project-access.js +5 -9
- package/lib/service/chat/tools.js +1 -2
- package/lib/service/cursor/AgentInstructionsGenerator.js +33 -15
- package/lib/service/cursor/CursorDeliveryPipeline.js +2 -1
- package/lib/service/cursor/KnowledgeCompressor.js +16 -7
- package/lib/service/guard/ComplianceReporter.js +5 -2
- package/lib/service/guard/GuardCheckEngine.js +53 -26
- package/lib/service/guard/GuardCodeChecks.js +217 -188
- package/lib/service/guard/GuardCrossFileChecks.js +203 -184
- package/lib/service/guard/GuardPatternUtils.js +17 -10
- package/lib/service/module/ModuleService.js +180 -56
- package/lib/service/recipe/RecipeCandidateValidator.js +11 -8
- package/lib/service/snippet/SnippetFactory.js +3 -3
- package/lib/service/snippet/SnippetInstaller.js +35 -11
- package/lib/service/snippet/codecs/VSCodeCodec.js +2 -2
- package/lib/service/wiki/WikiGenerator.js +67 -40
- package/lib/service/wiki/WikiRenderers.js +105 -80
- package/lib/service/wiki/WikiUtils.js +217 -80
- package/lib/shared/LanguageService.js +111 -53
- package/lib/shared/PathGuard.js +0 -8
- package/package.json +3 -9
- package/scripts/bench-real-projects.mjs +29 -29
- package/scripts/generate-recipe-drafts.js +17 -27
- package/scripts/init-snippets.js +43 -24
- package/scripts/install-vscode-copilot.js +3 -19
- package/scripts/setup-mcp-config.js +0 -4
|
@@ -85,12 +85,13 @@ function _walkNode(node, ctx) {
|
|
|
85
85
|
function _parseUseDecl(node, ctx) {
|
|
86
86
|
// 提取 use 路径文本
|
|
87
87
|
const argNode = node.namedChildren.find(
|
|
88
|
-
(c) =>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
(c) =>
|
|
89
|
+
c.type === 'use_wildcard' ||
|
|
90
|
+
c.type === 'use_list' ||
|
|
91
|
+
c.type === 'use_as_clause' ||
|
|
92
|
+
c.type === 'scoped_identifier' ||
|
|
93
|
+
c.type === 'identifier' ||
|
|
94
|
+
c.type === 'scoped_use_list'
|
|
94
95
|
);
|
|
95
96
|
if (argNode) {
|
|
96
97
|
ctx.imports.push(argNode.text);
|
|
@@ -129,7 +130,9 @@ function _parseStruct(node, ctx) {
|
|
|
129
130
|
if (fieldList) {
|
|
130
131
|
for (let i = 0; i < fieldList.namedChildCount; i++) {
|
|
131
132
|
const field = fieldList.namedChild(i);
|
|
132
|
-
if (field.type !== 'field_declaration')
|
|
133
|
+
if (field.type !== 'field_declaration') {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
133
136
|
const fieldId = field.namedChildren.find((c) => c.type === 'field_identifier');
|
|
134
137
|
if (fieldId) {
|
|
135
138
|
const isPublic = _hasPubVisibility(field);
|
|
@@ -151,10 +154,17 @@ function _parseStruct(node, ctx) {
|
|
|
151
154
|
for (let i = 0; i < orderedFields.namedChildCount; i++) {
|
|
152
155
|
const child = orderedFields.namedChild(i);
|
|
153
156
|
// Skip visibility markers
|
|
154
|
-
if (child.type === 'visibility_modifier')
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
if (child.type === 'visibility_modifier') {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (
|
|
161
|
+
child.type.includes('type') ||
|
|
162
|
+
child.type === 'primitive_type' ||
|
|
163
|
+
child.type === 'scoped_type_identifier' ||
|
|
164
|
+
child.type === 'type_identifier' ||
|
|
165
|
+
child.type === 'generic_type' ||
|
|
166
|
+
child.type === 'reference_type'
|
|
167
|
+
) {
|
|
158
168
|
fields.push(`${idx}`);
|
|
159
169
|
idx++;
|
|
160
170
|
}
|
|
@@ -221,8 +231,11 @@ function _parseTrait(node, ctx) {
|
|
|
221
231
|
if (bounds) {
|
|
222
232
|
for (let i = 0; i < bounds.namedChildCount; i++) {
|
|
223
233
|
const bound = bounds.namedChild(i);
|
|
224
|
-
if (
|
|
225
|
-
|
|
234
|
+
if (
|
|
235
|
+
bound.type === 'type_identifier' ||
|
|
236
|
+
bound.type === 'scoped_type_identifier' ||
|
|
237
|
+
bound.type === 'generic_type'
|
|
238
|
+
) {
|
|
226
239
|
superTraits.push(bound.text);
|
|
227
240
|
}
|
|
228
241
|
}
|
|
@@ -259,8 +272,10 @@ function _parseImpl(node, ctx) {
|
|
|
259
272
|
let traitName = null;
|
|
260
273
|
|
|
261
274
|
const typeIdNodes = node.namedChildren.filter(
|
|
262
|
-
(c) =>
|
|
263
|
-
|
|
275
|
+
(c) =>
|
|
276
|
+
c.type === 'type_identifier' ||
|
|
277
|
+
c.type === 'scoped_type_identifier' ||
|
|
278
|
+
c.type === 'generic_type'
|
|
264
279
|
);
|
|
265
280
|
|
|
266
281
|
// 检查是否有 "for" — trait impl
|
|
@@ -274,7 +289,9 @@ function _parseImpl(node, ctx) {
|
|
|
274
289
|
}
|
|
275
290
|
|
|
276
291
|
const body = node.namedChildren.find((c) => c.type === 'declaration_list');
|
|
277
|
-
if (!body || !selfType)
|
|
292
|
+
if (!body || !selfType) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
278
295
|
|
|
279
296
|
for (let i = 0; i < body.namedChildCount; i++) {
|
|
280
297
|
const item = body.namedChild(i);
|
|
@@ -290,10 +307,14 @@ function _parseImpl(node, ctx) {
|
|
|
290
307
|
function _parseImplMethod(node, selfType, traitName) {
|
|
291
308
|
const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
|
|
292
309
|
const name = nameNode?.text;
|
|
293
|
-
if (!name)
|
|
310
|
+
if (!name) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
294
313
|
|
|
295
314
|
const params = node.namedChildren.find((c) => c.type === 'parameters');
|
|
296
|
-
const { paramCount, hasSelfParam } = params
|
|
315
|
+
const { paramCount, hasSelfParam } = params
|
|
316
|
+
? _countRustParams(params)
|
|
317
|
+
: { paramCount: 0, hasSelfParam: false };
|
|
297
318
|
|
|
298
319
|
const isPublic = _hasPubVisibility(node);
|
|
299
320
|
const isAsync = node.children?.some((c) => c.text === 'async') || false;
|
|
@@ -324,7 +345,9 @@ function _parseImplMethod(node, selfType, traitName) {
|
|
|
324
345
|
function _parseFunctionItem(node) {
|
|
325
346
|
const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
|
|
326
347
|
const name = nameNode?.text;
|
|
327
|
-
if (!name)
|
|
348
|
+
if (!name) {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
328
351
|
|
|
329
352
|
const params = node.namedChildren.find((c) => c.type === 'parameters');
|
|
330
353
|
const { paramCount } = params ? _countRustParams(params) : { paramCount: 0 };
|
|
@@ -418,9 +441,7 @@ function detectRustPatterns(root, lang, methods, properties, classes) {
|
|
|
418
441
|
// Builder pattern: struct 有 builder() 或一系列链式 with_*/set_* 方法
|
|
419
442
|
for (const [typeName, methodList] of Object.entries(typeMethodMap)) {
|
|
420
443
|
const hasBuilder = methodList.some((m) => m.name === 'builder' || m.name === 'build');
|
|
421
|
-
const chainMethods = methodList.filter((m) =>
|
|
422
|
-
/^(?:with_|set_|add_)/.test(m.name)
|
|
423
|
-
);
|
|
444
|
+
const chainMethods = methodList.filter((m) => /^(?:with_|set_|add_)/.test(m.name));
|
|
424
445
|
if (hasBuilder || chainMethods.length >= 3) {
|
|
425
446
|
patterns.push({
|
|
426
447
|
type: 'builder',
|
|
@@ -524,7 +545,9 @@ function _detectAsync(root, patterns) {
|
|
|
524
545
|
function walk(node) {
|
|
525
546
|
if (node.type === 'function_item' || node.type === 'function_signature_item') {
|
|
526
547
|
const isAsync = node.children?.some((c) => c.text === 'async');
|
|
527
|
-
if (isAsync)
|
|
548
|
+
if (isAsync) {
|
|
549
|
+
asyncFnCount++;
|
|
550
|
+
}
|
|
528
551
|
}
|
|
529
552
|
if (node.type === 'await_expression') {
|
|
530
553
|
awaitCount++;
|
|
@@ -575,13 +598,17 @@ function _extractDerives(node) {
|
|
|
575
598
|
const siblings = [];
|
|
576
599
|
for (let i = 0; i < node.parent.namedChildCount; i++) {
|
|
577
600
|
const sib = node.parent.namedChild(i);
|
|
578
|
-
if (sib === node)
|
|
601
|
+
if (sib === node) {
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
579
604
|
siblings.push(sib);
|
|
580
605
|
}
|
|
581
606
|
// Collect attribute_item nodes immediately before this node
|
|
582
607
|
for (let i = siblings.length - 1; i >= 0; i--) {
|
|
583
608
|
const sib = siblings[i];
|
|
584
|
-
if (sib.type !== 'attribute_item')
|
|
609
|
+
if (sib.type !== 'attribute_item') {
|
|
610
|
+
break;
|
|
611
|
+
}
|
|
585
612
|
const text = sib.text;
|
|
586
613
|
const deriveMatch = text.match(/#\[derive\(([^)]+)\)\]/);
|
|
587
614
|
if (deriveMatch) {
|
|
@@ -596,9 +623,7 @@ function _extractDerives(node) {
|
|
|
596
623
|
// ── Helper: Visibility ──────────────────────────────────────
|
|
597
624
|
|
|
598
625
|
function _hasPubVisibility(node) {
|
|
599
|
-
return node.children?.some(
|
|
600
|
-
(c) => c.type === 'visibility_modifier' || c.text === 'pub'
|
|
601
|
-
) || false;
|
|
626
|
+
return node.children?.some((c) => c.type === 'visibility_modifier' || c.text === 'pub') || false;
|
|
602
627
|
}
|
|
603
628
|
|
|
604
629
|
// ── Helper: Count Parameters ────────────────────────────────
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* 下游 analyzeFile / findCallExpressions 等保持同步调用。
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { fileURLToPath } from 'node:url';
|
|
15
|
-
import path from 'node:path';
|
|
16
14
|
import { readFile } from 'node:fs/promises';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
17
|
|
|
18
18
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
19
|
|
|
@@ -30,7 +30,9 @@ let _initialized = false;
|
|
|
30
30
|
* 幂等 — 多次调用只执行一次
|
|
31
31
|
*/
|
|
32
32
|
export async function initParser() {
|
|
33
|
-
if (_initialized)
|
|
33
|
+
if (_initialized) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
34
36
|
|
|
35
37
|
try {
|
|
36
38
|
// web-tree-sitter ESM: 导出 { Parser, Language, ... } 命名空间
|
|
@@ -40,7 +42,7 @@ export async function initParser() {
|
|
|
40
42
|
Parser = typeof _namespace === 'function' ? _namespace : _namespace.Parser;
|
|
41
43
|
await Parser.init();
|
|
42
44
|
_initialized = true;
|
|
43
|
-
} catch
|
|
45
|
+
} catch {
|
|
44
46
|
// web-tree-sitter 不可用时优雅降级
|
|
45
47
|
Parser = null;
|
|
46
48
|
_initialized = false;
|
|
@@ -68,7 +70,9 @@ export function isParserReady() {
|
|
|
68
70
|
* @returns {Promise<object|null>} Language 对象,失败返回 null
|
|
69
71
|
*/
|
|
70
72
|
export async function loadLanguageWasm(wasmFileName) {
|
|
71
|
-
if (!_initialized || !_namespace)
|
|
73
|
+
if (!_initialized || !_namespace) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
72
76
|
|
|
73
77
|
const wasmPath = path.join(GRAMMARS_DIR, wasmFileName);
|
|
74
78
|
try {
|
|
@@ -308,7 +308,7 @@ export class DartDiscoverer extends ProjectDiscoverer {
|
|
|
308
308
|
|
|
309
309
|
try {
|
|
310
310
|
const content = readFileSync(melosPath, 'utf8');
|
|
311
|
-
const
|
|
311
|
+
const _melos = this.#parseSimpleYaml(content);
|
|
312
312
|
|
|
313
313
|
// Melos packages 字段(简化处理: 扫描 packages/ 目录)
|
|
314
314
|
const packagesDir = join(projectRoot, 'packages');
|
|
@@ -370,9 +370,7 @@ export class DartDiscoverer extends ProjectDiscoverer {
|
|
|
370
370
|
return;
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
-
const nodeSet = new Set(
|
|
374
|
-
this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id))
|
|
375
|
-
);
|
|
373
|
+
const nodeSet = new Set(this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id)));
|
|
376
374
|
const rootNode = this.#packageName;
|
|
377
375
|
|
|
378
376
|
const addDep = (name, isDev) => {
|
|
@@ -420,9 +418,7 @@ export class DartDiscoverer extends ProjectDiscoverer {
|
|
|
420
418
|
return;
|
|
421
419
|
}
|
|
422
420
|
|
|
423
|
-
const nodeSet = new Set(
|
|
424
|
-
this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id))
|
|
425
|
-
);
|
|
421
|
+
const nodeSet = new Set(this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id)));
|
|
426
422
|
const edgeSet = new Set();
|
|
427
423
|
|
|
428
424
|
// 收集 lib/ 下的子目录作为内部模块
|
|
@@ -457,9 +453,7 @@ export class DartDiscoverer extends ProjectDiscoverer {
|
|
|
457
453
|
const fromModule = relDir ? `lib/${relDir.split('/')[0]}` : this.#packageName;
|
|
458
454
|
|
|
459
455
|
// 匹配 import 'package:xxx/yyy.dart'
|
|
460
|
-
const imports = content.matchAll(
|
|
461
|
-
/import\s+['"]package:(\w+)\/([^'"]+)['"]/g
|
|
462
|
-
);
|
|
456
|
+
const imports = content.matchAll(/import\s+['"]package:(\w+)\/([^'"]+)['"]/g);
|
|
463
457
|
for (const m of imports) {
|
|
464
458
|
const pkg = m[1];
|
|
465
459
|
const filePath = m[2];
|
|
@@ -191,9 +191,7 @@ export class GoDiscoverer extends ProjectDiscoverer {
|
|
|
191
191
|
if (existsSync(dirPath)) {
|
|
192
192
|
try {
|
|
193
193
|
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
194
|
-
const hasGoFiles = entries.some(
|
|
195
|
-
(e) => e.isFile() && e.name.endsWith('.go')
|
|
196
|
-
);
|
|
194
|
+
const hasGoFiles = entries.some((e) => e.isFile() && e.name.endsWith('.go'));
|
|
197
195
|
const hasGoSubDirs = entries.some(
|
|
198
196
|
(e) => e.isDirectory() && !e.name.startsWith('.') && !EXCLUDE_DIRS.has(e.name)
|
|
199
197
|
);
|
|
@@ -313,30 +311,38 @@ export class GoDiscoverer extends ProjectDiscoverer {
|
|
|
313
311
|
* 发现内部子包——目录中包含 .go 文件即为一个 Go package
|
|
314
312
|
*/
|
|
315
313
|
#discoverInternalPackages(projectRoot) {
|
|
316
|
-
const nodeSet = new Set(this.#depGraph.nodes.map(n => (typeof n === 'string' ? n : n.id)));
|
|
314
|
+
const nodeSet = new Set(this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id)));
|
|
317
315
|
|
|
318
316
|
const walk = (dir, relPath, depth) => {
|
|
319
|
-
if (depth > 6)
|
|
317
|
+
if (depth > 6) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
320
|
try {
|
|
321
321
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
322
322
|
for (const entry of entries) {
|
|
323
|
-
if (!entry.isDirectory() || entry.name.startsWith('.') || EXCLUDE_DIRS.has(entry.name))
|
|
323
|
+
if (!entry.isDirectory() || entry.name.startsWith('.') || EXCLUDE_DIRS.has(entry.name)) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
324
326
|
const subDir = join(dir, entry.name);
|
|
325
327
|
const subRel = relPath ? `${relPath}/${entry.name}` : entry.name;
|
|
326
328
|
|
|
327
329
|
// 检查目录中是否包含 .go 文件
|
|
328
330
|
try {
|
|
329
331
|
const subEntries = readdirSync(subDir);
|
|
330
|
-
const hasGoFiles = subEntries.some(e => e.endsWith('.go'));
|
|
332
|
+
const hasGoFiles = subEntries.some((e) => e.endsWith('.go'));
|
|
331
333
|
if (hasGoFiles && !nodeSet.has(subRel)) {
|
|
332
334
|
this.#depGraph.nodes.push({ id: subRel, label: subRel, type: 'internal' });
|
|
333
335
|
nodeSet.add(subRel);
|
|
334
336
|
}
|
|
335
|
-
} catch {
|
|
337
|
+
} catch {
|
|
338
|
+
/* skip */
|
|
339
|
+
}
|
|
336
340
|
|
|
337
341
|
walk(subDir, subRel, depth + 1);
|
|
338
342
|
}
|
|
339
|
-
} catch {
|
|
343
|
+
} catch {
|
|
344
|
+
/* skip */
|
|
345
|
+
}
|
|
340
346
|
};
|
|
341
347
|
|
|
342
348
|
walk(projectRoot, '', 0);
|
|
@@ -351,10 +357,11 @@ export class GoDiscoverer extends ProjectDiscoverer {
|
|
|
351
357
|
return;
|
|
352
358
|
}
|
|
353
359
|
|
|
354
|
-
const nodeSet = new Set(this.#depGraph.nodes.map(n => (typeof n === 'string' ? n : n.id)));
|
|
355
|
-
const rootNode =
|
|
356
|
-
|
|
357
|
-
|
|
360
|
+
const nodeSet = new Set(this.#depGraph.nodes.map((n) => (typeof n === 'string' ? n : n.id)));
|
|
361
|
+
const rootNode =
|
|
362
|
+
typeof this.#depGraph.nodes[0] === 'string'
|
|
363
|
+
? this.#depGraph.nodes[0]
|
|
364
|
+
: this.#depGraph.nodes[0]?.id || 'root';
|
|
358
365
|
|
|
359
366
|
const addExtDep = (fullPath, indirect) => {
|
|
360
367
|
const shortName = fullPath.split('/').pop();
|
|
@@ -411,18 +418,21 @@ export class GoDiscoverer extends ProjectDiscoverer {
|
|
|
411
418
|
* 解析内部 Go import 语句,构建子包间依赖关系
|
|
412
419
|
*/
|
|
413
420
|
#parseInternalImports(projectRoot) {
|
|
414
|
-
if (!this.#modulePath)
|
|
421
|
+
if (!this.#modulePath) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
415
424
|
|
|
416
425
|
const internalNodes = new Set(
|
|
417
426
|
this.#depGraph.nodes
|
|
418
|
-
.filter(n =>
|
|
419
|
-
.map(n => n.id)
|
|
427
|
+
.filter((n) => typeof n === 'object' && n.type === 'internal')
|
|
428
|
+
.map((n) => n.id)
|
|
420
429
|
);
|
|
421
430
|
|
|
422
431
|
// 也包含根包
|
|
423
|
-
const rootNodeId =
|
|
424
|
-
|
|
425
|
-
|
|
432
|
+
const rootNodeId =
|
|
433
|
+
typeof this.#depGraph.nodes[0] === 'string'
|
|
434
|
+
? this.#depGraph.nodes[0]
|
|
435
|
+
: this.#depGraph.nodes[0]?.id;
|
|
426
436
|
|
|
427
437
|
const edgeSet = new Set();
|
|
428
438
|
|
|
@@ -430,7 +440,9 @@ export class GoDiscoverer extends ProjectDiscoverer {
|
|
|
430
440
|
try {
|
|
431
441
|
const entries = readdirSync(dir);
|
|
432
442
|
for (const entry of entries) {
|
|
433
|
-
if (!entry.endsWith('.go'))
|
|
443
|
+
if (!entry.endsWith('.go')) {
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
434
446
|
try {
|
|
435
447
|
const content = readFileSync(join(dir, entry), 'utf8');
|
|
436
448
|
// 匹配 import 块和单行 import
|
|
@@ -445,9 +457,13 @@ export class GoDiscoverer extends ProjectDiscoverer {
|
|
|
445
457
|
for (const m of singleImports) {
|
|
446
458
|
this.#matchInternalImport(`"${m[1]}"`, pkgId, rootNodeId, internalNodes, edgeSet);
|
|
447
459
|
}
|
|
448
|
-
} catch {
|
|
460
|
+
} catch {
|
|
461
|
+
/* skip */
|
|
462
|
+
}
|
|
449
463
|
}
|
|
450
|
-
} catch {
|
|
464
|
+
} catch {
|
|
465
|
+
/* skip */
|
|
466
|
+
}
|
|
451
467
|
};
|
|
452
468
|
|
|
453
469
|
// 扫描根包
|
|
@@ -464,10 +480,14 @@ export class GoDiscoverer extends ProjectDiscoverer {
|
|
|
464
480
|
*/
|
|
465
481
|
#matchInternalImport(line, fromPkgId, rootNodeId, internalNodes, edgeSet) {
|
|
466
482
|
const match = line.match(/"([^"]+)"/);
|
|
467
|
-
if (!match)
|
|
483
|
+
if (!match) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
468
486
|
|
|
469
487
|
const importPath = match[1];
|
|
470
|
-
if (!importPath.startsWith(this.#modulePath
|
|
488
|
+
if (!importPath.startsWith(`${this.#modulePath}/`)) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
471
491
|
|
|
472
492
|
// 去掉 module path 前缀得到相对路径
|
|
473
493
|
const relImport = importPath.slice(this.#modulePath.length + 1);
|
|
@@ -479,7 +499,7 @@ export class GoDiscoverer extends ProjectDiscoverer {
|
|
|
479
499
|
} else {
|
|
480
500
|
// 可能是子路径,匹配最近的已知包
|
|
481
501
|
for (const nodeId of internalNodes) {
|
|
482
|
-
if (relImport.startsWith(nodeId
|
|
502
|
+
if (relImport.startsWith(`${nodeId}/`) || relImport === nodeId) {
|
|
483
503
|
targetId = nodeId;
|
|
484
504
|
break;
|
|
485
505
|
}
|
|
@@ -92,9 +92,7 @@ export class NodeDiscoverer extends ProjectDiscoverer {
|
|
|
92
92
|
);
|
|
93
93
|
} else {
|
|
94
94
|
confidence *= 0.05;
|
|
95
|
-
reasons.push(
|
|
96
|
-
`${marker.lang} marker found — confidence heavily reduced`
|
|
97
|
-
);
|
|
95
|
+
reasons.push(`${marker.lang} marker found — confidence heavily reduced`);
|
|
98
96
|
}
|
|
99
97
|
break;
|
|
100
98
|
}
|
|
@@ -214,7 +214,13 @@ export class PythonDiscoverer extends ProjectDiscoverer {
|
|
|
214
214
|
if (deps.has('fastapi')) {
|
|
215
215
|
return 'fastapi';
|
|
216
216
|
}
|
|
217
|
-
if (
|
|
217
|
+
if (
|
|
218
|
+
deps.has('langchain') ||
|
|
219
|
+
deps.has('langchain-core') ||
|
|
220
|
+
deps.has('langgraph') ||
|
|
221
|
+
deps.has('llama-index') ||
|
|
222
|
+
deps.has('llama_index')
|
|
223
|
+
) {
|
|
218
224
|
return 'langchain';
|
|
219
225
|
}
|
|
220
226
|
if (deps.has('torch') || deps.has('tensorflow')) {
|