Fingerprint-Based Change Detection in Egonex-AI Understand-Anything: How It Works

Fingerprint-based change detection in Egonex-AI Understand-Anything creates compact structural fingerprints for source files to categorize changes as NONE, COSMETIC, or STRUCTURAL, enabling incremental knowledge-graph updates only when code signatures actually change.

Egonex-AI/Understand-Anything implements fingerprint-based change detection to optimize knowledge-graph generation by distinguishing between superficial edits and structural modifications. The system generates a compact structural fingerprint for every source file, capturing only the elements that affect the knowledge graph—function signatures, class definitions, imports, exports, and a SHA-256 content hash. This approach allows the tool to skip expensive graph rebuilds when only internal logic changes, while ensuring accurate updates when public APIs evolve.

What Is a Structural Fingerprint?

A structural fingerprint is a compact descriptor that captures only the parts of the code that affect the knowledge graph. According to the understand-anything-plugin/packages/core/src/fingerprint.ts implementation, each fingerprint contains:

  • Function and class signatures
  • Import and export lists
  • A SHA-256 hash of the raw file contents

By comparing two fingerprints, the system categorizes changes into three distinct levels without requiring full re-parsing of unchanged files.

How the Detection Pipeline Works

The fingerprint-based change detection pipeline operates through five distinct phases, orchestrated by the core engine and CLI tools.

Step 1: Parsing Source Files with TreeSitterPlugin

The process begins when a TreeSitterPlugin (or any parser registered in PluginRegistry) parses each source file. The parser produces a StructuralAnalysis object defined in packages/core/src/types.ts, containing AST-derived lists of functions, classes, imports, and exports. This structural data excludes internal implementation details like variable assignments or loop logic, focusing exclusively on the public API surface.

Step 2: Generating File Fingerprints

The extractFileFingerprint() function in fingerprint.ts combines the structural data from the StructuralAnalysis object with a SHA-256 hash of the raw file content. This produces a FileFingerprint object that serves as the canonical representation of the file's structural state at a specific point in time.

Step 3: Storing the Baseline

The buildFingerprintStore() function iterates over all source files in the project, invoking extractFileFingerprint() for each, and aggregates the results into a complete store. The CLI script skills/understand/build-fingerprints.mjs drives this operation during a full /understand run, persisting the collection to .understand-anything/fingerprints.json. This JSON file serves as the baseline for future comparisons.

Step 4: Detecting Changes

When the project changes, analyzeChanges() (also in fingerprint.ts) re-creates fingerprints for modified files and invokes compareFingerprints() to diff them against the baseline. This produces a ChangeAnalysis object that categorizes each file as new, deleted, structurally changed, cosmetic only, or unchanged.

Step 5: Driving Knowledge-Graph Updates

The change analysis feeds the auto-update pipeline. Only files marked with a STRUCTURAL change level trigger a regeneration of the knowledge graph, while COSMETIC changes skip the rebuild process entirely. This keeps incremental updates fast and precise.

Change Categories Explained

The compareFingerprints() function categorizes differences into three specific change levels:

  • NONE: The content hash is unchanged, indicating the file is identical to the baseline.
  • COSMETIC: The content differs, but structural signatures (functions, classes, imports, exports) remain unchanged. This indicates only internal logic was modified.
  • STRUCTURAL: One or more signatures changed—such as new or removed functions, altered parameters, or different exports. This requires the knowledge graph to be updated.

Core Implementation Files

The fingerprint-based change detection system relies on these specific source files:

File Role
understand-anything-plugin/packages/core/src/fingerprint.ts Contains extractFileFingerprint(), buildFingerprintStore(), analyzeChanges(), and compareFingerprints()
understand-anything-plugin/skills/understand/build-fingerprints.mjs CLI entry point that builds the baseline fingerprint store
understand-anything-plugin/packages/core/src/plugins/registry.ts Holds the PluginRegistry that coordinates parsers for structural analysis
understand-anything-plugin/packages/core/src/types.ts Defines StructuralAnalysis, FileFingerprint, and ChangeAnalysis types

Generating and Comparing Fingerprints

To generate a baseline fingerprint store, the skills script orchestrates the parsing and storage pipeline:

import {
  TreeSitterPlugin,
  PluginRegistry,
  builtinLanguageConfigs,
  registerAllParsers,
  buildFingerprintStore,
  saveFingerprints,
} from '@understand-anything/core';

// Set up parsers
const tsConfigs = builtinLanguageConfigs.filter(c => c.treeSitter);
const tsPlugin = new TreeSitterPlugin(tsConfigs);
await tsPlugin.init();

const registry = new PluginRegistry();
registry.register(tsPlugin);
registerAllParsers(registry);

// projectRoot, sourceFilePaths and gitCommitHash come from the CLI JSON input
const store = buildFingerprintStore(projectRoot, sourceFilePaths, registry, gitCommitHash);

// Persist the baseline
saveFingerprints(projectRoot, store);

To detect changes after a commit, load the previous fingerprint store and run the analysis:

import {
  PluginRegistry,
  TreeSitterPlugin,
  builtinLanguageConfigs,
  registerAllParsers,
  analyzeChanges,
} from '@understand-anything/core';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';

// Load previous fingerprint store
const previous = JSON.parse(
  readFileSync(join(projectRoot, '.understand-anything/fingerprints.json'), 'utf-8')
);

// Build a registry as before
const registry = new PluginRegistry();
const tsPlugin = new TreeSitterPlugin(builtinLanguageConfigs.filter(c => c.treeSitter));
await tsPlugin.init();
registry.register(tsPlugin);
registerAllParsers(registry);

// `changedFiles` is the set of paths reported by git (or any file-watcher)
const analysis = analyzeChanges(projectRoot, changedFiles, previous, registry);

console.log('Structural changes:', analysis.structurallyChangedFiles);
console.log('Cosmetic only changes:', analysis.cosmeticOnlyFiles);

Summary

  • Fingerprint-based change detection in Egonex-AI/Understand-Anything uses SHA-256 hashes and AST-derived structural signatures to categorize file changes.
  • The system distinguishes between COSMETIC changes (internal logic only) and STRUCTURAL changes (public API modifications), updating the knowledge graph only when necessary.
  • Core functions extractFileFingerprint(), buildFingerprintStore(), and analyzeChanges() in fingerprint.ts handle the generation, storage, and comparison operations.
  • Baseline fingerprints are stored in .understand-anything/fingerprints.json and generated via the build-fingerprints.mjs CLI script.

Frequently Asked Questions

What is the difference between a cosmetic change and a structural change?

A cosmetic change occurs when the file content differs from the baseline but the structural signatures—function names, class definitions, parameter lists, and import/export statements—remain identical. This typically indicates refactoring of internal logic or formatting changes. A structural change occurs when any of these signatures are modified, such as adding a new function, changing a parameter type, or altering an export, which affects the knowledge graph's representation of the codebase.

How does the system handle different programming languages?

The fingerprint generation relies on the PluginRegistry to coordinate language-specific parsers. The TreeSitterPlugin processes supported languages by generating a StructuralAnalysis object containing the AST-derived elements. Additional parsers can be registered in the registry to support new languages, as long as they conform to the StructuralAnalysis interface defined in packages/core/src/types.ts.

Where are the fingerprint files stored in the project?

The baseline fingerprint store is written to .understand-anything/fingerprints.json in the project root directory. This JSON file contains the complete collection of FileFingerprint objects for all source files, along with metadata including the git commit hash. The saveFingerprints() utility function handles the serialization and file system operations.

What triggers a knowledge-graph rebuild?

Only files categorized with a STRUCTURAL change level trigger a knowledge-graph rebuild. When analyzeChanges() detects structural differences through compareFingerprints(), it includes those files in the structurallyChangedFiles array. The auto-update pipeline uses this array to determine which files require re-processing, while cosmetic and unchanged files are skipped to minimize computational overhead.

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 →