How project-scanner Identifies Programming Languages and Frameworks in Understand-Anything
The project-scanner determines file types through deterministic filename and extension lookups, then delegates framework classification to the graph-builder which analyzes import maps against a registry of known frameworks.
The project-scanner in the Egonex-AI/Understand-Anything repository employs a fast, rule-based pipeline to classify source files. By separating language detection from framework inference, the tool maintains high performance while achieving accurate technology stack identification. This article examines the two-stage process that enables the scanner to identify programming languages and frameworks without LLM-based analysis.
Stage 1: Language Identification via Filename and Extension
The detectLanguage() function exported from understand-anything-plugin/skills/understand/scan-project.mjs (lines 21-48) performs language identification through a cascading lookup strategy designed for speed and determinism.
Filename-Based Detection
First, the function checks the file name against LANGUAGE_BY_FILENAME, a lookup table mapping well-known no-extension conventions to their respective language identifiers. This table includes entries like Dockerfile, Makefile, and various dot-file patterns.
For dot-files such as .env.local or .env.production, the helper dotfileKey() extracts the leading .env segment to normalize all variants to the same config category. This ensures that .env, .env.local, and .env.production all resolve to the same language ID.
Extension-Based Detection
If no special filename matches, the function lower-cases the file extension and queries the LANGUAGE_BY_EXT table (defined at lines 5-94 of the same file). This comprehensive lookup covers standard development extensions:
.tsand.tsx→typescript.py→python.go→go.rb→ruby.html→html.env→config
For unknown extensions, the leading dot is stripped and the remainder returned as the language ID (e.g., data.weirdext becomes weirdext). Files lacking both special names and extensions return 'unknown'.
Stage 2: Framework Inference via Import-Map Analysis
The scanner intentionally does not analyze file contents for frameworks. Instead, framework detection occurs later in the pipeline through the graph-builder located in packages/core/src/analyzer/graph-builder.ts.
The Framework Registry
The framework registry at packages/core/src/languages/framework-registry.ts maintains bidirectional mappings between language IDs and framework descriptors. Known frameworks including Express, Django, FastAPI, and Gin are registered with their associated language IDs.
Individual framework definition files—such as packages/core/src/languages/frameworks/express.ts—contain predicates that recognize framework-specific entry points. For example, the Express detector identifies require('express') or import ... from 'express' statements within JavaScript files.
Graph-Builder Integration
When processing the project graph, the builder invokes detectLanguage() to obtain the language ID, then cross-references this with the project's import map generated by the extract-import-map.mjs skill. If a file's language is javascript and the import map contains Express dependencies, the builder labels it as an Express framework file. This separation of concerns keeps the initial scan fast while allowing sophisticated framework detection through AST analysis.
Practical Implementation Examples
The following examples demonstrate direct usage of the scanner's detection utilities and their integration with the framework classification system:
// Direct usage of detection helpers (as tested in test_scan_project.test.mjs)
import { detectLanguage, detectCategory } from './scan-project.mjs';
console.log(detectLanguage('src/server.ts')); // → "typescript"
console.log(detectLanguage('Dockerfile.dev')); // → "dockerfile"
console.log(detectLanguage('.env.production')); // → "config"
console.log(detectLanguage('unknown.xyz')); // → "xyz"
// Framework assignment in the graph builder (simplified from graph-builder.ts)
import { detectLanguage } from '../../skills/understand/scan-project.mjs';
import { FRAMEWORK_REGISTRY } from '../languages/framework-registry';
function assignFramework(filePath: string, importMap: Set<string>) {
const lang = detectLanguage(filePath);
for (const fw of FRAMEWORK_REGISTRY[lang] ?? []) {
if (fw.matchesImportMap(importMap)) return fw.name; // e.g., "Express"
}
return null; // no framework detected
}
Summary
- Language detection relies on
detectLanguage()inscan-project.mjs, which uses lookup tables (LANGUAGE_BY_FILENAMEandLANGUAGE_BY_EXT) to classify files by name and extension. - Dot-file normalization is handled by
dotfileKey()to group variants like.env.localunder theconfigcategory. - Framework inference is performed separately by the graph-builder using the framework registry and import-map analysis rather than file content heuristics.
- Key files include the scanner script (
understand-anything-plugin/skills/understand/scan-project.mjs), the framework registry (packages/core/src/languages/framework-registry.ts), and the graph-builder (packages/core/src/analyzer/graph-builder.ts).
Frequently Asked Questions
How does project-scanner handle files without extensions?
Files without extensions are checked against LANGUAGE_BY_FILENAME for well-known names like Dockerfile or Makefile. If no match exists, the function returns 'unknown'.
Can the scanner detect frameworks directly from source code content?
No. The scanner only identifies programming languages. Framework detection occurs later in the pipeline when the graph-builder analyzes import statements against the framework registry.
Where are the language lookup tables defined?
The LANGUAGE_BY_FILENAME and LANGUAGE_BY_EXT tables are defined at lines 5-94 of understand-anything-plugin/skills/understand/scan-project.mjs.
How are environment configuration files like .env.local handled?
The dotfileKey() helper extracts the base name (e.g., .env) from dot-files, causing all .env.* variants to map to the config language ID in the lookup table.
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 →