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

Method handleExplore

src/mcp/tools.ts:3949–7443  ·  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

3947 * tax on small projects while earning its keep on large ones.
3948 */
3949 private async handleExplore(args: Record<string, unknown>): Promise<ToolResult> {
3950 const rawQuery = this.validateString(args.query, 'query');
3951 if (typeof rawQuery !== 'string') return rawQuery;
3952 // One normalization point so the flow-builder, relevance search, and
3953 // ranking all see the same canonical spelling (Erlang `mod:fn/arity`).
3954 const query = normalizeQuerySpelling(rawQuery);
3955
3956 const cg = this.getCodeGraph(args.projectPath as string | undefined);
3957 const projectRoot = cg.getProjectRoot();
3958
3959 // Resolve adaptive output budget from project size. Falls back to the
3960 // largest-tier defaults if stats aren't available, which preserves
3961 // pre-#185 behavior for callers that hit the rare stats failure.
3962 let budget: ExploreOutputBudget;
3963 let indexedFileCount = -1;
3964 let indexedNodeCount = -1;
3965 try {
3966 const stats = cg.getStats();
3967 indexedFileCount = stats.fileCount;
3968 indexedNodeCount = stats.nodeCount;
3969 budget = getExploreOutputBudget(indexedFileCount);
3970 } catch {
3971 budget = getExploreOutputBudget(Infinity);
3972 }
3973 const maxFiles = clamp((args.maxFiles as number) || budget.defaultMaxFiles, 1, 20);
3974
3975 // File paths named in the query become PINNED files: guaranteed admission,
3976 // top of the rank order, funded first — and their span is REMOVED from the
3977 // matching query. Runs on the RAW query (normalizeQuerySpelling strips
3978 // `/digits` tails, which would mangle numeric path segments). Without this,
3979 // a SvelteKit path like `runs/[runId]/+page.svelte` was shredded by the
3980 // seeding tokenizer (splits on brackets → `runId` seeded as a "named
3981 // symbol") and by FTS (`page`/`runs` fragments admitted every sibling
3982 // `+page.svelte`), starving the very files the agent asked for.
3983 let pinnedFiles: string[] = [];
3984 let unresolvedPathSpans: string[] = [];
3985 let lineAnchors: QueryLineAnchor[] = [];
3986 let setAsideMatches: QuerySetAsideMatch[] = [];
3987 let matchQuery = query;
3988 if (queryMightContainPaths(rawQuery)) {
3989 try {
3990 const extraction = extractQueryPaths(
3991 rawQuery,
3992 cg.getFiles().map((f) => f.path),
3993 {
3994 maxPins: maxFiles,
3995 existsOnDisk: (rel) => pathIsProjectFile(projectRoot, rel),
3996 symbolFiles: (symbol) => filesDefiningSymbol(cg, symbol),
3997 },
3998 );
3999 if (extraction.pinnedFiles.length > 0 || extraction.unresolvedPathSpans.length > 0) {
4000 pinnedFiles = extraction.pinnedFiles;
4001 unresolvedPathSpans = extraction.unresolvedPathSpans;
4002 lineAnchors = extraction.lineAnchors;
4003 setAsideMatches = extraction.setAsideMatches;
4004 matchQuery = normalizeQuerySpelling(extraction.strippedQuery);
4005 }
4006 } catch { /* path pinning must never fail an explore call */ }

Callers 1

dispatchToolMethod · 0.95

Calls 15

validateStringMethod · 0.95
getCodeGraphMethod · 0.95
textResultMethod · 0.95
findAllSymbolsMethod · 0.95
computeGraphRelevanceMethod · 0.95
isFileStaleOnDiskMethod · 0.95
answerResultMethod · 0.95
clampFunction · 0.90
queryMightContainPathsFunction · 0.90
extractQueryPathsFunction · 0.90

Tested by

no test coverage detected