feat(extraction): add Nix language support with module-system option wiring (#324, #332 via #648 — carries #1084) (#1190)

Carries @TyceHerrman's #1084 as the functional base. Extraction + file wiring (imports/modules lists, callPackage), module-system option-path synthesizer, lexical-scope resolution gates, ABI-15 wasm rebuilt from upstream source. Validated on agenix, nix-darwin, home-manager, and nixpkgs (44,368 files, 3m49s, 1.30M nodes).

Co-authored-by: Tyce Herrman <Tyce.Herrman@pm.me>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-07-06 12:41:32 -05:00
committed by GitHub
co-authored by Tyce Herrman Claude Fable 5
parent 99152212a9
commit 7f325134e0
17 changed files with 1262 additions and 7 deletions
+29 -2
View File
@@ -1944,11 +1944,18 @@ export class ToolHandler {
}
// Same token, non-callable synth endpoints (capped, precision-gated on an
// actual heuristic edge so plain config constants never qualify).
// Per-token sub-cap so one token's many endpoints (10 nix option writes
// of `programs.git.enable` across test configs) can't fill the pool
// before later tokens (`home.file`) get a slot.
if (dynNamed.size < 12) {
let tokenDyn = 0;
for (const n of hits) {
if (CALLABLE.has(n.kind) || !DYN_KINDS.has(n.kind) || dynNamed.has(n.id)) continue;
if (hasHeuristicEdge(n.id)) dynNamed.set(n.id, n);
if (dynNamed.size >= 12) break;
if (hasHeuristicEdge(n.id)) {
dynNamed.set(n.id, n);
tokenDyn++;
}
if (dynNamed.size >= 12 || tokenDyn >= 4) break;
}
}
if (named.size > 40) break;
@@ -4399,6 +4406,26 @@ export class ToolHandler {
* results across all matching symbols (e.g., multiple classes with an `execute` method).
*/
private findAllSymbols(cg: CodeGraph, symbol: string): { nodes: Node[]; note: string } {
// Nix option paths: the declaration is stored as `options.<path>` and
// config writes carry longer/quoted tails (`<path>."git/config".text`),
// so a dotted option token (`xdg.configFile`, `launchd.user.agents`) has
// no exact-name node and would degrade to bare-tail FTS soup — burying
// the declaration hub the nix-option-path edges hang off. Resolve the
// convention directly: declaration first, then the exact write, then a
// capped prefix scan of write sites. Three index hits; non-nix graphs
// fall straight through.
if (/^[a-z][\w'-]*(?:\.[\w'-]+)+$/.test(symbol)) {
const optionHits = [
...cg.getNodesByName(`options.${symbol}`),
...cg.getNodesByName(symbol),
...cg.getNodesByNamePrefix(`${symbol}.`, 12),
].filter((n) => n.language === 'nix');
if (optionHits.length > 0) {
const seen = new Set<string>();
const nodes = optionHits.filter((n) => !seen.has(n.id) && !!seen.add(n.id)).slice(0, 10);
return { nodes, note: '' };
}
}
let results = cg.searchNodes(symbol, { limit: 50 });
// Mirror the fallback in `findSymbol` for qualified queries — FTS