How Egonex-AI Understand-Anything Handles Incremental Updates with Fingerprint-Based Change Detection
Understand-Anything minimizes computational overhead by classifying file changes into NONE, COSMETIC, or STRUCTURAL categories using SHA-256 hashes and tree-sitter structural signatures, updating only the affected portions of the knowledge graph.
The Egonex-AI/Understand-Anything repository implements a sophisticated fingerprint-based change detection system to enable efficient incremental updates. By creating detailed FileFingerprint objects for each source file, the system distinguishes between cosmetic code modifications and structural changes that require knowledge graph recomputation.
Building the Fingerprint Store
The buildFingerprintStore function in fingerprint.ts generates a complete fingerprint catalog for the project. It walks every file, invokes the language-specific registry.analyzeFile parser, and creates a comprehensive fingerprint record.
// Definition of the fingerprint type – see
// fingerprint.ts#L9-L38
export interface FileFingerprint {
filePath: string;
contentHash: string;
functions: FunctionFingerprint[];
classes: ClassFingerprint[];
imports: ImportFingerprint[];
exports: string[];
totalLines: number;
hasStructuralAnalysis: boolean;
}
Files without tree-sitter support receive a hash-only fingerprint and are treated conservatively as potentially STRUCTURAL changes. The resulting JSON map is written to fingerprints.json and versioned with the current git commit hash.
// Build the store – see
// fingerprint.ts#L53-L84
export function buildFingerprintStore(
projectDir: string,
filePaths: string[],
registry: PluginRegistry,
gitCommitHash: string,
): FingerprintStore { … }
Detecting Changes with compareFingerprints
On subsequent runs, analyzeChanges re-fingerprints only files reported by the VCS as modified. Each file pair is processed through compareFingerprints to determine the change severity.
// Compare two fingerprints – see
// fingerprint.ts#L31-L46
export function compareFingerprints(oldFp: FileFingerprint, newFp: FileFingerprint): FileChangeResult { … }
The comparison follows a three-tier classification system:
- NONE – The SHA-256 content hash matches exactly, requiring no processing.
- COSMETIC – The hash differs but all structural signatures (function signatures, class members, import/export lists) remain identical, indicating only internal logic changed.
- STRUCTURAL – Any structural signature differs, or structural analysis is unavailable, necessitating a full knowledge graph recomputation for that file.
The function also produces a human-readable details array (e.g., "new function: foo", "imports changed") that downstream agents use for enriched reporting.
Aggregating Results for Incremental Processing
The analyzeChanges function aggregates per-file results into a ChangeAnalysis object, categorizing files as new, deleted, unchanged, cosmetically changed, or structurally changed.
// High-level change analysis – see
// fingerprint.ts#L94-L108
export function analyzeChanges(
projectDir: string,
changedFiles: string[],
existingStore: FingerprintStore,
registry: PluginRegistry,
): ChangeAnalysis { … }
This categorization drives the incremental graph builder: only files flagged as STRUCTURAL trigger node and edge updates, while COSMETIC files retain their existing graph representation. This selective recomputation dramatically reduces CPU and I/O for large codebases.
Implementation Details and Code Examples
The following example demonstrates generating a fingerprint store and analyzing changes after a git operation:
import {
buildFingerprintStore,
analyzeChanges,
readFileSync,
writeFileSync,
} from '@understand-anything/core';
// 1️⃣ Generate a fingerprint store for the whole project
const allFiles = await globby(['**/*.{ts,js,tsx,jsx,py,go,rs}']);
const registry = await import('@understand-anything/core/src/plugins/registry.js');
const store = buildFingerprintStore('/my/project', allFiles, registry, 'HEAD');
// Persist for later runs
writeFileSync('fingerprints.json', JSON.stringify(store));
// 2️⃣ Later – after git reports changed files
const changed = ['src/utils.ts', 'README.md']; // from `git diff --name-only`
const previousStore = JSON.parse(readFileSync('fingerprints.json', 'utf-8'));
const analysis = analyzeChanges('/my/project', changed, previousStore, registry);
// Use the analysis to trigger incremental graph updates
for (const file of analysis.structurallyChangedFiles) {
console.log(`⚙️ Re-process structural change in ${file}`);
}
for (const file of analysis.cosmeticOnlyFiles) {
console.log(`✨ Cosmetic change only – graph unchanged for ${file}`);
}
Key Source Files
fingerprint.ts: Contains core data structures and algorithms includingextractFileFingerprint,compareFingerprints,buildFingerprintStore, andanalyzeChanges.types.ts: Defines shared type definitions used by the fingerprint module, such asStructuralAnalysis.registry.ts: Supplies language-specific tree-sitter parsers for structural analysis via the plugin registry.staleness.ts: Implements higher-level logic determining when the knowledge graph requires updates based on fingerprint analysis.change-classifier.test.ts: Test suite confirming correct classification of NONE, COSMETIC, and STRUCTURAL changes.
Summary
- FileFingerprint objects capture SHA-256 content hashes and tree-sitter structural signatures (functions, classes, imports, exports) for every source file.
- The
compareFingerprintsfunction classifies changes into NONE, COSMETIC, or STRUCTURAL categories, enabling precise incremental updates. - Files without tree-sitter support fall back to hash-only comparison and are treated conservatively as structural changes.
- The
analyzeChangespipeline processes only VCS-reported modified files, minimizing I/O and CPU usage for large repositories. - This architecture ensures the knowledge graph updates only when actual structural dependencies change, not when code formatting or internal logic shifts.
Frequently Asked Questions
How does the system handle files in languages without tree-sitter support?
Files without available tree-sitter parsers receive a hash-only fingerprint where hasStructuralAnalysis is set to false. According to the conservative fallback strategy implemented in buildFingerprintStore, these files are automatically classified as STRUCTURAL changes if their content hash differs, ensuring correctness by forcing a full recomputation when the system cannot determine the actual structural impact.
What is the performance benefit of cosmetic change detection?
By distinguishing COSMETIC changes (internal logic modifications) from STRUCTURAL changes (API modifications), the system avoids unnecessary knowledge graph rebuilds. When compareFingerprints detects a COSMETIC change, the existing graph nodes and edges remain valid, reducing CPU usage and I/O operations to only the files requiring actual structural updates.
How does the fingerprint store persist across runs?
The buildFingerprintStore function generates a JSON-serializable FingerprintStore object that maps file paths to their FileFingerprint records. This store is typically written to fingerprints.json alongside a git commit hash for versioning. On subsequent runs, analyzeChanges loads this persisted store and compares it against fresh fingerprints of only the changed files reported by the VCS.
Why use SHA-256 instead of simpler hash functions?
SHA-256 provides cryptographic collision resistance that ensures two different file contents will virtually never produce the same hash. This guarantees that the NONE classification in compareFingerprints is absolutely reliable, preventing the incremental update system from missing actual content changes that could affect the knowledge graph's accuracy.
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 →