Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | 3x 3x 45x 45x 3x 3x 3x 3x 8x 8x 8x 3x 5x 8x 38x 38x 64x 27x 27x 27x 27x 27x 8x 33x 12x 5x 26x 3x 3x 5x 3x 4x 6x 8x 4x 6x 7x 6x 5x 5x 5x 3x 18x 5x 5x 7x 25x 8x 5x 20x 35x 3x 20x 44x 5x 25x 11x 8x 19x 8x 38x 8x 3x 11x 66x 66x 6x 6x 18x 28x 18x 9x 3x 33x 3x 33x 9x 3x 6x 6x 6x 6x 9x 6x 6x 6x 6x 6x 6x 3x 3x 3x | import {
scanFiles,
Severity,
IssueType,
getSeverityLevel,
} from '@aiready/core';
import type { AnalysisResult, Issue } from '@aiready/core';
import type {
ConsistencyOptions,
ConsistencyReport,
ConsistencyIssue,
} from './types';
import { analyzeNamingAST } from './analyzers/naming-ast';
import { analyzeNamingGeneralized } from './analyzers/naming-generalized';
import { analyzePatterns } from './analyzers/patterns';
import { calculateConsistencyScore as calculateConsistencyScoreFromScoring } from './scoring';
/**
* Main consistency analyzer that orchestrates all analysis types.
* Supports: TypeScript, JavaScript, Python, Java, C#, Go.
*
* @param options - Configuration for consistency analysis and file scanning.
* @returns Promise resolving to the comprehensive consistency report.
* @lastUpdated 2026-03-18
*/
export async function analyzeConsistency(
options: ConsistencyOptions
): Promise<ConsistencyReport> {
const {
checkNaming = true,
checkPatterns = true,
E checkArchitecture = false, // Not implemented yet
minSeverity = Severity.Info,
...scanOptions
} = options;
// Mark intentionally-unused option to avoid lint warnings
void checkArchitecture;
// Scan files
const filePaths = await scanFiles(scanOptions);
// Collect issues by category
let namingIssues: any[] = [];
Eif (checkNaming) {
// 1. Generalized naming analysis for all supported files
namingIssues = await analyzeNamingGeneralized(filePaths);
// 2. Targeted deep AST analysis for TS/JS (handled by specialized analyzer)
const tsJsFiles = filePaths.filter((f) => /\.(ts|tsx|js|jsx)$/i.test(f));
Eif (tsJsFiles.length > 0) {
const deepTsIssues = await analyzeNamingAST(tsJsFiles);
// Merge issues, avoiding duplicates for exports if already checked
namingIssues = [...namingIssues, ...deepTsIssues];
}
}
const patternIssues = checkPatterns ? await analyzePatterns(filePaths) : [];
// Convert to AnalysisResult format
const results: AnalysisResult[] = [];
const fileIssuesMap = new Map<string, ConsistencyIssue[]>();
// Process naming issues
for (const issue of namingIssues) {
if (!shouldIncludeSeverity(issue.severity, minSeverity)) continue;
const fileName =
(issue as any).fileName ||
(issue as any).file ||
(issue as any).filePath ||
'unknown';
if (!fileIssuesMap.has(fileName)) fileIssuesMap.set(fileName, []);
fileIssuesMap.get(fileName)!.push(issue as unknown as ConsistencyIssue);
}
// Process pattern issues
for (const issue of patternIssues) {
if (!shouldIncludeSeverity(issue.severity, minSeverity)) continue;
const fileName =
(issue as any).fileName ||
(issue as any).file ||
(issue as any).filePath ||
(Array.isArray((issue as any).files)
? (issue as any).files[0]
: 'unknown');
if (!fileIssuesMap.has(fileName)) fileIssuesMap.set(fileName, []);
fileIssuesMap.get(fileName)!.push(issue as unknown as ConsistencyIssue);
}
// Build final results
for (const [fileName, issues] of fileIssuesMap.entries()) {
const scoreResult = calculateConsistencyScoreFromScoring(
issues,
filePaths.length
);
results.push({
fileName,
issues: issues.map((i) => transformToIssue(i)),
metrics: {
consistencyScore: scoreResult.score / 100,
},
});
}
// Generate high-level recommendations
const recommendations: string[] = [];
if (namingIssues.length > 0) {
recommendations.push('Standardize naming conventions across the codebase');
}
if (patternIssues.length > 0) {
recommendations.push('Consolidate repetitive implementation patterns');
}
Iif (results.some((r) => (r.metrics?.consistencyScore ?? 1) < 0.8)) {
recommendations.push(
'Improve cross-module consistency to reduce AI confusion'
);
}
return {
results,
summary: {
filesAnalyzed: filePaths.length,
totalIssues: results.reduce((acc, r) => acc + r.issues.length, 0),
namingIssues: namingIssues.length,
patternIssues: patternIssues.length,
architectureIssues: 0,
},
recommendations,
metadata: {
toolName: 'naming-consistency',
timestamp: new Date().toISOString(),
},
} as unknown as ConsistencyReport;
}
/**
* Check if an issue severity meets the minimum threshold.
*
* @param severity - The severity of the issue.
* @param minSeverity - The minimum severity threshold.
* @returns True if severity is greater than or equal to minSeverity.
*/
function shouldIncludeSeverity(
severity: Severity | string,
minSeverity: Severity | string
): boolean {
return getSeverityLevel(severity) >= getSeverityLevel(minSeverity);
}
/**
* Map string type to IssueType enum value.
*
* @param type - The raw issue type string.
* @returns Normalized IssueType enum.
*/
function getIssueType(type: string | undefined): IssueType {
Iif (!type) return IssueType.NamingInconsistency;
// Map string values to enum
const typeMap: Record<string, IssueType> = {
'naming-inconsistency': IssueType.NamingInconsistency,
'naming-quality': IssueType.NamingQuality,
'pattern-inconsistency': IssueType.PatternInconsistency,
'architecture-inconsistency': IssueType.ArchitectureInconsistency,
'error-handling': IssueType.PatternInconsistency,
'async-style': IssueType.PatternInconsistency,
'import-style': IssueType.PatternInconsistency,
'api-design': IssueType.PatternInconsistency,
};
return typeMap[type] || IssueType.NamingInconsistency;
}
E
/**
* TIransform NamingIssue or PatternIssue to the required Issue format.
*
* @param i - The raw issue object to transform.
* @returns Standardized Issue object.
*/
function transformToIssue(i: any): Issue {
// If already has message and location, return as is
IifI (i.message && i.location) {
return {
type: getIssueType(i.type),
severity: i.severity as Severity,
message: i.message,
location: i.location,
suggestion: i.suggestion,
I };
}
// Handle NamingIssue format (has file, line, column, identifier, suggestion)
Eif (i.identifier || i.type) {
const line = i.line || 1;
const column = i.column || 1;
return {
type: getIssueType(i.type),
E severity: i.severity as Severity,
message: i.suggestion
? `Naming issue: ${i.suggestion}`
: `Naming issue for '${i.identifier || 'unknown'}'`,
location: {
file: i.file || i.fileName || '',
line,
E column,
endLine: line,
endColumn: column + (i.identifier?.length || 10),
},
suggestion: i.suggestion,
};
}
E
// Handle PatternIssue format (has description, files)
if (i.description || i.files) {
const fileName = Array.isArray(i.files) ? i.files[0] : i.file || '';
return {
type: getIssueType(i.type),
severity: i.severity as Severity,
message: i.description || 'Pattern inconsistency found',
location: {
file: fileName,
line: 1,
column: 1,
endLine: 1,
endColumn: 10,
},
suggestion: i.examples?.[0],
};
}
// Fallback
return {
type: getIssueType(i.type),
severity: i.severity as Severity,
message: i.message || 'Unknown issue',
location: i.location || { file: '', line: 1, column: 1 },
suggestion: i.suggestion,
};
}
|