MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / handleExplore

Method handleExplore

src/mcp/tools.ts:3254–5980  ·  view source on GitHub ↗

* Handle codegraph_explore — deep exploration in a single call * * Strategy: find relevant symbols via graph traversal, group by file, * then read contiguous file sections covering all symbols per file. * This replaces multiple codegraph_node + Read calls. * * Output size is adapti

(args: Record<string, unknown>)

Source from the content-addressed store, hash-verified

3252 * tax on small projects while earning its keep on large ones.
3253 */
3254 private async handleExplore(args: Record<string, unknown>): Promise<ToolResult> {
3255 const rawQuery = this.validateString(args.query, 'query');
3256 if (typeof rawQuery !== 'string') return rawQuery;
3257 // One normalization point so the flow-builder, relevance search, and
3258 // ranking all see the same canonical spelling (Erlang `mod:fn/arity`).
3259 const query = normalizeQuerySpelling(rawQuery);
3260
3261 const cg = this.getCodeGraph(args.projectPath as string | undefined);
3262 const projectRoot = cg.getProjectRoot();
3263
3264 // Resolve adaptive output budget from project size. Falls back to the
3265 // largest-tier defaults if stats aren't available, which preserves
3266 // pre-#185 behavior for callers that hit the rare stats failure.
3267 let budget: ExploreOutputBudget;
3268 let indexedFileCount = -1;
3269 try {
3270 indexedFileCount = cg.getStats().fileCount;
3271 budget = getExploreOutputBudget(indexedFileCount);
3272 } catch {
3273 budget = getExploreOutputBudget(Infinity);
3274 }
3275 const maxFiles = clamp((args.maxFiles as number) || budget.defaultMaxFiles, 1, 20);
3276
3277 // File paths named in the query become PINNED files: guaranteed admission,
3278 // top of the rank order, funded first — and their span is REMOVED from the
3279 // matching query. Runs on the RAW query (normalizeQuerySpelling strips
3280 // `/digits` tails, which would mangle numeric path segments). Without this,
3281 // a SvelteKit path like `runs/[runId]/+page.svelte` was shredded by the
3282 // seeding tokenizer (splits on brackets → `runId` seeded as a "named
3283 // symbol") and by FTS (`page`/`runs` fragments admitted every sibling
3284 // `+page.svelte`), starving the very files the agent asked for.
3285 let pinnedFiles: string[] = [];
3286 let unresolvedPathSpans: string[] = [];
3287 let matchQuery = query;
3288 if (queryMightContainPaths(rawQuery)) {
3289 try {
3290 const extraction = extractQueryPaths(
3291 rawQuery,
3292 cg.getFiles().map((f) => f.path),
3293 { maxPins: maxFiles },
3294 );
3295 if (extraction.pinnedFiles.length > 0 || extraction.unresolvedPathSpans.length > 0) {
3296 pinnedFiles = extraction.pinnedFiles;
3297 unresolvedPathSpans = extraction.unresolvedPathSpans;
3298 matchQuery = normalizeQuerySpelling(extraction.strippedQuery);
3299 }
3300 } catch { /* path pinning must never fail an explore call */ }
3301 }
3302 const pinnedSet = new Set(pinnedFiles);
3303 const pinnedOrder = new Map(pinnedFiles.map((p, i) => [p, i]));
3304
3305 // Per-file allocation diagnostic (CG-4). `null` unless CODEGRAPH_EXPLORE_DEBUG
3306 // is set — every `diag?.` below is then a no-op and the response is
3307 // byte-identical. It only OBSERVES: it must never feed back into rendering.
3308 const diag = ExploreDiagnostics.start(query, projectRoot, budget, maxFiles, indexedFileCount);
3309
3310 // What this session has already been served for THIS project (CG-17), and
3311 // whether this call may act on it (CG-18). Dedup is off on the session's

Callers 1

dispatchToolMethod · 0.95

Calls 15

validateStringMethod · 0.95
getCodeGraphMethod · 0.95
exploreResultMethod · 0.95
findAllSymbolsMethod · 0.95
computeGraphRelevanceMethod · 0.95
isFileStaleOnDiskMethod · 0.95
clampFunction · 0.90
queryMightContainPathsFunction · 0.90
extractQueryPathsFunction · 0.90
viewForProjectFunction · 0.90

Tested by

no test coverage detected