MCPcopy Create free account
hub / github.com/QodeXcli/QodeX / expandViaGraph

Function expandViaGraph

src/context/import-graph.ts:216–265  ·  view source on GitHub ↗
(
  graph: ImportGraph,
  seeds: string[],
  opts: { hops?: number; maxFiles?: number; hubDamping?: boolean } = {},
)

Source from the content-addressed store, hash-verified

214 * since "how does X work / why is X broken" usually lives in what X uses.
215 */
216export function expandViaGraph(
217 graph: ImportGraph,
218 seeds: string[],
219 opts: { hops?: number; maxFiles?: number; hubDamping?: boolean } = {},
220): Array<{ file: string; distance: number; weight: number }> {
221 const hops = opts.hops ?? 1;
222 const maxFiles = opts.maxFiles ?? 12;
223 const hubDamping = opts.hubDamping !== false;
224
225 // distance + which direction we first reached it (down=dependency, up=dependent)
226 const dist = new Map<string, number>();
227 const viaDownstream = new Map<string, boolean>();
228 for (const s of seeds) if (graph.files.has(s)) { dist.set(s, 0); viaDownstream.set(s, true); }
229
230 let frontier = [...dist.keys()];
231 for (let h = 1; h <= hops; h++) {
232 const next: string[] = [];
233 for (const f of frontier) {
234 const downstream = graph.out.get(f) ?? new Set<string>();
235 const upstream = graph.in.get(f) ?? new Set<string>();
236 for (const nbr of downstream) {
237 if (!dist.has(nbr)) { dist.set(nbr, h); viaDownstream.set(nbr, true); next.push(nbr); }
238 }
239 for (const nbr of upstream) {
240 if (!dist.has(nbr)) { dist.set(nbr, h); viaDownstream.set(nbr, false); next.push(nbr); }
241 }
242 }
243 frontier = next;
244 }
245
246 const inDegreeOf = (f: string): number => (graph.in.get(f)?.size ?? 0);
247
248 const scored = [...dist.entries()].map(([file, distance]) => {
249 // Base relevance decays with distance; seeds are 1.0.
250 let weight = distance === 0 ? 1.0 : 1.0 / (distance + 1);
251 // Downstream (dependencies) are more relevant than upstream (dependents).
252 if (distance > 0 && !viaDownstream.get(file)) weight *= 0.6;
253 // Hub damping: demote files imported by many others (low query specificity).
254 if (hubDamping && distance > 0) {
255 const deg = inDegreeOf(file);
256 weight *= 1 / Math.log2(2 + deg);
257 }
258 return { file, distance, weight };
259 });
260
261 // Seeds always kept; non-seeds ranked by weight, then taken up to the cap.
262 const seedRows = scored.filter(s => s.distance === 0);
263 const rest = scored.filter(s => s.distance > 0).sort((a, b) => b.weight - a.weight);
264 return [...seedRows, ...rest].slice(0, maxFiles);
265}

Callers 4

expandRankedViaGraphFunction · 0.85
buildTaskContextFunction · 0.85

Calls 5

inDegreeOfFunction · 0.85
hasMethod · 0.45
setMethod · 0.45
getMethod · 0.45
pushMethod · 0.45

Tested by

no test coverage detected