bluera-knowledge 0.9.32 → 0.9.34
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/.claude/hooks/post-edit-check.sh +5 -3
- package/.claude/skills/atomic-commits/SKILL.md +3 -1
- package/.husky/pre-commit +3 -2
- package/.prettierrc +9 -0
- package/.versionrc.json +1 -1
- package/CHANGELOG.md +33 -0
- package/CLAUDE.md +6 -0
- package/README.md +25 -13
- package/bun.lock +277 -33
- package/dist/{chunk-L2YVNC63.js → chunk-6FHWC36B.js} +9 -1
- package/dist/chunk-6FHWC36B.js.map +1 -0
- package/dist/{chunk-RST4XGRL.js → chunk-DC7CGSGT.js} +288 -241
- package/dist/chunk-DC7CGSGT.js.map +1 -0
- package/dist/{chunk-6PBP5DVD.js → chunk-WFNPNAAP.js} +3212 -3054
- package/dist/chunk-WFNPNAAP.js.map +1 -0
- package/dist/{chunk-WT2DAEO7.js → chunk-Z2KKVH45.js} +548 -482
- package/dist/chunk-Z2KKVH45.js.map +1 -0
- package/dist/index.js +871 -758
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +3 -3
- package/dist/watch.service-BJV3TI3F.js +7 -0
- package/dist/workers/background-worker-cli.js +46 -45
- package/dist/workers/background-worker-cli.js.map +1 -1
- package/eslint.config.js +43 -1
- package/package.json +18 -11
- package/plugin.json +8 -0
- package/python/requirements.txt +1 -1
- package/src/analysis/ast-parser.test.ts +12 -11
- package/src/analysis/ast-parser.ts +28 -22
- package/src/analysis/code-graph.test.ts +52 -62
- package/src/analysis/code-graph.ts +9 -13
- package/src/analysis/dependency-usage-analyzer.test.ts +91 -271
- package/src/analysis/dependency-usage-analyzer.ts +52 -24
- package/src/analysis/go-ast-parser.test.ts +22 -22
- package/src/analysis/go-ast-parser.ts +18 -25
- package/src/analysis/parser-factory.test.ts +9 -9
- package/src/analysis/parser-factory.ts +3 -3
- package/src/analysis/python-ast-parser.test.ts +27 -27
- package/src/analysis/python-ast-parser.ts +2 -2
- package/src/analysis/repo-url-resolver.test.ts +82 -82
- package/src/analysis/rust-ast-parser.test.ts +19 -19
- package/src/analysis/rust-ast-parser.ts +17 -27
- package/src/analysis/tree-sitter-parser.test.ts +3 -3
- package/src/analysis/tree-sitter-parser.ts +10 -16
- package/src/cli/commands/crawl.test.ts +40 -24
- package/src/cli/commands/crawl.ts +186 -166
- package/src/cli/commands/index-cmd.test.ts +90 -90
- package/src/cli/commands/index-cmd.ts +52 -36
- package/src/cli/commands/mcp.test.ts +6 -6
- package/src/cli/commands/mcp.ts +2 -2
- package/src/cli/commands/plugin-api.test.ts +16 -18
- package/src/cli/commands/plugin-api.ts +9 -6
- package/src/cli/commands/search.test.ts +16 -7
- package/src/cli/commands/search.ts +124 -87
- package/src/cli/commands/serve.test.ts +67 -25
- package/src/cli/commands/serve.ts +18 -3
- package/src/cli/commands/setup.test.ts +176 -101
- package/src/cli/commands/setup.ts +140 -117
- package/src/cli/commands/store.test.ts +82 -53
- package/src/cli/commands/store.ts +56 -37
- package/src/cli/program.ts +2 -2
- package/src/crawl/article-converter.test.ts +4 -1
- package/src/crawl/article-converter.ts +46 -31
- package/src/crawl/bridge.test.ts +240 -132
- package/src/crawl/bridge.ts +87 -30
- package/src/crawl/claude-client.test.ts +124 -56
- package/src/crawl/claude-client.ts +7 -15
- package/src/crawl/intelligent-crawler.test.ts +65 -22
- package/src/crawl/intelligent-crawler.ts +86 -53
- package/src/crawl/markdown-utils.ts +1 -4
- package/src/db/embeddings.ts +4 -6
- package/src/db/lance.test.ts +4 -4
- package/src/db/lance.ts +16 -12
- package/src/index.ts +26 -17
- package/src/logging/index.ts +1 -5
- package/src/logging/logger.ts +3 -5
- package/src/logging/payload.test.ts +1 -1
- package/src/logging/payload.ts +3 -5
- package/src/mcp/commands/index.ts +2 -2
- package/src/mcp/commands/job.commands.ts +12 -18
- package/src/mcp/commands/meta.commands.ts +13 -13
- package/src/mcp/commands/registry.ts +5 -8
- package/src/mcp/commands/store.commands.ts +19 -19
- package/src/mcp/handlers/execute.handler.test.ts +10 -10
- package/src/mcp/handlers/execute.handler.ts +4 -5
- package/src/mcp/handlers/index.ts +10 -14
- package/src/mcp/handlers/job.handler.test.ts +10 -10
- package/src/mcp/handlers/job.handler.ts +22 -25
- package/src/mcp/handlers/search.handler.test.ts +36 -65
- package/src/mcp/handlers/search.handler.ts +135 -104
- package/src/mcp/handlers/store.handler.test.ts +41 -52
- package/src/mcp/handlers/store.handler.ts +108 -88
- package/src/mcp/schemas/index.test.ts +73 -68
- package/src/mcp/schemas/index.ts +18 -12
- package/src/mcp/server.test.ts +1 -1
- package/src/mcp/server.ts +59 -46
- package/src/plugin/commands.test.ts +230 -95
- package/src/plugin/commands.ts +24 -25
- package/src/plugin/dependency-analyzer.test.ts +52 -52
- package/src/plugin/dependency-analyzer.ts +85 -22
- package/src/plugin/git-clone.test.ts +24 -13
- package/src/plugin/git-clone.ts +3 -7
- package/src/server/app.test.ts +109 -109
- package/src/server/app.ts +32 -23
- package/src/server/index.test.ts +64 -66
- package/src/services/chunking.service.test.ts +32 -32
- package/src/services/chunking.service.ts +16 -9
- package/src/services/code-graph.service.test.ts +30 -36
- package/src/services/code-graph.service.ts +24 -10
- package/src/services/code-unit.service.test.ts +55 -11
- package/src/services/code-unit.service.ts +85 -11
- package/src/services/config.service.test.ts +37 -18
- package/src/services/config.service.ts +30 -7
- package/src/services/index.service.test.ts +49 -18
- package/src/services/index.service.ts +98 -48
- package/src/services/index.ts +6 -9
- package/src/services/job.service.test.ts +22 -22
- package/src/services/job.service.ts +18 -18
- package/src/services/project-root.service.test.ts +1 -3
- package/src/services/search.service.test.ts +248 -120
- package/src/services/search.service.ts +286 -156
- package/src/services/services.test.ts +1 -1
- package/src/services/snippet.service.test.ts +14 -6
- package/src/services/snippet.service.ts +7 -5
- package/src/services/store.service.test.ts +68 -29
- package/src/services/store.service.ts +41 -12
- package/src/services/watch.service.test.ts +34 -14
- package/src/services/watch.service.ts +11 -1
- package/src/types/brands.test.ts +3 -1
- package/src/types/index.ts +2 -13
- package/src/types/search.ts +10 -8
- package/src/utils/type-guards.test.ts +20 -15
- package/src/utils/type-guards.ts +1 -1
- package/src/workers/background-worker-cli.ts +2 -2
- package/src/workers/background-worker.test.ts +54 -40
- package/src/workers/background-worker.ts +76 -60
- package/src/workers/spawn-worker.test.ts +22 -10
- package/src/workers/spawn-worker.ts +6 -6
- package/tests/analysis/ast-parser.test.ts +3 -3
- package/tests/analysis/code-graph.test.ts +5 -5
- package/tests/fixtures/code-snippets/api/error-handling.ts +4 -15
- package/tests/fixtures/code-snippets/api/rest-controller.ts +3 -9
- package/tests/fixtures/code-snippets/auth/jwt-auth.ts +5 -21
- package/tests/fixtures/code-snippets/auth/oauth-flow.ts +4 -4
- package/tests/fixtures/code-snippets/database/repository-pattern.ts +11 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/handler.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-pages/handler.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/serve-static.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/client/client.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/client/types.ts +22 -20
- package/tests/fixtures/corpus/oss-repos/hono/src/context.ts +13 -10
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/accepts/accepts.ts +10 -7
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/adapter/index.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/css/index.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/factory/index.ts +16 -16
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/ssg.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/hono-base.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/hono.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/css.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/intrinsic-element/components.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/render.ts +7 -7
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/hooks/index.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-element/components.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/utils.ts +6 -6
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jsx-renderer/index.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/serve-static/index.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/quick.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/tiny.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/router/pattern-router/router.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/node.ts +4 -4
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/router.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/node.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/types.ts +166 -169
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/body.ts +8 -8
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/color.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/cookie.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/encode.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/types.ts +30 -33
- package/tests/fixtures/corpus/oss-repos/hono/src/validator/validator.ts +2 -2
- package/tests/fixtures/test-server.ts +3 -2
- package/tests/helpers/performance-metrics.ts +8 -25
- package/tests/helpers/search-relevance.ts +14 -69
- package/tests/integration/cli-consistency.test.ts +5 -4
- package/tests/integration/python-bridge.test.ts +13 -3
- package/tests/mcp/server.test.ts +1 -1
- package/tests/services/code-unit.service.test.ts +48 -0
- package/tests/services/job.service.test.ts +124 -0
- package/tests/services/search.progressive-context.test.ts +2 -2
- package/.claude-plugin/plugin.json +0 -13
- package/dist/chunk-6PBP5DVD.js.map +0 -1
- package/dist/chunk-L2YVNC63.js.map +0 -1
- package/dist/chunk-RST4XGRL.js.map +0 -1
- package/dist/chunk-WT2DAEO7.js.map +0 -1
- package/dist/watch.service-YAIKKDCF.js +0 -7
- package/skills/atomic-commits/SKILL.md +0 -77
- /package/dist/{watch.service-YAIKKDCF.js.map → watch.service-BJV3TI3F.js.map} +0 -0
|
@@ -1,15 +1,30 @@
|
|
|
1
|
-
import { readFile, readdir } from 'node:fs/promises';
|
|
2
1
|
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
3
3
|
import { join, extname } from 'node:path';
|
|
4
4
|
import { ASTParser } from './ast-parser.js';
|
|
5
|
-
import type { Result } from '../types/result.js';
|
|
6
5
|
import { ok, err } from '../types/result.js';
|
|
7
6
|
import type { SupportedLanguage } from './repo-url-resolver.js';
|
|
7
|
+
import type { Result } from '../types/result.js';
|
|
8
8
|
|
|
9
9
|
const TEXT_EXTENSIONS = new Set([
|
|
10
|
-
'.ts',
|
|
11
|
-
'.
|
|
12
|
-
'.
|
|
10
|
+
'.ts',
|
|
11
|
+
'.tsx',
|
|
12
|
+
'.js',
|
|
13
|
+
'.jsx',
|
|
14
|
+
'.mjs',
|
|
15
|
+
'.cjs',
|
|
16
|
+
'.py',
|
|
17
|
+
'.rb',
|
|
18
|
+
'.go',
|
|
19
|
+
'.java',
|
|
20
|
+
'.rs',
|
|
21
|
+
'.php',
|
|
22
|
+
'.md',
|
|
23
|
+
'.txt',
|
|
24
|
+
'.json',
|
|
25
|
+
'.yml',
|
|
26
|
+
'.yaml',
|
|
27
|
+
'.toml',
|
|
13
28
|
]);
|
|
14
29
|
|
|
15
30
|
export interface PackageUsage {
|
|
@@ -60,7 +75,7 @@ export class DependencyUsageAnalyzer {
|
|
|
60
75
|
usages: [],
|
|
61
76
|
totalFilesScanned: 0,
|
|
62
77
|
skippedFiles: 0,
|
|
63
|
-
analysisTimeMs: Date.now() - startTime
|
|
78
|
+
analysisTimeMs: Date.now() - startTime,
|
|
64
79
|
});
|
|
65
80
|
}
|
|
66
81
|
|
|
@@ -72,7 +87,7 @@ export class DependencyUsageAnalyzer {
|
|
|
72
87
|
usages: [],
|
|
73
88
|
totalFilesScanned: 0,
|
|
74
89
|
skippedFiles: 0,
|
|
75
|
-
analysisTimeMs: Date.now() - startTime
|
|
90
|
+
analysisTimeMs: Date.now() - startTime,
|
|
76
91
|
});
|
|
77
92
|
}
|
|
78
93
|
|
|
@@ -92,20 +107,18 @@ export class DependencyUsageAnalyzer {
|
|
|
92
107
|
if (packageName !== null && declaredDeps.has(packageName)) {
|
|
93
108
|
const dep = declaredDeps.get(packageName);
|
|
94
109
|
if (dep !== undefined) {
|
|
95
|
-
this.incrementUsage(
|
|
96
|
-
usageMap,
|
|
97
|
-
packageName,
|
|
98
|
-
filePath,
|
|
99
|
-
dep.isDev,
|
|
100
|
-
dep.language
|
|
101
|
-
);
|
|
110
|
+
this.incrementUsage(usageMap, packageName, filePath, dep.isDev, dep.language);
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
processedCount++;
|
|
107
116
|
if (onProgress !== undefined && processedCount % 10 === 0) {
|
|
108
|
-
onProgress(
|
|
117
|
+
onProgress(
|
|
118
|
+
processedCount,
|
|
119
|
+
files.length,
|
|
120
|
+
`Analyzed ${String(processedCount)}/${String(files.length)} files`
|
|
121
|
+
);
|
|
109
122
|
}
|
|
110
123
|
} catch {
|
|
111
124
|
// Skip files that can't be read or parsed
|
|
@@ -114,14 +127,15 @@ export class DependencyUsageAnalyzer {
|
|
|
114
127
|
}
|
|
115
128
|
|
|
116
129
|
// 4. Sort by usage frequency
|
|
117
|
-
const sortedUsages = Array.from(usageMap.values())
|
|
118
|
-
|
|
130
|
+
const sortedUsages = Array.from(usageMap.values()).sort(
|
|
131
|
+
(a, b) => b.importCount - a.importCount
|
|
132
|
+
);
|
|
119
133
|
|
|
120
134
|
return ok({
|
|
121
135
|
usages: sortedUsages,
|
|
122
136
|
totalFilesScanned: processedCount,
|
|
123
137
|
skippedFiles: skippedCount,
|
|
124
|
-
analysisTimeMs: Date.now() - startTime
|
|
138
|
+
analysisTimeMs: Date.now() - startTime,
|
|
125
139
|
});
|
|
126
140
|
} catch (error) {
|
|
127
141
|
const errorObj = new Error(
|
|
@@ -178,7 +192,10 @@ export class DependencyUsageAnalyzer {
|
|
|
178
192
|
return [];
|
|
179
193
|
}
|
|
180
194
|
|
|
181
|
-
private extractImportsRegex(
|
|
195
|
+
private extractImportsRegex(
|
|
196
|
+
content: string,
|
|
197
|
+
language: 'javascript' | 'python'
|
|
198
|
+
): Array<{ source: string }> {
|
|
182
199
|
const imports: Array<{ source: string }> = [];
|
|
183
200
|
|
|
184
201
|
if (language === 'javascript') {
|
|
@@ -234,7 +251,7 @@ export class DependencyUsageAnalyzer {
|
|
|
234
251
|
fileCount: 1,
|
|
235
252
|
files: [filePath],
|
|
236
253
|
isDevDependency,
|
|
237
|
-
language
|
|
254
|
+
language,
|
|
238
255
|
});
|
|
239
256
|
}
|
|
240
257
|
}
|
|
@@ -250,7 +267,18 @@ export class DependencyUsageAnalyzer {
|
|
|
250
267
|
|
|
251
268
|
if (entry.isDirectory()) {
|
|
252
269
|
// Skip common ignored directories
|
|
253
|
-
if (
|
|
270
|
+
if (
|
|
271
|
+
![
|
|
272
|
+
'node_modules',
|
|
273
|
+
'.git',
|
|
274
|
+
'dist',
|
|
275
|
+
'build',
|
|
276
|
+
'coverage',
|
|
277
|
+
'__pycache__',
|
|
278
|
+
'.venv',
|
|
279
|
+
'venv',
|
|
280
|
+
].includes(entry.name)
|
|
281
|
+
) {
|
|
254
282
|
files.push(...(await this.scanDirectory(fullPath)));
|
|
255
283
|
}
|
|
256
284
|
} else if (entry.isFile()) {
|
|
@@ -312,7 +340,7 @@ export class DependencyUsageAnalyzer {
|
|
|
312
340
|
|
|
313
341
|
// Parse package name (before ==, >=, etc.)
|
|
314
342
|
const match = /^([a-zA-Z0-9_-]+)/.exec(trimmed);
|
|
315
|
-
if (match
|
|
343
|
+
if (match?.[1] !== undefined) {
|
|
316
344
|
const name = match[1].toLowerCase();
|
|
317
345
|
deps.set(name, { name, isDev: false, language: 'python' });
|
|
318
346
|
}
|
|
@@ -350,7 +378,7 @@ export class DependencyUsageAnalyzer {
|
|
|
350
378
|
// or serde = { version = "1.0", features = [...] }
|
|
351
379
|
const inDepsSection = /\[dependencies\]([\s\S]*?)(?=\n\[|$)/;
|
|
352
380
|
const depsMatch = inDepsSection.exec(content);
|
|
353
|
-
if (depsMatch
|
|
381
|
+
if (depsMatch?.[1] !== undefined) {
|
|
354
382
|
const depsSection = depsMatch[1];
|
|
355
383
|
// Match crate names at start of lines
|
|
356
384
|
const cratePattern = /^([a-zA-Z0-9_-]+)\s*=/gm;
|
|
@@ -364,7 +392,7 @@ export class DependencyUsageAnalyzer {
|
|
|
364
392
|
// Also check [dev-dependencies]
|
|
365
393
|
const inDevDepsSection = /\[dev-dependencies\]([\s\S]*?)(?=\n\[|$)/;
|
|
366
394
|
const devDepsMatch = inDevDepsSection.exec(content);
|
|
367
|
-
if (devDepsMatch
|
|
395
|
+
if (devDepsMatch?.[1] !== undefined) {
|
|
368
396
|
const devDepsSection = devDepsMatch[1];
|
|
369
397
|
const cratePattern = /^([a-zA-Z0-9_-]+)\s*=/gm;
|
|
370
398
|
for (const match of devDepsSection.matchAll(cratePattern)) {
|
|
@@ -14,7 +14,7 @@ describe('GoASTParser', () => {
|
|
|
14
14
|
type: 'function',
|
|
15
15
|
name: 'hello',
|
|
16
16
|
exported: false,
|
|
17
|
-
async: false
|
|
17
|
+
async: false,
|
|
18
18
|
});
|
|
19
19
|
});
|
|
20
20
|
|
|
@@ -68,9 +68,9 @@ func Third() int { return 3 }
|
|
|
68
68
|
`.trim();
|
|
69
69
|
const nodes = parser.parse(code, 'test.go');
|
|
70
70
|
|
|
71
|
-
const functions = nodes.filter(n => n.type === 'function');
|
|
71
|
+
const functions = nodes.filter((n) => n.type === 'function');
|
|
72
72
|
expect(functions).toHaveLength(3);
|
|
73
|
-
expect(functions.map(f => f.name)).toEqual(['first', 'second', 'Third']);
|
|
73
|
+
expect(functions.map((f) => f.name)).toEqual(['first', 'second', 'Third']);
|
|
74
74
|
expect(functions[2]?.exported).toBe(true);
|
|
75
75
|
});
|
|
76
76
|
});
|
|
@@ -84,7 +84,7 @@ func Third() int { return 3 }
|
|
|
84
84
|
expect(nodes[0]).toMatchObject({
|
|
85
85
|
type: 'class',
|
|
86
86
|
name: 'user',
|
|
87
|
-
exported: false
|
|
87
|
+
exported: false,
|
|
88
88
|
});
|
|
89
89
|
});
|
|
90
90
|
|
|
@@ -139,7 +139,7 @@ type Data struct {
|
|
|
139
139
|
expect(nodes[0]).toMatchObject({
|
|
140
140
|
type: 'interface',
|
|
141
141
|
name: 'drawable',
|
|
142
|
-
exported: false
|
|
142
|
+
exported: false,
|
|
143
143
|
});
|
|
144
144
|
expect(nodes[0]?.methods).toBeDefined();
|
|
145
145
|
});
|
|
@@ -195,7 +195,7 @@ type Animal interface {
|
|
|
195
195
|
expect(nodes[0]).toMatchObject({
|
|
196
196
|
type: 'type',
|
|
197
197
|
name: 'myInt',
|
|
198
|
-
exported: false
|
|
198
|
+
exported: false,
|
|
199
199
|
});
|
|
200
200
|
});
|
|
201
201
|
|
|
@@ -224,7 +224,7 @@ type Animal interface {
|
|
|
224
224
|
expect(nodes[0]).toMatchObject({
|
|
225
225
|
type: 'const',
|
|
226
226
|
name: 'maxSize',
|
|
227
|
-
exported: false
|
|
227
|
+
exported: false,
|
|
228
228
|
});
|
|
229
229
|
});
|
|
230
230
|
|
|
@@ -254,9 +254,9 @@ const (
|
|
|
254
254
|
`.trim();
|
|
255
255
|
const nodes = parser.parse(code, 'test.go');
|
|
256
256
|
|
|
257
|
-
const constants = nodes.filter(n => n.type === 'const');
|
|
257
|
+
const constants = nodes.filter((n) => n.type === 'const');
|
|
258
258
|
expect(constants).toHaveLength(3);
|
|
259
|
-
expect(constants.map(c => c.name)).toEqual(['Red', 'Green', 'Blue']);
|
|
259
|
+
expect(constants.map((c) => c.name)).toEqual(['Red', 'Green', 'Blue']);
|
|
260
260
|
});
|
|
261
261
|
|
|
262
262
|
it('parses var declaration', () => {
|
|
@@ -265,7 +265,7 @@ const (
|
|
|
265
265
|
|
|
266
266
|
expect(nodes[0]).toMatchObject({
|
|
267
267
|
type: 'const',
|
|
268
|
-
name: 'counter'
|
|
268
|
+
name: 'counter',
|
|
269
269
|
});
|
|
270
270
|
});
|
|
271
271
|
});
|
|
@@ -281,7 +281,7 @@ func (u User) GetName() string {
|
|
|
281
281
|
`.trim();
|
|
282
282
|
const nodes = parser.parse(code, 'test.go');
|
|
283
283
|
|
|
284
|
-
const userStruct = nodes.find(n => n.name === 'User');
|
|
284
|
+
const userStruct = nodes.find((n) => n.name === 'User');
|
|
285
285
|
expect(userStruct).toBeDefined();
|
|
286
286
|
expect(userStruct?.methods).toHaveLength(1);
|
|
287
287
|
expect(userStruct?.methods?.[0]?.name).toBe('GetName');
|
|
@@ -297,7 +297,7 @@ func (c *Counter) Increment() {
|
|
|
297
297
|
`.trim();
|
|
298
298
|
const nodes = parser.parse(code, 'test.go');
|
|
299
299
|
|
|
300
|
-
const counter = nodes.find(n => n.name === 'Counter');
|
|
300
|
+
const counter = nodes.find((n) => n.name === 'Counter');
|
|
301
301
|
expect(counter?.methods).toHaveLength(1);
|
|
302
302
|
expect(counter?.methods?.[0]?.name).toBe('Increment');
|
|
303
303
|
});
|
|
@@ -318,9 +318,9 @@ func (s *Stack) Pop() int {
|
|
|
318
318
|
`.trim();
|
|
319
319
|
const nodes = parser.parse(code, 'test.go');
|
|
320
320
|
|
|
321
|
-
const stack = nodes.find(n => n.name === 'Stack');
|
|
321
|
+
const stack = nodes.find((n) => n.name === 'Stack');
|
|
322
322
|
expect(stack?.methods).toHaveLength(2);
|
|
323
|
-
expect(stack?.methods?.map(m => m.name)).toEqual(['Push', 'Pop']);
|
|
323
|
+
expect(stack?.methods?.map((m) => m.name)).toEqual(['Push', 'Pop']);
|
|
324
324
|
});
|
|
325
325
|
|
|
326
326
|
it('does not count methods as standalone functions', () => {
|
|
@@ -332,7 +332,7 @@ func standalone() {}
|
|
|
332
332
|
`.trim();
|
|
333
333
|
const nodes = parser.parse(code, 'test.go');
|
|
334
334
|
|
|
335
|
-
const functions = nodes.filter(n => n.type === 'function');
|
|
335
|
+
const functions = nodes.filter((n) => n.type === 'function');
|
|
336
336
|
expect(functions).toHaveLength(1);
|
|
337
337
|
expect(functions[0]?.name).toBe('standalone');
|
|
338
338
|
});
|
|
@@ -347,7 +347,7 @@ func standalone() {}
|
|
|
347
347
|
expect(imports[0]).toMatchObject({
|
|
348
348
|
source: 'fmt',
|
|
349
349
|
specifiers: [],
|
|
350
|
-
isType: false
|
|
350
|
+
isType: false,
|
|
351
351
|
});
|
|
352
352
|
});
|
|
353
353
|
|
|
@@ -376,7 +376,7 @@ import (
|
|
|
376
376
|
const imports = parser.extractImports(code);
|
|
377
377
|
|
|
378
378
|
expect(imports).toHaveLength(3);
|
|
379
|
-
expect(imports.map(i => i.source)).toEqual(['fmt', 'os', 'net/http']);
|
|
379
|
+
expect(imports.map((i) => i.source)).toEqual(['fmt', 'os', 'net/http']);
|
|
380
380
|
});
|
|
381
381
|
|
|
382
382
|
it('extracts aliased import', () => {
|
|
@@ -470,10 +470,10 @@ type Handler interface {
|
|
|
470
470
|
|
|
471
471
|
expect(nodes.length).toBeGreaterThan(0);
|
|
472
472
|
|
|
473
|
-
const structs = nodes.filter(n => n.type === 'class');
|
|
474
|
-
const functions = nodes.filter(n => n.type === 'function');
|
|
475
|
-
const interfaces = nodes.filter(n => n.type === 'interface');
|
|
476
|
-
const constants = nodes.filter(n => n.type === 'const');
|
|
473
|
+
const structs = nodes.filter((n) => n.type === 'class');
|
|
474
|
+
const functions = nodes.filter((n) => n.type === 'function');
|
|
475
|
+
const interfaces = nodes.filter((n) => n.type === 'interface');
|
|
476
|
+
const constants = nodes.filter((n) => n.type === 'const');
|
|
477
477
|
|
|
478
478
|
expect(structs).toHaveLength(1);
|
|
479
479
|
expect(functions).toHaveLength(1); // Only main, not the method
|
|
@@ -521,7 +521,7 @@ func (t Test) method() {
|
|
|
521
521
|
`.trim();
|
|
522
522
|
const nodes = parser.parse(code, 'test.go');
|
|
523
523
|
|
|
524
|
-
const test = nodes.find(n => n.name === 'Test');
|
|
524
|
+
const test = nodes.find((n) => n.name === 'Test');
|
|
525
525
|
const method = test?.methods?.[0];
|
|
526
526
|
|
|
527
527
|
expect(method?.startLine).toBe(3);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { CodeNode, ImportInfo } from './ast-parser.js';
|
|
2
1
|
import {
|
|
3
2
|
parseGoCode,
|
|
4
3
|
queryNodesByType,
|
|
@@ -7,8 +6,9 @@ import {
|
|
|
7
6
|
getFunctionSignature,
|
|
8
7
|
getFirstChildOfType,
|
|
9
8
|
type TreeSitterNode,
|
|
10
|
-
type TreeSitterTree
|
|
9
|
+
type TreeSitterTree,
|
|
11
10
|
} from './tree-sitter-parser.js';
|
|
11
|
+
import type { CodeNode, ImportInfo } from './ast-parser.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Parser for Go code using tree-sitter
|
|
@@ -87,13 +87,14 @@ export class GoASTParser {
|
|
|
87
87
|
|
|
88
88
|
// Extract string content from interpreted_string_literal
|
|
89
89
|
const stringContent = pathNode.descendantsOfType('interpreted_string_literal_content')[0];
|
|
90
|
-
const path =
|
|
90
|
+
const path =
|
|
91
|
+
stringContent !== undefined ? stringContent.text : pathNode.text.replace(/"/g, '');
|
|
91
92
|
|
|
92
93
|
if (path !== '') {
|
|
93
94
|
imports.push({
|
|
94
95
|
source: path,
|
|
95
96
|
specifiers: [],
|
|
96
|
-
isType: false
|
|
97
|
+
isType: false,
|
|
97
98
|
});
|
|
98
99
|
}
|
|
99
100
|
}
|
|
@@ -131,7 +132,7 @@ export class GoASTParser {
|
|
|
131
132
|
async: false,
|
|
132
133
|
startLine,
|
|
133
134
|
endLine,
|
|
134
|
-
signature
|
|
135
|
+
signature,
|
|
135
136
|
});
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -176,7 +177,7 @@ export class GoASTParser {
|
|
|
176
177
|
startLine,
|
|
177
178
|
endLine,
|
|
178
179
|
signature: name,
|
|
179
|
-
methods: []
|
|
180
|
+
methods: [],
|
|
180
181
|
});
|
|
181
182
|
}
|
|
182
183
|
|
|
@@ -223,7 +224,7 @@ export class GoASTParser {
|
|
|
223
224
|
startLine,
|
|
224
225
|
endLine,
|
|
225
226
|
signature: name,
|
|
226
|
-
methods
|
|
227
|
+
methods,
|
|
227
228
|
});
|
|
228
229
|
}
|
|
229
230
|
|
|
@@ -267,7 +268,7 @@ export class GoASTParser {
|
|
|
267
268
|
exported,
|
|
268
269
|
startLine,
|
|
269
270
|
endLine,
|
|
270
|
-
signature
|
|
271
|
+
signature,
|
|
271
272
|
});
|
|
272
273
|
}
|
|
273
274
|
|
|
@@ -296,9 +297,7 @@ export class GoASTParser {
|
|
|
296
297
|
const endLine = positionToLineNumber(spec.endPosition);
|
|
297
298
|
|
|
298
299
|
const typeNode = getChildByFieldName(spec, 'type');
|
|
299
|
-
const signature = typeNode !== null
|
|
300
|
-
? `${name}: ${typeNode.text}`
|
|
301
|
-
: name;
|
|
300
|
+
const signature = typeNode !== null ? `${name}: ${typeNode.text}` : name;
|
|
302
301
|
|
|
303
302
|
nodes.push({
|
|
304
303
|
type: 'const',
|
|
@@ -306,7 +305,7 @@ export class GoASTParser {
|
|
|
306
305
|
exported,
|
|
307
306
|
startLine,
|
|
308
307
|
endLine,
|
|
309
|
-
signature
|
|
308
|
+
signature,
|
|
310
309
|
});
|
|
311
310
|
}
|
|
312
311
|
}
|
|
@@ -327,9 +326,7 @@ export class GoASTParser {
|
|
|
327
326
|
const endLine = positionToLineNumber(spec.endPosition);
|
|
328
327
|
|
|
329
328
|
const typeNode = getChildByFieldName(spec, 'type');
|
|
330
|
-
const signature = typeNode !== null
|
|
331
|
-
? `${name}: ${typeNode.text}`
|
|
332
|
-
: name;
|
|
329
|
+
const signature = typeNode !== null ? `${name}: ${typeNode.text}` : name;
|
|
333
330
|
|
|
334
331
|
nodes.push({
|
|
335
332
|
type: 'const',
|
|
@@ -337,7 +334,7 @@ export class GoASTParser {
|
|
|
337
334
|
exported,
|
|
338
335
|
startLine,
|
|
339
336
|
endLine,
|
|
340
|
-
signature
|
|
337
|
+
signature,
|
|
341
338
|
});
|
|
342
339
|
}
|
|
343
340
|
}
|
|
@@ -368,17 +365,15 @@ export class GoASTParser {
|
|
|
368
365
|
const endLine = positionToLineNumber(methodNode.endPosition);
|
|
369
366
|
|
|
370
367
|
// Find the corresponding struct and attach method
|
|
371
|
-
const structNode = nodes.find(
|
|
372
|
-
node => node.type === 'class' && node.name === receiverType
|
|
373
|
-
);
|
|
368
|
+
const structNode = nodes.find((node) => node.type === 'class' && node.name === receiverType);
|
|
374
369
|
|
|
375
|
-
if (structNode
|
|
370
|
+
if (structNode?.methods !== undefined) {
|
|
376
371
|
structNode.methods.push({
|
|
377
372
|
name,
|
|
378
373
|
async: false,
|
|
379
374
|
signature,
|
|
380
375
|
startLine,
|
|
381
|
-
endLine
|
|
376
|
+
endLine,
|
|
382
377
|
});
|
|
383
378
|
}
|
|
384
379
|
}
|
|
@@ -420,7 +415,7 @@ export class GoASTParser {
|
|
|
420
415
|
async: false,
|
|
421
416
|
signature,
|
|
422
417
|
startLine,
|
|
423
|
-
endLine
|
|
418
|
+
endLine,
|
|
424
419
|
});
|
|
425
420
|
}
|
|
426
421
|
|
|
@@ -448,9 +443,7 @@ export class GoASTParser {
|
|
|
448
443
|
|
|
449
444
|
// Handle pointer receivers (*Type)
|
|
450
445
|
if (typeNode.type === 'pointer_type') {
|
|
451
|
-
const innerType = typeNode.children.find(
|
|
452
|
-
child => child.type === 'type_identifier'
|
|
453
|
-
);
|
|
446
|
+
const innerType = typeNode.children.find((child) => child.type === 'type_identifier');
|
|
454
447
|
return innerType !== undefined ? innerType.text : null;
|
|
455
448
|
}
|
|
456
449
|
|
|
@@ -13,7 +13,7 @@ describe('ParserFactory', () => {
|
|
|
13
13
|
expect(nodes[0]).toMatchObject({
|
|
14
14
|
type: 'function',
|
|
15
15
|
name: 'hello',
|
|
16
|
-
exported: true
|
|
16
|
+
exported: true,
|
|
17
17
|
});
|
|
18
18
|
});
|
|
19
19
|
|
|
@@ -35,7 +35,7 @@ describe('ParserFactory', () => {
|
|
|
35
35
|
expect(nodes[0]).toMatchObject({
|
|
36
36
|
type: 'function',
|
|
37
37
|
name: 'add',
|
|
38
|
-
exported: true
|
|
38
|
+
exported: true,
|
|
39
39
|
});
|
|
40
40
|
});
|
|
41
41
|
|
|
@@ -57,13 +57,13 @@ describe('ParserFactory', () => {
|
|
|
57
57
|
exported: true,
|
|
58
58
|
startLine: 1,
|
|
59
59
|
endLine: 2,
|
|
60
|
-
signature: 'def greet(name: str) -> str'
|
|
61
|
-
}
|
|
62
|
-
]
|
|
60
|
+
signature: 'def greet(name: str) -> str',
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
const mockBridge = {
|
|
66
|
-
parsePython: vi.fn().mockResolvedValue(mockResult)
|
|
66
|
+
parsePython: vi.fn().mockResolvedValue(mockResult),
|
|
67
67
|
} as unknown as PythonBridge;
|
|
68
68
|
|
|
69
69
|
const factory = new ParserFactory(mockBridge);
|
|
@@ -75,7 +75,7 @@ describe('ParserFactory', () => {
|
|
|
75
75
|
expect(nodes[0]).toMatchObject({
|
|
76
76
|
type: 'function',
|
|
77
77
|
name: 'greet',
|
|
78
|
-
exported: true
|
|
78
|
+
exported: true,
|
|
79
79
|
});
|
|
80
80
|
});
|
|
81
81
|
|
|
@@ -97,7 +97,7 @@ describe('ParserFactory', () => {
|
|
|
97
97
|
expect(nodes[0]).toMatchObject({
|
|
98
98
|
type: 'function',
|
|
99
99
|
name: 'calculate',
|
|
100
|
-
exported: true
|
|
100
|
+
exported: true,
|
|
101
101
|
});
|
|
102
102
|
});
|
|
103
103
|
|
|
@@ -110,7 +110,7 @@ describe('ParserFactory', () => {
|
|
|
110
110
|
expect(nodes[0]).toMatchObject({
|
|
111
111
|
type: 'function',
|
|
112
112
|
name: 'Add',
|
|
113
|
-
exported: true
|
|
113
|
+
exported: true,
|
|
114
114
|
});
|
|
115
115
|
});
|
|
116
116
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import type { PythonBridge } from '../crawl/bridge.js';
|
|
3
2
|
import { ASTParser, type CodeNode } from './ast-parser.js';
|
|
3
|
+
import { GoASTParser } from './go-ast-parser.js';
|
|
4
4
|
import { PythonASTParser } from './python-ast-parser.js';
|
|
5
5
|
import { RustASTParser } from './rust-ast-parser.js';
|
|
6
|
-
import {
|
|
6
|
+
import type { PythonBridge } from '../crawl/bridge.js';
|
|
7
7
|
|
|
8
8
|
export class ParserFactory {
|
|
9
9
|
constructor(private readonly pythonBridge?: PythonBridge) {}
|
|
@@ -26,7 +26,7 @@ export class ParserFactory {
|
|
|
26
26
|
throw new Error('Python bridge not available for parsing Python files');
|
|
27
27
|
}
|
|
28
28
|
const parser = new PythonASTParser(this.pythonBridge);
|
|
29
|
-
return
|
|
29
|
+
return parser.parse(code, filePath);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
if (ext === '.rs') {
|
|
@@ -4,7 +4,7 @@ import type { PythonBridge, ParsePythonResult } from '../crawl/bridge.js';
|
|
|
4
4
|
|
|
5
5
|
function createMockBridge(result: ParsePythonResult): PythonBridge {
|
|
6
6
|
return {
|
|
7
|
-
parsePython: vi.fn().mockResolvedValue(result)
|
|
7
|
+
parsePython: vi.fn().mockResolvedValue(result),
|
|
8
8
|
} as unknown as PythonBridge;
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -18,10 +18,10 @@ describe('PythonASTParser', () => {
|
|
|
18
18
|
name: 'hello',
|
|
19
19
|
exported: true,
|
|
20
20
|
startLine: 1,
|
|
21
|
-
endLine: 2
|
|
22
|
-
}
|
|
21
|
+
endLine: 2,
|
|
22
|
+
},
|
|
23
23
|
],
|
|
24
|
-
imports: []
|
|
24
|
+
imports: [],
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
const bridge = createMockBridge(mockResult);
|
|
@@ -35,7 +35,7 @@ describe('PythonASTParser', () => {
|
|
|
35
35
|
name: 'hello',
|
|
36
36
|
exported: true,
|
|
37
37
|
startLine: 1,
|
|
38
|
-
endLine: 2
|
|
38
|
+
endLine: 2,
|
|
39
39
|
});
|
|
40
40
|
});
|
|
41
41
|
|
|
@@ -48,10 +48,10 @@ describe('PythonASTParser', () => {
|
|
|
48
48
|
exported: true,
|
|
49
49
|
startLine: 1,
|
|
50
50
|
endLine: 3,
|
|
51
|
-
async: true
|
|
52
|
-
}
|
|
51
|
+
async: true,
|
|
52
|
+
},
|
|
53
53
|
],
|
|
54
|
-
imports: []
|
|
54
|
+
imports: [],
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
const bridge = createMockBridge(mockResult);
|
|
@@ -71,10 +71,10 @@ describe('PythonASTParser', () => {
|
|
|
71
71
|
exported: true,
|
|
72
72
|
startLine: 1,
|
|
73
73
|
endLine: 2,
|
|
74
|
-
signature: 'def greet(name: str) -> str'
|
|
75
|
-
}
|
|
74
|
+
signature: 'def greet(name: str) -> str',
|
|
75
|
+
},
|
|
76
76
|
],
|
|
77
|
-
imports: []
|
|
77
|
+
imports: [],
|
|
78
78
|
};
|
|
79
79
|
|
|
80
80
|
const bridge = createMockBridge(mockResult);
|
|
@@ -100,7 +100,7 @@ describe('PythonASTParser', () => {
|
|
|
100
100
|
signature: 'def add(self, a: int, b: int) -> int',
|
|
101
101
|
startLine: 2,
|
|
102
102
|
endLine: 3,
|
|
103
|
-
calls: []
|
|
103
|
+
calls: [],
|
|
104
104
|
},
|
|
105
105
|
{
|
|
106
106
|
name: 'subtract',
|
|
@@ -108,12 +108,12 @@ describe('PythonASTParser', () => {
|
|
|
108
108
|
signature: 'def subtract(self, a: int, b: int) -> int',
|
|
109
109
|
startLine: 4,
|
|
110
110
|
endLine: 5,
|
|
111
|
-
calls: []
|
|
112
|
-
}
|
|
113
|
-
]
|
|
114
|
-
}
|
|
111
|
+
calls: [],
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
115
|
],
|
|
116
|
-
imports: []
|
|
116
|
+
imports: [],
|
|
117
117
|
};
|
|
118
118
|
|
|
119
119
|
const bridge = createMockBridge(mockResult);
|
|
@@ -134,11 +134,11 @@ describe('PythonASTParser', () => {
|
|
|
134
134
|
name: 'simple',
|
|
135
135
|
exported: false,
|
|
136
136
|
startLine: 1,
|
|
137
|
-
endLine: 1
|
|
137
|
+
endLine: 1,
|
|
138
138
|
// No async, signature, or methods
|
|
139
|
-
}
|
|
139
|
+
},
|
|
140
140
|
],
|
|
141
|
-
imports: []
|
|
141
|
+
imports: [],
|
|
142
142
|
};
|
|
143
143
|
|
|
144
144
|
const bridge = createMockBridge(mockResult);
|
|
@@ -148,7 +148,7 @@ describe('PythonASTParser', () => {
|
|
|
148
148
|
expect(nodes[0]).toMatchObject({
|
|
149
149
|
type: 'function',
|
|
150
150
|
name: 'simple',
|
|
151
|
-
exported: false
|
|
151
|
+
exported: false,
|
|
152
152
|
});
|
|
153
153
|
// Optional fields should not be present
|
|
154
154
|
expect(nodes[0]?.async).toBeUndefined();
|
|
@@ -164,24 +164,24 @@ describe('PythonASTParser', () => {
|
|
|
164
164
|
name: 'func1',
|
|
165
165
|
exported: true,
|
|
166
166
|
startLine: 1,
|
|
167
|
-
endLine: 2
|
|
167
|
+
endLine: 2,
|
|
168
168
|
},
|
|
169
169
|
{
|
|
170
170
|
type: 'class',
|
|
171
171
|
name: 'MyClass',
|
|
172
172
|
exported: true,
|
|
173
173
|
startLine: 3,
|
|
174
|
-
endLine: 10
|
|
174
|
+
endLine: 10,
|
|
175
175
|
},
|
|
176
176
|
{
|
|
177
177
|
type: 'function',
|
|
178
178
|
name: 'func2',
|
|
179
179
|
exported: false,
|
|
180
180
|
startLine: 11,
|
|
181
|
-
endLine: 12
|
|
182
|
-
}
|
|
181
|
+
endLine: 12,
|
|
182
|
+
},
|
|
183
183
|
],
|
|
184
|
-
imports: []
|
|
184
|
+
imports: [],
|
|
185
185
|
};
|
|
186
186
|
|
|
187
187
|
const bridge = createMockBridge(mockResult);
|
|
@@ -197,7 +197,7 @@ describe('PythonASTParser', () => {
|
|
|
197
197
|
it('returns empty array when no nodes found', async () => {
|
|
198
198
|
const mockResult: ParsePythonResult = {
|
|
199
199
|
nodes: [],
|
|
200
|
-
imports: []
|
|
200
|
+
imports: [],
|
|
201
201
|
};
|
|
202
202
|
|
|
203
203
|
const bridge = createMockBridge(mockResult);
|