Enhance Map_symbols_by_query With Directory Parameter

Alex Johnson
-
Enhance Map_symbols_by_query With Directory Parameter

Enhancing the map_symbols_by_query tool with a directory parameter aims to streamline directory-based exploration. This enhancement introduces a directory parameter as a convenient wrapper around KQL filtering, making the tool more intuitive, especially for Large Language Models (LLMs) after they've discovered directories using discover_directories. Instead of renaming the tool, we're enhancing it to avoid introducing breaking changes, which keeps things smooth for everyone already using it.

Why This Enhancement?

The key reason for this update lies in making the tool more user-friendly for LLMs. Currently, LLMs need to write complex KQL queries like filePath: src/platform/packages/kbn-esql-utils/* to explore directories. This can be cumbersome and less intuitive. The goal is to simplify this process by allowing LLMs to use a more straightforward directory parameter, such as directory: "src/platform/packages/kbn-esql-utils". This approach offers a clearer intent, making the tool easier to use and creating a more natural workflow following the discover_directories function. This enhancement not only simplifies the query process but also aligns better with how developers naturally explore codebases, improving overall efficiency and reducing the learning curve.

This improvement directly addresses the existing friction in using the tool for directory exploration. By providing a dedicated directory parameter, it abstracts away the complexity of KQL syntax, allowing users to focus on the specific directory they want to investigate. This is particularly beneficial for LLMs, which can then leverage this tool more effectively in automated code analysis and understanding workflows. The result is a more intuitive and efficient process for exploring codebases, ultimately leading to faster development cycles and improved code quality.

Furthermore, the directory parameter promotes a more discoverable and accessible tool. New users, or LLMs unfamiliar with the intricacies of KQL, can quickly grasp the tool's purpose and usage by simply specifying the directory they wish to explore. This ease of use encourages broader adoption and integration of the tool into various development workflows. In contrast, requiring users to construct KQL queries necessitates a deeper understanding of the underlying search syntax, which can be a barrier to entry for some users. By providing a simpler alternative, this enhancement democratizes access to the tool's functionality, making it more inclusive and user-friendly for a wider range of users and applications.

Implementation Roadmap

Task 1: Update Tool Input Schema ✅

File: src/tools/map_symbols_by_query.ts (or wherever this tool is defined)

Changes: Add a directory parameter to the input schema:

const MapSymbolsInput = z.object({
 kql: z.string().optional().describe('KQL query string for filtering (e.g., "language: typescript and kind: function_declaration")'),
 directory: z.string().optional().describe('Directory path to explore (e.g., "src/platform/packages/kbn-esql-utils"). Convenience wrapper for filePath filtering.'),
 size: z.number().optional().default(1000).describe('Maximum number of files to return (default: 1000)'),
 index: z.string().optional().describe('Elasticsearch index to search (optional)')
});

Validation: Ensure kql and directory are mutually exclusive or properly combined.

Why: Makes the tool more discoverable and easier to use in the common case of exploring a specific directory.

Dependencies: None

Task 2: Implement Directory-to-KQL Conversion ✅

File: src/tools/map_symbols_by_query.ts

Changes: Add logic to convert the directory parameter to appropriate KQL:

function buildKqlFromDirectory(directory: string): string {
 // Remove trailing slash if present
 const cleanDir = directory.replace(/\/$/, '');
 
 // Use wildcard for directory and all subdirectories
 return `filePath: ${cleanDir}/*`;
}

function validateAndBuildQuery(input: z.infer<typeof MapSymbolsInput>): string {
 // Check for conflicting parameters
 if (input.directory && input.kql) {
 throw new Error(
 'Cannot use both "directory" and "kql" parameters together.\n' +
 'Use "directory" for simple directory exploration, or "kql" for advanced filtering.'
 );
 }
 
 // Check for missing parameters
 if (!input.directory && !input.kql) {
 throw new Error(
 'Must provide either "directory" or "kql" parameter.\n' +
 'Examples:\n' +
 ' - { "directory": "src/platform/packages/kbn-esql-utils" }\n' +
 ' - { "kql": "language: typescript and kind: function_declaration" }'
 );
 }
 
 // Build query based on input
 if (input.directory) {
 return buildKqlFromDirectory(input.directory);
 }
 
 return input.kql!;
}

export const mapSymbolsByQueryTool = {
 name: 'map_symbols_by_query',
 description: `Query for a structured map of files containing specific symbols, grouped by file path.

This tool is ideal for exploring directories discovered via discover_directories or when you need to see which files contain related symbols.

**Two ways to use this tool:**

1. **Directory exploration (recommended after discover_directories):**
 Use the 'directory' parameter to explore a specific directory:
 { "directory": "src/platform/packages/kbn-esql-utils" }

2. **Advanced filtering with KQL:**
 Use the 'kql' parameter for complex queries:
 { "kql": "language: typescript and kind: function_declaration" }

**You cannot use both 'directory' and 'kql' together - choose the one that fits your need.**

**Typical workflow:**
1. discover_directories → finds "src/platform/packages/kbn-esql-utils"
2. map_symbols_by_query → explore that directory with { "directory": "src/platform/packages/kbn-esql-utils" }
3. read_file_from_chunks → read specific files discovered

**Output format:**
Returns a JSON object where:
- Keys are file paths
- Values contain symbols grouped by type (function.call, variable.name, etc.)
- Files with more symbol matches are typically more relevant`,

 inputSchema: MapSymbolsInput,
 
 handler: async (input: z.infer<typeof MapSymbolsInput>) => {
 const { client, index } = getElasticsearchClient(input.index);
 
 // Validate and build KQL
 const kql = validateAndBuildQuery(input);
 
 // Parse KQL to Elasticsearch query
 const esQuery = parseKqlToElasticsearch(kql);
 
 // Execute the existing map symbols logic
 const symbolMap = await mapSymbolsByQuery(client, index, esQuery, input.size);
 
 return {
 content: [
 {
 type: 'text',
 text: JSON.stringify(symbolMap, null, 2)
 }
 ]
 };
 }
};

Why: Provides a clear, intuitive way to explore directories without requiring LLMs to understand KQL syntax for the common case.

Dependencies: Task 1

Task 3: Update Tool Description ✅

File: src/tools/map_symbols_by_query.ts

Changes: The tool description is already updated in Task 2. Ensure it clearly explains both usage patterns and the workflow integration.

Why: Clear documentation helps LLMs understand when to use directory vs kql and how this tool fits in the investigation workflow.

Dependencies: Task 2

Task 4: Update Documentation ✅

File: README.md or docs/TOOLS.md

Changes: Update the tool documentation to show both usage patterns:

Query for a structured map of files containing specific symbols, grouped by file path.

### When to Use
- After using discover_directories to explore a specific directory
- Finding which files contain multiple related symbols
- Getting a structured overview before reading full file contents
- Understanding symbol distribution across files

### Parameters
- `directory` (optional): Directory path to explore (e.g., "src/platform/packages/kbn-esql-utils")
- `kql` (optional): KQL query string for advanced filtering
- `size` (optional): Maximum files to return (default: 1000)
- `index` (optional): Specific index to search

**Note**: Cannot use both `directory` and `kql` together - choose one.

### Example Usage

**Example 1: Explore a directory (recommended after discover_directories)**
```json
{
 "directory": "src/platform/packages/kbn-esql-utils"
}

Example 2: Advanced KQL filtering

{
 "kql": "language: typescript and kind: function_declaration and filePath: *parser*"
}

Example 3: Combine with semantic search results After semantic_code_search finds relevant symbols, explore their directory:

{
 "directory": "src/platform/packages/shared/kbn-esql-ast"
}

Output Format

Returns a JSON object with files as keys and symbol/import information as values:

{
 "src/path/to/file.ts": {
 "symbols": {
 "function.call": [
 { "name": "parseQuery", "line": 42 }
 ],
 "variable.name": [
 { "name": "parser", "line": 35 }
 ]
 },
 "imports": {
 "module": [
 { "path": "@kbn/esql-ast", "symbols": ["Parser"] }
 ]
 }
 }
}

Files with more matching symbols are typically more relevant to investigate.

Workflow Integration

discover_directories → map_symbols_by_query → read_file_from_chunks
 ↓ ↓ ↓
 Find significant Explore directory Read specific
 directories structure implementations

**Why**: Comprehensive documentation with examples helps LLMs use the tool effectively in different scenarios.

**Dependencies**: Tasks 1-3

## Technical Details

### Directory-to-KQL Conversion Examples

```typescript
// Example 1: Simple directory
buildKqlFromDirectory('src/utils')
// Returns: "filePath: src/utils/*"

// Example 2: Nested package directory 
buildKqlFromDirectory('src/platform/packages/shared/kbn-esql-utils')
// Returns: "filePath: src/platform/packages/shared/kbn-esql-utils/*"

// Example 3: Trailing slash removed
buildKqlFromDirectory('src/utils/')
// Returns: "filePath: src/utils/*"

Integration with discover_directories

The workflow becomes very natural:

// Step 1: Discover
const dirs = await discover_directories({
 query: "ESQL parsing utilities",
 minFiles: 5
});

// Result: dirs[0].path = "src/platform/packages/shared/kbn-esql-utils"

// Step 2: Explore (now much simpler!)
const symbolMap = await map_symbols_by_query({
 directory: dirs[0].path // Just pass the path directly!
});

// Step 3: Read
const file = await read_file_from_chunks({
 filePaths: ["src/platform/packages/shared/kbn-esql-utils/parser.ts"]
});

Equivalent queries:

// These produce identical Elasticsearch queries:
{ directory: "src/utils" }
{ kql: "filePath: src/utils/*" }

Testing Strategy

Unit Tests:

describe('map_symbols_by_query with directory parameter', () => {
 it('should convert directory to KQL correctly', () => {
 const kql = buildKqlFromDirectory('src/platform/packages/kbn-esql');
 expect(kql).toBe('filePath: src/platform/packages/kbn-esql/*');
 });
 
 it('should remove trailing slashes', () => {
 const kql = buildKqlFromDirectory('src/utils/');
 expect(kql).toBe('filePath: src/utils/*');
 });
 
 it('should throw error when both directory and kql provided', () => {
 expect(() => {
 validateAndBuildQuery({ directory: 'src', kql: 'language: typescript' });
 }).toThrow('Cannot use both');
 });
 
 it('should throw error when neither directory nor kql provided', () => {
 expect(() => {
 validateAndBuildQuery({});
 }).toThrow('Must provide either');
 });
});

Validation Checklist

  • [ ] Input schema updated with directory parameter
  • [ ] Validation logic prevents using both directory and kql
  • [ ] Validation logic requires at least one parameter
  • [ ] Directory-to-KQL conversion function implemented
  • [ ] Trailing slashes handled correctly
  • [ ] Tool description updated with both usage patterns
  • [ ] Error messages are clear and helpful
  • [ ] Documentation updated with examples
  • [ ] Unit tests for directory conversion
  • [ ] Unit tests for validation logic

External Resources

For more information about KQL and its syntax, you can check out the official Elasticsearch Query DSL documentation.

You may also like