viberag 0.1.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 (151) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +219 -0
  3. package/dist/cli/__tests__/mcp-setup.test.d.ts +6 -0
  4. package/dist/cli/__tests__/mcp-setup.test.js +597 -0
  5. package/dist/cli/app.d.ts +2 -0
  6. package/dist/cli/app.js +238 -0
  7. package/dist/cli/commands/handlers.d.ts +57 -0
  8. package/dist/cli/commands/handlers.js +231 -0
  9. package/dist/cli/commands/index.d.ts +2 -0
  10. package/dist/cli/commands/index.js +2 -0
  11. package/dist/cli/commands/mcp-setup.d.ts +107 -0
  12. package/dist/cli/commands/mcp-setup.js +509 -0
  13. package/dist/cli/commands/useRagCommands.d.ts +23 -0
  14. package/dist/cli/commands/useRagCommands.js +180 -0
  15. package/dist/cli/components/CleanWizard.d.ts +17 -0
  16. package/dist/cli/components/CleanWizard.js +169 -0
  17. package/dist/cli/components/InitWizard.d.ts +20 -0
  18. package/dist/cli/components/InitWizard.js +370 -0
  19. package/dist/cli/components/McpSetupWizard.d.ts +37 -0
  20. package/dist/cli/components/McpSetupWizard.js +387 -0
  21. package/dist/cli/components/SearchResultsDisplay.d.ts +13 -0
  22. package/dist/cli/components/SearchResultsDisplay.js +130 -0
  23. package/dist/cli/components/WelcomeBanner.d.ts +10 -0
  24. package/dist/cli/components/WelcomeBanner.js +26 -0
  25. package/dist/cli/components/index.d.ts +1 -0
  26. package/dist/cli/components/index.js +1 -0
  27. package/dist/cli/data/mcp-editors.d.ts +80 -0
  28. package/dist/cli/data/mcp-editors.js +270 -0
  29. package/dist/cli/index.d.ts +2 -0
  30. package/dist/cli/index.js +26 -0
  31. package/dist/cli-bundle.cjs +5269 -0
  32. package/dist/common/commands/terminalSetup.d.ts +2 -0
  33. package/dist/common/commands/terminalSetup.js +144 -0
  34. package/dist/common/components/CommandSuggestions.d.ts +9 -0
  35. package/dist/common/components/CommandSuggestions.js +20 -0
  36. package/dist/common/components/StaticWithResize.d.ts +23 -0
  37. package/dist/common/components/StaticWithResize.js +62 -0
  38. package/dist/common/components/StatusBar.d.ts +8 -0
  39. package/dist/common/components/StatusBar.js +64 -0
  40. package/dist/common/components/TextInput.d.ts +12 -0
  41. package/dist/common/components/TextInput.js +239 -0
  42. package/dist/common/components/index.d.ts +3 -0
  43. package/dist/common/components/index.js +3 -0
  44. package/dist/common/hooks/index.d.ts +4 -0
  45. package/dist/common/hooks/index.js +4 -0
  46. package/dist/common/hooks/useCommandHistory.d.ts +7 -0
  47. package/dist/common/hooks/useCommandHistory.js +51 -0
  48. package/dist/common/hooks/useCtrlC.d.ts +9 -0
  49. package/dist/common/hooks/useCtrlC.js +40 -0
  50. package/dist/common/hooks/useKittyKeyboard.d.ts +10 -0
  51. package/dist/common/hooks/useKittyKeyboard.js +26 -0
  52. package/dist/common/hooks/useStaticOutputBuffer.d.ts +31 -0
  53. package/dist/common/hooks/useStaticOutputBuffer.js +58 -0
  54. package/dist/common/hooks/useTerminalResize.d.ts +28 -0
  55. package/dist/common/hooks/useTerminalResize.js +51 -0
  56. package/dist/common/hooks/useTextBuffer.d.ts +13 -0
  57. package/dist/common/hooks/useTextBuffer.js +165 -0
  58. package/dist/common/index.d.ts +13 -0
  59. package/dist/common/index.js +17 -0
  60. package/dist/common/types.d.ts +162 -0
  61. package/dist/common/types.js +1 -0
  62. package/dist/mcp/index.d.ts +12 -0
  63. package/dist/mcp/index.js +66 -0
  64. package/dist/mcp/server.d.ts +25 -0
  65. package/dist/mcp/server.js +837 -0
  66. package/dist/mcp/watcher.d.ts +86 -0
  67. package/dist/mcp/watcher.js +334 -0
  68. package/dist/rag/__tests__/grammar-smoke.test.d.ts +9 -0
  69. package/dist/rag/__tests__/grammar-smoke.test.js +161 -0
  70. package/dist/rag/__tests__/helpers.d.ts +30 -0
  71. package/dist/rag/__tests__/helpers.js +67 -0
  72. package/dist/rag/__tests__/merkle.test.d.ts +5 -0
  73. package/dist/rag/__tests__/merkle.test.js +161 -0
  74. package/dist/rag/__tests__/metadata-extraction.test.d.ts +10 -0
  75. package/dist/rag/__tests__/metadata-extraction.test.js +202 -0
  76. package/dist/rag/__tests__/multi-language.test.d.ts +13 -0
  77. package/dist/rag/__tests__/multi-language.test.js +535 -0
  78. package/dist/rag/__tests__/rag.test.d.ts +10 -0
  79. package/dist/rag/__tests__/rag.test.js +311 -0
  80. package/dist/rag/__tests__/search-exhaustive.test.d.ts +9 -0
  81. package/dist/rag/__tests__/search-exhaustive.test.js +87 -0
  82. package/dist/rag/__tests__/search-filters.test.d.ts +10 -0
  83. package/dist/rag/__tests__/search-filters.test.js +250 -0
  84. package/dist/rag/__tests__/search-modes.test.d.ts +8 -0
  85. package/dist/rag/__tests__/search-modes.test.js +133 -0
  86. package/dist/rag/config/index.d.ts +61 -0
  87. package/dist/rag/config/index.js +111 -0
  88. package/dist/rag/constants.d.ts +41 -0
  89. package/dist/rag/constants.js +57 -0
  90. package/dist/rag/embeddings/fastembed.d.ts +62 -0
  91. package/dist/rag/embeddings/fastembed.js +124 -0
  92. package/dist/rag/embeddings/gemini.d.ts +26 -0
  93. package/dist/rag/embeddings/gemini.js +116 -0
  94. package/dist/rag/embeddings/index.d.ts +10 -0
  95. package/dist/rag/embeddings/index.js +9 -0
  96. package/dist/rag/embeddings/local-4b.d.ts +28 -0
  97. package/dist/rag/embeddings/local-4b.js +51 -0
  98. package/dist/rag/embeddings/local.d.ts +29 -0
  99. package/dist/rag/embeddings/local.js +119 -0
  100. package/dist/rag/embeddings/mistral.d.ts +22 -0
  101. package/dist/rag/embeddings/mistral.js +85 -0
  102. package/dist/rag/embeddings/openai.d.ts +22 -0
  103. package/dist/rag/embeddings/openai.js +85 -0
  104. package/dist/rag/embeddings/types.d.ts +37 -0
  105. package/dist/rag/embeddings/types.js +1 -0
  106. package/dist/rag/gitignore/index.d.ts +57 -0
  107. package/dist/rag/gitignore/index.js +178 -0
  108. package/dist/rag/index.d.ts +15 -0
  109. package/dist/rag/index.js +25 -0
  110. package/dist/rag/indexer/chunker.d.ts +129 -0
  111. package/dist/rag/indexer/chunker.js +1352 -0
  112. package/dist/rag/indexer/index.d.ts +6 -0
  113. package/dist/rag/indexer/index.js +6 -0
  114. package/dist/rag/indexer/indexer.d.ts +73 -0
  115. package/dist/rag/indexer/indexer.js +356 -0
  116. package/dist/rag/indexer/types.d.ts +68 -0
  117. package/dist/rag/indexer/types.js +47 -0
  118. package/dist/rag/logger/index.d.ts +20 -0
  119. package/dist/rag/logger/index.js +75 -0
  120. package/dist/rag/manifest/index.d.ts +50 -0
  121. package/dist/rag/manifest/index.js +97 -0
  122. package/dist/rag/merkle/diff.d.ts +26 -0
  123. package/dist/rag/merkle/diff.js +95 -0
  124. package/dist/rag/merkle/hash.d.ts +34 -0
  125. package/dist/rag/merkle/hash.js +165 -0
  126. package/dist/rag/merkle/index.d.ts +68 -0
  127. package/dist/rag/merkle/index.js +298 -0
  128. package/dist/rag/merkle/node.d.ts +51 -0
  129. package/dist/rag/merkle/node.js +69 -0
  130. package/dist/rag/search/filters.d.ts +21 -0
  131. package/dist/rag/search/filters.js +100 -0
  132. package/dist/rag/search/fts.d.ts +32 -0
  133. package/dist/rag/search/fts.js +61 -0
  134. package/dist/rag/search/hybrid.d.ts +17 -0
  135. package/dist/rag/search/hybrid.js +58 -0
  136. package/dist/rag/search/index.d.ts +89 -0
  137. package/dist/rag/search/index.js +367 -0
  138. package/dist/rag/search/types.d.ts +130 -0
  139. package/dist/rag/search/types.js +4 -0
  140. package/dist/rag/search/vector.d.ts +25 -0
  141. package/dist/rag/search/vector.js +44 -0
  142. package/dist/rag/storage/index.d.ts +92 -0
  143. package/dist/rag/storage/index.js +287 -0
  144. package/dist/rag/storage/lancedb-native.d.ts +7 -0
  145. package/dist/rag/storage/lancedb-native.js +10 -0
  146. package/dist/rag/storage/schema.d.ts +23 -0
  147. package/dist/rag/storage/schema.js +50 -0
  148. package/dist/rag/storage/types.d.ts +100 -0
  149. package/dist/rag/storage/types.js +68 -0
  150. package/package.json +67 -0
  151. package/scripts/check-node-version.js +37 -0
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Merkle tree unit tests.
3
+ * Tests tree structure building and change detection (diffing).
4
+ */
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
+ import { MerkleTree } from '../merkle/index.js';
7
+ import { copyFixtureToTemp, addFile, modifyFile, deleteFile, waitForFs, } from './helpers.js';
8
+ /**
9
+ * Helper to collect all file paths from tree.
10
+ */
11
+ function collectFiles(node) {
12
+ if (!node)
13
+ return [];
14
+ if (node.type === 'file')
15
+ return [node.path];
16
+ const files = [];
17
+ if (node.children) {
18
+ for (const child of node.children.values()) {
19
+ files.push(...collectFiles(child));
20
+ }
21
+ }
22
+ return files;
23
+ }
24
+ describe('MerkleTree', () => {
25
+ let ctx;
26
+ beforeEach(async () => {
27
+ ctx = await copyFixtureToTemp('codebase');
28
+ });
29
+ afterEach(async () => {
30
+ await ctx.cleanup();
31
+ });
32
+ describe('Tree structure', () => {
33
+ it('includes all files from nested directories', async () => {
34
+ const tree = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx', '.js', '.py'], []);
35
+ // Count all files in tree
36
+ const allFiles = collectFiles(tree.root);
37
+ // Should have all ~15 files (5 original + 10 nested)
38
+ expect(allFiles.length).toBeGreaterThanOrEqual(10);
39
+ // Should include deeply nested file
40
+ expect(allFiles.some(f => f.includes('deep/nested/file.ts'))).toBe(true);
41
+ // Should include files at different levels
42
+ expect(allFiles.some(f => f.includes('src/components/Button.tsx'))).toBe(true);
43
+ expect(allFiles.some(f => f.includes('src/components/forms/LoginForm.tsx'))).toBe(true);
44
+ });
45
+ it('populates directory children correctly', async () => {
46
+ const tree = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx', '.js', '.py'], []);
47
+ // Navigate to src/components
48
+ const src = tree.root?.children?.get('src');
49
+ expect(src).toBeDefined();
50
+ expect(src?.type).toBe('directory');
51
+ const components = src?.children?.get('components');
52
+ expect(components).toBeDefined();
53
+ expect(components?.type).toBe('directory');
54
+ // Should have Button.tsx, Input.tsx, and forms/ as children
55
+ expect(components?.children?.size).toBeGreaterThanOrEqual(2);
56
+ // Check Button.tsx is a child
57
+ const button = components?.children?.get('Button.tsx');
58
+ expect(button).toBeDefined();
59
+ expect(button?.type).toBe('file');
60
+ });
61
+ it('handles 4+ levels of nesting', async () => {
62
+ const tree = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx', '.js', '.py'], []);
63
+ // Navigate: src → utils → deep → nested → file.ts
64
+ const src = tree.root?.children?.get('src');
65
+ expect(src).toBeDefined();
66
+ const utils = src?.children?.get('utils');
67
+ expect(utils).toBeDefined();
68
+ const deep = utils?.children?.get('deep');
69
+ expect(deep).toBeDefined();
70
+ const nested = deep?.children?.get('nested');
71
+ expect(nested).toBeDefined();
72
+ const file = nested?.children?.get('file.ts');
73
+ expect(file).toBeDefined();
74
+ expect(file?.type).toBe('file');
75
+ expect(file?.path).toBe('src/utils/deep/nested/file.ts');
76
+ });
77
+ it('handles multiple siblings at each level', async () => {
78
+ const tree = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx', '.js', '.py'], []);
79
+ // src/services should have api.ts AND auth.ts
80
+ const services = tree.root?.children
81
+ ?.get('src')
82
+ ?.children?.get('services');
83
+ expect(services?.children?.has('api.ts')).toBe(true);
84
+ expect(services?.children?.has('auth.ts')).toBe(true);
85
+ // Root should have src/ AND lib/
86
+ expect(tree.root?.children?.has('src')).toBe(true);
87
+ expect(tree.root?.children?.has('lib')).toBe(true);
88
+ });
89
+ });
90
+ describe('Tree diffing', () => {
91
+ it('detects new file in subdirectory', async () => {
92
+ const tree1 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], []);
93
+ await addFile(ctx.projectRoot, 'src/utils/newHelper.ts', 'export const x = 1;');
94
+ await waitForFs();
95
+ const tree2 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], [], tree1);
96
+ const diff = tree1.compare(tree2);
97
+ expect(diff.new).toContain('src/utils/newHelper.ts');
98
+ expect(diff.modified.length).toBe(0);
99
+ expect(diff.deleted.length).toBe(0);
100
+ });
101
+ it('detects modified file in deeply nested directory', async () => {
102
+ const tree1 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], []);
103
+ await modifyFile(ctx.projectRoot, 'src/utils/deep/nested/file.ts', '// changed\nexport const y = 2;');
104
+ await waitForFs();
105
+ const tree2 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], [], tree1);
106
+ const diff = tree1.compare(tree2);
107
+ expect(diff.modified).toContain('src/utils/deep/nested/file.ts');
108
+ });
109
+ it('detects deleted file from subdirectory', async () => {
110
+ const tree1 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], []);
111
+ await deleteFile(ctx.projectRoot, 'src/components/Input.tsx');
112
+ await waitForFs();
113
+ const tree2 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], [], tree1);
114
+ const diff = tree1.compare(tree2);
115
+ expect(diff.deleted).toContain('src/components/Input.tsx');
116
+ });
117
+ it('detects new directory with files', async () => {
118
+ const tree1 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], []);
119
+ // Add new directory with multiple files
120
+ await addFile(ctx.projectRoot, 'src/newDir/a.ts', 'export const a = 1;');
121
+ await addFile(ctx.projectRoot, 'src/newDir/b.ts', 'export const b = 2;');
122
+ await waitForFs();
123
+ const tree2 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], [], tree1);
124
+ const diff = tree1.compare(tree2);
125
+ expect(diff.new).toContain('src/newDir/a.ts');
126
+ expect(diff.new).toContain('src/newDir/b.ts');
127
+ });
128
+ it('directory hash changes when child file changes', async () => {
129
+ const tree1 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], []);
130
+ const srcHash1 = tree1.root?.children?.get('src')?.hash;
131
+ const utilsHash1 = tree1.root?.children
132
+ ?.get('src')
133
+ ?.children?.get('utils')?.hash;
134
+ await modifyFile(ctx.projectRoot, 'src/utils/helpers.ts', '// changed');
135
+ await waitForFs();
136
+ const tree2 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], []);
137
+ const srcHash2 = tree2.root?.children?.get('src')?.hash;
138
+ const utilsHash2 = tree2.root?.children
139
+ ?.get('src')
140
+ ?.children?.get('utils')?.hash;
141
+ // Both parent directories should have different hashes
142
+ expect(utilsHash2).not.toBe(utilsHash1);
143
+ expect(srcHash2).not.toBe(srcHash1);
144
+ });
145
+ it('sibling directory hash unchanged when other sibling changes', async () => {
146
+ const tree1 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], []);
147
+ const servicesHash1 = tree1.root?.children
148
+ ?.get('src')
149
+ ?.children?.get('services')?.hash;
150
+ // Modify file in utils (sibling of services)
151
+ await modifyFile(ctx.projectRoot, 'src/utils/helpers.ts', '// changed');
152
+ await waitForFs();
153
+ const tree2 = await MerkleTree.build(ctx.projectRoot, ['.ts', '.tsx'], []);
154
+ const servicesHash2 = tree2.root?.children
155
+ ?.get('src')
156
+ ?.children?.get('services')?.hash;
157
+ // Services hash should be unchanged
158
+ expect(servicesHash2).toBe(servicesHash1);
159
+ });
160
+ });
161
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Tests for metadata extraction.
3
+ *
4
+ * Tests that the Chunker correctly extracts:
5
+ * - signature: Function/method signature lines
6
+ * - docstring: JSDoc/Python docstrings
7
+ * - is_exported: Export keyword detection
8
+ * - decorator_names: Decorator/annotation extraction
9
+ */
10
+ export {};
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Tests for metadata extraction.
3
+ *
4
+ * Tests that the Chunker correctly extracts:
5
+ * - signature: Function/method signature lines
6
+ * - docstring: JSDoc/Python docstrings
7
+ * - is_exported: Export keyword detection
8
+ * - decorator_names: Decorator/annotation extraction
9
+ */
10
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
11
+ import { Indexer } from '../indexer/indexer.js';
12
+ import { SearchEngine } from '../search/index.js';
13
+ import { copyFixtureToTemp } from './helpers.js';
14
+ describe('Metadata Extraction', () => {
15
+ let ctx;
16
+ let search;
17
+ beforeAll(async () => {
18
+ // Setup once - index the codebase
19
+ ctx = await copyFixtureToTemp('codebase');
20
+ const indexer = new Indexer(ctx.projectRoot);
21
+ await indexer.index();
22
+ indexer.close();
23
+ search = new SearchEngine(ctx.projectRoot);
24
+ }, 120000);
25
+ afterAll(async () => {
26
+ search.close();
27
+ await ctx.cleanup();
28
+ });
29
+ describe('signature extraction', () => {
30
+ it('extracts TypeScript function signature', async () => {
31
+ const results = await search.search('getUser', {
32
+ mode: 'definition',
33
+ symbolName: 'getUser',
34
+ });
35
+ expect(results.results.length).toBeGreaterThan(0);
36
+ const result = results.results[0];
37
+ // Signature should contain the function declaration
38
+ if (result?.signature) {
39
+ expect(result.signature).toContain('getUser');
40
+ }
41
+ }, 60000);
42
+ it('extracts Python function signature', async () => {
43
+ const results = await search.search('add_two_numbers', {
44
+ mode: 'definition',
45
+ symbolName: 'add_two_numbers',
46
+ });
47
+ if (results.results.length > 0) {
48
+ const result = results.results[0];
49
+ if (result?.signature) {
50
+ expect(result.signature).toContain('add_two_numbers');
51
+ }
52
+ }
53
+ }, 60000);
54
+ it('extracts class signature', async () => {
55
+ // Use semantic search with class filter
56
+ const results = await search.search('UserService class service', {
57
+ mode: 'semantic',
58
+ filters: { type: ['class'] },
59
+ });
60
+ // Should find some classes with signatures
61
+ if (results.results.length > 0) {
62
+ const classResult = results.results.find(r => r.type === 'class');
63
+ if (classResult?.signature) {
64
+ expect(classResult.signature).toContain('class');
65
+ }
66
+ }
67
+ }, 60000);
68
+ });
69
+ describe('export detection', () => {
70
+ it('detects exported functions', async () => {
71
+ // Search for exported function in exported.ts
72
+ const results = await search.search('publicUtil', {
73
+ mode: 'definition',
74
+ symbolName: 'publicUtil',
75
+ });
76
+ expect(results.results.length).toBeGreaterThan(0);
77
+ const result = results.results.find(r => r.filepath.includes('exported.ts'));
78
+ expect(result?.isExported).toBe(true);
79
+ }, 60000);
80
+ it('detects non-exported functions', async () => {
81
+ // Search for internal helper in exported.ts
82
+ const results = await search.search('internalHelper', {
83
+ mode: 'definition',
84
+ symbolName: 'internalHelper',
85
+ });
86
+ if (results.results.length > 0) {
87
+ const result = results.results.find(r => r.filepath.includes('exported.ts'));
88
+ if (result) {
89
+ expect(result.isExported).toBe(false);
90
+ }
91
+ }
92
+ }, 60000);
93
+ it('detects exported classes', async () => {
94
+ // Use semantic search with export filter
95
+ const results = await search.search('class service user', {
96
+ mode: 'semantic',
97
+ filters: {
98
+ type: ['class'],
99
+ isExported: true,
100
+ },
101
+ });
102
+ // Should find exported classes
103
+ if (results.results.length > 0) {
104
+ results.results.forEach(r => {
105
+ expect(r.isExported).toBe(true);
106
+ });
107
+ }
108
+ }, 60000);
109
+ it('filters work with isExported', async () => {
110
+ // Get only exported symbols
111
+ const exportedResults = await search.search('user', {
112
+ filters: { isExported: true },
113
+ });
114
+ // All should be exported
115
+ exportedResults.results.forEach(r => {
116
+ expect(r.isExported).toBe(true);
117
+ });
118
+ // Get non-exported
119
+ const internalResults = await search.search('helper', {
120
+ filters: { isExported: false },
121
+ });
122
+ // All should NOT be exported
123
+ internalResults.results.forEach(r => {
124
+ expect(r.isExported).toBe(false);
125
+ });
126
+ }, 60000);
127
+ });
128
+ describe('docstring extraction', () => {
129
+ it('finds documented functions via hasDocstring filter', async () => {
130
+ const results = await search.search('process', {
131
+ filters: { hasDocstring: true },
132
+ });
133
+ // Should find documented functions
134
+ expect(results.results.length).toBeGreaterThan(0);
135
+ }, 60000);
136
+ it('documented Python functions are findable', async () => {
137
+ // process_data in decorators.py has a docstring
138
+ const results = await search.search('process_data', {
139
+ mode: 'definition',
140
+ symbolName: 'process_data',
141
+ });
142
+ if (results.results.length > 0) {
143
+ const result = results.results.find(r => r.filepath.includes('decorators.py'));
144
+ // The function should be found
145
+ expect(result).toBeDefined();
146
+ }
147
+ }, 60000);
148
+ });
149
+ describe('decorator extraction', () => {
150
+ it('finds decorated Python functions', async () => {
151
+ // Search semantically for Python functions in decorators file
152
+ const results = await search.search('process data logging decorator python', {
153
+ mode: 'semantic',
154
+ filters: { extension: ['.py'] },
155
+ });
156
+ // Should find functions from decorators.py
157
+ if (results.results.length > 0) {
158
+ expect(results.results.some(r => r.filepath.includes('decorators.py'))).toBe(true);
159
+ }
160
+ }, 60000);
161
+ it('decoratorContains filter works', async () => {
162
+ const results = await search.search('function', {
163
+ filters: { decoratorContains: 'log' },
164
+ });
165
+ // Should find functions with 'log' in decorator names
166
+ // Results may be empty if extraction isn't working, but filter should not error
167
+ expect(results).toBeDefined();
168
+ }, 60000);
169
+ it('finds functions with multiple decorators', async () => {
170
+ // Search for transform function in Python files
171
+ const results = await search.search('transform value validate input', {
172
+ mode: 'semantic',
173
+ filters: { extension: ['.py'] },
174
+ });
175
+ // Should find functions from decorators.py
176
+ if (results.results.length > 0) {
177
+ expect(results.results.some(r => r.filepath.includes('.py'))).toBe(true);
178
+ }
179
+ }, 60000);
180
+ });
181
+ describe('metadata in search results', () => {
182
+ it('includes signature in results when available', async () => {
183
+ const results = await search.search('async function fetch', {
184
+ mode: 'semantic',
185
+ limit: 5,
186
+ });
187
+ // At least some results should have signatures
188
+ const withSignature = results.results.filter(r => r.signature);
189
+ expect(withSignature.length).toBeGreaterThan(0);
190
+ }, 60000);
191
+ it('includes isExported in results', async () => {
192
+ const results = await search.search('export function', {
193
+ mode: 'semantic',
194
+ limit: 10,
195
+ });
196
+ // Results should have isExported defined
197
+ results.results.forEach(r => {
198
+ expect(typeof r.isExported).toBe('boolean');
199
+ });
200
+ }, 60000);
201
+ });
202
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Multi-Language Support Tests.
3
+ *
4
+ * Tests that the indexer correctly handles 8 additional languages:
5
+ * - Go, Rust, Java, C#, Dart, Swift, Kotlin, PHP
6
+ *
7
+ * Validates:
8
+ * - Export detection (capitalization, pub, public, underscore prefix)
9
+ * - Decorator/attribute extraction (#[], @, [])
10
+ * - Docstring extraction (various comment styles)
11
+ * - Cross-language search capabilities
12
+ */
13
+ export {};