Add under-anything knowledge dashboard
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
import { dirname } from "node:path";
|
||||
import type { ChangeAnalysis } from "./fingerprint.js";
|
||||
|
||||
export interface UpdateDecision {
|
||||
action: "SKIP" | "PARTIAL_UPDATE" | "ARCHITECTURE_UPDATE" | "FULL_UPDATE";
|
||||
filesToReanalyze: string[];
|
||||
rerunArchitecture: boolean;
|
||||
rerunTour: boolean;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify the type of graph update needed based on structural change analysis.
|
||||
*
|
||||
* Decision matrix:
|
||||
* - SKIP: all files NONE or COSMETIC only
|
||||
* - PARTIAL_UPDATE: some STRUCTURAL, same directories
|
||||
* - ARCHITECTURE_UPDATE: new/deleted directories or >10 structural files
|
||||
* - FULL_UPDATE: >30 structural files or >50% of total files changed structurally
|
||||
*/
|
||||
export function classifyUpdate(
|
||||
analysis: ChangeAnalysis,
|
||||
totalFilesInGraph: number,
|
||||
allKnownFiles: string[] = [],
|
||||
): UpdateDecision {
|
||||
const { newFiles, deletedFiles, structurallyChangedFiles, cosmeticOnlyFiles } = analysis;
|
||||
const structuralCount = structurallyChangedFiles.length + newFiles.length + deletedFiles.length;
|
||||
|
||||
// No structural changes at all — skip
|
||||
if (structuralCount === 0) {
|
||||
const cosmeticCount = cosmeticOnlyFiles.length;
|
||||
const reason = cosmeticCount > 0
|
||||
? `${cosmeticCount} file(s) have cosmetic-only changes (no structural impact)`
|
||||
: "No changes detected";
|
||||
|
||||
return {
|
||||
action: "SKIP",
|
||||
filesToReanalyze: [],
|
||||
rerunArchitecture: false,
|
||||
rerunTour: false,
|
||||
reason,
|
||||
};
|
||||
}
|
||||
|
||||
// Too many structural changes — suggest full rebuild
|
||||
const triggeredByCount = structuralCount > 30;
|
||||
const triggeredByPercentage = totalFilesInGraph > 0 && structuralCount / totalFilesInGraph > 0.5;
|
||||
if (triggeredByCount || triggeredByPercentage) {
|
||||
const thresholdReason =
|
||||
triggeredByCount && triggeredByPercentage
|
||||
? ">30 files and >50% of project"
|
||||
: triggeredByCount
|
||||
? ">30 files"
|
||||
: ">50% of project";
|
||||
return {
|
||||
action: "FULL_UPDATE",
|
||||
filesToReanalyze: [...structurallyChangedFiles, ...newFiles],
|
||||
rerunArchitecture: true,
|
||||
rerunTour: true,
|
||||
reason: `${structuralCount} files have structural changes (${thresholdReason}) — full rebuild recommended`,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if directory structure changed (new/deleted top-level directories)
|
||||
const hasDirectoryChanges = detectDirectoryChanges(newFiles, deletedFiles, allKnownFiles);
|
||||
|
||||
if (hasDirectoryChanges || structuralCount > 10) {
|
||||
return {
|
||||
action: "ARCHITECTURE_UPDATE",
|
||||
filesToReanalyze: [...structurallyChangedFiles, ...newFiles],
|
||||
rerunArchitecture: true,
|
||||
rerunTour: true,
|
||||
reason: hasDirectoryChanges
|
||||
? `Directory structure changed (${newFiles.length} new, ${deletedFiles.length} deleted files)`
|
||||
: `${structuralCount} files have structural changes — architecture re-analysis needed`,
|
||||
};
|
||||
}
|
||||
|
||||
// Localized structural changes — partial update
|
||||
return {
|
||||
action: "PARTIAL_UPDATE",
|
||||
filesToReanalyze: [...structurallyChangedFiles, ...newFiles],
|
||||
rerunArchitecture: false,
|
||||
rerunTour: false,
|
||||
reason: `${structuralCount} file(s) have structural changes: ${summarizeChanges(analysis)}`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if the changes affect the directory structure (new or removed directories).
|
||||
* Uses all known files in the project as the baseline for existing directories,
|
||||
* then checks if any new/deleted files introduce or remove a top-level source directory.
|
||||
*/
|
||||
function detectDirectoryChanges(
|
||||
newFiles: string[],
|
||||
deletedFiles: string[],
|
||||
allKnownFiles: string[],
|
||||
): boolean {
|
||||
const existingDirs = new Set(
|
||||
allKnownFiles.map((f) => topDirectory(f)).filter(Boolean),
|
||||
);
|
||||
|
||||
for (const f of newFiles) {
|
||||
const dir = topDirectory(f);
|
||||
if (dir && !existingDirs.has(dir)) return true;
|
||||
}
|
||||
|
||||
for (const f of deletedFiles) {
|
||||
const dir = topDirectory(f);
|
||||
if (dir && !existingDirs.has(dir)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the top-level directory of a file path (first path segment).
|
||||
*/
|
||||
function topDirectory(filePath: string): string | null {
|
||||
const dir = dirname(filePath);
|
||||
if (dir === "." || dir === "") return null;
|
||||
const segments = dir.split("/");
|
||||
return segments[0] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a concise human-readable summary of structural changes.
|
||||
*/
|
||||
function summarizeChanges(analysis: ChangeAnalysis): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
if (analysis.newFiles.length > 0) {
|
||||
parts.push(`${analysis.newFiles.length} new`);
|
||||
}
|
||||
if (analysis.deletedFiles.length > 0) {
|
||||
parts.push(`${analysis.deletedFiles.length} deleted`);
|
||||
}
|
||||
if (analysis.structurallyChangedFiles.length > 0) {
|
||||
parts.push(`${analysis.structurallyChangedFiles.length} modified`);
|
||||
}
|
||||
|
||||
return parts.join(", ");
|
||||
}
|
||||
Reference in New Issue
Block a user