/** * Chunked batch search for codebase file relevance ranking * * This module provides utilities for splitting file summaries into batches * and preparing prompts for GLM 4.6v-flash to rank file relevance to a * search query. Handles codebases of any size through chunking. */ import { type CodebaseMap } from "../../cache/map.js"; import { type LLMClient } from "../../llm/client.js"; import { type ConcurrencyPool, type ProgressCallback } from "../../llm/pool.js"; /** * Number of files per batch for LLM processing * * This value balances: * - Token limits: Each file path + summary can be ~100-500 tokens * - Context quality: Too many files reduces ranking accuracy * - API efficiency: Fewer batches means fewer API calls */ export declare const BATCH_SIZE = 50; /** * System prompt for GLM 4.6v-flash to rank file relevance * * Instructs the model to: * 1. Analyze file paths and summaries against the search query * 2. Rank each file's relevance: high, medium, low, or none * 3. Return structured JSON response */ export declare const SEARCH_SYSTEM_PROMPT = "You are a code search assistant that ranks file relevance for a given query.\n\n## Your Role\n\nGiven a search query and a list of files with their summaries, you determine how relevant each file is to the query.\n\n## Relevance Levels\n\n- **high**: File directly implements, contains, or is central to the searched functionality\n- **medium**: File is related to the search (imports, dependencies, tests, related utilities)\n- **low**: File has tangential connection (same module, shared types, configuration)\n- **none**: File is unrelated to the search query\n\n## Instructions\n\n1. Analyze the search query to understand what the user is looking for\n2. For each file, compare its path and summary against the query\n3. Consider both direct matches and indirect relationships\n4. Rank each file's relevance\n\n## Output Format\n\nYou MUST respond with ONLY a valid JSON array. No markdown, no explanation, no code blocks.\n\nEach element must have exactly these fields:\n- \"path\": The file path (exactly as provided)\n- \"relevance\": One of \"high\", \"medium\", \"low\", or \"none\"\n- \"reason\": Brief explanation (max 50 characters)\n\nExample output:\n[\n {\"path\": \"src/auth/login.ts\", \"relevance\": \"high\", \"reason\": \"Implements login functionality\"},\n {\"path\": \"src/utils/hash.ts\", \"relevance\": \"medium\", \"reason\": \"Used for password hashing\"},\n {\"path\": \"src/config.ts\", \"relevance\": \"low\", \"reason\": \"May contain auth settings\"},\n {\"path\": \"src/ui/button.ts\", \"relevance\": \"none\", \"reason\": \"UI component, unrelated\"}\n]\n\n## Guidelines\n\n1. Be conservative with \"high\" - only for files that directly answer the query\n2. Use \"medium\" for supporting files that are likely needed\n3. Use \"low\" for files that might be tangentially useful\n4. Use \"none\" freely - most files won't be relevant to any given query\n5. Consider file paths as hints (e.g., src/auth/* likely relates to authentication)\n6. Consider function/class names mentioned in summaries\n\nRemember: Output ONLY the JSON array. No other text."; /** * Relevance level for a file */ export type RelevanceLevel = "high" | "medium" | "low" | "none"; /** * A single file entry in a search batch */ export interface SearchBatchFile { /** Relative file path from project root */ path: string; /** LLM-generated summary of the file's purpose */ summary: string; } /** * A batch of files for LLM processing */ export interface SearchBatch { /** Zero-indexed batch number */ batchIndex: number; /** Files in this batch */ files: SearchBatchFile[]; } /** * Result for a single file from LLM ranking */ export interface SearchFileResult { /** Relative file path */ path: string; /** Relevance level assigned by LLM */ relevance: RelevanceLevel; /** Brief reason for the ranking */ reason: string; } /** * Result of processing a single batch */ export interface SearchBatchResult { /** The batch that was processed */ batch: SearchBatch; /** Results for each file in the batch */ results: SearchFileResult[]; /** Whether this batch was successfully processed */ success: boolean; /** Error message if processing failed */ error?: string; } /** * Final search result for a file * * Represents a deduplicated, ranked result from the search operation. */ export interface SearchResult { /** Relative file path from project root */ file: string; /** Relevance level assigned by LLM */ relevance: RelevanceLevel; /** LLM-generated summary of the file's purpose */ summary: string; /** Brief reason for the relevance ranking from the LLM */ reason?: string; } /** * Split codebase file summaries into batches for LLM processing * * Filters out files without summaries (not yet analyzed) and splits * the remaining files into chunks of the specified size. * * @param cache - The codebase map containing file entries * @param batchSize - Number of files per batch (default: BATCH_SIZE) * @returns Array of SearchBatch objects ready for LLM processing * * @example * ```typescript * const cache = loadCache('/path/to/project'); * const batches = chunkSummaries(cache, 20); * console.log(`Split into ${batches.length} batches`); * ``` */ export declare function chunkSummaries(cache: CodebaseMap, batchSize?: number): SearchBatch[]; /** * Format files in a batch for LLM consumption * * Creates a numbered list with file paths and summaries * for the LLM to process. * * @param files - Array of files to format * @returns Formatted string for LLM input */ declare function formatBatchFiles(files: SearchBatchFile[]): string; /** * Create a search prompt for a single batch * * Formats the query and batch files into a user message * ready for the LLM. * * @param query - The search query from the user * @param batch - The batch of files to rank * @returns Formatted user message string * * @example * ```typescript * const prompt = createSearchBatchPrompt("authentication logic", batch); * // Send to LLM with SEARCH_SYSTEM_PROMPT * ``` */ export declare function createSearchBatchPrompt(query: string, batch: SearchBatch): string; /** * Extract JSON array from LLM response * * Handles various response formats: * - Raw JSON array * - JSON wrapped in markdown code blocks * - JSON with leading/trailing whitespace * * @param response - Raw LLM response string * @returns Extracted JSON string */ declare function extractJSON(response: string): string; /** * Parse and validate a single file result from the LLM response * * @param obj - Parsed JSON object to validate * @param index - Index for error reporting * @param validPaths - Set of valid file paths in this batch * @returns Validated SearchFileResult */ declare function validateFileResult(obj: unknown, index: number, validPaths: Set): SearchFileResult; /** * Parse LLM response for a search batch * * Extracts and validates the JSON array of file relevance rankings. * Handles partial failures gracefully. * * @param response - Raw LLM response string * @param batch - The batch that was processed (for validation) * @returns Array of validated SearchFileResult objects */ export declare function parseSearchBatchResponse(response: string, batch: SearchBatch): SearchFileResult[]; /** * Get statistics about chunked batches * * @param batches - Array of search batches * @returns Statistics object */ export declare function getBatchStats(batches: SearchBatch[]): { totalBatches: number; totalFiles: number; avgFilesPerBatch: number; }; /** * Execute a parallel search across all cached file summaries * * This function: * 1. Chunks the cache summaries into batches * 2. Executes all batch queries in parallel through the concurrency pool * 3. Merges results from all batches * 4. Deduplicates by file path (keeping highest relevance) * 5. Sorts by relevance (high > medium > low) * 6. Filters out "none" relevance results * * @param client - LLM client for API calls * @param model - Model ID to use (e.g., "glm-4.6v-flash") * @param pool - Concurrency pool for rate limiting * @param cache - Codebase map containing file summaries * @param query - Search query from the user * @returns Promise resolving to sorted, deduplicated SearchResult array * * @example * ```typescript * const results = await executeSearch( * client, * "glm-4.6v-flash", * pool, * cache, * "authentication logic" * ); * for (const result of results) { * console.log(`${result.relevance}: ${result.file}`); * } * ``` */ export declare function executeSearch(client: LLMClient, model: string, pool: ConcurrencyPool, cache: CodebaseMap, query: string, onProgress?: ProgressCallback): Promise; /** * Options for the searchCodebase function */ export interface SearchCodebaseOptions { /** * Maximum number of results to return * @default 20 */ maxResults?: number; /** * Minimum relevance level to include in results * @default "low" */ minRelevance?: RelevanceLevel; /** * Custom concurrency limit (overrides config) * @default config.concurrency */ concurrency?: number; /** * Progress callback for reporting search progress */ onProgress?: ProgressCallback; } /** * Result of the searchCodebase operation */ export interface SearchCodebaseResult { /** Matching files ranked by relevance */ results: SearchResult[]; /** Number of files searched */ files_searched?: number; /** Error message if the search failed */ error?: string; } /** * Error thrown when search fails due to configuration or cache issues */ export declare class SearchCodebaseError extends Error { readonly code: string; constructor(message: string, code: string); } /** * Search a codebase for files relevant to a query * * This is the main implementation for the scampi_search MCP tool. * It performs the following steps: * * 1. Load configuration and create LLM client * 2. Load cached codebase map * 3. If cache is empty/missing, return error suggesting to run map first * 4. Execute parallel search using executeSearch() * 5. Filter and limit results based on options * 6. Return search results * * @param directory - Absolute path to the directory to search * @param query - Natural language or keyword search query * @param options - Optional configuration for the search operation * @returns Promise resolving to SearchCodebaseResult * * @example * ```typescript * const result = await searchCodebase('/path/to/project', 'authentication logic'); * for (const file of result.results) { * console.log(`${file.relevance}: ${file.file}`); * console.log(` ${file.summary}`); * } * ``` * * @example * ```typescript * // Limit to high-relevance results * const result = await searchCodebase('/path/to/project', 'database queries', { * maxResults: 10, * minRelevance: 'high', * }); * ``` */ export declare function searchCodebase(directory: string, query: string, options?: SearchCodebaseOptions): Promise; export { extractJSON as _extractJSON }; export { validateFileResult as _validateFileResult }; export { formatBatchFiles as _formatBatchFiles }; //# sourceMappingURL=search.d.ts.map