How to Coordinate the Egonex Multi-Agent Pipeline: Project-Scanner, File-Analyzer, and Architecture-Analyzer
The Egonex multi-agent pipeline coordinates three specialized agents through deterministic JSON contracts, where the project-scanner creates an inventory, the file-analyzer processes batched graph fragments, and the architecture-analyzer synthesizes layered architecture by reading and writing specific files in the .understand-anything directory.
The Egonex "Understand-Anything" plugin orchestrates a deterministic three-stage pipeline that transforms raw codebases into layered architecture graphs. To coordinate the project-scanner, file-analyzer, and architecture-analyzer agents effectively, you must understand their strict file-based contracts and the exact JSON schemas they exchange through the repository's .understand-anything directory. This guide walks through the precise data flow, authoritative source file paths, and deterministic scripts that bind these agents together.
Stage 1: Project-Scanner – Creating the Codebase Inventory
The project-scanner agent initiates the pipeline by performing a deterministic discovery of the codebase structure, followed by an LLM-driven narrative extraction phase. This agent writes the canonical scan-result.json file that downstream agents consume.
Discovery Phase: File Enumeration and Import Mapping
The scanner executes two bundled Node.js scripts in sequence to ensure reproducible results. First, it runs scan-project.mjs to enumerate every file, assign deterministic language and fileCategory values, count lines, and compute estimatedComplexity metrics. According to the implementation in agents/project-scanner.md, this script writes its output to ua-scan-files.json in the temporary directory.
Immediately following, the scanner invokes extract-import-map.mjs to parse all supported languages using tree-sitter and produce a deterministic import map. This script reads the previous output and writes ua-import-map-output.json containing the resolved dependency graph.
Final Assembly and the Scan-Result Contract
After the deterministic scripts complete, the agent merges the LLM-generated narrative fields (extracted from README, package.json, pyproject.toml, etc.) with the script outputs to build the final inventory. The resulting JSON is written to $PROJECT_ROOT/.understand-anything/intermediate/scan-result.json.
This file must contain:
files: An array of file objects withpath,language,fileCategory,sizeLines, andestimatedComplexityimportMap: A mapping of file paths to their import targetsname,rawDescription,readmeHead,frameworks, andlanguages: LLM-extracted metadata
# Execute the deterministic scanning scripts
node understand-anything-plugin/skills/understand/scan-project.mjs \
"$PROJECT_ROOT" \
"$PROJECT_ROOT/.understand-anything/tmp/ua-scan-files.json"
node understand-anything-plugin/skills/understand/extract-import-map.mjs \
"$PROJECT_ROOT/.understand-anything/tmp/ua-scan-files.json" \
"$PROJECT_ROOT/.understand-anything/tmp/ua-import-map-output.json"
Stage 2: File-Analyzer – Processing Batched Graph Fragments
The file-analyzer agent consumes scan-result.json and processes the files array in batches to generate graph fragments containing nodes and edges. This agent must ensure every file from the scanner appears in exactly one batch and every import listed in the importMap is emitted as an edge.
Structural Extraction with extract-structure.mjs
For each batch, the analyzer writes a temporary input JSON (ua-file-analyzer-input-<batchIndex>.json) containing the path, language, sizeLines, and fileCategory for files in that batch, along with the relevant slice of the importMap. It then executes the bundled extract-structure.mjs script as specified in agents/file-analyzer.md.
This script performs static analysis to extract functions, classes, and structural metrics, writing results to ua-file-extract-results-<batchIndex>.json.
Semantic Analysis and Mandatory Edge Emission
Following structural extraction, the agent creates Graph Node objects for each file, function, class, and configuration element. Crucially, it must emit an imports edge for every entry in batchImportData[filePath] as defined in the contract. The resulting fragment is validated to ensure no duplicate node IDs exist and all imports are represented.
The final batch fragments are written to .understand-anything/intermediate/batch-<batchIndex>.json (or split into batch-<batchIndex>-part-<k>.json files if the fragment exceeds size limits).
Batch Processing Orchestration
A typical orchestrator chunks the file list into batches of 60 or fewer files to manage memory and processing constraints:
// run-file-analyzer.js
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const batchIdx = process.argv[2];
const batchFiles = JSON.parse(fs.readFileSync(process.argv[3], 'utf8'));
const importMap = JSON.parse(fs.readFileSync(process.argv[4], 'utf8'));
const tmpDir = path.resolve('.understand-anything/tmp');
const interDir = path.resolve('.understand-anything/intermediate');
// Build input for extract-structure.mjs
const input = {
projectRoot: process.cwd(),
batchFiles,
batchImportData: importMap
};
const inputPath = `${tmpDir}/ua-file-analyzer-input-${batchIdx}.json`;
fs.writeFileSync(inputPath, JSON.stringify(input, null, 2));
// Execute structural extraction
execSync(`node understand-anything-plugin/skills/understand/extract-structure.mjs \
${inputPath} \
${tmpDir}/ua-file-extract-results-${batchIdx}.json`, { stdio: 'inherit' });
// Convert to graph fragment (simplified)
const results = JSON.parse(fs.readFileSync(`${tmpDir}/ua-file-extract-results-${batchIdx}.json`));
const fragment = {
nodes: results.results.map(r => ({
id: `file:${r.path}`,
type: 'file',
name: path.basename(r.path),
filePath: r.path,
complexity: r.metrics?.functionCount > 5 ? 'complex' : 'simple'
})),
edges: [] // Populate with imports from batchImportData
};
fs.writeFileSync(`${interDir}/batch-${batchIdx}.json`, JSON.stringify(fragment, null, 2));
Stage 3: Architecture-Analyzer – Synthesizing Layered Architecture
The architecture-analyzer consumes the merged knowledge graph (produced by stitching all batch fragments together) and runs a deterministic analysis script to compute structural patterns and assign files to architectural layers.
Input Preparation and Deterministic Analysis
The agent prepares ua-arch-input.json containing three top-level arrays: fileNodes, importEdges, and allEdges. It then executes the ua-arch-analyze.js script, which computes:
directoryGroups: Files grouped by top-level foldernodeTypeGroups: Categorization of code vs. config vs. documentscrossCategoryEdges: Dependencies between different file typesinterGroupImportsandintraGroupDensity: Connectivity metricspatternMatches: Recognized architectural patterns (e.g.,routes → api,services → service)
# Prepare merged graph (conceptual merge step)
jq -s 'add' .understand-anything/intermediate/batch-*.json > .understand-anything/tmp/merged-graph.json
# Build architecture input
jq -n \
--argfile nodes .understand-anything/tmp/merged-graph.json \
'{fileNodes: $nodes.nodes, importEdges: $nodes.edges, allEdges: $nodes.edges}' \
> .understand-anything/tmp/ua-arch-input.json
# Run deterministic analysis
node understand-anything-plugin/agents/architecture-analyzer.mjs \
.understand-anything/tmp/ua-arch-input.json \
.understand-anything/intermediate/layers.json
Layer Selection and Node Assignment
Based on the analysis results, the agent selects 3-10 layers using specific heuristics:
- Pattern matches provide strong signals for layer names
- Intra-group density exceeding 0.3 indicates a distinct layer boundary
- Dependency direction identifies foundation layers (high inbound edge count)
- Non-code signals map
Dockerfiletoinfrastructureand CI configs toci-cd
Every file node from the input must be assigned to exactly one layer's nodeIds array. The agent validates that the total count matches the input fileStats.totalFileNodes before writing the final output.
Final Output Contract
The analyzer writes a JSON array to .understand-anything/intermediate/layers.json, where each layer object contains:
id: Unique identifier (e.g.,layer:api)name: Human-readable layer namedescription: Contextual explanation of the layer's purposenodeIds: Array of file and service identifiers belonging to this layer
[
{
"id": "layer:api",
"name": "API Layer",
"description": "HTTP route handlers that expose the public REST endpoints.",
"nodeIds": [
"file:src/routes/index.ts",
"file:src/controllers/auth.ts"
]
},
{
"id": "layer:infrastructure",
"name": "Infrastructure",
"description": "Dockerfile, compose file and CI pipelines that build and deploy the service.",
"nodeIds": [
"service:Dockerfile",
"pipeline:.github/workflows/ci.yml"
]
}
]
End-to-End Data Flow and Contract Boundaries
The pipeline follows a strict linear progression where each agent's output becomes the next agent's input:
- Project-Scanner writes
scan-result.jsoncontaining the complete file inventory and import map - File-Analyzer reads
scan-result.json, processes batches, and writesbatch-<N>.jsonfragments - Merge Script (typically
merge-batch-graphs.pyor equivalent) consolidates fragments into a unified graph - Architecture-Analyzer reads the merged graph, runs
ua-arch-analyze.js, and writeslayers.json
This separation of concerns ensures determinism: the bundled scripts (scan-project.mjs, extract-import-map.mjs, extract-structure.mjs, and ua-arch-analyze.js) guarantee identical outputs on every execution, which is required for reproducible graph construction. Never re-implement the file walking, language detection, or import resolution logic, as downstream agents rely on the exact JSON payload shapes produced by these canonical scripts.
Summary
- Project-Scanner builds the deterministic inventory using
scan-project.mjsandextract-import-map.mjs, outputtingscan-result.jsonwith narrative metadata and file statistics. - File-Analyzer processes the inventory in batches via
extract-structure.mjs, emitting complete graph fragments with all import edges preserved inbatch-<N>.jsonfiles. - Architecture-Analyzer consumes the merged graph, executes
ua-arch-analyze.jsto compute structural patterns, and produceslayers.jsoncontaining 3-10 validated architectural layers. - Contract Compliance requires adhering to specific file paths, JSON schemas, and edge emission rules to ensure pipeline reliability.
Frequently Asked Questions
How do the agents communicate if they run in separate processes?
The Egonex multi-agent pipeline uses file-based communication exclusively through the .understand-anything directory. Each agent writes JSON files to specific subdirectories (tmp/ for intermediate files, intermediate/ for stage outputs) that subsequent agents read as input. This design ensures process isolation and provides an audit trail of the pipeline state at every stage.
What happens if a file from the scanner doesn't appear in the analyzer output?
The file-analyzer contract requires that every file listed in scan-result.json must appear in exactly one batch fragment. If a file is missing, the downstream merge-batch-graphs script will detect the discrepancy when validating against fileStats.totalFileNodes, and the architecture-analyzer will fail to assign the node to a layer. Always verify that your batch processing logic handles the complete file list without omission.
Can I modify the layer selection logic in the architecture-analyzer?
While you can adjust the heuristic thresholds (such as the 0.3 intra-group density threshold) in your implementation of the agent logic, you must not modify the deterministic ua-arch-analyze.js script itself. The script computes the structural metrics (directory groups, cross-category edges, pattern matches) that feed into the layer selection algorithm. Modify the selection logic that runs after the script execution, but ensure the final layers.json still assigns every file node to exactly one layer and maintains the required JSON schema.
Why must imports be emitted as edges in every batch?
The file-analyzer must emit an imports edge for every entry in batchImportData[filePath] because the downstream architecture-analyzer relies on the complete dependency graph to calculate crossCategoryEdges and identify foundation layers. Missing import edges would cause the density calculations and layer boundary detection to fail, resulting in an incorrect architectural view that doesn't reflect actual code dependencies.
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 →