@aiready/consistency 0.8.30 → 0.8.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +2 -2
- package/.turbo/turbo-build.log +9 -9
- package/.turbo/turbo-test.log +11 -11
- package/CONTRIBUTING.md +10 -3
- package/dist/chunk-EIQ5K6OO.mjs +1579 -0
- package/dist/chunk-J5IFYDVU.mjs +1579 -0
- package/dist/cli.js +136 -37
- package/dist/cli.mjs +28 -5
- package/dist/index.js +191 -50
- package/dist/index.mjs +76 -19
- package/package.json +2 -2
- package/src/__tests__/analyzer.test.ts +63 -8
- package/src/__tests__/language-filter.test.ts +6 -9
- package/src/__tests__/scoring.test.ts +35 -10
- package/src/analyzer.ts +60 -29
- package/src/analyzers/naming-ast.ts +134 -46
- package/src/analyzers/naming-constants.ts +356 -42
- package/src/analyzers/naming-python.ts +27 -9
- package/src/analyzers/naming.ts +160 -68
- package/src/analyzers/patterns.ts +59 -35
- package/src/cli.ts +42 -18
- package/src/scoring.ts +35 -31
- package/src/types.ts +1 -1
- package/src/utils/ast-parser.ts +32 -14
- package/src/utils/config-loader.ts +8 -3
- package/src/utils/context-detector.ts +76 -43
- package/src/utils/scope-tracker.ts +11 -11
package/dist/cli.js
CHANGED
|
@@ -49,6 +49,7 @@ function parseFile(filePath, content) {
|
|
|
49
49
|
filePath: isTypeScript ? filePath : void 0
|
|
50
50
|
});
|
|
51
51
|
} catch (error) {
|
|
52
|
+
void error;
|
|
52
53
|
return null;
|
|
53
54
|
}
|
|
54
55
|
}
|
|
@@ -287,6 +288,7 @@ var ScopeTracker = class {
|
|
|
287
288
|
|
|
288
289
|
// src/utils/context-detector.ts
|
|
289
290
|
function detectFileType(filePath, ast) {
|
|
291
|
+
void ast;
|
|
290
292
|
const path = filePath.toLowerCase();
|
|
291
293
|
if (path.match(/\.(test|spec)\.(ts|tsx|js|jsx)$/) || path.includes("__tests__")) {
|
|
292
294
|
return "test";
|
|
@@ -317,7 +319,9 @@ function detectCodeLayer(ast) {
|
|
|
317
319
|
}
|
|
318
320
|
if (node.type === "FunctionDeclaration" && node.id) {
|
|
319
321
|
const name = node.id.name;
|
|
320
|
-
if (name.match(
|
|
322
|
+
if (name.match(
|
|
323
|
+
/^(get|post|put|delete|patch|handle|api|route|controller)/i
|
|
324
|
+
)) {
|
|
321
325
|
hasAPIIndicators++;
|
|
322
326
|
}
|
|
323
327
|
if (name.match(/^(calculate|process|validate|transform|compute|analyze)/i)) {
|
|
@@ -326,7 +330,9 @@ function detectCodeLayer(ast) {
|
|
|
326
330
|
if (name.match(/^(find|create|update|delete|save|fetch|query|insert)/i)) {
|
|
327
331
|
hasDataIndicators++;
|
|
328
332
|
}
|
|
329
|
-
if (name.match(
|
|
333
|
+
if (name.match(
|
|
334
|
+
/^(format|parse|convert|normalize|sanitize|encode|decode)/i
|
|
335
|
+
)) {
|
|
330
336
|
hasUtilityIndicators++;
|
|
331
337
|
}
|
|
332
338
|
}
|
|
@@ -845,10 +851,15 @@ async function loadNamingConfig(files) {
|
|
|
845
851
|
const rootDir = files.length > 0 ? (0, import_path.dirname)(files[0]) : process.cwd();
|
|
846
852
|
const config = await (0, import_core.loadConfig)(rootDir);
|
|
847
853
|
const consistencyConfig = config?.tools?.["consistency"];
|
|
848
|
-
const customAbbreviations = new Set(
|
|
854
|
+
const customAbbreviations = new Set(
|
|
855
|
+
consistencyConfig?.acceptedAbbreviations || []
|
|
856
|
+
);
|
|
849
857
|
const customShortWords = new Set(consistencyConfig?.shortWords || []);
|
|
850
858
|
const disabledChecks = new Set(consistencyConfig?.disableChecks || []);
|
|
851
|
-
const allAbbreviations = /* @__PURE__ */ new Set([
|
|
859
|
+
const allAbbreviations = /* @__PURE__ */ new Set([
|
|
860
|
+
...ACCEPTABLE_ABBREVIATIONS,
|
|
861
|
+
...customAbbreviations
|
|
862
|
+
]);
|
|
852
863
|
const allShortWords = /* @__PURE__ */ new Set([...COMMON_SHORT_WORDS, ...customShortWords]);
|
|
853
864
|
return {
|
|
854
865
|
customAbbreviations,
|
|
@@ -897,9 +908,14 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
897
908
|
if ("params" in node) {
|
|
898
909
|
for (const param of node.params) {
|
|
899
910
|
if (param.type === "Identifier") {
|
|
900
|
-
scopeTracker.declareVariable(
|
|
901
|
-
|
|
902
|
-
|
|
911
|
+
scopeTracker.declareVariable(
|
|
912
|
+
param.name,
|
|
913
|
+
param,
|
|
914
|
+
getLineNumber(param),
|
|
915
|
+
{
|
|
916
|
+
isParameter: true
|
|
917
|
+
}
|
|
918
|
+
);
|
|
903
919
|
} else if (param.type === "ObjectPattern" || param.type === "ArrayPattern") {
|
|
904
920
|
extractIdentifiersFromPattern(param, scopeTracker, true);
|
|
905
921
|
}
|
|
@@ -914,7 +930,7 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
914
930
|
}
|
|
915
931
|
if (node.type === "VariableDeclarator") {
|
|
916
932
|
if (node.id.type === "Identifier") {
|
|
917
|
-
|
|
933
|
+
void isCoverageContext(node, ancestors);
|
|
918
934
|
scopeTracker.declareVariable(
|
|
919
935
|
node.id.name,
|
|
920
936
|
node.id,
|
|
@@ -926,7 +942,12 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
926
942
|
}
|
|
927
943
|
);
|
|
928
944
|
} else if (node.id.type === "ObjectPattern" || node.id.type === "ArrayPattern") {
|
|
929
|
-
extractIdentifiersFromPattern(
|
|
945
|
+
extractIdentifiersFromPattern(
|
|
946
|
+
node.id,
|
|
947
|
+
scopeTracker,
|
|
948
|
+
false,
|
|
949
|
+
ancestors
|
|
950
|
+
);
|
|
930
951
|
}
|
|
931
952
|
}
|
|
932
953
|
if (node.type === "Identifier" && parent) {
|
|
@@ -993,7 +1014,10 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
993
1014
|
}
|
|
994
1015
|
if (!disabledChecks.has("convention-mix") && file.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
995
1016
|
if (name.includes("_") && !name.startsWith("_") && name.toLowerCase() === name) {
|
|
996
|
-
const camelCase = name.replace(
|
|
1017
|
+
const camelCase = name.replace(
|
|
1018
|
+
/_([a-z])/g,
|
|
1019
|
+
(_, letter) => letter.toUpperCase()
|
|
1020
|
+
);
|
|
997
1021
|
issues.push({
|
|
998
1022
|
file,
|
|
999
1023
|
line,
|
|
@@ -1013,16 +1037,37 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
1013
1037
|
if (!name) return;
|
|
1014
1038
|
const line = getLineNumber(node);
|
|
1015
1039
|
if (["main", "init", "setup", "bootstrap"].includes(name)) return;
|
|
1016
|
-
const hasActionVerb = name.match(
|
|
1017
|
-
|
|
1040
|
+
const hasActionVerb = name.match(
|
|
1041
|
+
/^(get|set|is|has|can|should|create|update|delete|fetch|load|save|process|handle|validate|check|find|search|filter|map|reduce|make|do|run|start|stop|build|parse|format|render|calculate|compute|generate|transform|convert|normalize|sanitize|encode|decode|compress|extract|merge|split|join|sort|compare|test|verify|ensure|apply|execute|invoke|call|emit|dispatch|trigger|listen|subscribe|unsubscribe|add|remove|clear|reset|toggle|enable|disable|open|close|connect|disconnect|send|receive|read|write|import|export|register|unregister|mount|unmount|track|store|persist|upsert|derive|classify|combine|discover|activate|require|assert|expect|mask|escape|sign|put|list|complete|page|safe|mock|pick|pluralize|text|count|detect|select)/
|
|
1042
|
+
);
|
|
1043
|
+
const isFactoryPattern = name.match(
|
|
1044
|
+
/(Factory|Builder|Creator|Generator|Provider|Adapter|Mock)$/
|
|
1045
|
+
);
|
|
1018
1046
|
const isEventHandler = name.match(/^on[A-Z]/);
|
|
1019
1047
|
const isDescriptiveLong = name.length > 15;
|
|
1020
1048
|
const isReactHook = name.match(/^use[A-Z]/);
|
|
1021
1049
|
const isHelperPattern = name.match(/^(to|from|with|without|for|as|into)\w+/) || name.match(/^\w+(To|From|With|Without|For|As|Into)\w*$/);
|
|
1022
|
-
const isUtilityName = [
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1050
|
+
const isUtilityName = [
|
|
1051
|
+
"cn",
|
|
1052
|
+
"proxy",
|
|
1053
|
+
"sitemap",
|
|
1054
|
+
"robots",
|
|
1055
|
+
"gtag"
|
|
1056
|
+
].includes(name);
|
|
1057
|
+
const isLanguageKeyword = [
|
|
1058
|
+
"constructor",
|
|
1059
|
+
"toString",
|
|
1060
|
+
"valueOf",
|
|
1061
|
+
"toJSON"
|
|
1062
|
+
].includes(name);
|
|
1063
|
+
const isFrameworkPattern = name.match(
|
|
1064
|
+
/^(goto|fill|click|select|submit|wait|expect)\w*/
|
|
1065
|
+
);
|
|
1066
|
+
const isDescriptivePattern = name.match(
|
|
1067
|
+
/^(default|total|count|sum|avg|max|min|initial|current|previous|next)\w+/
|
|
1068
|
+
) || name.match(
|
|
1069
|
+
/\w+(Count|Total|Sum|Average|List|Map|Set|Config|Settings|Options|Props|Data|Info|Details|State|Status|Response|Result)$/
|
|
1070
|
+
);
|
|
1026
1071
|
const capitalCount = (name.match(/[A-Z]/g) || []).length;
|
|
1027
1072
|
const isCompoundWord = capitalCount >= 3;
|
|
1028
1073
|
if (!hasActionVerb && !isFactoryPattern && !isEventHandler && !isDescriptiveLong && !isReactHook && !isHelperPattern && !isUtilityName && !isDescriptivePattern && !isCompoundWord && !isLanguageKeyword && !isFrameworkPattern) {
|
|
@@ -1042,6 +1087,7 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
1042
1087
|
return issues;
|
|
1043
1088
|
}
|
|
1044
1089
|
function extractIdentifiersFromPattern(pattern, scopeTracker, isParameter, ancestors) {
|
|
1090
|
+
void ancestors;
|
|
1045
1091
|
if (pattern.type === "ObjectPattern") {
|
|
1046
1092
|
for (const prop of pattern.properties) {
|
|
1047
1093
|
if (prop.type === "Property" && prop.value.type === "Identifier") {
|
|
@@ -1099,7 +1145,12 @@ async function analyzePythonNaming(files) {
|
|
|
1099
1145
|
const code = await fs.promises.readFile(file, "utf-8");
|
|
1100
1146
|
const result = parser.parse(code, file);
|
|
1101
1147
|
for (const exp of result.exports) {
|
|
1102
|
-
const nameIssue = checkPythonNaming(
|
|
1148
|
+
const nameIssue = checkPythonNaming(
|
|
1149
|
+
exp.name,
|
|
1150
|
+
exp.type,
|
|
1151
|
+
file,
|
|
1152
|
+
exp.loc?.start.line || 0
|
|
1153
|
+
);
|
|
1103
1154
|
if (nameIssue) {
|
|
1104
1155
|
issues.push(nameIssue);
|
|
1105
1156
|
}
|
|
@@ -1107,7 +1158,12 @@ async function analyzePythonNaming(files) {
|
|
|
1107
1158
|
for (const imp of result.imports) {
|
|
1108
1159
|
for (const spec of imp.specifiers) {
|
|
1109
1160
|
if (spec !== "*" && spec !== "default") {
|
|
1110
|
-
const nameIssue = checkPythonNaming(
|
|
1161
|
+
const nameIssue = checkPythonNaming(
|
|
1162
|
+
spec,
|
|
1163
|
+
"variable",
|
|
1164
|
+
file,
|
|
1165
|
+
imp.loc?.start.line || 0
|
|
1166
|
+
);
|
|
1111
1167
|
if (nameIssue) {
|
|
1112
1168
|
issues.push(nameIssue);
|
|
1113
1169
|
}
|
|
@@ -1230,15 +1286,19 @@ async function analyzeErrorHandling(files) {
|
|
|
1230
1286
|
}
|
|
1231
1287
|
}
|
|
1232
1288
|
const issues = [];
|
|
1233
|
-
const strategiesUsed = Object.values(patterns).filter(
|
|
1289
|
+
const strategiesUsed = Object.values(patterns).filter(
|
|
1290
|
+
(p) => p.length > 0
|
|
1291
|
+
).length;
|
|
1234
1292
|
if (strategiesUsed > 2) {
|
|
1235
1293
|
issues.push({
|
|
1236
|
-
files: [
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1294
|
+
files: [
|
|
1295
|
+
.../* @__PURE__ */ new Set([
|
|
1296
|
+
...patterns.tryCatch,
|
|
1297
|
+
...patterns.throwsError,
|
|
1298
|
+
...patterns.returnsNull,
|
|
1299
|
+
...patterns.returnsError
|
|
1300
|
+
])
|
|
1301
|
+
],
|
|
1242
1302
|
type: "error-handling",
|
|
1243
1303
|
description: "Inconsistent error handling strategies across codebase",
|
|
1244
1304
|
examples: [
|
|
@@ -1359,6 +1419,7 @@ async function analyzeConsistency(options) {
|
|
|
1359
1419
|
minSeverity = "info",
|
|
1360
1420
|
...scanOptions
|
|
1361
1421
|
} = options;
|
|
1422
|
+
void checkArchitecture;
|
|
1362
1423
|
const filePaths = await (0, import_core4.scanFiles)(scanOptions);
|
|
1363
1424
|
const tsJsFiles = filePaths.filter((f) => /\.(ts|tsx|js|jsx)$/i.test(f));
|
|
1364
1425
|
const pythonFiles = filePaths.filter((f) => /\.py$/i.test(f));
|
|
@@ -1428,10 +1489,14 @@ async function analyzeConsistency(options) {
|
|
|
1428
1489
|
results.sort((fileResultA, fileResultB) => {
|
|
1429
1490
|
const severityOrder = { critical: 0, major: 1, minor: 2, info: 3 };
|
|
1430
1491
|
const maxSeverityA = Math.min(
|
|
1431
|
-
...fileResultA.issues.map(
|
|
1492
|
+
...fileResultA.issues.map(
|
|
1493
|
+
(i) => severityOrder[i.severity]
|
|
1494
|
+
)
|
|
1432
1495
|
);
|
|
1433
1496
|
const maxSeverityB = Math.min(
|
|
1434
|
-
...fileResultB.issues.map(
|
|
1497
|
+
...fileResultB.issues.map(
|
|
1498
|
+
(i) => severityOrder[i.severity]
|
|
1499
|
+
)
|
|
1435
1500
|
);
|
|
1436
1501
|
if (maxSeverityA !== maxSeverityB) {
|
|
1437
1502
|
return maxSeverityA - maxSeverityB;
|
|
@@ -1439,8 +1504,12 @@ async function analyzeConsistency(options) {
|
|
|
1439
1504
|
return fileResultB.issues.length - fileResultA.issues.length;
|
|
1440
1505
|
});
|
|
1441
1506
|
const recommendations = generateRecommendations(namingIssues, patternIssues);
|
|
1442
|
-
const namingCountFiltered = namingIssues.filter(
|
|
1443
|
-
|
|
1507
|
+
const namingCountFiltered = namingIssues.filter(
|
|
1508
|
+
(i) => shouldIncludeSeverity(i.severity, minSeverity)
|
|
1509
|
+
).length;
|
|
1510
|
+
const patternCountFiltered = patternIssues.filter(
|
|
1511
|
+
(i) => shouldIncludeSeverity(i.severity, minSeverity)
|
|
1512
|
+
).length;
|
|
1444
1513
|
return {
|
|
1445
1514
|
summary: {
|
|
1446
1515
|
totalIssues: namingCountFiltered + patternCountFiltered,
|
|
@@ -1459,19 +1528,26 @@ function shouldIncludeSeverity(severity, minSeverity) {
|
|
|
1459
1528
|
}
|
|
1460
1529
|
function calculateConsistencyScore(issues) {
|
|
1461
1530
|
const weights = { critical: 10, major: 5, minor: 2, info: 1 };
|
|
1462
|
-
const totalWeight = issues.reduce(
|
|
1531
|
+
const totalWeight = issues.reduce(
|
|
1532
|
+
(sum, issue) => sum + weights[issue.severity],
|
|
1533
|
+
0
|
|
1534
|
+
);
|
|
1463
1535
|
return Math.max(0, 1 - totalWeight / 100);
|
|
1464
1536
|
}
|
|
1465
1537
|
function generateRecommendations(namingIssues, patternIssues) {
|
|
1466
1538
|
const recommendations = [];
|
|
1467
1539
|
if (namingIssues.length > 0) {
|
|
1468
|
-
const conventionMixCount = namingIssues.filter(
|
|
1540
|
+
const conventionMixCount = namingIssues.filter(
|
|
1541
|
+
(i) => i.type === "convention-mix"
|
|
1542
|
+
).length;
|
|
1469
1543
|
if (conventionMixCount > 0) {
|
|
1470
1544
|
recommendations.push(
|
|
1471
1545
|
`Standardize naming conventions: Found ${conventionMixCount} snake_case variables in TypeScript/JavaScript (use camelCase)`
|
|
1472
1546
|
);
|
|
1473
1547
|
}
|
|
1474
|
-
const poorNamingCount = namingIssues.filter(
|
|
1548
|
+
const poorNamingCount = namingIssues.filter(
|
|
1549
|
+
(i) => i.type === "poor-naming"
|
|
1550
|
+
).length;
|
|
1475
1551
|
if (poorNamingCount > 0) {
|
|
1476
1552
|
recommendations.push(
|
|
1477
1553
|
`Improve variable naming: Found ${poorNamingCount} single-letter or unclear variable names`
|
|
@@ -1479,7 +1555,9 @@ function generateRecommendations(namingIssues, patternIssues) {
|
|
|
1479
1555
|
}
|
|
1480
1556
|
}
|
|
1481
1557
|
if (patternIssues.length > 0) {
|
|
1482
|
-
const errorHandlingIssues = patternIssues.filter(
|
|
1558
|
+
const errorHandlingIssues = patternIssues.filter(
|
|
1559
|
+
(i) => i.type === "error-handling"
|
|
1560
|
+
);
|
|
1483
1561
|
if (errorHandlingIssues.length > 0) {
|
|
1484
1562
|
recommendations.push(
|
|
1485
1563
|
"Standardize error handling strategy across the codebase (prefer try-catch with typed errors)"
|
|
@@ -1499,7 +1577,9 @@ function generateRecommendations(namingIssues, patternIssues) {
|
|
|
1499
1577
|
}
|
|
1500
1578
|
}
|
|
1501
1579
|
if (recommendations.length === 0) {
|
|
1502
|
-
recommendations.push(
|
|
1580
|
+
recommendations.push(
|
|
1581
|
+
"No major consistency issues found! Your codebase follows good practices."
|
|
1582
|
+
);
|
|
1503
1583
|
}
|
|
1504
1584
|
return recommendations;
|
|
1505
1585
|
}
|
|
@@ -1510,7 +1590,11 @@ var import_fs2 = require("fs");
|
|
|
1510
1590
|
var import_path2 = require("path");
|
|
1511
1591
|
var import_core5 = require("@aiready/core");
|
|
1512
1592
|
var program = new import_commander.Command();
|
|
1513
|
-
program.name("aiready-consistency").description(
|
|
1593
|
+
program.name("aiready-consistency").description(
|
|
1594
|
+
"Detect consistency patterns in naming, code structure, and architecture"
|
|
1595
|
+
).version("0.1.0").addHelpText(
|
|
1596
|
+
"after",
|
|
1597
|
+
`
|
|
1514
1598
|
LANGUAGE SUPPORT:
|
|
1515
1599
|
Supported: TypeScript (.ts, .tsx), JavaScript (.js, .jsx)
|
|
1516
1600
|
Note: Python, Java, and other language files will be safely ignored
|
|
@@ -1529,7 +1613,18 @@ EXAMPLES:
|
|
|
1529
1613
|
aiready-consistency . --no-naming # Skip naming checks
|
|
1530
1614
|
aiready-consistency . --min-severity major # Only show major+ patterns
|
|
1531
1615
|
aiready-consistency . --output json > report.json # JSON export
|
|
1532
|
-
`
|
|
1616
|
+
`
|
|
1617
|
+
).argument("<directory>", "Directory to analyze").option("--naming", "Check naming conventions and quality (default: true)").option("--no-naming", "Skip naming analysis").option("--patterns", "Check code pattern consistency (default: true)").option("--no-patterns", "Skip pattern analysis").option(
|
|
1618
|
+
"--architecture",
|
|
1619
|
+
"Check architectural consistency (not yet implemented)"
|
|
1620
|
+
).option(
|
|
1621
|
+
"--min-severity <level>",
|
|
1622
|
+
"Minimum severity: info|minor|major|critical. Default: info"
|
|
1623
|
+
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option(
|
|
1624
|
+
"-o, --output <format>",
|
|
1625
|
+
"Output format: console|json|markdown",
|
|
1626
|
+
"console"
|
|
1627
|
+
).option("--output-file <path>", "Output file path (for json/markdown)").action(async (directory, options) => {
|
|
1533
1628
|
console.log(import_chalk.default.blue("\u{1F50D} Analyzing consistency...\n"));
|
|
1534
1629
|
const startTime = Date.now();
|
|
1535
1630
|
const config = await (0, import_core5.loadConfig)(directory);
|
|
@@ -1595,7 +1690,11 @@ function displayConsoleReport(report, elapsedTime) {
|
|
|
1595
1690
|
console.log(`Analysis Time: ${import_chalk.default.gray(elapsedTime + "s")}
|
|
1596
1691
|
`);
|
|
1597
1692
|
if (summary.totalIssues === 0) {
|
|
1598
|
-
console.log(
|
|
1693
|
+
console.log(
|
|
1694
|
+
import_chalk.default.green(
|
|
1695
|
+
"\u2728 No consistency patterns found! Your codebase is AI-friendly.\n"
|
|
1696
|
+
)
|
|
1697
|
+
);
|
|
1599
1698
|
return;
|
|
1600
1699
|
}
|
|
1601
1700
|
const namingResults = results.filter(
|
package/dist/cli.mjs
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
analyzeConsistency
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-J5IFYDVU.mjs";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
10
10
|
import { dirname } from "path";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
loadConfig,
|
|
13
|
+
mergeConfigWithDefaults,
|
|
14
|
+
resolveOutputPath
|
|
15
|
+
} from "@aiready/core";
|
|
12
16
|
var program = new Command();
|
|
13
|
-
program.name("aiready-consistency").description(
|
|
17
|
+
program.name("aiready-consistency").description(
|
|
18
|
+
"Detect consistency patterns in naming, code structure, and architecture"
|
|
19
|
+
).version("0.1.0").addHelpText(
|
|
20
|
+
"after",
|
|
21
|
+
`
|
|
14
22
|
LANGUAGE SUPPORT:
|
|
15
23
|
Supported: TypeScript (.ts, .tsx), JavaScript (.js, .jsx)
|
|
16
24
|
Note: Python, Java, and other language files will be safely ignored
|
|
@@ -29,7 +37,18 @@ EXAMPLES:
|
|
|
29
37
|
aiready-consistency . --no-naming # Skip naming checks
|
|
30
38
|
aiready-consistency . --min-severity major # Only show major+ patterns
|
|
31
39
|
aiready-consistency . --output json > report.json # JSON export
|
|
32
|
-
`
|
|
40
|
+
`
|
|
41
|
+
).argument("<directory>", "Directory to analyze").option("--naming", "Check naming conventions and quality (default: true)").option("--no-naming", "Skip naming analysis").option("--patterns", "Check code pattern consistency (default: true)").option("--no-patterns", "Skip pattern analysis").option(
|
|
42
|
+
"--architecture",
|
|
43
|
+
"Check architectural consistency (not yet implemented)"
|
|
44
|
+
).option(
|
|
45
|
+
"--min-severity <level>",
|
|
46
|
+
"Minimum severity: info|minor|major|critical. Default: info"
|
|
47
|
+
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option(
|
|
48
|
+
"-o, --output <format>",
|
|
49
|
+
"Output format: console|json|markdown",
|
|
50
|
+
"console"
|
|
51
|
+
).option("--output-file <path>", "Output file path (for json/markdown)").action(async (directory, options) => {
|
|
33
52
|
console.log(chalk.blue("\u{1F50D} Analyzing consistency...\n"));
|
|
34
53
|
const startTime = Date.now();
|
|
35
54
|
const config = await loadConfig(directory);
|
|
@@ -95,7 +114,11 @@ function displayConsoleReport(report, elapsedTime) {
|
|
|
95
114
|
console.log(`Analysis Time: ${chalk.gray(elapsedTime + "s")}
|
|
96
115
|
`);
|
|
97
116
|
if (summary.totalIssues === 0) {
|
|
98
|
-
console.log(
|
|
117
|
+
console.log(
|
|
118
|
+
chalk.green(
|
|
119
|
+
"\u2728 No consistency patterns found! Your codebase is AI-friendly.\n"
|
|
120
|
+
)
|
|
121
|
+
);
|
|
99
122
|
return;
|
|
100
123
|
}
|
|
101
124
|
const namingResults = results.filter(
|