How Egonex-AI Implements Incremental Code Analysis with File Fingerprints

Egonex-AI's Understand Anything toolchain performs incremental code analysis by generating cryptographic file fingerprints that combine SHA-256 content hashes with structural AST signatures, enabling O(Δ) performance by re-analyzing only changed files.

The Egonex-AI/Understand-Anything repository solves the scalability problem of large codebase analysis by avoiding full re-scans on every run. Instead, it implements an incremental code analysis system using file fingerprints that persist across sessions in fingerprints.json. This approach allows the tool to distinguish between cosmetic edits and structural modifications, ensuring downstream agents process only files that have meaningfully changed.

The Fingerprint Architecture

At the core of the system is the FileFingerprint object, which captures two distinct dimensions of a source file. First, a SHA-256 content hash provides a deterministic checksum of the raw file text. Second, a structural signature records extracted symbols—functions, classes, imports, exports, and their signatures—via Tree-Sitter parsing.

This dual-layer approach lives in [packages/core/src/fingerprint.ts](https://github.com/Egonex-AI/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/fingerprint.ts). The fingerprint generation process handles both fully-parsable files and unsupported file types through a graceful fallback mechanism.

Building the Fingerprint Store

Structural Signature Extraction

When a project is first scanned, buildFingerprintStore walks the entire file tree and invokes extractFileFingerprint for each path. This function converts Tree-Sitter's StructuralAnalysis output into a lightweight, serializable format that records:

  • Function signatures and class members
  • Import and export declarations
  • Line count and file metadata

According to the source implementation, the structural extraction preserves only the API surface and dependencies, ignoring whitespace and comments that would trigger false positives in a pure hash-based system.

Content Hashing

Simultaneously, the system computes a contentHash using Node.js crypto.createHash('sha256') to produce a deterministic digest of the raw file text (source).

The complete fingerprint store is then serialized to fingerprints.json, including the current Git commit hash to detect repository resets. The persistence logic handles versioning and atomic writes to prevent corruption during interrupted scans.

Example: Initializing a Project Store

import { buildFingerprintStore } from '@understand-anything/core/fingerprint';
import { defaultPluginRegistry } from '@understand-anything/core/plugins/registry';
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';

const projectRoot = '/path/to/project';
const allFiles = ['src/index.ts', 'src/utils.ts', 'src/parser.ts'];
const gitHash = 'abcdef1234567890';  // from git rev-parse HEAD

const store = buildFingerprintStore(
  projectRoot,
  allFiles,
  defaultPluginRegistry,
  gitHash,
);

writeFileSync(
  join(projectRoot, 'fingerprints.json'), 
  JSON.stringify(store, null, 2)
);

Detecting and Classifying Changes

The Comparison Algorithm

On subsequent runs, analyzeChanges accepts a list of files from Git's diff output (via git diff --name-only) and loads the previous fingerprint store from disk. For each changed path, it reconstructs a fresh fingerprint and delegates to compareFingerprints to determine the modification severity (source).

The comparison logic implements three distinct change levels:

  • NONE: Identical content hash indicates the file is byte-for-byte unchanged.
  • COSMETIC: Content differs, but structural signatures (function signatures, class members, imports/exports) remain identical. This captures formatting changes, comment edits, or whitespace adjustments.
  • STRUCTURAL: Any modification to the API surface, including added or removed symbols, signature changes, or import/export modifications. Files lacking Tree-Sitter support are conservatively labeled STRUCTURAL on any change (source).

The compareFingerprints function returns a detailed ChangeLevel result including a details array describing specific modifications (source).

Aggregating Change Analysis

The analyzeChanges function aggregates per-file results into a ChangeAnalysis object that categorizes the codebase into:

  • New files: Paths present in the current scan but missing from the store.
  • Deleted files: Paths present in the store but missing from disk.
  • Unchanged files: Files with NONE change level (skipped for analysis).
  • Cosmetic-only files: Files with COSMETIC changes (may skip deep analysis depending on agent configuration).
  • Structurally-changed files: Files requiring full re-analysis.

Example: Running Incremental Analysis

import { analyzeChanges, ChangeAnalysis } from '@understand-anything/core/fingerprint';
import { readFileSync } from 'node:fs';
import { defaultPluginRegistry } from '@understand-anything/core/plugins/registry';
import { join } from 'node:path';

const projectRoot = '/path/to/project';

// Load persisted fingerprints
const previousStore = JSON.parse(
  readFileSync(join(projectRoot, 'fingerprints.json'), 'utf-8')
);

// Files returned by git diff --name-only <prevHash>..HEAD
const changedFiles = ['src/parser.ts', 'src/index.ts'];

const analysis: ChangeAnalysis = analyzeChanges(
  projectRoot,
  changedFiles,
  previousStore,
  defaultPluginRegistry,
);

console.log('Files needing deep analysis:', analysis.structurallyChangedFiles);
console.log('Cosmetic-only changes:', analysis.cosmeticOnlyFiles);
console.log('Unchanged (skipped):', analysis.unchangedFiles);

Key Design Decisions

The fingerprint system optimizes for accuracy and performance through several specific implementation choices:

  • Graceful degradation for unsupported languages: Files without Tree-Sitter parsers receive a hash-only fingerprint with hasStructuralAnalysis: false. Any subsequent content change is treated as STRUCTURAL, ensuring conservative correctness (source).

  • O(Δ) complexity: Only files returned by changedFiles are re-analyzed. All other files are retrieved from the existing store, making the analysis time proportional to the number of changes rather than the total codebase size.

  • Git-aware invalidation: The fingerprint store includes a gitCommitHash field. If the repository is reset to an earlier commit or the history is rewritten, the system detects the mismatch and rebuilds the entire store to prevent false assumptions about file states.

  • Deterministic hashing: The SHA-256 implementation uses Node.js native crypto to ensure consistent fingerprints across different platforms and Node.js versions.

Summary

Egonex-AI implements incremental code analysis with file fingerprints through a three-stage pipeline:

  • Fingerprint generation combines SHA-256 content hashes with Tree-Sitter structural signatures to create unique file identifiers stored in packages/core/src/fingerprint.ts.
  • Change detection compares previous and current fingerprints using compareFingerprints to classify edits as NONE, COSMETIC, or STRUCTURAL.
  • Selective re-analysis processes only structurally changed or new files, providing O(Δ) performance for large repositories.

The system persists data in fingerprints.json and integrates with Git workflows to ensure accurate incremental scanning across development sessions.

Frequently Asked Questions

What is the performance benefit of using file fingerprints for incremental analysis?

File fingerprints reduce analysis complexity from O(N) to O(Δ), where N is the total file count and Δ represents changed files. By persisting structural and content signatures in fingerprints.json, the system skips unchanged files entirely. For a repository with 10,000 files where only 50 have changed, the tool analyzes just those 50 files rather than the entire codebase, achieving near-instant incremental updates.

How does the system handle files in languages without Tree-Sitter support?

Unsupported files receive a hash-only fingerprint with hasStructuralAnalysis: false. According to the fallback implementation, any content change in these files is conservatively classified as STRUCTURAL. This ensures the system never assumes a change is cosmetic when it cannot parse the code structure, maintaining correctness at the cost of potentially re-analyzing formatting-only changes in unsupported languages.

What data is stored in the fingerprints.json file?

The JSON file contains a FingerprintStore object with two primary fields: a gitCommitHash string tracking the repository state at creation time, and a fingerprints object mapping relative file paths to FileFingerprint records. Each fingerprint includes the SHA-256 contentHash, structural metadata (functions, classes, imports), line count, and a boolean flag indicating whether structural analysis was available for that file type.

How does the system distinguish between cosmetic and structural code changes?

The compareFingerprints function implements a two-tier comparison algorithm. First, it compares SHA-256 content hashes; if identical, the level is NONE. If hashes differ, it compares the extracted structural signatures—function names, signatures, class members, and import/export declarations. If these match exactly, the change is COSMETIC (formatting/comments only). Any difference in the structural signature or lack thereof results in a STRUCTURAL classification (source).

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →