lsp-intelligence 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +12 -7
  2. package/dist/analysis/pattern/collectSearchFiles.d.ts +5 -0
  3. package/dist/analysis/pattern/collectSearchFiles.js +38 -0
  4. package/dist/analysis/pattern/collectSearchFiles.js.map +1 -0
  5. package/dist/analysis/pattern/runPatternSearch.d.ts +17 -0
  6. package/dist/analysis/pattern/runPatternSearch.js +63 -0
  7. package/dist/analysis/pattern/runPatternSearch.js.map +1 -0
  8. package/dist/analysis/ts/extractDeclarations.d.ts +7 -0
  9. package/dist/analysis/ts/extractDeclarations.js +93 -0
  10. package/dist/analysis/ts/extractDeclarations.js.map +1 -0
  11. package/dist/analysis/ts/extractUsages.d.ts +7 -0
  12. package/dist/analysis/ts/extractUsages.js +120 -0
  13. package/dist/analysis/ts/extractUsages.js.map +1 -0
  14. package/dist/analysis/ts/parseSourceFile.d.ts +10 -0
  15. package/dist/analysis/ts/parseSourceFile.js +28 -0
  16. package/dist/analysis/ts/parseSourceFile.js.map +1 -0
  17. package/dist/analysis/ts/snippets.d.ts +14 -0
  18. package/dist/analysis/ts/snippets.js +29 -0
  19. package/dist/analysis/ts/snippets.js.map +1 -0
  20. package/dist/analysis/ts/structuralPredicates.d.ts +10 -0
  21. package/dist/analysis/ts/structuralPredicates.js +170 -0
  22. package/dist/analysis/ts/structuralPredicates.js.map +1 -0
  23. package/dist/ast/diffDeclarationShapes.d.ts +16 -0
  24. package/dist/ast/diffDeclarationShapes.js +179 -0
  25. package/dist/ast/diffDeclarationShapes.js.map +1 -0
  26. package/dist/ast/extractExportDeclarations.d.ts +21 -0
  27. package/dist/ast/extractExportDeclarations.js +218 -0
  28. package/dist/ast/extractExportDeclarations.js.map +1 -0
  29. package/dist/ast/findNodeAtPosition.d.ts +12 -0
  30. package/dist/ast/findNodeAtPosition.js +75 -0
  31. package/dist/ast/findNodeAtPosition.js.map +1 -0
  32. package/dist/ast/parseFile.d.ts +10 -0
  33. package/dist/ast/parseFile.js +29 -0
  34. package/dist/ast/parseFile.js.map +1 -0
  35. package/dist/engine/waitForDiagnostics.d.ts +7 -0
  36. package/dist/engine/waitForDiagnostics.js +16 -0
  37. package/dist/engine/waitForDiagnostics.js.map +1 -0
  38. package/dist/git/getBaseFileContent.d.ts +5 -0
  39. package/dist/git/getBaseFileContent.js +16 -0
  40. package/dist/git/getBaseFileContent.js.map +1 -0
  41. package/dist/git/getChangedFiles.d.ts +4 -0
  42. package/dist/git/getChangedFiles.js +21 -0
  43. package/dist/git/getChangedFiles.js.map +1 -0
  44. package/dist/git/getChangedHunks.d.ts +13 -0
  45. package/dist/git/getChangedHunks.js +51 -0
  46. package/dist/git/getChangedHunks.js.map +1 -0
  47. package/dist/git/getMergeBase.d.ts +5 -0
  48. package/dist/git/getMergeBase.js +23 -0
  49. package/dist/git/getMergeBase.js.map +1 -0
  50. package/dist/index.js +8 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/resolve/searchScope.d.ts +11 -0
  53. package/dist/resolve/searchScope.js +52 -0
  54. package/dist/resolve/searchScope.js.map +1 -0
  55. package/dist/resolve/targetResolver.d.ts +33 -0
  56. package/dist/resolve/targetResolver.js +84 -0
  57. package/dist/resolve/targetResolver.js.map +1 -0
  58. package/dist/search/families/behaviorFamilies.d.ts +22 -0
  59. package/dist/search/families/behaviorFamilies.js +90 -0
  60. package/dist/search/families/behaviorFamilies.js.map +1 -0
  61. package/dist/search/index/declarationIndex.d.ts +6 -0
  62. package/dist/search/index/declarationIndex.js +13 -0
  63. package/dist/search/index/declarationIndex.js.map +1 -0
  64. package/dist/search/index/types.d.ts +1 -0
  65. package/dist/search/index/types.js +2 -0
  66. package/dist/search/index/types.js.map +1 -0
  67. package/dist/search/index/usageIndex.d.ts +6 -0
  68. package/dist/search/index/usageIndex.js +13 -0
  69. package/dist/search/index/usageIndex.js.map +1 -0
  70. package/dist/search/index/workspaceIndex.d.ts +8 -0
  71. package/dist/search/index/workspaceIndex.js +91 -0
  72. package/dist/search/index/workspaceIndex.js.map +1 -0
  73. package/dist/search/query/parseQuery.d.ts +9 -0
  74. package/dist/search/query/parseQuery.js +244 -0
  75. package/dist/search/query/parseQuery.js.map +1 -0
  76. package/dist/search/query/planQuery.d.ts +9 -0
  77. package/dist/search/query/planQuery.js +58 -0
  78. package/dist/search/query/planQuery.js.map +1 -0
  79. package/dist/search/ranking/assessConfidence.d.ts +6 -0
  80. package/dist/search/ranking/assessConfidence.js +25 -0
  81. package/dist/search/ranking/assessConfidence.js.map +1 -0
  82. package/dist/search/ranking/mergeCandidates.d.ts +10 -0
  83. package/dist/search/ranking/mergeCandidates.js +46 -0
  84. package/dist/search/ranking/mergeCandidates.js.map +1 -0
  85. package/dist/search/ranking/rankCandidates.d.ts +12 -0
  86. package/dist/search/ranking/rankCandidates.js +58 -0
  87. package/dist/search/ranking/rankCandidates.js.map +1 -0
  88. package/dist/search/retrievers/behaviorRetriever.d.ts +7 -0
  89. package/dist/search/retrievers/behaviorRetriever.js +91 -0
  90. package/dist/search/retrievers/behaviorRetriever.js.map +1 -0
  91. package/dist/search/retrievers/identifierRetriever.d.ts +7 -0
  92. package/dist/search/retrievers/identifierRetriever.js +66 -0
  93. package/dist/search/retrievers/identifierRetriever.js.map +1 -0
  94. package/dist/search/retrievers/structuralRetriever.d.ts +11 -0
  95. package/dist/search/retrievers/structuralRetriever.js +141 -0
  96. package/dist/search/retrievers/structuralRetriever.js.map +1 -0
  97. package/dist/search/types.d.ts +121 -0
  98. package/dist/search/types.js +11 -0
  99. package/dist/search/types.js.map +1 -0
  100. package/dist/tools/composites/apiGuard.d.ts +1 -0
  101. package/dist/tools/composites/apiGuard.js +194 -0
  102. package/dist/tools/composites/apiGuard.js.map +1 -0
  103. package/dist/tools/composites/explainError.js +3 -2
  104. package/dist/tools/composites/explainError.js.map +1 -1
  105. package/dist/tools/composites/findCode.d.ts +1 -0
  106. package/dist/tools/composites/findCode.js +68 -0
  107. package/dist/tools/composites/findCode.js.map +1 -0
  108. package/dist/tools/composites/findPattern.d.ts +5 -0
  109. package/dist/tools/composites/findPattern.js +41 -0
  110. package/dist/tools/composites/findPattern.js.map +1 -0
  111. package/dist/tools/composites/rootCauseTrace.d.ts +1 -0
  112. package/dist/tools/composites/rootCauseTrace.js +213 -0
  113. package/dist/tools/composites/rootCauseTrace.js.map +1 -0
  114. package/dist/tools/composites/semanticDiff.js +24 -77
  115. package/dist/tools/composites/semanticDiff.js.map +1 -1
  116. package/dist/tools/live/liveDiagnostics.js +3 -2
  117. package/dist/tools/live/liveDiagnostics.js.map +1 -1
  118. package/dist/tools/primitives/diagnostics.js +2 -2
  119. package/dist/tools/primitives/diagnostics.js.map +1 -1
  120. package/dist/tools/registry.d.ts +10 -2
  121. package/dist/tools/registry.js +7 -2
  122. package/dist/tools/registry.js.map +1 -1
  123. package/package.json +8 -4
  124. package/dist/tools/primitives/typeHierarchy.d.ts +0 -1
  125. package/dist/tools/primitives/typeHierarchy.js +0 -55
  126. package/dist/tools/primitives/typeHierarchy.js.map +0 -1
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Assess overall confidence of the search results.
3
+ * High = strong signals from multiple sources. Low = weak/generic matches.
4
+ */
5
+ export function assessConfidence(ranked, ir, plan) {
6
+ if (ranked.length === 0)
7
+ return 'low';
8
+ const top = ranked[0];
9
+ const multiSource = top.sources.length > 1;
10
+ const hasStructural = top.sources.includes('structural');
11
+ const highScore = top.score >= 15;
12
+ const hasOverlap = top.evidence.some((e) => e.includes('overlap'));
13
+ if (multiSource && highScore)
14
+ return 'high';
15
+ if (hasStructural && top.score >= 10)
16
+ return 'high';
17
+ if (hasOverlap)
18
+ return 'high';
19
+ if (highScore)
20
+ return 'medium';
21
+ if (ir.modeConfidence === 'low')
22
+ return 'low';
23
+ return 'medium';
24
+ }
25
+ //# sourceMappingURL=assessConfidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assessConfidence.js","sourceRoot":"","sources":["../../../src/search/ranking/assessConfidence.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAuB,EACvB,EAAW,EACX,IAAgB;IAEhB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEtC,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAC3C,MAAM,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;IAClC,MAAM,UAAU,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IAEnE,IAAI,WAAW,IAAI,SAAS;QAAE,OAAO,MAAM,CAAC;IAC5C,IAAI,aAAa,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC;IACpD,IAAI,UAAU;QAAE,OAAO,MAAM,CAAC;IAC9B,IAAI,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC/B,IAAI,EAAE,CAAC,cAAc,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAE9C,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { CodeCandidate } from '../types.js';
2
+ /**
3
+ * Merge candidates from all retrievers, deduplicating by match identity.
4
+ * Overlapping candidates get a score boost.
5
+ */
6
+ export declare function mergeCandidates(inputs: {
7
+ behavior: CodeCandidate[];
8
+ identifier: CodeCandidate[];
9
+ structural: CodeCandidate[];
10
+ }): CodeCandidate[];
@@ -0,0 +1,46 @@
1
+ import { candidateKey } from '../types.js';
2
+ /**
3
+ * Merge candidates from all retrievers, deduplicating by match identity.
4
+ * Overlapping candidates get a score boost.
5
+ */
6
+ export function mergeCandidates(inputs) {
7
+ const merged = new Map();
8
+ // Add behavior candidates
9
+ for (const c of inputs.behavior) {
10
+ merged.set(candidateKey(c), { ...c });
11
+ }
12
+ // Merge identifier candidates — boost on overlap
13
+ for (const c of inputs.identifier) {
14
+ const key = candidateKey(c);
15
+ const existing = merged.get(key);
16
+ if (existing) {
17
+ existing.score += c.score + 3; // overlap bonus
18
+ existing.evidence.push(...c.evidence, 'multi-retriever-overlap');
19
+ for (const s of c.sources) {
20
+ if (!existing.sources.includes(s))
21
+ existing.sources.push(s);
22
+ }
23
+ }
24
+ else {
25
+ merged.set(key, { ...c });
26
+ }
27
+ }
28
+ // Merge structural candidates — boost on overlap
29
+ for (const c of inputs.structural) {
30
+ const key = candidateKey(c);
31
+ const existing = merged.get(key);
32
+ if (existing) {
33
+ existing.score += c.score + 3;
34
+ existing.evidence.push(...c.evidence, 'structural-overlap');
35
+ for (const s of c.sources) {
36
+ if (!existing.sources.includes(s))
37
+ existing.sources.push(s);
38
+ }
39
+ }
40
+ else {
41
+ merged.set(key, { ...c });
42
+ }
43
+ }
44
+ return [...merged.values()];
45
+ }
46
+ //# sourceMappingURL=mergeCandidates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeCandidates.js","sourceRoot":"","sources":["../../../src/search/ranking/mergeCandidates.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAI/B;IACC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;IAEhD,0BAA0B;IAC1B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,iDAAiD;IACjD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,gBAAgB;YAC/C,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,yBAAyB,CAAC,CAAC;YACjE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC1B,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;oBAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;YAC9B,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;YAC5D,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC1B,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;oBAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { CodeCandidate, QueryIR, SearchPlan, SearchScope } from '../types.js';
2
+ import type { LspEngine } from '../../engine/LspEngine.js';
3
+ /**
4
+ * Rank candidates with mode-aware scoring + LSP enrichment.
5
+ * Applies penalties, enriches top candidates with hover signatures.
6
+ */
7
+ export declare function rankCandidates(candidates: CodeCandidate[], ctx: {
8
+ ir: QueryIR;
9
+ plan: SearchPlan;
10
+ engine: LspEngine;
11
+ scope: SearchScope;
12
+ }): Promise<CodeCandidate[]>;
@@ -0,0 +1,58 @@
1
+ import { formatHover } from '../../format/markdown.js';
2
+ import { relativePath } from '../../engine/positions.js';
3
+ const TEST_PATTERN = /\.(spec|test|stories)\.(ts|tsx|js|jsx)$/;
4
+ const GENERATED_PATTERN = /\/(dist|es|build|generated|__generated__|\.cache)\//;
5
+ const DEMO_PATTERN = /\/(demo|example|storybook|stories|fixtures)\//;
6
+ /**
7
+ * Rank candidates with mode-aware scoring + LSP enrichment.
8
+ * Applies penalties, enriches top candidates with hover signatures.
9
+ */
10
+ export async function rankCandidates(candidates, ctx) {
11
+ // Apply penalties
12
+ for (const c of candidates) {
13
+ if (TEST_PATTERN.test(c.filePath)) {
14
+ c.score -= 3;
15
+ c.evidence.push('penalty: test-file');
16
+ }
17
+ if (GENERATED_PATTERN.test(c.filePath)) {
18
+ c.score -= 10;
19
+ c.evidence.push('penalty: generated');
20
+ }
21
+ if (DEMO_PATTERN.test(c.filePath)) {
22
+ c.score -= 4;
23
+ c.evidence.push('penalty: demo');
24
+ }
25
+ if (c.filePath.endsWith('.d.ts')) {
26
+ c.score -= 6;
27
+ c.evidence.push('penalty: declaration-file');
28
+ }
29
+ }
30
+ // Filter out negative scores
31
+ const filtered = candidates.filter((c) => c.score > 0);
32
+ // Sort by score
33
+ filtered.sort((a, b) => b.score - a.score);
34
+ // LSP enrichment for top candidates (use actual match position, not line 0)
35
+ const enrichLimit = Math.min(15, filtered.length);
36
+ for (let i = 0; i < enrichLimit; i++) {
37
+ const c = filtered[i];
38
+ try {
39
+ const { uri } = await ctx.engine.prepareFile(c.filePath);
40
+ const position = { line: c.line - 1, character: c.column ?? 0 };
41
+ const hover = await ctx.engine.request('textDocument/hover', { textDocument: { uri }, position }, 5000);
42
+ if (hover) {
43
+ c.signature = formatHover(hover).substring(0, 200);
44
+ if (!c.sources.includes('lsp'))
45
+ c.sources.push('lsp');
46
+ }
47
+ }
48
+ catch (err) {
49
+ c.evidence.push(`lsp-enrich-failed: ${err?.message ?? 'unknown'}`);
50
+ }
51
+ }
52
+ // Relativize file paths
53
+ for (const c of filtered) {
54
+ c.filePath = relativePath(c.filePath, ctx.engine.workspaceRoot);
55
+ }
56
+ return filtered;
57
+ }
58
+ //# sourceMappingURL=rankCandidates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rankCandidates.js","sourceRoot":"","sources":["../../../src/search/ranking/rankCandidates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { QueryIR, SearchScope, WorkspaceIndex, CodeCandidate } from '../types.js';
2
+ /**
3
+ * Retrieve declaration-oriented candidates based on behavior family matching.
4
+ * Uses declaration index + family classifier/expansion terms + file/symbol hints.
5
+ * Does NOT use exact identifier matching — that's the identifier retriever's job.
6
+ */
7
+ export declare function retrieveBehaviorCandidates(ir: QueryIR, scope: SearchScope, index: WorkspaceIndex): CodeCandidate[];
@@ -0,0 +1,91 @@
1
+ import { BEHAVIOR_FAMILIES } from '../families/behaviorFamilies.js';
2
+ import { buildSnippetFromFile } from '../../analysis/ts/snippets.js';
3
+ /**
4
+ * Prefix-based partial match with minimum length guard.
5
+ * Prevents false positives like 'permission'.includes('is').
6
+ */
7
+ function isStrongPartialMatch(a, b) {
8
+ if (a.length < 4 || b.length < 4)
9
+ return false;
10
+ return a.startsWith(b) || b.startsWith(a);
11
+ }
12
+ /**
13
+ * Retrieve declaration-oriented candidates based on behavior family matching.
14
+ * Uses declaration index + family classifier/expansion terms + file/symbol hints.
15
+ * Does NOT use exact identifier matching — that's the identifier retriever's job.
16
+ */
17
+ export function retrieveBehaviorCandidates(ir, scope, index) {
18
+ const candidates = [];
19
+ const matchedFamilies = BEHAVIOR_FAMILIES.filter((f) => ir.familyScores[f.id] > 0);
20
+ if (matchedFamilies.length === 0 && ir.nlTokens.length === 0)
21
+ return [];
22
+ // Score each declaration against NL tokens + family hints
23
+ for (const decl of index.declarations) {
24
+ let score = 0;
25
+ const evidence = [];
26
+ // Family symbol hints
27
+ for (const family of matchedFamilies) {
28
+ for (const hint of family.symbolHints) {
29
+ for (const tok of decl.symbolTokens) {
30
+ if (tok === hint) {
31
+ score += 5;
32
+ evidence.push(`symbol-hint: ${hint}`);
33
+ break;
34
+ }
35
+ if (isStrongPartialMatch(tok, hint)) {
36
+ score += 2;
37
+ evidence.push(`symbol-partial: ${hint}~${tok}`);
38
+ break;
39
+ }
40
+ }
41
+ }
42
+ for (const hint of family.fileHints) {
43
+ for (const tok of decl.pathTokens) {
44
+ if (tok.includes(hint)) {
45
+ score += 3;
46
+ evidence.push(`file-hint: ${hint}`);
47
+ break;
48
+ }
49
+ }
50
+ }
51
+ }
52
+ // NL token matching against symbol tokens
53
+ for (const nlTok of ir.nlTokens) {
54
+ for (const symTok of decl.symbolTokens) {
55
+ if (symTok === nlTok) {
56
+ score += 4;
57
+ evidence.push(`nl-match: ${nlTok}`);
58
+ break;
59
+ }
60
+ if (isStrongPartialMatch(symTok, nlTok)) {
61
+ score += 2;
62
+ evidence.push(`nl-partial: ${nlTok}~${symTok}`);
63
+ break;
64
+ }
65
+ }
66
+ }
67
+ // Export bonus
68
+ if (decl.isExported && score > 0) {
69
+ score += 2;
70
+ evidence.push('exported');
71
+ }
72
+ if (score > 0) {
73
+ const { snippet, context } = buildSnippetFromFile(decl.filePath, decl.line, 1);
74
+ candidates.push({
75
+ candidateType: 'declaration',
76
+ filePath: decl.filePath,
77
+ line: decl.line,
78
+ column: decl.column,
79
+ symbol: decl.symbol,
80
+ kind: decl.kind,
81
+ snippet,
82
+ context,
83
+ score,
84
+ evidence,
85
+ sources: ['behavior'],
86
+ });
87
+ }
88
+ }
89
+ return candidates.sort((a, b) => b.score - a.score);
90
+ }
91
+ //# sourceMappingURL=behaviorRetriever.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"behaviorRetriever.js","sourceRoot":"","sources":["../../../src/search/retrievers/behaviorRetriever.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { QueryIR, SearchScope, WorkspaceIndex, CodeCandidate } from '../types.js';
2
+ /**
3
+ * Retrieve usage-oriented candidates based on exact identifier matching.
4
+ * Uses usage index — call expressions, member calls, imports, JSX tags.
5
+ * This is the missing backend that makes useEffect, Promise.all queries work.
6
+ */
7
+ export declare function retrieveIdentifierCandidates(ir: QueryIR, scope: SearchScope, index: WorkspaceIndex): CodeCandidate[];
@@ -0,0 +1,66 @@
1
+ import { buildSnippetFromFile } from '../../analysis/ts/snippets.js';
2
+ /**
3
+ * Retrieve usage-oriented candidates based on exact identifier matching.
4
+ * Uses usage index — call expressions, member calls, imports, JSX tags.
5
+ * This is the missing backend that makes useEffect, Promise.all queries work.
6
+ */
7
+ export function retrieveIdentifierCandidates(ir, scope, index) {
8
+ const candidates = [];
9
+ if (ir.exactIdentifiers.length === 0 && ir.dottedIdentifiers.length === 0)
10
+ return [];
11
+ for (const usage of index.usages) {
12
+ let score = 0;
13
+ const evidence = [];
14
+ // Exact identifier match
15
+ for (const id of ir.exactIdentifiers) {
16
+ if (usage.identifier === id || usage.normalizedIdentifier === id) {
17
+ score += 10;
18
+ evidence.push(`exact-identifier: ${id}`);
19
+ }
20
+ }
21
+ // Dotted identifier match (Promise.all, React.useEffect)
22
+ for (const id of ir.dottedIdentifiers) {
23
+ if (usage.identifier === id) {
24
+ score += 10;
25
+ evidence.push(`dotted-identifier: ${id}`);
26
+ }
27
+ else if (usage.identifier.endsWith(id.split('.').pop() ?? '')) {
28
+ score += 5;
29
+ evidence.push(`dotted-partial: ${id}`);
30
+ }
31
+ }
32
+ if (score === 0)
33
+ continue;
34
+ // Usage kind bonus
35
+ if (usage.kind === 'call' || usage.kind === 'member-call') {
36
+ score += 2;
37
+ evidence.push(`usage-kind: ${usage.kind}`);
38
+ }
39
+ else if (usage.kind === 'import') {
40
+ score += 1;
41
+ evidence.push('usage-kind: import');
42
+ }
43
+ // Enclosing symbol context
44
+ if (usage.enclosingSymbol) {
45
+ evidence.push(`enclosing: ${usage.enclosingSymbol}`);
46
+ }
47
+ const { snippet, context } = buildSnippetFromFile(usage.filePath, usage.line, 1);
48
+ candidates.push({
49
+ candidateType: 'usage',
50
+ filePath: usage.filePath,
51
+ line: usage.line,
52
+ column: usage.column,
53
+ matchedIdentifier: usage.identifier,
54
+ enclosingSymbol: usage.enclosingSymbol,
55
+ enclosingKind: usage.enclosingKind,
56
+ kind: 'usage',
57
+ snippet,
58
+ context,
59
+ score,
60
+ evidence,
61
+ sources: ['identifier'],
62
+ });
63
+ }
64
+ return candidates.sort((a, b) => b.score - a.score);
65
+ }
66
+ //# sourceMappingURL=identifierRetriever.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifierRetriever.js","sourceRoot":"","sources":["../../../src/search/retrievers/identifierRetriever.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { QueryIR, SearchScope, WorkspaceIndex, CodeCandidate } from '../types.js';
2
+ /**
3
+ * Retrieve candidates using structural predicate evaluation.
4
+ *
5
+ * Two-step design:
6
+ * Step 1: Locate candidate nodes (prefer usage sites of exact identifiers)
7
+ * Step 2: Evaluate structural predicates on each candidate node
8
+ *
9
+ * This is NOT recipe-only string patterns — it uses TS AST node evaluation.
10
+ */
11
+ export declare function retrieveStructuralCandidates(ir: QueryIR, scope: SearchScope, index: WorkspaceIndex): CodeCandidate[];
@@ -0,0 +1,141 @@
1
+ import { parseSourceFile } from '../../analysis/ts/parseSourceFile.js';
2
+ import { evaluateStructuralPredicates } from '../../analysis/ts/structuralPredicates.js';
3
+ import { buildSnippetFromFile } from '../../analysis/ts/snippets.js';
4
+ import ts from 'typescript';
5
+ /**
6
+ * Retrieve candidates using structural predicate evaluation.
7
+ *
8
+ * Two-step design:
9
+ * Step 1: Locate candidate nodes (prefer usage sites of exact identifiers)
10
+ * Step 2: Evaluate structural predicates on each candidate node
11
+ *
12
+ * This is NOT recipe-only string patterns — it uses TS AST node evaluation.
13
+ */
14
+ export function retrieveStructuralCandidates(ir, scope, index) {
15
+ if (ir.structuralPredicates.length === 0)
16
+ return [];
17
+ const candidates = [];
18
+ const maxFiles = 80;
19
+ let filesChecked = 0;
20
+ // Step 1: Determine which files to check
21
+ // If we have exact identifiers, only check files with matching usages
22
+ const targetFiles = new Set();
23
+ if (ir.exactIdentifiers.length > 0 || ir.dottedIdentifiers.length > 0) {
24
+ const allIds = [...ir.exactIdentifiers, ...ir.dottedIdentifiers];
25
+ for (const usage of index.usages) {
26
+ if (allIds.some((id) => usage.identifier === id || usage.normalizedIdentifier === id)) {
27
+ targetFiles.add(usage.filePath);
28
+ }
29
+ }
30
+ }
31
+ else {
32
+ // No specific identifier — check files from behavior or all indexed files (capped)
33
+ for (const file of index.files.keys()) {
34
+ targetFiles.add(file);
35
+ if (targetFiles.size >= maxFiles)
36
+ break;
37
+ }
38
+ }
39
+ // Step 2: For each target file, find nodes and evaluate predicates
40
+ for (const filePath of targetFiles) {
41
+ if (filesChecked >= maxFiles)
42
+ break;
43
+ filesChecked++;
44
+ const sf = parseSourceFile(filePath);
45
+ if (!sf)
46
+ continue;
47
+ // Find candidate nodes: call expressions matching our identifiers
48
+ const allIds = [...ir.exactIdentifiers, ...ir.dottedIdentifiers];
49
+ const nodes = findTargetCallNodes(sf, allIds);
50
+ for (const { node, identifier, line } of nodes) {
51
+ const { matched, evidence } = evaluateStructuralPredicates(sf, node, ir.structuralPredicates);
52
+ if (matched.length === 0)
53
+ continue;
54
+ const score = matched.length * 5 + (matched.length === ir.structuralPredicates.length ? 5 : 0);
55
+ const { snippet, context } = buildSnippetFromFile(filePath, line, 2);
56
+ // Find enclosing function/component name
57
+ const enclosing = findEnclosingDeclaration(node);
58
+ candidates.push({
59
+ candidateType: 'usage',
60
+ filePath,
61
+ line,
62
+ column: sf.getLineAndCharacterOfPosition(node.getStart(sf)).character,
63
+ matchedIdentifier: identifier,
64
+ enclosingSymbol: enclosing?.name,
65
+ enclosingKind: enclosing?.kind,
66
+ kind: 'usage',
67
+ snippet,
68
+ context,
69
+ score,
70
+ evidence: [
71
+ `structural-match: ${matched.join(', ')}`,
72
+ `predicates: ${matched.length}/${ir.structuralPredicates.length}`,
73
+ ...evidence,
74
+ ],
75
+ sources: ['structural'],
76
+ });
77
+ }
78
+ }
79
+ return candidates.sort((a, b) => b.score - a.score);
80
+ }
81
+ /**
82
+ * Find call expression nodes that match the target identifiers.
83
+ * For queries without identifiers, returns all top-level call expressions.
84
+ */
85
+ function findTargetCallNodes(sf, identifiers) {
86
+ const nodes = [];
87
+ function visit(node) {
88
+ if (ts.isCallExpression(node)) {
89
+ const expr = node.expression;
90
+ let name = null;
91
+ if (ts.isIdentifier(expr)) {
92
+ name = expr.text;
93
+ }
94
+ else if (ts.isPropertyAccessExpression(expr)) {
95
+ name = getFullPropertyAccess(expr);
96
+ }
97
+ if (name) {
98
+ const matches = identifiers.length === 0 ||
99
+ identifiers.some((id) => name === id || name.endsWith(`.${id}`));
100
+ if (matches) {
101
+ const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
102
+ nodes.push({ node, identifier: name, line });
103
+ }
104
+ }
105
+ }
106
+ ts.forEachChild(node, visit);
107
+ }
108
+ visit(sf);
109
+ return nodes;
110
+ }
111
+ function getFullPropertyAccess(node) {
112
+ const parts = [node.name.text];
113
+ let current = node.expression;
114
+ while (ts.isPropertyAccessExpression(current)) {
115
+ parts.unshift(current.name.text);
116
+ current = current.expression;
117
+ }
118
+ if (ts.isIdentifier(current))
119
+ parts.unshift(current.text);
120
+ return parts.join('.');
121
+ }
122
+ function findEnclosingDeclaration(node) {
123
+ let current = node.parent;
124
+ while (current) {
125
+ if (ts.isFunctionDeclaration(current) && current.name) {
126
+ return { name: current.name.text, kind: 'function' };
127
+ }
128
+ if (ts.isVariableDeclaration(current) && ts.isIdentifier(current.name)) {
129
+ return { name: current.name.text, kind: 'variable' };
130
+ }
131
+ if (ts.isMethodDeclaration(current) && ts.isIdentifier(current.name)) {
132
+ return { name: current.name.text, kind: 'method' };
133
+ }
134
+ if (ts.isClassDeclaration(current) && current.name) {
135
+ return { name: current.name.text, kind: 'class' };
136
+ }
137
+ current = current.parent;
138
+ }
139
+ return undefined;
140
+ }
141
+ //# sourceMappingURL=structuralRetriever.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structuralRetriever.js","sourceRoot":"","sources":["../../../src/search/retrievers/structuralRetriever.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,4BAA4B,EAAE,MAAM,2CAA2C,CAAC;AACzF,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B;;;;;;;;GAQG;AACH,MAAM,UAAU,4BAA4B,CAC1C,EAAW,EACX,KAAkB,EAClB,KAAqB;IAErB,IAAI,EAAE,CAAC,oBAAoB,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpD,MAAM,UAAU,GAAoB,EAAE,CAAC;IACvC,MAAM,QAAQ,GAAG,EAAE,CAAC;IACpB,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,yCAAyC;IACzC,sEAAsE;IACtE,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IAEtC,IAAI,EAAE,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtE,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,CAAC;QACjE,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,KAAK,EAAE,IAAI,KAAK,CAAC,oBAAoB,KAAK,EAAE,CAAC,EAAE,CAAC;gBACtF,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;SAAM,CAAC;QACN,mFAAmF;QACnF,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YACtC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,WAAW,CAAC,IAAI,IAAI,QAAQ;gBAAE,MAAM;QAC1C,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,KAAK,MAAM,QAAQ,IAAI,WAAW,EAAE,CAAC;QACnC,IAAI,YAAY,IAAI,QAAQ;YAAE,MAAM;QACpC,YAAY,EAAE,CAAC;QAEf,MAAM,EAAE,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,EAAE;YAAE,SAAS;QAElB,kEAAkE;QAClE,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,CAAC;QACjE,MAAM,KAAK,GAAG,mBAAmB,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAE9C,KAAK,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,CAAC;YAC/C,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,4BAA4B,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,CAAC;YAE9F,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAEnC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/F,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,oBAAoB,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAErE,yCAAyC;YACzC,MAAM,SAAS,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC;YAEjD,UAAU,CAAC,IAAI,CAAC;gBACd,aAAa,EAAE,OAAO;gBACtB,QAAQ;gBACR,IAAI;gBACJ,MAAM,EAAE,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;gBACrE,iBAAiB,EAAE,UAAU;gBAC7B,eAAe,EAAE,SAAS,EAAE,IAAI;gBAChC,aAAa,EAAE,SAAS,EAAE,IAAI;gBAC9B,IAAI,EAAE,OAAO;gBACb,OAAO;gBACP,OAAO;gBACP,KAAK;gBACL,QAAQ,EAAE;oBACR,qBAAqB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBACzC,eAAe,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,oBAAoB,CAAC,MAAM,EAAE;oBACjE,GAAG,QAAQ;iBACZ;gBACD,OAAO,EAAE,CAAC,YAAY,CAAC;aACxB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AACtD,CAAC;AAQD;;;GAGG;AACH,SAAS,mBAAmB,CAAC,EAAiB,EAAE,WAAqB;IACnE,MAAM,KAAK,GAAiB,EAAE,CAAC;IAE/B,SAAS,KAAK,CAAC,IAAa;QAC1B,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;YAC7B,IAAI,IAAI,GAAkB,IAAI,CAAC;YAE/B,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACnB,CAAC;iBAAM,IAAI,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/C,IAAI,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;YACrC,CAAC;YAED,IAAI,IAAI,EAAE,CAAC;gBACT,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,KAAK,CAAC;oBACtC,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,IAAI,IAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;gBAEpE,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,IAAI,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;oBAC1E,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/C,CAAC;YACH,CAAC;QACH,CAAC;QAED,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAiC;IAC9D,MAAM,KAAK,GAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,OAAO,GAAkB,IAAI,CAAC,UAAU,CAAC;IAC7C,OAAO,EAAE,CAAC,0BAA0B,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9C,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC;IAC/B,CAAC;IACD,IAAI,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC;QAAE,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,wBAAwB,CAAC,IAAa;IAC7C,IAAI,OAAO,GAAwB,IAAI,CAAC,MAAM,CAAC;IAC/C,OAAO,OAAO,EAAE,CAAC;QACf,IAAI,EAAE,CAAC,qBAAqB,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACtD,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QACvD,CAAC;QACD,IAAI,EAAE,CAAC,qBAAqB,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACvE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QACvD,CAAC;QACD,IAAI,EAAE,CAAC,mBAAmB,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACrE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QACrD,CAAC;QACD,IAAI,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACnD,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QACpD,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAC3B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,121 @@
1
+ export type StructuralPredicate = 'conditional' | 'returns-function' | 'returns-cleanup' | 'no-cleanup' | 'has-try-catch' | 'no-try-catch' | 'switch-no-default' | 'await-in-loop' | 'inside-hook' | 'hook-callback';
2
+ export interface QueryIR {
3
+ raw: string;
4
+ /** Natural-language tokens (lowercased, stop-words removed) */
5
+ nlTokens: string[];
6
+ /** Multi-word phrases preserved from the query */
7
+ phrases: string[];
8
+ /** Exact identifiers: useEffect, AbortController, useMemo */
9
+ exactIdentifiers: string[];
10
+ /** Dotted identifiers: Promise.all, React.useEffect */
11
+ dottedIdentifiers: string[];
12
+ /** Code-oriented tokens that aren't exact identifiers: cleanup, callback */
13
+ codeTokens: string[];
14
+ /** Per-family relevance scores */
15
+ familyScores: Record<string, number>;
16
+ /** Structural predicates inferred from the query */
17
+ structuralPredicates: StructuralPredicate[];
18
+ /** Routing mode */
19
+ mode: 'behavior' | 'identifier' | 'structural' | 'mixed';
20
+ modeConfidence: 'high' | 'medium' | 'low';
21
+ routingReasons: string[];
22
+ }
23
+ export interface SearchPlan {
24
+ mode: 'behavior' | 'identifier' | 'structural' | 'mixed';
25
+ retrievers: Array<'behavior' | 'identifier' | 'structural'>;
26
+ reasons: string[];
27
+ }
28
+ export interface SearchScope {
29
+ roots: string[];
30
+ filePaths?: string[];
31
+ includeTests: boolean;
32
+ }
33
+ export interface CodeCandidate {
34
+ candidateType: 'declaration' | 'usage' | 'pattern';
35
+ filePath: string;
36
+ line: number;
37
+ column?: number;
38
+ symbol?: string;
39
+ enclosingSymbol?: string;
40
+ enclosingKind?: string;
41
+ matchedIdentifier?: string;
42
+ kind?: 'function' | 'class' | 'method' | 'variable' | 'component' | 'file' | 'usage' | 'pattern';
43
+ signature?: string;
44
+ snippet?: string;
45
+ context?: string;
46
+ score: number;
47
+ confidence?: 'high' | 'medium' | 'low';
48
+ evidence: string[];
49
+ sources: Array<'behavior' | 'identifier' | 'structural' | 'lsp'>;
50
+ }
51
+ /**
52
+ * Deduplication key for candidates.
53
+ * Uses match identity, not just symbol name.
54
+ */
55
+ export declare function candidateKey(c: CodeCandidate): string;
56
+ export interface DeclarationIndexEntry {
57
+ symbol: string;
58
+ kind: string;
59
+ filePath: string;
60
+ line: number;
61
+ column: number;
62
+ isExported: boolean;
63
+ pathTokens: string[];
64
+ symbolTokens: string[];
65
+ }
66
+ export interface UsageIndexEntry {
67
+ identifier: string;
68
+ normalizedIdentifier: string;
69
+ kind: 'call' | 'member-call' | 'identifier' | 'import' | 'jsx-tag';
70
+ filePath: string;
71
+ line: number;
72
+ column: number;
73
+ enclosingSymbol?: string;
74
+ enclosingKind?: string;
75
+ pathTokens: string[];
76
+ }
77
+ export interface IndexedFile {
78
+ filePath: string;
79
+ mtimeMs: number;
80
+ declarations: DeclarationIndexEntry[];
81
+ usages: UsageIndexEntry[];
82
+ }
83
+ export interface WorkspaceIndex {
84
+ root: string;
85
+ builtAt: number;
86
+ files: Map<string, IndexedFile>;
87
+ declarations: DeclarationIndexEntry[];
88
+ usages: UsageIndexEntry[];
89
+ }
90
+ export interface FindCodeResult {
91
+ query: string;
92
+ ir: QueryIR;
93
+ plan: SearchPlan;
94
+ confidence: 'high' | 'medium' | 'low';
95
+ candidates: CodeCandidate[];
96
+ stats: {
97
+ filesIndexed: number;
98
+ declarationHits: number;
99
+ usageHits: number;
100
+ structuralHits: number;
101
+ lspEnriched: number;
102
+ elapsedMs: number;
103
+ partialResult: boolean;
104
+ };
105
+ warnings: string[];
106
+ }
107
+ export interface PatternMatch {
108
+ filePath: string;
109
+ line: number;
110
+ column?: number;
111
+ text: string;
112
+ context: string;
113
+ }
114
+ export interface FindPatternResult {
115
+ pattern: string;
116
+ language: 'typescript' | 'tsx' | 'javascript';
117
+ filesScanned: number;
118
+ matchCount: number;
119
+ matches: PatternMatch[];
120
+ warnings: string[];
121
+ }