How Egonex Language Auto‑Detection Works: First Run vs. Subsequent Runs
TLDR: Egonex uses a two-stage detection system where the first run builds a language registry from file extensions and special filenames, caches the results in a knowledge graph, and subsequent runs reuse that persisted data to skip redundant lookups.
Egonex's language auto‑detection system in the Understand-Anything repository optimizes performance by distinguishing between initial project scans and follow-up analyses. The architecture centers on the LanguageRegistry class in packages/core/src/languages/language-registry.ts, which maps file paths to language configurations during the first run, while the persistence layer ensures subsequent executions avoid repeating this expensive work.
First Run: Building the Language Registry
During the initial scan of a fresh project, Egonex constructs a comprehensive mapping of file paths to language identifiers using only filename and extension heuristics.
Registry Initialization
The process begins by instantiating a default registry that loads every built‑in language configuration. According to the source code in packages/core/src/languages/language-registry.ts, the following call creates this registry:
const registry = LanguageRegistry.createDefault(); // registers all builtin configs
The createDefault method populates the registry using the builtinLanguageConfigs array sourced from packages/core/src/languages/configs/*. Each configuration defines valid extensions, special filenames, and optional detection keywords.
File‑to‑Language Mapping Logic
For each file encountered by the scanner, Egonex queries the registry to determine the appropriate language. The graph-builder.ts implementation invokes:
const lang = registry.getForFile(filePath);
The getForFile method performs a two‑stage lookup:
- Filename‑based match – Checks for special filenames like
Dockerfile,Makefile, orJenkinsfile. - Extension‑based match – Falls back to file extensions such as
.ts,.py, or.json.
This lookup is case‑insensitive and normalizes extensions so that .ts and ts are treated identically. The following utility function demonstrates the first‑run detection logic:
import { LanguageRegistry } from "./languages/language-registry.js";
const registry = LanguageRegistry.createDefault(); // loads built‑ins
function detectLanguage(filePath: string): string | null {
const config = registry.getForFile(filePath);
return config?.id ?? null;
}
Graph Population and Persistence
Once detected, the language ID is attached to the file node within the knowledge graph. The packages/core/src/analyzer/graph-builder.ts implementation stores this metadata:
graph.addNode({
type: "File",
path: filePath,
language: detectLanguage(filePath),
});
The complete graph is then persisted to .understand-anything/knowledge-graph.json via the persistence layer in packages/core/src/persistence/index.ts. This ensures that the language auto‑detection results survive between process executions.
Subsequent Runs: Leveraging the Cached Knowledge Graph
When analyzing a project that has been scanned previously, Egonex bypasses the extension‑lookup entirely by loading the persisted state.
Loading Persisted State
The scanner first checks for existing cached data using loadKnowledgeGraphIfExists() from packages/core/src/persistence/index.ts:
const persisted = await loadKnowledgeGraphIfExists(); // from persistence/index.ts
If the knowledge graph exists, the language information is reused directly; the scanner does not repeat the registry.getForFile lookup for every file. This design makes subsequent runs substantially faster, as the language IDs are already attached to each file node.
Incremental Analysis
The implementation typically loads the graph as follows:
import { loadGraph } from "./persistence/index.js";
async function runScanner() {
const graph = await loadGraph(); // reads .understand-anything/knowledge-graph.json
if (graph) {
// Language info already present, skip re‑detecting:
console.log("Using cached language data for", graph.nodeCount, "files");
} else {
// Fallback to first‑run logic
const registry = LanguageRegistry.createDefault();
// ... perform full scan
}
}
Only new or changed files trigger fresh detection calls, while existing files retain their previously determined language identifiers.
Content‑Based Detection Hooks
While the current implementation relies solely on filename and extension matching, the architecture includes hooks for future content‑based refinement. The LanguageConfig type defined in packages/core/src/languages/types.ts includes an optional detectionKeywords array:
interface LanguageConfig {
id: string;
extensions: string[];
filenames?: string[];
detectionKeywords?: string[]; // Hook for content analysis
}
This allows for a potential second‑pass detection that could scan file contents for specific keywords. Such analysis would only occur during the first run (or when adding new language configurations), as the results would be cached for subsequent runs.
Summary
- First run:
LanguageRegistry.createDefault()initializes built‑in configs, thenregistry.getForFile()matches files by filename or extension, storing results in the knowledge graph. - Subsequent runs:
loadKnowledgeGraphIfExists()retrieves cached language IDs from.understand-anything/knowledge-graph.json, eliminating redundant lookups. - Performance: The two‑stage design ensures heavy file‑system analysis only happens once, with subsequent runs reading from the persisted JSON cache.
- Extensibility: The
detectionKeywordsfield inLanguageConfigprovides a hook for future content‑based detection without breaking the caching mechanism.
Frequently Asked Questions
How does Egonex detect languages on the first scan?
On the first run, Egonex creates a LanguageRegistry instance via createDefault() and calls getForFile(filePath) for each discovered file. This method first checks for special filenames (like Dockerfile or Makefile), then falls back to extension matching (such as .ts or .py). The detection is case‑insensitive and stores the resulting language ID in the knowledge graph.
Where does Egonex store language information between runs?
Language metadata is stored in the knowledge graph file located at .understand-anything/knowledge-graph.json. The persistence layer in packages/core/src/persistence/index.ts handles serialization and deserialization, allowing the loadGraph() function to retrieve cached language data on subsequent executions.
Does Egonex analyze file content to determine language?
Currently, no. The implementation in packages/core/src/languages/language-registry.ts uses only filename and extension heuristics. However, the LanguageConfig interface in packages/core/src/languages/types.ts includes a detectionKeywords array that provides a hook for future content‑based detection, which would run during the first scan and be cached for later runs.
What happens if I add a new file type to my project?
When new files are added, the scanner detects them as changed entities during subsequent runs. While the cached graph provides language data for existing files, the system performs fresh registry.getForFile() lookups only for new or modified files, then updates the persisted knowledge graph to include the new language mappings.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →