MCPcopy
hub / github.com/colbymchenry/codegraph / buildFlowFromNamedSymbols

Method buildFlowFromNamedSymbols

src/mcp/tools.ts:1847–2106  ·  view source on GitHub ↗

* Flow-from-named-symbols: an agent's codegraph_explore query is a bag of * symbol names that usually spans the flow it's investigating (e.g. * "PmsProductController getList PmsProductService list PmsProductServiceImpl"). * Surface the longest call chain AMONG those named symbols — scoped t

(cg: CodeGraph, query: string)

Source from the content-addressed store, hash-verified

1845 * dropping unrelated `OmsOrderService::list`.
1846 */
1847 private buildFlowFromNamedSymbols(cg: CodeGraph, query: string): { text: string; pathNodeIds: Set<string>; namedNodeIds: Set<string>; uniqueNamedNodeIds: Set<string>; spineCallSites: Map<string, number> } {
1848 // spineCallSites: for each spine node, the line where it CALLS the next hop —
1849 // lets the source assembler window an oversize spine method (e.g. n8n's 962-line
1850 // processRunExecutionData) to the call site instead of dumping the whole body.
1851 const EMPTY = { text: '', pathNodeIds: new Set<string>(), namedNodeIds: new Set<string>(), uniqueNamedNodeIds: new Set<string>(), spineCallSites: new Map<string, number>() };
1852 try {
1853 const CALLABLE = new Set(['method', 'function', 'component', 'constructor']);
1854 // Strip only a REAL file extension (Create.cs → Create); KEEP qualified
1855 // names (Class.method / Class::method) — the agent's most precise input,
1856 // resolved exactly by findAllSymbols. (The old strip mangled Class.method
1857 // into Class, throwing the method away.)
1858 const FILE_EXT = /\.(?:java|kt|kts|ts|tsx|js|jsx|mjs|cjs|cs|py|go|rb|php|swift|rs|cpp|cc|cxx|c|h|hpp|scala|lua|dart|vue|svelte|astro)$/i;
1859 const tokens = [...new Set(
1860 query.split(/[\s,()[\]]+/)
1861 .map((t) => t.replace(FILE_EXT, '').trim())
1862 .filter((t) => t.length >= 3 && /^[A-Za-z_$][\w$]*(?:(?:::|\.)[\w$]+)*$/.test(t))
1863 )].slice(0, 16);
1864 if (tokens.length < 2) return EMPTY;
1865 // Pool of name SEGMENTS (Class + method from every token) used to
1866 // disambiguate an ambiguous SIMPLE name: keep a candidate only if its
1867 // CONTAINER class is itself named in the query.
1868 const segPool = new Set<string>();
1869 for (const t of tokens) for (const s of t.toLowerCase().split(/::|\./)) if (s) segPool.add(s);
1870 const named = new Map<string, Node>();
1871 // Nodes whose token is SPECIFIC — a (near-)unique callable name (<=3 defs in
1872 // the whole graph). These are safe to SPARE a file on: the agent named THIS
1873 // method (`getResponseWithInterceptorChain`, 1 def). A hyper-polymorphic name
1874 // (`as_sql`, 110 defs across every Expression/Compiler subclass) is NOT here,
1875 // so naming it doesn't keep every backend variant full and flood the budget.
1876 const uniqueNamedNodeIds = new Set<string>();
1877 // token → resolved node ids: drives the token-coverage check that gates
1878 // the dynamic-boundary scan (a token is covered when ANY of its nodes
1879 // lands on the main chain — overloads off the chain don't count against).
1880 const tokenNodes = new Map<string, string[]>();
1881 // token → its full same-name callable family (before the container filter).
1882 // A LARGE family that fails to connect on the chain is a polymorphic
1883 // interface/registry dispatch — surfaced by buildPolymorphicBoundaries below.
1884 const tokenFamily = new Map<string, Node[]>();
1885 // Non-callable endpoints (CONSTANT/VARIABLE/FIELD) connected by a SYNTHESIZED
1886 // edge. RTK thunks are `const X = createAsyncThunk(...)`, so a thunk→thunk hop
1887 // is constant→constant — the CALLABLE-only `named` set can't hold it, and
1888 // without this the hop is invisible to the Flow path at every tier (the
1889 // Relationships section catches it only on repos ≥500 files). Kept SEPARATE
1890 // from `named` (which drives the call-chain + source sizing, callable-only);
1891 // fed only to the dynamic-dispatch-links scan below.
1892 const dynNamed = new Map<string, Node>();
1893 const DYN_KINDS = new Set(['constant', 'variable', 'field', 'property']);
1894 const hasHeuristicEdge = (id: string): boolean =>
1895 [...cg.getCallers(id), ...cg.getCallees(id)].some(({ edge }) => edge.provenance === 'heuristic');
1896 for (const t of tokens) {
1897 const hits = this.findAllSymbols(cg, t).nodes;
1898 const cands = hits.filter((n) => CALLABLE.has(n.kind));
1899 tokenFamily.set(t, cands);
1900 // A qualified or otherwise-specific name (<=3 hits) keeps all; an
1901 // ambiguous simple name keeps only candidates whose container is named.
1902 const specific = cands.length <= 3;
1903 const pick = specific
1904 ? cands

Callers 1

handleExploreMethod · 0.95

Calls 9

findAllSymbolsMethod · 0.95
synthEdgeNoteMethod · 0.95
hasMethod · 0.80
setMethod · 0.80
joinMethod · 0.80
getMethod · 0.65
getCalleesMethod · 0.45

Tested by

no test coverage detected