get_code_context
Analyzes a project directory to generate a comprehensive codebase overview, including directory structure and code symbols. Use to quickly understand project layout and key elements at task initiation.
Instructions
Returns Complete Context of a given project directory, including directory tree, and code symbols. Useful for getting a quick overview of a project. Use this tool when you need to get a comprehensive overview of a project's codebase. Useful at the start of a new task.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| absolutePath | Yes | Absolute path to the directory to analyze. For windows, it is recommended to use forward slashes to avoid escaping (e.g. C:/Users/username/Documents/project/src) | |
| analyzeJs | No | Whether to analyze JavaScript/TypeScript and Python files. Returns the count of functions, variables, classes, imports, and exports in the codebase. | |
| includeSymbols | No | Whether to include code symbols in the response. Returns the code symbols for each file. | |
| maxDepth | No | Maximum directory depth for code analysis (default: 5 levels). Directory tree will still be built for all levels. Reduce the depth if you only need a quick overview of the project. | |
| symbolType | No | Type of symbols to include if includeSymbols is true. Otherwise, returns only the directory tree. | all |
Implementation Reference
- index.js:921-1031 (handler)The main execution handler for the get_code_context tool. Handles input parameters, initializes Tree-sitter if needed for code analysis, generates directory tree and symbol analysis using helper functions, compiles the response.async ({ absolutePath, analyzeJs, includeSymbols, symbolType, filePatterns, maxDepth = 5 }) => { try { // Check if the path is C:/ drive root or common non-project directories on Windows OS if (process.platform === 'win32') { // Check for C: drive root or common system directories that are not project directories const nonProjectPaths = [ /^[cC]:[\\/]?$/, // C:/ or C: /^[cC]:[\\/]Users[\\/]?$/, // C:/Users/ /^[cC]:[\\/]Windows[\\/]?$/, // C:/Windows/ /^[cC]:[\\/]Program Files[\\/]?$/, // C:/Program Files/ /^[cC]:[\\/]Program Files \(x86\)[\\/]?$/ // C:/Program Files (x86)/ ]; if (nonProjectPaths.some(regex => regex.test(absolutePath))) { return { content: [{ type: "text", text: "C drive is not a project directory. Try different path" }], isError: true }; } } // Ensure TreeSitter is initialized if we're going to analyze code if (analyzeJs && !initialized) { // Create parsers directory and ensure it exists const parsersDir = ensureParsersDirectory(); console.error(`Using parsers directory: ${parsersDir}`); // Initialize TreeSitter await initializeTreeSitter(); if (!initialized) { return { content: [{ type: "text", text: "Error: Failed to initialize code analysis parser. WASM parsers may be missing." }], isError: true }; } } // Normalize path to handle both Windows and Unix-style paths const normalizedPath = path.normalize(absolutePath); console.error(`Analyzing directory: ${normalizedPath} (analyzeJs: ${analyzeJs}, maxAnalysisDepth: ${maxDepth !== 5 ? maxDepth : '5 (default)'})`); // Reset code symbols if analyzing JS if (analyzeJs) { codeSymbols.functions = {}; codeSymbols.variables = {}; codeSymbols.classes = {}; codeSymbols.imports = {}; codeSymbols.exports = {}; codeSymbols.files = new Set(); } // Get the directory tree, passing along all the symbol-related parameters const tree = await getDirectoryTree( normalizedPath, normalizedPath, [], filePatterns, '', analyzeJs, includeSymbols, symbolType, 0, maxDepth ); // Generate summary of analyzed files if applicable let analysisSummary = ''; if (analyzeJs && codeSymbols.files.size > 0) { const totalFunctions = Object.values(codeSymbols.functions).reduce((sum, arr) => sum + arr.length, 0); const totalVariables = Object.values(codeSymbols.variables).reduce((sum, arr) => sum + arr.length, 0); const totalClasses = Object.values(codeSymbols.classes).reduce((sum, arr) => sum + arr.length, 0); analysisSummary = `\n\nCode Analysis Summary: - Files analyzed: ${codeSymbols.files.size} - Total functions: ${totalFunctions} - Total variables: ${totalVariables} - Total classes: ${totalClasses}`; // Add language support info and custom pattern info if (filePatterns && filePatterns.length > 0) { analysisSummary += `\n\nAnalyzed files matching patterns: ${filePatterns.join(', ')}`; } else { analysisSummary += `\n\nNote: Symbol analysis is supported for JavaScript/TypeScript (.js, .jsx, .ts, .tsx) and Python (.py) files only.`; } // Add depth limit info if applicable if (maxDepth !== 5) { analysisSummary += `\n\nCode analysis limited to a maximum depth of ${maxDepth} directory levels.`; } else { analysisSummary += `\n\nCode analysis limited to a maximum depth of 5 directory levels (default).`; } } // Return the result return { content: [ { type: "text", text: `Directory structure for: ${normalizedPath}${analysisSummary}\n\n${tree}` } ] }; } catch (error) { console.error(`Error in get_code_context tool: ${error.message}`); return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true }; } }
- index.js:914-920 (schema)Zod input schema defining parameters for directory path, analysis options, symbol filtering, and depth limit.{ absolutePath: z.string().describe("Absolute path to the directory to analyze. For windows, it is recommended to use forward slashes to avoid escaping (e.g. C:/Users/username/Documents/project/src)"), analyzeJs: z.boolean().optional().default(false).describe("Whether to analyze JavaScript/TypeScript and Python files. Returns the count of functions, variables, classes, imports, and exports in the codebase."), includeSymbols: z.boolean().optional().default(false).describe("Whether to include code symbols in the response. Returns the code symbols for each file."), symbolType: z.enum(['functions', 'variables', 'classes', 'imports', 'exports', 'all']).optional().default('all').describe("Type of symbols to include if includeSymbols is true. Otherwise, returns only the directory tree."), maxDepth: z.number().optional().default(5).describe("Maximum directory depth for code analysis (default: 5 levels). Directory tree will still be built for all levels. Reduce the depth if you only need a quick overview of the project.") },
- index.js:910-1032 (registration)Registers the get_code_context tool on the MCP server, providing name, description, input schema, and handler function.// Add the get_code_context tool server.tool( "get_code_context", "Returns Complete Context of a given project directory, including directory tree, and code symbols. Useful for getting a quick overview of a project. Use this tool when you need to get a comprehensive overview of a project's codebase. Useful at the start of a new task.", { absolutePath: z.string().describe("Absolute path to the directory to analyze. For windows, it is recommended to use forward slashes to avoid escaping (e.g. C:/Users/username/Documents/project/src)"), analyzeJs: z.boolean().optional().default(false).describe("Whether to analyze JavaScript/TypeScript and Python files. Returns the count of functions, variables, classes, imports, and exports in the codebase."), includeSymbols: z.boolean().optional().default(false).describe("Whether to include code symbols in the response. Returns the code symbols for each file."), symbolType: z.enum(['functions', 'variables', 'classes', 'imports', 'exports', 'all']).optional().default('all').describe("Type of symbols to include if includeSymbols is true. Otherwise, returns only the directory tree."), maxDepth: z.number().optional().default(5).describe("Maximum directory depth for code analysis (default: 5 levels). Directory tree will still be built for all levels. Reduce the depth if you only need a quick overview of the project.") }, async ({ absolutePath, analyzeJs, includeSymbols, symbolType, filePatterns, maxDepth = 5 }) => { try { // Check if the path is C:/ drive root or common non-project directories on Windows OS if (process.platform === 'win32') { // Check for C: drive root or common system directories that are not project directories const nonProjectPaths = [ /^[cC]:[\\/]?$/, // C:/ or C: /^[cC]:[\\/]Users[\\/]?$/, // C:/Users/ /^[cC]:[\\/]Windows[\\/]?$/, // C:/Windows/ /^[cC]:[\\/]Program Files[\\/]?$/, // C:/Program Files/ /^[cC]:[\\/]Program Files \(x86\)[\\/]?$/ // C:/Program Files (x86)/ ]; if (nonProjectPaths.some(regex => regex.test(absolutePath))) { return { content: [{ type: "text", text: "C drive is not a project directory. Try different path" }], isError: true }; } } // Ensure TreeSitter is initialized if we're going to analyze code if (analyzeJs && !initialized) { // Create parsers directory and ensure it exists const parsersDir = ensureParsersDirectory(); console.error(`Using parsers directory: ${parsersDir}`); // Initialize TreeSitter await initializeTreeSitter(); if (!initialized) { return { content: [{ type: "text", text: "Error: Failed to initialize code analysis parser. WASM parsers may be missing." }], isError: true }; } } // Normalize path to handle both Windows and Unix-style paths const normalizedPath = path.normalize(absolutePath); console.error(`Analyzing directory: ${normalizedPath} (analyzeJs: ${analyzeJs}, maxAnalysisDepth: ${maxDepth !== 5 ? maxDepth : '5 (default)'})`); // Reset code symbols if analyzing JS if (analyzeJs) { codeSymbols.functions = {}; codeSymbols.variables = {}; codeSymbols.classes = {}; codeSymbols.imports = {}; codeSymbols.exports = {}; codeSymbols.files = new Set(); } // Get the directory tree, passing along all the symbol-related parameters const tree = await getDirectoryTree( normalizedPath, normalizedPath, [], filePatterns, '', analyzeJs, includeSymbols, symbolType, 0, maxDepth ); // Generate summary of analyzed files if applicable let analysisSummary = ''; if (analyzeJs && codeSymbols.files.size > 0) { const totalFunctions = Object.values(codeSymbols.functions).reduce((sum, arr) => sum + arr.length, 0); const totalVariables = Object.values(codeSymbols.variables).reduce((sum, arr) => sum + arr.length, 0); const totalClasses = Object.values(codeSymbols.classes).reduce((sum, arr) => sum + arr.length, 0); analysisSummary = `\n\nCode Analysis Summary: - Files analyzed: ${codeSymbols.files.size} - Total functions: ${totalFunctions} - Total variables: ${totalVariables} - Total classes: ${totalClasses}`; // Add language support info and custom pattern info if (filePatterns && filePatterns.length > 0) { analysisSummary += `\n\nAnalyzed files matching patterns: ${filePatterns.join(', ')}`; } else { analysisSummary += `\n\nNote: Symbol analysis is supported for JavaScript/TypeScript (.js, .jsx, .ts, .tsx) and Python (.py) files only.`; } // Add depth limit info if applicable if (maxDepth !== 5) { analysisSummary += `\n\nCode analysis limited to a maximum depth of ${maxDepth} directory levels.`; } else { analysisSummary += `\n\nCode analysis limited to a maximum depth of 5 directory levels (default).`; } } // Return the result return { content: [ { type: "text", text: `Directory structure for: ${normalizedPath}${analysisSummary}\n\n${tree}` } ] }; } catch (error) { console.error(`Error in get_code_context tool: ${error.message}`); return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true }; } } );
- index.js:716-902 (helper)Key helper function that recursively builds the directory tree, applies ignore patterns, analyzes supported code files by extracting symbols, and formats output with optional details.async function getDirectoryTree(dirPath, rootPath = dirPath, ignorePatterns = [], filePatterns = null, indent = '', analyzeJs = false, includeSymbols = false, symbolType = 'all', currentDepth = 0, maxDepth = 5) { try { if (!fs.existsSync(dirPath)) { return `${indent}Path does not exist: ${dirPath}`; } // Default patterns to ignore common directories and files const defaultIgnorePatterns = [ '^node_modules($|/)', '^.git($|/)', '\\.log$', '\\.tmp$', '\\.temp$', '\\.swp$', '\\.DS_Store$', '\\.vscode($|/)', '\\.idea($|/)', '\\.vs($|/)', '^dist($|/)', '^build($|/)', '^coverage($|/)' ]; // Combine default patterns with any provided patterns let allIgnorePatterns = [...defaultIgnorePatterns, ...ignorePatterns]; // Check for .gitignore file in this directory const gitignorePath = path.join(dirPath, '.gitignore'); if (fs.existsSync(gitignorePath)) { const newPatterns = parseGitignore(gitignorePath); allIgnorePatterns = [...allIgnorePatterns, ...newPatterns]; } let output = ''; const items = fs.readdirSync(dirPath); for (let i = 0; i < items.length; i++) { const itemName = items[i]; const itemPath = path.join(dirPath, itemName); // Skip .gitignore files if (itemName === '.gitignore') { continue; } // Skip hidden files/directories if (itemName.startsWith('.')) { continue; } // Skip items that match ignore patterns if (shouldIgnore(itemPath, allIgnorePatterns, rootPath)) { continue; } const isLast = i === items.length - 1; const stats = fs.statSync(itemPath); // Generate the prefix for current item const prefix = isLast ? '└── ' : '├── '; // Generate the prefix for child items const childIndent = indent + (isLast ? ' ' : '│ '); if (stats.isDirectory()) { output += `${indent}${prefix}${itemName}/\n`; // Always recurse to build the directory tree, but only analyze code if we're within maxDepth const shouldAnalyze = analyzeJs && (currentDepth < maxDepth); output += await getDirectoryTree( itemPath, rootPath, allIgnorePatterns, filePatterns, childIndent, shouldAnalyze, // Only analyze if within depth limit includeSymbols, symbolType, currentDepth + 1, maxDepth ); } else { const sizeInKB = Math.ceil(stats.size / 1024); output += `${indent}${prefix}${itemName} (${sizeInKB} KB)\n`; // Analyze supported files if requested AND we're within the max depth limit if (analyzeJs && currentDepth <= maxDepth && isSupportedFile(itemPath, filePatterns)) { try { const fileContent = fs.readFileSync(itemPath, 'utf8'); const symbols = await extractCodeSymbols(itemPath, fileContent); if (symbols) { // Store the extracted symbols codeSymbols.functions[itemPath] = symbols.functions; codeSymbols.variables[itemPath] = symbols.variables; codeSymbols.classes[itemPath] = symbols.classes; codeSymbols.imports[itemPath] = symbols.imports; codeSymbols.exports[itemPath] = symbols.exports; codeSymbols.files.add(itemPath); // Add a summary of what was found output += `${childIndent}└── [Analyzed: ${symbols.functions.length} functions, ${symbols.variables.length} variables, ${symbols.classes.length} classes]\n`; // Add detailed symbol information if requested if (includeSymbols) { // Functions if ((symbolType === 'functions' || symbolType === 'all') && symbols.functions.length > 0) { // Always filter out anonymous functions by default const fileFunctions = symbols.functions.filter(fn => fn.name !== 'anonymous'); if (fileFunctions.length > 0) { output += `${childIndent} Functions:\n`; output += fileFunctions.map(fn => `${childIndent} - ${fn.name}${fn.parent ? ` (in ${fn.parent})` : ''} [${fn.position.startLine}:${fn.position.startCol}]` ).join('\n') + '\n'; } } // Variables if ((symbolType === 'variables' || symbolType === 'all') && symbols.variables.length > 0) { output += `${childIndent} Variables:\n`; output += symbols.variables.map(v => `${childIndent} - ${v.kind} ${v.name} [${v.position.startLine}:${v.position.startCol}]` ).join('\n') + '\n'; } // Classes if ((symbolType === 'classes' || symbolType === 'all') && symbols.classes.length > 0) { output += `${childIndent} Classes:\n`; output += symbols.classes.map(c => { let classInfo = `${childIndent} - ${c.name} [${c.position.startLine}:${c.position.startCol}]`; if (c.methods.length > 0) { classInfo += `\n${childIndent} Methods:\n`; classInfo += c.methods.map(m => `${childIndent} - ${m.isStatic ? 'static ' : ''}${m.name} [${m.position.startLine}:${m.position.startCol}]` ).join('\n'); } return classInfo; }).join('\n') + '\n'; } // Imports if ((symbolType === 'imports' || symbolType === 'all') && symbols.imports.length > 0) { output += `${childIndent} Imports:\n`; output += symbols.imports.map(imp => { let importInfo = `${childIndent} - from '${imp.source}'`; if (imp.items.length > 0) { importInfo += ': ' + imp.items.map(item => `${item.name}${item.alias ? ` as ${item.alias}` : ''}` ).join(', '); } return importInfo; }).join('\n') + '\n'; } // Exports if ((symbolType === 'exports' || symbolType === 'all') && symbols.exports.length > 0) { output += `${childIndent} Exports:\n`; output += symbols.exports.map(exp => { let exportInfo = `${childIndent} - ${exp.isDefault ? 'default export' : 'export'}`; if (exp.source) { exportInfo += ` from '${exp.source}'`; } if (exp.items.length > 0) { exportInfo += ': ' + exp.items.map(item => `${item.name}${item.alias ? ` as ${item.alias}` : ''}` ).join(', '); } return exportInfo; }).join('\n') + '\n'; } } } } catch (error) { console.error(`Error analyzing ${itemPath}: ${error.message}`); } } } } return output; } catch (error) { console.error(`Error processing directory ${dirPath}: ${error.message}`); return `${indent}Error: ${error.message}\n`; } }
- index.js:113-604 (helper)Core code analysis helper using Tree-sitter to parse files and extract detailed symbols (functions, variables, classes, imports, exports) for JS/TS/Python files.async function extractCodeSymbols(filePath, fileContent) { // Ensure TreeSitter is initialized if (!initialized) { await initializeTreeSitter(); } try { // Set the appropriate language based on file extension const wasmFile = getLanguageFromExtension(filePath); if (!wasmFile || !languageInstances[wasmFile]) { return null; // Unsupported language } const parser = new Parser(); parser.setLanguage(languageInstances[wasmFile]); const tree = parser.parse(fileContent); const rootNode = tree.rootNode; const functions = []; const variables = []; const classes = []; const imports = []; const exports = []; // Helper to get line and column info const getPosition = (node) => { return { startLine: node.startPosition.row + 1, startCol: node.startPosition.column, endLine: node.endPosition.row + 1, endCol: node.endPosition.column }; }; // Helper to check if a function is significant enough to track const isSignificantFunction = (node, name) => { // Skip tiny arrow functions like () => {} or x => x if (node.type === 'arrow_function' && node.text.length < 15) { return false; } // Skip callback functions in array methods if they're simple const parent = node.parent; if (parent && (parent.type === 'call_expression' || parent.type === 'member_expression') && node.text.length < 50) { // Check if it's a callback in common array methods const callText = parent.text.slice(0, 30).toLowerCase(); if (callText.includes('.map(') || callText.includes('.filter(') || callText.includes('.forEach(') || callText.includes('.find(') || callText.includes('.reduce(')) { return false; } } // Keep named functions and significant anonymous ones return name !== 'anonymous' || node.text.length > 100; }; // Helper to infer function name from context const inferFunctionName = (node) => { let name = 'anonymous'; // Check if function is assigned to a variable const parent = node.parent; if (parent) { if (parent.type === 'variable_declarator') { // Case: const myFunc = function() {...} or const myFunc = () => {...} const nameNode = parent.childForFieldName('name'); if (nameNode) { return nameNode.text; } } else if (parent.type === 'pair' && parent.parent && parent.parent.type === 'object') { // Case: { myMethod: function() {...} } or { myMethod: () => {...} } const keyNode = parent.childForFieldName('key'); if (keyNode) { return keyNode.text.replace(/['"]/g, ''); } } else if (parent.type === 'assignment_expression') { // Case: obj.method = function() {...} or MyClass.prototype.method = function() {...} const leftNode = parent.childForFieldName('left'); if (leftNode) { if (leftNode.type === 'member_expression') { // Get rightmost part, e.g., 'method' from 'obj.method' const propertyNode = leftNode.childForFieldName('property'); if (propertyNode) { return propertyNode.text; } } else { return leftNode.text; } } } else if (parent.type === 'property_identifier' && parent.parent && parent.parent.type === 'member_expression') { // Case for method callbacks like .then(() => {...}) return parent.text; } } return name; }; // Process different languages if (wasmFile === 'tree-sitter-javascript.wasm') { // Process JavaScript/TypeScript // Find function declarations const functionNodes = rootNode.descendantsOfType([ 'function_declaration', 'method_definition', 'arrow_function', 'function' ]); for (const node of functionNodes) { // Get function name let name = 'anonymous'; let parentFunction = null; if (node.type === 'function_declaration') { const nameNode = node.firstNamedChild; if (nameNode) name = nameNode.text; } else if (node.type === 'method_definition') { const nameNode = node.childForFieldName('name'); if (nameNode) name = nameNode.text; // Get parent class or object const classNode = node.parent?.parent; if (classNode?.type === 'class_declaration') { const classNameNode = classNode.childForFieldName('name'); if (classNameNode) parentFunction = classNameNode.text; } } else if (node.type === 'function' || node.type === 'arrow_function') { // Try to infer the name from context name = inferFunctionName(node); } // Only add significant functions if (isSignificantFunction(node, name)) { functions.push({ name, parent: parentFunction, position: getPosition(node), code: node.text }); } } // Find variable declarations const variableNodes = rootNode.descendantsOfType([ 'variable_declaration', 'lexical_declaration' ]); for (const node of variableNodes) { const declarators = node.descendantsOfType('variable_declarator'); for (const declarator of declarators) { const nameNode = declarator.childForFieldName('name'); if (nameNode) { variables.push({ name: nameNode.text, kind: node.childForFieldName('kind')?.text || 'var', position: getPosition(declarator), code: declarator.text }); } } } // Find class declarations const classNodes = rootNode.descendantsOfType('class_declaration'); for (const node of classNodes) { const nameNode = node.childForFieldName('name'); if (nameNode) { const className = nameNode.text; // Get class members const methods = []; const methodNodes = node.descendantsOfType('method_definition'); for (const methodNode of methodNodes) { const methodNameNode = methodNode.childForFieldName('name'); if (methodNameNode) { methods.push({ name: methodNameNode.text, position: getPosition(methodNode), isStatic: methodNode.childForFieldName('static')?.text === 'static', code: methodNode.text }); } } classes.push({ name: className, position: getPosition(node), methods, code: node.text }); } } // Find imports const importNodes = rootNode.descendantsOfType('import_statement'); for (const node of importNodes) { const sourceNode = node.childForFieldName('source'); if (sourceNode) { const source = sourceNode.text.replace(/['"]/g, ''); const importedItems = []; const specifiers = node.descendantsOfType([ 'import_specifier', 'namespace_import' ]); for (const specNode of specifiers) { if (specNode.type === 'import_specifier') { const nameNode = specNode.childForFieldName('name'); const aliasNode = specNode.childForFieldName('alias'); if (nameNode) { importedItems.push({ name: nameNode.text, alias: aliasNode ? aliasNode.text : null }); } } else if (specNode.type === 'namespace_import') { const nameNode = specNode.childForFieldName('name'); if (nameNode) { importedItems.push({ name: '*', alias: nameNode.text }); } } } // Check for default imports const defaultImportNode = node.descendantsOfType('identifier')[0]; if (defaultImportNode && !specifiers.length) { importedItems.push({ name: 'default', alias: defaultImportNode.text }); } imports.push({ source, items: importedItems, position: getPosition(node), code: node.text }); } } // Find exports const exportNodes = rootNode.descendantsOfType([ 'export_statement', 'lexical_declaration', 'function_declaration' ]); for (const node of exportNodes) { if (node.type === 'export_statement') { const sourceNode = node.childForFieldName('source'); const source = sourceNode ? sourceNode.text.replace(/['"]/g, '') : null; const exportedItems = []; const specifiers = node.descendantsOfType('export_specifier'); for (const specNode of specifiers) { const nameNode = specNode.childForFieldName('name'); const aliasNode = specNode.childForFieldName('alias'); if (nameNode) { exportedItems.push({ name: nameNode.text, alias: aliasNode ? aliasNode.text : null }); } } exports.push({ source, items: exportedItems, isDefault: node.childForFieldName('default')?.text === 'default', position: getPosition(node), code: node.text }); } else { // Check for export modifier on declaration const parent = node.parent; if (parent?.type === 'export_statement') { let name = ''; if (node.type === 'function_declaration') { const nameNode = node.childForFieldName('name'); if (nameNode) name = nameNode.text; } else if (node.type === 'lexical_declaration') { const declarator = node.descendantsOfType('variable_declarator')[0]; if (declarator) { const nameNode = declarator.childForFieldName('name'); if (nameNode) name = nameNode.text; } } if (name) { exports.push({ source: null, items: [{ name, alias: null }], isDefault: parent.childForFieldName('default')?.text === 'default', position: getPosition(parent), code: parent.text }); } } } } } else if (wasmFile === 'tree-sitter-python.wasm') { // Process Python // Find function declarations const functionNodes = rootNode.descendantsOfType('function_definition'); for (const node of functionNodes) { // Get function name let name = 'anonymous'; let parentFunction = null; const nameNode = node.childForFieldName('name'); if (nameNode) name = nameNode.text; // Check if this is a class method const parent = node.parent?.parent; if (parent?.type === 'class_definition') { const classNameNode = parent.childForFieldName('name'); if (classNameNode) parentFunction = classNameNode.text; } functions.push({ name, parent: parentFunction, position: getPosition(node), code: node.text }); } // Find variable assignments (global and class level) const assignmentNodes = rootNode.descendantsOfType('assignment'); for (const node of assignmentNodes) { // Only consider top-level or class-level assignments const parent = node.parent; if (parent?.type === 'module' || parent?.type === 'block' && parent.parent?.type === 'class_definition') { const left = node.childForFieldName('left'); if (left && left.type === 'identifier') { variables.push({ name: left.text, kind: 'var', // Python doesn't have explicit variable declarations position: getPosition(node), code: node.text }); } } } // Find class declarations const classNodes = rootNode.descendantsOfType('class_definition'); for (const node of classNodes) { const nameNode = node.childForFieldName('name'); if (nameNode) { const className = nameNode.text; // Get class methods const methods = []; const methodNodes = node.descendantsOfType('function_definition'); for (const methodNode of methodNodes) { const methodNameNode = methodNode.childForFieldName('name'); if (methodNameNode) { // Check if method is static (has @staticmethod decorator) let isStatic = false; const decorators = methodNode.childForFieldName('decorator_list'); if (decorators) { const decoratorNodes = decorators.children; for (const decorator of decoratorNodes) { if (decorator.text === '@staticmethod') { isStatic = true; break; } } } methods.push({ name: methodNameNode.text, position: getPosition(methodNode), isStatic, code: methodNode.text }); } } classes.push({ name: className, position: getPosition(node), methods, code: node.text }); } } // Find imports const importNodes = rootNode.descendantsOfType(['import_statement', 'import_from_statement']); for (const node of importNodes) { if (node.type === 'import_statement') { // Case: import module [as alias] const namesNode = node.childForFieldName('names'); if (namesNode) { const importedModules = namesNode.descendantsOfType('dotted_name'); for (const moduleNode of importedModules) { const moduleName = moduleNode.text; const aliasNode = moduleNode.nextNamedSibling; imports.push({ source: moduleName, items: [{ name: 'module', alias: aliasNode ? aliasNode.text : null }], position: getPosition(node), code: node.text }); } } } else if (node.type === 'import_from_statement') { // Case: from module import name [as alias], ... const moduleNode = node.childForFieldName('module'); const namesNode = node.childForFieldName('names'); if (moduleNode && namesNode) { const moduleName = moduleNode.text; const importedItems = []; const importedNames = namesNode.namedChildren; for (const nameNode of importedNames) { if (nameNode.type === 'aliased_import') { const name = nameNode.childForFieldName('name')?.text; const alias = nameNode.childForFieldName('alias')?.text; if (name) { importedItems.push({ name, alias }); } } else if (nameNode.type === 'identifier') { importedItems.push({ name: nameNode.text, alias: null }); } } imports.push({ source: moduleName, items: importedItems, position: getPosition(node), code: node.text }); } } } } return { functions, variables, classes, imports, exports }; } catch (error) { console.error(`Error parsing ${filePath}: ${error.message}`); return null; } }