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,12 +1,17 @@
|
|
|
1
|
-
import type { LanceStore } from '../db/lance.js';
|
|
2
|
-
import type { EmbeddingEngine } from '../db/embeddings.js';
|
|
3
|
-
import type { SearchQuery, SearchResponse, SearchResult, DetailLevel } from '../types/search.js';
|
|
4
|
-
import type { StoreId } from '../types/brands.js';
|
|
5
1
|
import { CodeUnitService } from './code-unit.service.js';
|
|
6
|
-
import
|
|
2
|
+
import { createLogger } from '../logging/index.js';
|
|
7
3
|
import type { CodeGraphService } from './code-graph.service.js';
|
|
8
4
|
import type { CodeGraph } from '../analysis/code-graph.js';
|
|
9
|
-
import {
|
|
5
|
+
import type { EmbeddingEngine } from '../db/embeddings.js';
|
|
6
|
+
import type { LanceStore } from '../db/lance.js';
|
|
7
|
+
import type { StoreId } from '../types/brands.js';
|
|
8
|
+
import type {
|
|
9
|
+
SearchQuery,
|
|
10
|
+
SearchResponse,
|
|
11
|
+
SearchResult,
|
|
12
|
+
DetailLevel,
|
|
13
|
+
CodeUnit,
|
|
14
|
+
} from '../types/search.js';
|
|
10
15
|
|
|
11
16
|
const logger = createLogger('search-service');
|
|
12
17
|
|
|
@@ -32,54 +37,54 @@ export interface ClassifiedIntent {
|
|
|
32
37
|
*/
|
|
33
38
|
const INTENT_FILE_BOOSTS: Record<QueryIntent, Record<string, number>> = {
|
|
34
39
|
'how-to': {
|
|
35
|
-
'documentation-primary': 1.3,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'source-internal': 0.7,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
'documentation-primary': 1.3, // Strong boost for docs
|
|
41
|
+
documentation: 1.2,
|
|
42
|
+
example: 1.5, // Examples are ideal for "how to"
|
|
43
|
+
source: 0.85, // Moderate penalty - source might still have good content
|
|
44
|
+
'source-internal': 0.7, // Stronger penalty - internal code less useful
|
|
45
|
+
test: 0.8,
|
|
46
|
+
config: 0.7,
|
|
47
|
+
other: 0.9,
|
|
43
48
|
},
|
|
44
|
-
|
|
49
|
+
implementation: {
|
|
45
50
|
'documentation-primary': 0.95,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
'source-internal': 1.05,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
documentation: 1.0,
|
|
52
|
+
example: 1.0,
|
|
53
|
+
source: 1.1, // Slight boost for source code
|
|
54
|
+
'source-internal': 1.05, // Internal code can be relevant
|
|
55
|
+
test: 1.0,
|
|
56
|
+
config: 0.95,
|
|
57
|
+
other: 1.0,
|
|
53
58
|
},
|
|
54
|
-
|
|
59
|
+
conceptual: {
|
|
55
60
|
'documentation-primary': 1.1,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
documentation: 1.05,
|
|
62
|
+
example: 1.0,
|
|
63
|
+
source: 0.95,
|
|
59
64
|
'source-internal': 0.9,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
test: 0.9,
|
|
66
|
+
config: 0.85,
|
|
67
|
+
other: 0.95,
|
|
63
68
|
},
|
|
64
|
-
|
|
69
|
+
comparison: {
|
|
65
70
|
'documentation-primary': 1.15,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
documentation: 1.1,
|
|
72
|
+
example: 1.05,
|
|
73
|
+
source: 0.9,
|
|
69
74
|
'source-internal': 0.85,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
test: 0.9,
|
|
76
|
+
config: 0.85,
|
|
77
|
+
other: 0.95,
|
|
73
78
|
},
|
|
74
|
-
|
|
79
|
+
debugging: {
|
|
75
80
|
'documentation-primary': 1.0,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
documentation: 1.0,
|
|
82
|
+
example: 1.05,
|
|
83
|
+
source: 1.0, // Source code helps with debugging
|
|
79
84
|
'source-internal': 0.95,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
test: 1.05, // Tests can show expected behavior
|
|
86
|
+
config: 0.9,
|
|
87
|
+
other: 1.0,
|
|
83
88
|
},
|
|
84
89
|
};
|
|
85
90
|
|
|
@@ -153,23 +158,23 @@ function classifyQueryIntents(query: string): ClassifiedIntent[] {
|
|
|
153
158
|
const intents: ClassifiedIntent[] = [];
|
|
154
159
|
|
|
155
160
|
// Check all pattern groups and add matching intents with confidence
|
|
156
|
-
if (IMPLEMENTATION_PATTERNS.some(p => p.test(q))) {
|
|
161
|
+
if (IMPLEMENTATION_PATTERNS.some((p) => p.test(q))) {
|
|
157
162
|
intents.push({ intent: 'implementation', confidence: 0.9 });
|
|
158
163
|
}
|
|
159
164
|
|
|
160
|
-
if (DEBUGGING_PATTERNS.some(p => p.test(q))) {
|
|
165
|
+
if (DEBUGGING_PATTERNS.some((p) => p.test(q))) {
|
|
161
166
|
intents.push({ intent: 'debugging', confidence: 0.85 });
|
|
162
167
|
}
|
|
163
168
|
|
|
164
|
-
if (COMPARISON_PATTERNS.some(p => p.test(q))) {
|
|
169
|
+
if (COMPARISON_PATTERNS.some((p) => p.test(q))) {
|
|
165
170
|
intents.push({ intent: 'comparison', confidence: 0.8 });
|
|
166
171
|
}
|
|
167
172
|
|
|
168
|
-
if (HOW_TO_PATTERNS.some(p => p.test(q))) {
|
|
173
|
+
if (HOW_TO_PATTERNS.some((p) => p.test(q))) {
|
|
169
174
|
intents.push({ intent: 'how-to', confidence: 0.75 });
|
|
170
175
|
}
|
|
171
176
|
|
|
172
|
-
if (CONCEPTUAL_PATTERNS.some(p => p.test(q))) {
|
|
177
|
+
if (CONCEPTUAL_PATTERNS.some((p) => p.test(q))) {
|
|
173
178
|
intents.push({ intent: 'conceptual', confidence: 0.7 });
|
|
174
179
|
}
|
|
175
180
|
|
|
@@ -202,7 +207,7 @@ const RRF_PRESETS = {
|
|
|
202
207
|
* Detect if results are primarily web content (have urls vs file paths).
|
|
203
208
|
*/
|
|
204
209
|
function detectContentType(results: SearchResult[]): 'web' | 'code' {
|
|
205
|
-
const webCount = results.filter(r => 'url' in r.metadata).length;
|
|
210
|
+
const webCount = results.filter((r) => 'url' in r.metadata).length;
|
|
206
211
|
return webCount > results.length / 2 ? 'web' : 'code';
|
|
207
212
|
}
|
|
208
213
|
|
|
@@ -250,15 +255,18 @@ export class SearchService {
|
|
|
250
255
|
const intents = classifyQueryIntents(query.query);
|
|
251
256
|
const primaryIntent = getPrimaryIntent(intents);
|
|
252
257
|
|
|
253
|
-
logger.debug(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
258
|
+
logger.debug(
|
|
259
|
+
{
|
|
260
|
+
query: query.query,
|
|
261
|
+
mode,
|
|
262
|
+
limit,
|
|
263
|
+
stores,
|
|
264
|
+
detail,
|
|
265
|
+
intent: primaryIntent,
|
|
266
|
+
intents,
|
|
267
|
+
},
|
|
268
|
+
'Search query received'
|
|
269
|
+
);
|
|
262
270
|
|
|
263
271
|
let allResults: SearchResult[] = [];
|
|
264
272
|
|
|
@@ -281,28 +289,31 @@ export class SearchService {
|
|
|
281
289
|
// Load code graphs for stores in results (for contextual/full detail levels)
|
|
282
290
|
const graphs = new Map<string, CodeGraph | null>();
|
|
283
291
|
if (detail === 'contextual' || detail === 'full') {
|
|
284
|
-
const storeIds = new Set(resultsToEnhance.map(r => r.metadata.storeId));
|
|
292
|
+
const storeIds = new Set(resultsToEnhance.map((r) => r.metadata.storeId));
|
|
285
293
|
for (const storeId of storeIds) {
|
|
286
294
|
graphs.set(storeId, await this.loadGraphForStore(storeId));
|
|
287
295
|
}
|
|
288
296
|
}
|
|
289
297
|
|
|
290
298
|
// Enhance results with progressive context
|
|
291
|
-
const enhancedResults = resultsToEnhance.map(r => {
|
|
299
|
+
const enhancedResults = resultsToEnhance.map((r) => {
|
|
292
300
|
const graph = graphs.get(r.metadata.storeId) ?? null;
|
|
293
301
|
return this.addProgressiveContext(r, query.query, detail, graph);
|
|
294
302
|
});
|
|
295
303
|
|
|
296
304
|
const timeMs = Date.now() - startTime;
|
|
297
305
|
|
|
298
|
-
logger.info(
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
+
logger.info(
|
|
307
|
+
{
|
|
308
|
+
query: query.query,
|
|
309
|
+
mode,
|
|
310
|
+
resultCount: enhancedResults.length,
|
|
311
|
+
dedupedFrom: allResults.length,
|
|
312
|
+
intents: intents.map((i) => `${i.intent}(${i.confidence.toFixed(2)})`),
|
|
313
|
+
timeMs,
|
|
314
|
+
},
|
|
315
|
+
'Search complete'
|
|
316
|
+
);
|
|
306
317
|
|
|
307
318
|
return {
|
|
308
319
|
query: query.query,
|
|
@@ -320,7 +331,10 @@ export class SearchService {
|
|
|
320
331
|
*/
|
|
321
332
|
private deduplicateBySource(results: SearchResult[], query: string): SearchResult[] {
|
|
322
333
|
const bySource = new Map<string, SearchResult>();
|
|
323
|
-
const queryTerms = query
|
|
334
|
+
const queryTerms = query
|
|
335
|
+
.toLowerCase()
|
|
336
|
+
.split(/\s+/)
|
|
337
|
+
.filter((t) => t.length > 2);
|
|
324
338
|
|
|
325
339
|
for (const result of results) {
|
|
326
340
|
// Use file path as the source key (or url for web content, or id as last resort)
|
|
@@ -353,7 +367,7 @@ export class SearchService {
|
|
|
353
367
|
*/
|
|
354
368
|
private countQueryTerms(content: string, queryTerms: string[]): number {
|
|
355
369
|
const lowerContent = content.toLowerCase();
|
|
356
|
-
return queryTerms.filter(term => lowerContent.includes(term)).length;
|
|
370
|
+
return queryTerms.filter((term) => lowerContent.includes(term)).length;
|
|
357
371
|
}
|
|
358
372
|
|
|
359
373
|
private async vectorSearch(
|
|
@@ -367,12 +381,14 @@ export class SearchService {
|
|
|
367
381
|
|
|
368
382
|
for (const storeId of stores) {
|
|
369
383
|
const hits = await this.lanceStore.search(storeId, queryVector, limit, threshold);
|
|
370
|
-
results.push(
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
384
|
+
results.push(
|
|
385
|
+
...hits.map((r) => ({
|
|
386
|
+
id: r.id,
|
|
387
|
+
score: r.score,
|
|
388
|
+
content: r.content,
|
|
389
|
+
metadata: r.metadata,
|
|
390
|
+
}))
|
|
391
|
+
);
|
|
376
392
|
}
|
|
377
393
|
|
|
378
394
|
return results.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
@@ -387,12 +403,14 @@ export class SearchService {
|
|
|
387
403
|
|
|
388
404
|
for (const storeId of stores) {
|
|
389
405
|
const hits = await this.lanceStore.fullTextSearch(storeId, query, limit);
|
|
390
|
-
results.push(
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
406
|
+
results.push(
|
|
407
|
+
...hits.map((r) => ({
|
|
408
|
+
id: r.id,
|
|
409
|
+
score: r.score,
|
|
410
|
+
content: r.content,
|
|
411
|
+
metadata: r.metadata,
|
|
412
|
+
}))
|
|
413
|
+
);
|
|
396
414
|
}
|
|
397
415
|
|
|
398
416
|
return results.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
@@ -501,7 +519,12 @@ export class SearchService {
|
|
|
501
519
|
|
|
502
520
|
rrfScores.push({
|
|
503
521
|
id,
|
|
504
|
-
score:
|
|
522
|
+
score:
|
|
523
|
+
(vectorRRF + ftsRRF) *
|
|
524
|
+
fileTypeBoost *
|
|
525
|
+
frameworkBoost *
|
|
526
|
+
urlKeywordBoost *
|
|
527
|
+
pathKeywordBoost,
|
|
505
528
|
result,
|
|
506
529
|
metadata,
|
|
507
530
|
});
|
|
@@ -515,7 +538,7 @@ export class SearchService {
|
|
|
515
538
|
const first = sorted[0];
|
|
516
539
|
const last = sorted[sorted.length - 1];
|
|
517
540
|
if (first === undefined || last === undefined) {
|
|
518
|
-
return sorted.map(r => ({
|
|
541
|
+
return sorted.map((r) => ({
|
|
519
542
|
...r.result,
|
|
520
543
|
score: r.score,
|
|
521
544
|
rankingMetadata: r.metadata,
|
|
@@ -526,7 +549,7 @@ export class SearchService {
|
|
|
526
549
|
const range = maxScore - minScore;
|
|
527
550
|
|
|
528
551
|
if (range > 0) {
|
|
529
|
-
return sorted.map(r => ({
|
|
552
|
+
return sorted.map((r) => ({
|
|
530
553
|
...r.result,
|
|
531
554
|
score: (r.score - minScore) / range,
|
|
532
555
|
rankingMetadata: r.metadata,
|
|
@@ -534,17 +557,14 @@ export class SearchService {
|
|
|
534
557
|
}
|
|
535
558
|
}
|
|
536
559
|
|
|
537
|
-
return sorted.map(r => ({
|
|
560
|
+
return sorted.map((r) => ({
|
|
538
561
|
...r.result,
|
|
539
562
|
score: r.score,
|
|
540
563
|
rankingMetadata: r.metadata,
|
|
541
564
|
}));
|
|
542
565
|
}
|
|
543
566
|
|
|
544
|
-
async searchAllStores(
|
|
545
|
-
query: SearchQuery,
|
|
546
|
-
storeIds: StoreId[]
|
|
547
|
-
): Promise<SearchResponse> {
|
|
567
|
+
async searchAllStores(query: SearchQuery, storeIds: StoreId[]): Promise<SearchResponse> {
|
|
548
568
|
return this.search({
|
|
549
569
|
...query,
|
|
550
570
|
stores: storeIds,
|
|
@@ -562,25 +582,25 @@ export class SearchService {
|
|
|
562
582
|
let baseBoost: number;
|
|
563
583
|
switch (fileType) {
|
|
564
584
|
case 'documentation-primary':
|
|
565
|
-
baseBoost = 1.8;
|
|
585
|
+
baseBoost = 1.8; // README, guides get very strong boost
|
|
566
586
|
break;
|
|
567
587
|
case 'documentation':
|
|
568
|
-
baseBoost = 1.5;
|
|
588
|
+
baseBoost = 1.5; // docs/, tutorials/ get strong boost
|
|
569
589
|
break;
|
|
570
590
|
case 'example':
|
|
571
|
-
baseBoost = 1.4;
|
|
591
|
+
baseBoost = 1.4; // examples/, demos/ are highly valuable
|
|
572
592
|
break;
|
|
573
593
|
case 'source':
|
|
574
|
-
baseBoost = 1.0;
|
|
594
|
+
baseBoost = 1.0; // Source code baseline
|
|
575
595
|
break;
|
|
576
596
|
case 'source-internal':
|
|
577
|
-
baseBoost = 0.75;
|
|
597
|
+
baseBoost = 0.75; // Internal implementation files (not too harsh)
|
|
578
598
|
break;
|
|
579
599
|
case 'test':
|
|
580
|
-
baseBoost = 0.7;
|
|
600
|
+
baseBoost = 0.7; // Tests significantly lower
|
|
581
601
|
break;
|
|
582
602
|
case 'config':
|
|
583
|
-
baseBoost = 0.5;
|
|
603
|
+
baseBoost = 0.5; // Config files rarely answer questions
|
|
584
604
|
break;
|
|
585
605
|
default:
|
|
586
606
|
baseBoost = 1.0;
|
|
@@ -597,9 +617,7 @@ export class SearchService {
|
|
|
597
617
|
totalConfidence += confidence;
|
|
598
618
|
}
|
|
599
619
|
|
|
600
|
-
const blendedMultiplier = totalConfidence > 0
|
|
601
|
-
? weightedMultiplier / totalConfidence
|
|
602
|
-
: 1.0;
|
|
620
|
+
const blendedMultiplier = totalConfidence > 0 ? weightedMultiplier / totalConfidence : 1.0;
|
|
603
621
|
|
|
604
622
|
return baseBoost * blendedMultiplier;
|
|
605
623
|
}
|
|
@@ -618,27 +636,52 @@ export class SearchService {
|
|
|
618
636
|
|
|
619
637
|
// Common stop words to filter from queries
|
|
620
638
|
const stopWords = new Set([
|
|
621
|
-
'how',
|
|
622
|
-
'
|
|
623
|
-
'
|
|
639
|
+
'how',
|
|
640
|
+
'to',
|
|
641
|
+
'the',
|
|
642
|
+
'a',
|
|
643
|
+
'an',
|
|
644
|
+
'is',
|
|
645
|
+
'are',
|
|
646
|
+
'what',
|
|
647
|
+
'why',
|
|
648
|
+
'when',
|
|
649
|
+
'where',
|
|
650
|
+
'can',
|
|
651
|
+
'do',
|
|
652
|
+
'does',
|
|
653
|
+
'i',
|
|
654
|
+
'my',
|
|
655
|
+
'your',
|
|
656
|
+
'it',
|
|
657
|
+
'in',
|
|
658
|
+
'on',
|
|
659
|
+
'for',
|
|
660
|
+
'with',
|
|
661
|
+
'this',
|
|
662
|
+
'that',
|
|
663
|
+
'get',
|
|
664
|
+
'use',
|
|
665
|
+
'using',
|
|
624
666
|
]);
|
|
625
667
|
|
|
626
668
|
// Extract meaningful query terms
|
|
627
|
-
const queryTerms = query
|
|
669
|
+
const queryTerms = query
|
|
670
|
+
.toLowerCase()
|
|
628
671
|
.split(/\s+/)
|
|
629
|
-
.filter(t => t.length > 2 && !stopWords.has(t));
|
|
672
|
+
.filter((t) => t.length > 2 && !stopWords.has(t));
|
|
630
673
|
|
|
631
674
|
if (queryTerms.length === 0) return 1.0;
|
|
632
675
|
|
|
633
676
|
// Count matching terms in URL path
|
|
634
|
-
const matchingTerms = queryTerms.filter(term => urlPath.includes(term));
|
|
677
|
+
const matchingTerms = queryTerms.filter((term) => urlPath.includes(term));
|
|
635
678
|
|
|
636
679
|
if (matchingTerms.length === 0) return 1.0;
|
|
637
680
|
|
|
638
681
|
// Boost based on proportion of matching terms
|
|
639
682
|
// Single match: ~1.5, all terms match: ~2.0
|
|
640
683
|
const matchRatio = matchingTerms.length / queryTerms.length;
|
|
641
|
-
return 1.0 +
|
|
684
|
+
return 1.0 + 1.0 * matchRatio;
|
|
642
685
|
}
|
|
643
686
|
|
|
644
687
|
/**
|
|
@@ -655,27 +698,52 @@ export class SearchService {
|
|
|
655
698
|
|
|
656
699
|
// Common stop words to filter from queries
|
|
657
700
|
const stopWords = new Set([
|
|
658
|
-
'how',
|
|
659
|
-
'
|
|
660
|
-
'
|
|
701
|
+
'how',
|
|
702
|
+
'to',
|
|
703
|
+
'the',
|
|
704
|
+
'a',
|
|
705
|
+
'an',
|
|
706
|
+
'is',
|
|
707
|
+
'are',
|
|
708
|
+
'what',
|
|
709
|
+
'why',
|
|
710
|
+
'when',
|
|
711
|
+
'where',
|
|
712
|
+
'can',
|
|
713
|
+
'do',
|
|
714
|
+
'does',
|
|
715
|
+
'i',
|
|
716
|
+
'my',
|
|
717
|
+
'your',
|
|
718
|
+
'it',
|
|
719
|
+
'in',
|
|
720
|
+
'on',
|
|
721
|
+
'for',
|
|
722
|
+
'with',
|
|
723
|
+
'this',
|
|
724
|
+
'that',
|
|
725
|
+
'get',
|
|
726
|
+
'use',
|
|
727
|
+
'using',
|
|
661
728
|
]);
|
|
662
729
|
|
|
663
730
|
// Extract meaningful query terms
|
|
664
|
-
const queryTerms = query
|
|
731
|
+
const queryTerms = query
|
|
732
|
+
.toLowerCase()
|
|
665
733
|
.split(/\s+/)
|
|
666
|
-
.filter(t => t.length > 2 && !stopWords.has(t));
|
|
734
|
+
.filter((t) => t.length > 2 && !stopWords.has(t));
|
|
667
735
|
|
|
668
736
|
if (queryTerms.length === 0) return 1.0;
|
|
669
737
|
|
|
670
738
|
// Count matching terms in file path
|
|
671
|
-
const matchingTerms = queryTerms.filter(term => pathSegments.includes(term));
|
|
739
|
+
const matchingTerms = queryTerms.filter((term) => pathSegments.includes(term));
|
|
672
740
|
|
|
673
741
|
if (matchingTerms.length === 0) return 1.0;
|
|
674
742
|
|
|
675
743
|
// Boost based on proportion of matching terms
|
|
676
744
|
// Single match: ~1.5, all terms match: ~2.0
|
|
677
745
|
const matchRatio = matchingTerms.length / queryTerms.length;
|
|
678
|
-
return 1.0 +
|
|
746
|
+
return 1.0 + 1.0 * matchRatio;
|
|
679
747
|
}
|
|
680
748
|
|
|
681
749
|
/**
|
|
@@ -691,8 +759,8 @@ export class SearchService {
|
|
|
691
759
|
for (const { pattern, terms } of FRAMEWORK_PATTERNS) {
|
|
692
760
|
if (pattern.test(query)) {
|
|
693
761
|
// Query mentions this framework - check if result is from that framework
|
|
694
|
-
const resultMatchesFramework = terms.some(
|
|
695
|
-
pathLower.includes(term) || content.includes(term)
|
|
762
|
+
const resultMatchesFramework = terms.some(
|
|
763
|
+
(term) => pathLower.includes(term) || content.includes(term)
|
|
696
764
|
);
|
|
697
765
|
|
|
698
766
|
if (resultMatchesFramework) {
|
|
@@ -728,8 +796,8 @@ export class SearchService {
|
|
|
728
796
|
name: symbolName,
|
|
729
797
|
signature: codeUnit?.signature ?? '',
|
|
730
798
|
purpose: this.generatePurpose(result.content, query),
|
|
731
|
-
location: `${path}${codeUnit ?
|
|
732
|
-
relevanceReason: this.generateRelevanceReason(result, query)
|
|
799
|
+
location: `${path}${codeUnit ? `:${String(codeUnit.startLine)}` : ''}`,
|
|
800
|
+
relevanceReason: this.generateRelevanceReason(result, query),
|
|
733
801
|
};
|
|
734
802
|
|
|
735
803
|
// Layer 2: Add context if requested
|
|
@@ -741,7 +809,7 @@ export class SearchService {
|
|
|
741
809
|
interfaces: this.extractInterfaces(result.content),
|
|
742
810
|
keyImports: this.extractImports(result.content),
|
|
743
811
|
relatedConcepts: this.extractConcepts(result.content, query),
|
|
744
|
-
usage
|
|
812
|
+
usage,
|
|
745
813
|
};
|
|
746
814
|
}
|
|
747
815
|
|
|
@@ -754,7 +822,7 @@ export class SearchService {
|
|
|
754
822
|
completeCode: codeUnit?.fullContent ?? result.content,
|
|
755
823
|
relatedCode,
|
|
756
824
|
documentation: this.extractDocumentation(result.content),
|
|
757
|
-
tests: undefined
|
|
825
|
+
tests: undefined,
|
|
758
826
|
};
|
|
759
827
|
}
|
|
760
828
|
|
|
@@ -766,8 +834,12 @@ export class SearchService {
|
|
|
766
834
|
if (path === undefined || path === '') return undefined;
|
|
767
835
|
|
|
768
836
|
const ext = path.split('.').pop() ?? '';
|
|
769
|
-
const language =
|
|
770
|
-
|
|
837
|
+
const language =
|
|
838
|
+
ext === 'ts' || ext === 'tsx'
|
|
839
|
+
? 'typescript'
|
|
840
|
+
: ext === 'js' || ext === 'jsx'
|
|
841
|
+
? 'javascript'
|
|
842
|
+
: ext;
|
|
771
843
|
|
|
772
844
|
// Try to find a symbol name in the content
|
|
773
845
|
const symbolName = this.extractSymbolName(result.content);
|
|
@@ -779,45 +851,54 @@ export class SearchService {
|
|
|
779
851
|
private extractSymbolName(content: string): string {
|
|
780
852
|
// Extract function or class name
|
|
781
853
|
const funcMatch = content.match(/(?:export\s+)?(?:async\s+)?function\s+(\w+)/);
|
|
782
|
-
if (funcMatch
|
|
854
|
+
if (funcMatch?.[1] !== undefined && funcMatch[1] !== '') return funcMatch[1];
|
|
783
855
|
|
|
784
856
|
const classMatch = content.match(/(?:export\s+)?class\s+(\w+)/);
|
|
785
|
-
if (classMatch
|
|
857
|
+
if (classMatch?.[1] !== undefined && classMatch[1] !== '') return classMatch[1];
|
|
786
858
|
|
|
787
859
|
const constMatch = content.match(/(?:export\s+)?const\s+(\w+)/);
|
|
788
|
-
if (constMatch
|
|
860
|
+
if (constMatch?.[1] !== undefined && constMatch[1] !== '') return constMatch[1];
|
|
789
861
|
|
|
790
862
|
// Fallback: return "(anonymous)" for unnamed symbols
|
|
791
863
|
return '(anonymous)';
|
|
792
864
|
}
|
|
793
865
|
|
|
794
|
-
private inferType(
|
|
866
|
+
private inferType(
|
|
867
|
+
fileType: string | undefined,
|
|
868
|
+
codeUnit: CodeUnit | undefined
|
|
869
|
+
): import('../types/search.js').ResultSummary['type'] {
|
|
795
870
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
796
871
|
if (codeUnit) return codeUnit.type as import('../types/search.js').ResultSummary['type'];
|
|
797
|
-
if (fileType === 'documentation' || fileType === 'documentation-primary')
|
|
872
|
+
if (fileType === 'documentation' || fileType === 'documentation-primary')
|
|
873
|
+
return 'documentation';
|
|
798
874
|
return 'function';
|
|
799
875
|
}
|
|
800
876
|
|
|
801
877
|
private generatePurpose(content: string, query: string): string {
|
|
802
878
|
// Extract first line of JSDoc comment if present
|
|
803
879
|
const docMatch = content.match(/\/\*\*\s*\n\s*\*\s*([^\n]+)/);
|
|
804
|
-
if (docMatch
|
|
880
|
+
if (docMatch?.[1] !== undefined && docMatch[1] !== '') return docMatch[1].trim();
|
|
805
881
|
|
|
806
882
|
const lines = content.split('\n');
|
|
807
|
-
const queryTerms = query
|
|
883
|
+
const queryTerms = query
|
|
884
|
+
.toLowerCase()
|
|
885
|
+
.split(/\s+/)
|
|
886
|
+
.filter((t) => t.length > 2);
|
|
808
887
|
|
|
809
888
|
// Helper to check if line is skippable (imports, declarations)
|
|
810
889
|
const shouldSkip = (cleaned: string): boolean => {
|
|
811
|
-
return
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
890
|
+
return (
|
|
891
|
+
cleaned.startsWith('import ') ||
|
|
892
|
+
cleaned.startsWith('export ') ||
|
|
893
|
+
cleaned.startsWith('interface ') ||
|
|
894
|
+
cleaned.startsWith('type ')
|
|
895
|
+
);
|
|
815
896
|
};
|
|
816
897
|
|
|
817
898
|
// Helper to score a line based on query term matches
|
|
818
899
|
const scoreLine = (cleaned: string): number => {
|
|
819
900
|
const lowerLine = cleaned.toLowerCase();
|
|
820
|
-
return queryTerms.filter(term => lowerLine.includes(term)).length;
|
|
901
|
+
return queryTerms.filter((term) => lowerLine.includes(term)).length;
|
|
821
902
|
};
|
|
822
903
|
|
|
823
904
|
// Helper to check if line is meaningful (length, not a comment)
|
|
@@ -839,18 +920,18 @@ export class SearchService {
|
|
|
839
920
|
if (shouldSkip(cleaned) || !isMeaningful(cleaned)) continue;
|
|
840
921
|
|
|
841
922
|
let score = scoreLine(cleaned);
|
|
842
|
-
|
|
923
|
+
|
|
843
924
|
// Boost score for complete sentences (end with period, !, ?)
|
|
844
925
|
if (/[.!?]$/.test(cleaned)) {
|
|
845
926
|
score += 0.5;
|
|
846
927
|
}
|
|
847
|
-
|
|
928
|
+
|
|
848
929
|
// Boost score for code examples (contains function calls or assignments)
|
|
849
930
|
// Favor complete patterns: function calls WITH arguments, assignments with values
|
|
850
931
|
if (/\w+\([^)]*\)|=\s*\w+\(|=>/.test(cleaned)) {
|
|
851
|
-
score += 0.6;
|
|
932
|
+
score += 0.6; // Enhanced boost to preserve code examples in snippets
|
|
852
933
|
}
|
|
853
|
-
|
|
934
|
+
|
|
854
935
|
if (score > bestScore) {
|
|
855
936
|
bestScore = score;
|
|
856
937
|
bestLine = cleaned;
|
|
@@ -864,7 +945,7 @@ export class SearchService {
|
|
|
864
945
|
if (firstSentence && firstSentence[0].length >= 20 && firstSentence[0].length <= 150) {
|
|
865
946
|
return firstSentence[0].trim();
|
|
866
947
|
}
|
|
867
|
-
return bestLine.substring(0, 147)
|
|
948
|
+
return `${bestLine.substring(0, 147)}...`;
|
|
868
949
|
}
|
|
869
950
|
return bestLine;
|
|
870
951
|
}
|
|
@@ -879,7 +960,7 @@ export class SearchService {
|
|
|
879
960
|
if (firstSentence && firstSentence[0].length >= 20 && firstSentence[0].length <= 150) {
|
|
880
961
|
return firstSentence[0].trim();
|
|
881
962
|
}
|
|
882
|
-
return cleaned.substring(0, 147)
|
|
963
|
+
return `${cleaned.substring(0, 147)}...`;
|
|
883
964
|
}
|
|
884
965
|
|
|
885
966
|
return cleaned;
|
|
@@ -889,10 +970,13 @@ export class SearchService {
|
|
|
889
970
|
}
|
|
890
971
|
|
|
891
972
|
private generateRelevanceReason(result: SearchResult, query: string): string {
|
|
892
|
-
const queryTerms = query
|
|
973
|
+
const queryTerms = query
|
|
974
|
+
.toLowerCase()
|
|
975
|
+
.split(/\s+/)
|
|
976
|
+
.filter((t) => t.length > 2);
|
|
893
977
|
const contentLower = result.content.toLowerCase();
|
|
894
978
|
|
|
895
|
-
const matchedTerms = queryTerms.filter(term => contentLower.includes(term));
|
|
979
|
+
const matchedTerms = queryTerms.filter((term) => contentLower.includes(term));
|
|
896
980
|
|
|
897
981
|
if (matchedTerms.length > 0) {
|
|
898
982
|
return `Matches: ${matchedTerms.join(', ')}`;
|
|
@@ -924,14 +1008,60 @@ export class SearchService {
|
|
|
924
1008
|
|
|
925
1009
|
// Common stopwords to filter out
|
|
926
1010
|
const stopwords = new Set([
|
|
927
|
-
'this',
|
|
928
|
-
'
|
|
929
|
-
'
|
|
930
|
-
'
|
|
931
|
-
'
|
|
932
|
-
'
|
|
933
|
-
'
|
|
934
|
-
'
|
|
1011
|
+
'this',
|
|
1012
|
+
'that',
|
|
1013
|
+
'these',
|
|
1014
|
+
'those',
|
|
1015
|
+
'from',
|
|
1016
|
+
'with',
|
|
1017
|
+
'have',
|
|
1018
|
+
'will',
|
|
1019
|
+
'would',
|
|
1020
|
+
'should',
|
|
1021
|
+
'could',
|
|
1022
|
+
'about',
|
|
1023
|
+
'been',
|
|
1024
|
+
'were',
|
|
1025
|
+
'being',
|
|
1026
|
+
'function',
|
|
1027
|
+
'return',
|
|
1028
|
+
'const',
|
|
1029
|
+
'import',
|
|
1030
|
+
'export',
|
|
1031
|
+
'default',
|
|
1032
|
+
'type',
|
|
1033
|
+
'interface',
|
|
1034
|
+
'class',
|
|
1035
|
+
'extends',
|
|
1036
|
+
'implements',
|
|
1037
|
+
'async',
|
|
1038
|
+
'await',
|
|
1039
|
+
'then',
|
|
1040
|
+
'catch',
|
|
1041
|
+
'throw',
|
|
1042
|
+
'error',
|
|
1043
|
+
'undefined',
|
|
1044
|
+
'null',
|
|
1045
|
+
'true',
|
|
1046
|
+
'false',
|
|
1047
|
+
'void',
|
|
1048
|
+
'number',
|
|
1049
|
+
'string',
|
|
1050
|
+
'boolean',
|
|
1051
|
+
'object',
|
|
1052
|
+
'array',
|
|
1053
|
+
'promise',
|
|
1054
|
+
'callback',
|
|
1055
|
+
'resolve',
|
|
1056
|
+
'reject',
|
|
1057
|
+
'value',
|
|
1058
|
+
'param',
|
|
1059
|
+
'params',
|
|
1060
|
+
'args',
|
|
1061
|
+
'props',
|
|
1062
|
+
'options',
|
|
1063
|
+
'config',
|
|
1064
|
+
'data',
|
|
935
1065
|
]);
|
|
936
1066
|
|
|
937
1067
|
// Simple keyword extraction
|
|
@@ -953,11 +1083,11 @@ export class SearchService {
|
|
|
953
1083
|
|
|
954
1084
|
private extractDocumentation(content: string): string {
|
|
955
1085
|
const docMatch = content.match(/\/\*\*([\s\S]*?)\*\//);
|
|
956
|
-
if (docMatch
|
|
1086
|
+
if (docMatch?.[1] !== undefined && docMatch[1] !== '') {
|
|
957
1087
|
return docMatch[1]
|
|
958
1088
|
.split('\n')
|
|
959
|
-
.map(line => line.replace(/^\s*\*\s?/, '').trim())
|
|
960
|
-
.filter(line => line.length > 0)
|
|
1089
|
+
.map((line) => line.replace(/^\s*\*\s?/, '').trim())
|
|
1090
|
+
.filter((line) => line.length > 0)
|
|
961
1091
|
.join('\n');
|
|
962
1092
|
}
|
|
963
1093
|
return '';
|
|
@@ -979,7 +1109,7 @@ export class SearchService {
|
|
|
979
1109
|
const nodeId = `${filePath}:${symbolName}`;
|
|
980
1110
|
return {
|
|
981
1111
|
calledBy: graph.getCalledByCount(nodeId),
|
|
982
|
-
calls: graph.getCallsCount(nodeId)
|
|
1112
|
+
calls: graph.getCallsCount(nodeId),
|
|
983
1113
|
};
|
|
984
1114
|
}
|
|
985
1115
|
|
|
@@ -1008,7 +1138,7 @@ export class SearchService {
|
|
|
1008
1138
|
related.push({
|
|
1009
1139
|
file,
|
|
1010
1140
|
summary: symbol ? `${symbol}()` : 'unknown',
|
|
1011
|
-
relationship: 'calls this'
|
|
1141
|
+
relationship: 'calls this',
|
|
1012
1142
|
});
|
|
1013
1143
|
}
|
|
1014
1144
|
}
|
|
@@ -1022,7 +1152,7 @@ export class SearchService {
|
|
|
1022
1152
|
related.push({
|
|
1023
1153
|
file,
|
|
1024
1154
|
summary: symbol ? `${symbol}()` : 'unknown',
|
|
1025
|
-
relationship: 'called by this'
|
|
1155
|
+
relationship: 'called by this',
|
|
1026
1156
|
});
|
|
1027
1157
|
}
|
|
1028
1158
|
}
|