skannr 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/.eslintrc.js +22 -0
  2. package/README.md +109 -0
  3. package/dist/agent-cli.d.ts +7 -0
  4. package/dist/agent-cli.d.ts.map +1 -0
  5. package/dist/agent-cli.js +364 -0
  6. package/dist/agent-cli.js.map +1 -0
  7. package/dist/agent.d.ts +115 -0
  8. package/dist/agent.d.ts.map +1 -0
  9. package/dist/agent.js +340 -0
  10. package/dist/agent.js.map +1 -0
  11. package/dist/benchmark.d.ts +53 -0
  12. package/dist/benchmark.d.ts.map +1 -0
  13. package/dist/benchmark.js +307 -0
  14. package/dist/benchmark.js.map +1 -0
  15. package/dist/cache.d.ts +97 -0
  16. package/dist/cache.d.ts.map +1 -0
  17. package/dist/cache.js +284 -0
  18. package/dist/cache.js.map +1 -0
  19. package/dist/cli.d.ts +6 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +238 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/config.d.ts +8 -0
  24. package/dist/config.d.ts.map +1 -0
  25. package/dist/config.js +52 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/index.d.ts +18 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +176 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/languages/GenericAdapter.d.ts +10 -0
  32. package/dist/languages/GenericAdapter.d.ts.map +1 -0
  33. package/dist/languages/GenericAdapter.js +41 -0
  34. package/dist/languages/GenericAdapter.js.map +1 -0
  35. package/dist/languages/LanguageAdapter.d.ts +14 -0
  36. package/dist/languages/LanguageAdapter.d.ts.map +1 -0
  37. package/dist/languages/LanguageAdapter.js +3 -0
  38. package/dist/languages/LanguageAdapter.js.map +1 -0
  39. package/dist/languages/PythonAdapter.d.ts +10 -0
  40. package/dist/languages/PythonAdapter.d.ts.map +1 -0
  41. package/dist/languages/PythonAdapter.js +98 -0
  42. package/dist/languages/PythonAdapter.js.map +1 -0
  43. package/dist/languages/TypeScriptAdapter.d.ts +17 -0
  44. package/dist/languages/TypeScriptAdapter.d.ts.map +1 -0
  45. package/dist/languages/TypeScriptAdapter.js +321 -0
  46. package/dist/languages/TypeScriptAdapter.js.map +1 -0
  47. package/dist/languages/registry.d.ts +4 -0
  48. package/dist/languages/registry.d.ts.map +1 -0
  49. package/dist/languages/registry.js +86 -0
  50. package/dist/languages/registry.js.map +1 -0
  51. package/dist/mapper.d.ts +49 -0
  52. package/dist/mapper.d.ts.map +1 -0
  53. package/dist/mapper.js +386 -0
  54. package/dist/mapper.js.map +1 -0
  55. package/dist/ranker-enhanced.d.ts +37 -0
  56. package/dist/ranker-enhanced.d.ts.map +1 -0
  57. package/dist/ranker-enhanced.js +395 -0
  58. package/dist/ranker-enhanced.js.map +1 -0
  59. package/dist/ranker.d.ts +14 -0
  60. package/dist/ranker.d.ts.map +1 -0
  61. package/dist/ranker.js +105 -0
  62. package/dist/ranker.js.map +1 -0
  63. package/dist/rocket-chat-scope.d.ts +7 -0
  64. package/dist/rocket-chat-scope.d.ts.map +1 -0
  65. package/dist/rocket-chat-scope.js +95 -0
  66. package/dist/rocket-chat-scope.js.map +1 -0
  67. package/dist/scanner.d.ts +16 -0
  68. package/dist/scanner.d.ts.map +1 -0
  69. package/dist/scanner.js +228 -0
  70. package/dist/scanner.js.map +1 -0
  71. package/dist/skeletonizer.d.ts +5 -0
  72. package/dist/skeletonizer.d.ts.map +1 -0
  73. package/dist/skeletonizer.js +52 -0
  74. package/dist/skeletonizer.js.map +1 -0
  75. package/dist/tokenizer.d.ts +9 -0
  76. package/dist/tokenizer.d.ts.map +1 -0
  77. package/dist/tokenizer.js +21 -0
  78. package/dist/tokenizer.js.map +1 -0
  79. package/dist/types.d.ts +68 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +6 -0
  82. package/dist/types.js.map +1 -0
  83. package/gemini-extension/GEMINI.md +26 -0
  84. package/gemini-extension/gemini-extension.json +12 -0
  85. package/gemini-extension/package.json +14 -0
  86. package/gemini-extension/src/server.ts +63 -0
  87. package/gemini-extension/tsconfig.json +14 -0
  88. package/jest.config.js +5 -0
  89. package/package.json +46 -0
  90. package/src/agent-cli.ts +383 -0
  91. package/src/agent.ts +344 -0
  92. package/src/benchmark.ts +389 -0
  93. package/src/cache.ts +317 -0
  94. package/src/cli.ts +223 -0
  95. package/src/config.ts +22 -0
  96. package/src/index.ts +215 -0
  97. package/src/languages/GenericAdapter.ts +44 -0
  98. package/src/languages/LanguageAdapter.ts +14 -0
  99. package/src/languages/PythonAdapter.ts +74 -0
  100. package/src/languages/TypeScriptAdapter.ts +338 -0
  101. package/src/languages/registry.ts +49 -0
  102. package/src/mapper.ts +448 -0
  103. package/src/ranker-enhanced.ts +460 -0
  104. package/src/ranker.ts +92 -0
  105. package/src/scanner.ts +201 -0
  106. package/src/skeletonizer.ts +16 -0
  107. package/src/tokenizer.ts +20 -0
  108. package/src/types.ts +71 -0
  109. package/tests/agent.tools.test.ts +81 -0
  110. package/tests/benchmark.test.ts +31 -0
  111. package/tests/fixtures/sample.py +17 -0
  112. package/tests/fixtures/sample.ts +13 -0
  113. package/tests/fixtures/src/api/routes.ts +1 -0
  114. package/tests/fixtures/src/auth/permission.ts +3 -0
  115. package/tests/ranker-enhanced.test.ts +68 -0
  116. package/tests/ranker.test.ts +75 -0
  117. package/tests/scanner.scope.test.ts +41 -0
  118. package/tests/setup-fixtures.js +29 -0
  119. package/tests/skeletonizer.test.ts +142 -0
  120. package/tsconfig.json +21 -0
  121. package/uca-landing/index.html +17 -0
  122. package/uca-landing/package.json +23 -0
  123. package/uca-landing/postcss.config.js +6 -0
  124. package/uca-landing/src/App.jsx +43 -0
  125. package/uca-landing/src/components/AgentMode.jsx +45 -0
  126. package/uca-landing/src/components/CliReference.jsx +49 -0
  127. package/uca-landing/src/components/Features.jsx +63 -0
  128. package/uca-landing/src/components/Footer.jsx +35 -0
  129. package/uca-landing/src/components/Hero.jsx +124 -0
  130. package/uca-landing/src/components/HowItWorks.jsx +60 -0
  131. package/uca-landing/src/components/Install.jsx +90 -0
  132. package/uca-landing/src/components/LanguageSupport.jsx +63 -0
  133. package/uca-landing/src/components/Navbar.jsx +86 -0
  134. package/uca-landing/src/components/Problem.jsx +51 -0
  135. package/uca-landing/src/components/Reveal.jsx +40 -0
  136. package/uca-landing/src/components/WorksWith.jsx +59 -0
  137. package/uca-landing/src/hooks/useScrollNav.js +13 -0
  138. package/uca-landing/src/hooks/useTypewriter.js +41 -0
  139. package/uca-landing/src/index.css +13 -0
  140. package/uca-landing/src/main.jsx +10 -0
  141. package/uca-landing/tailwind.config.js +68 -0
  142. package/uca-landing/vite.config.js +6 -0
package/src/agent.ts ADDED
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Gemini AI Agent Integration for Code Analysis
3
+ * This module provides tool definitions and agent setup for agentic code analysis
4
+ */
5
+
6
+ import { GoogleGenerativeAI, SchemaType } from '@google/generative-ai';
7
+ import { getSymbolDetails, loadMappingFromFile, type SymbolMapping } from './mapper';
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+
11
+ /**
12
+ * Tool definition for getting symbol details
13
+ */
14
+ export const getSymbolDetailsTool = {
15
+ name: 'get_symbol_details',
16
+ description: `Retrieve the full implementation of a code symbol (function, method, class) from the original source file.
17
+ Use this when you need to see the actual implementation of a function or method that was marked with [SYMBOL:...] in the skeleton.
18
+ The symbolId is in the format "ClassName.methodName" or "functionName" for top-level functions.`,
19
+ parameters: {
20
+ type: SchemaType.OBJECT,
21
+ properties: {
22
+ symbolId: {
23
+ type: SchemaType.STRING,
24
+ description: 'The symbol identifier from the skeleton code (e.g., "AuthService.login" or "sendMessage")',
25
+ },
26
+ },
27
+ required: ['symbolId'],
28
+ },
29
+ };
30
+
31
+ /**
32
+ * Tool definition for searching code symbols
33
+ */
34
+ export const searchSymbolsTool = {
35
+ name: 'search_symbols',
36
+ description: `Search for available symbols in the generated skeletons. Returns a list of matching symbols that can be retrieved using get_symbol_details.
37
+ Use this to discover what functions/methods are available before requesting their full implementation.`,
38
+ parameters: {
39
+ type: SchemaType.OBJECT,
40
+ properties: {
41
+ query: {
42
+ type: SchemaType.STRING,
43
+ description: 'Search query to match against symbol names, file paths, or signatures',
44
+ },
45
+ symbolType: {
46
+ type: SchemaType.STRING,
47
+ description: 'Optional filter by symbol type: function, method, constructor, arrow-function, variable',
48
+ enum: ['function', 'method', 'constructor', 'arrow-function', 'variable'],
49
+ },
50
+ },
51
+ required: ['query'],
52
+ },
53
+ };
54
+
55
+ /**
56
+ * Tool definition for analyzing file dependencies
57
+ */
58
+ export const analyzeFileDependenciesTool = {
59
+ name: 'analyze_file_dependencies',
60
+ description: `Analyze import/export relationships for a specific file from the skeleton.
61
+ Use this to understand what other files a given file depends on.`,
62
+ parameters: {
63
+ type: SchemaType.OBJECT,
64
+ properties: {
65
+ filePath: {
66
+ type: SchemaType.STRING,
67
+ description: 'Relative path to the file (as shown in the skeleton)',
68
+ },
69
+ },
70
+ required: ['filePath'],
71
+ },
72
+ };
73
+
74
+ /**
75
+ * Code Analysis Agent
76
+ */
77
+ export class CodeAnalysisAgent {
78
+ private genAI: GoogleGenerativeAI;
79
+ private mapping: SymbolMapping | null = null;
80
+ private model: any;
81
+
82
+ constructor(apiKey: string, modelName: string = 'gemini-2.0-flash-exp') {
83
+ this.genAI = new GoogleGenerativeAI(apiKey);
84
+ this.model = this.genAI.getGenerativeModel({
85
+ model: modelName,
86
+ tools: [
87
+ {
88
+ functionDeclarations: [
89
+ getSymbolDetailsTool,
90
+ searchSymbolsTool,
91
+ analyzeFileDependenciesTool,
92
+ ],
93
+ },
94
+ ],
95
+ });
96
+ }
97
+
98
+ /**
99
+ * Load symbol mapping
100
+ */
101
+ loadMapping(mappingPath: string): void {
102
+ if (!fs.existsSync(mappingPath)) {
103
+ throw new Error(`Mapping file not found: ${mappingPath}`);
104
+ }
105
+ this.mapping = loadMappingFromFile(mappingPath);
106
+ console.log(`Loaded mapping with ${Object.keys(this.mapping.files).length} files`);
107
+ }
108
+
109
+ /**
110
+ * Handle tool calls from the model
111
+ */
112
+ private async handleToolCall(functionCall: any): Promise<any> {
113
+ const { name, args } = functionCall;
114
+
115
+ switch (name) {
116
+ case 'get_symbol_details':
117
+ return this.handleGetSymbolDetails(args.symbolId);
118
+
119
+ case 'search_symbols':
120
+ return this.handleSearchSymbols(args.query, args.symbolType);
121
+
122
+ case 'analyze_file_dependencies':
123
+ return this.handleAnalyzeFileDependencies(args.filePath);
124
+
125
+ default:
126
+ return { error: `Unknown tool: ${name}` };
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Get symbol details
132
+ */
133
+ private handleGetSymbolDetails(symbolId: string): any {
134
+ if (!this.mapping) {
135
+ return { error: 'No mapping loaded. Please load a mapping file first.' };
136
+ }
137
+
138
+ const result = getSymbolDetails(symbolId, this.mapping);
139
+ if (!result) {
140
+ return { error: `Symbol not found: ${symbolId}` };
141
+ }
142
+
143
+ return {
144
+ symbolId,
145
+ content: result.content,
146
+ location: {
147
+ file: result.location.filePath,
148
+ lines: `${result.location.startLine}-${result.location.endLine}`,
149
+ type: result.location.symbolType,
150
+ parentClass: result.location.parentClass,
151
+ },
152
+ };
153
+ }
154
+
155
+ /**
156
+ * Search symbols
157
+ */
158
+ private handleSearchSymbols(query: string, symbolType?: string): any {
159
+ if (!this.mapping) {
160
+ return { error: 'No mapping loaded. Please load a mapping file first.' };
161
+ }
162
+
163
+ const results: any[] = [];
164
+ const queryLower = query.toLowerCase();
165
+
166
+ for (const [filePath, fileInfo] of Object.entries(this.mapping.files)) {
167
+ for (const symbol of fileInfo.symbols) {
168
+ // Filter by type if specified
169
+ if (symbolType && symbol.symbolType !== symbolType) {
170
+ continue;
171
+ }
172
+
173
+ // Check if query matches
174
+ const matchesName = symbol.symbolName.toLowerCase().includes(queryLower);
175
+ const matchesFile = filePath.toLowerCase().includes(queryLower);
176
+ const matchesSignature = symbol.signature?.toLowerCase().includes(queryLower);
177
+
178
+ if (matchesName || matchesFile || matchesSignature) {
179
+ results.push({
180
+ symbolId: symbol.symbolId,
181
+ name: symbol.symbolName,
182
+ type: symbol.symbolType,
183
+ file: filePath,
184
+ parentClass: symbol.parentClass,
185
+ signature: symbol.signature,
186
+ });
187
+ }
188
+ }
189
+ }
190
+
191
+ return {
192
+ query,
193
+ count: results.length,
194
+ results: results.slice(0, 20), // Limit to 20 results
195
+ };
196
+ }
197
+
198
+ /**
199
+ * Analyze file dependencies
200
+ */
201
+ private handleAnalyzeFileDependencies(filePath: string): any {
202
+ if (!this.mapping) {
203
+ return { error: 'No mapping loaded. Please load a mapping file first.' };
204
+ }
205
+
206
+ const fileInfo = this.mapping.files[filePath];
207
+ if (!fileInfo) {
208
+ return { error: `File not found in mapping: ${filePath}` };
209
+ }
210
+
211
+ // Read the original file to extract imports
212
+ const fullPath = path.join(this.mapping.rootPath, filePath);
213
+ if (!fs.existsSync(fullPath)) {
214
+ return { error: `Source file not found: ${fullPath}` };
215
+ }
216
+
217
+ const content = fs.readFileSync(fullPath, 'utf-8');
218
+ const importRegex = /import\s+(?:{[^}]*}|[^;]+)\s+from\s+['"]([^'"]+)['"]/g;
219
+ const imports: string[] = [];
220
+ let match;
221
+
222
+ while ((match = importRegex.exec(content)) !== null) {
223
+ imports.push(match[1]);
224
+ }
225
+
226
+ return {
227
+ file: filePath,
228
+ imports,
229
+ symbolCount: fileInfo.symbols.length,
230
+ symbols: fileInfo.symbols.map(s => ({
231
+ id: s.symbolId,
232
+ name: s.symbolName,
233
+ type: s.symbolType,
234
+ })),
235
+ };
236
+ }
237
+
238
+ /**
239
+ * Start a conversation with the agent
240
+ */
241
+ async chat(userMessage: string, skeletonContext?: string): Promise<string> {
242
+ if (!this.mapping) {
243
+ throw new Error('No mapping loaded. Call loadMapping() first.');
244
+ }
245
+
246
+ let context = userMessage;
247
+ if (skeletonContext) {
248
+ context = [
249
+ 'You are a code analysis assistant.',
250
+ 'Base your answer on the provided skeletons and tool results.',
251
+ 'If evidence is insufficient, say so explicitly.',
252
+ 'Include a final section titled "Evidence" with bullet points that reference concrete files or symbols used.',
253
+ '',
254
+ 'Here are the code skeletons for context:',
255
+ skeletonContext,
256
+ '',
257
+ `User question: ${userMessage}`,
258
+ ].join('\n');
259
+ }
260
+
261
+ const chat = this.model.startChat({
262
+ history: [],
263
+ });
264
+
265
+ let response = await chat.sendMessage(context);
266
+ let responseText = '';
267
+
268
+ // Handle function calls in a loop
269
+ while (response.response.functionCalls()) {
270
+ const functionCalls = response.response.functionCalls();
271
+ const functionResponses: any[] = [];
272
+
273
+ for (const call of functionCalls) {
274
+ console.log(`[Agent] Calling tool: ${call.name}(${JSON.stringify(call.args)})`);
275
+ const result = await this.handleToolCall(call);
276
+ functionResponses.push({
277
+ name: call.name,
278
+ response: result,
279
+ });
280
+ }
281
+
282
+ // Send function responses back to the model
283
+ response = await chat.sendMessage(functionResponses);
284
+ }
285
+
286
+ responseText = response.response.text();
287
+ return responseText;
288
+ }
289
+
290
+ /**
291
+ * Local wrapper for symbol search without model invocation.
292
+ */
293
+ searchSymbols(query: string, symbolType?: string): any {
294
+ return this.handleSearchSymbols(query, symbolType);
295
+ }
296
+
297
+ /**
298
+ * Local wrapper for fetching symbol implementation by ID.
299
+ */
300
+ getSymbolDetails(symbolId: string): any {
301
+ return this.handleGetSymbolDetails(symbolId);
302
+ }
303
+
304
+ /**
305
+ * Local wrapper for file dependency analysis.
306
+ */
307
+ analyzeFileDependencies(filePath: string): any {
308
+ return this.handleAnalyzeFileDependencies(filePath);
309
+ }
310
+
311
+ /**
312
+ * Get statistics about the loaded mapping
313
+ */
314
+ getStats(): any {
315
+ if (!this.mapping) {
316
+ return { error: 'No mapping loaded' };
317
+ }
318
+
319
+ let totalSymbols = 0;
320
+ const symbolTypes: { [key: string]: number } = {};
321
+
322
+ for (const fileInfo of Object.values(this.mapping.files)) {
323
+ totalSymbols += fileInfo.symbols.length;
324
+ for (const symbol of fileInfo.symbols) {
325
+ symbolTypes[symbol.symbolType] = (symbolTypes[symbol.symbolType] || 0) + 1;
326
+ }
327
+ }
328
+
329
+ return {
330
+ rootPath: this.mapping.rootPath,
331
+ generatedAt: this.mapping.generatedAt,
332
+ totalFiles: Object.keys(this.mapping.files).length,
333
+ totalSymbols,
334
+ symbolsByType: symbolTypes,
335
+ };
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Helper function to create an agent instance
341
+ */
342
+ export function createCodeAnalysisAgent(apiKey: string, modelName?: string): CodeAnalysisAgent {
343
+ return new CodeAnalysisAgent(apiKey, modelName);
344
+ }