@aiready/consistency 0.16.3 → 0.18.0
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/.turbo/turbo-build.log +10 -10
- package/.turbo/turbo-lint.log +13 -1
- package/.turbo/turbo-test.log +14 -12
- package/dist/chunk-IXBC6GVT.mjs +832 -0
- package/dist/cli.js +12 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +128 -73
- package/dist/index.mjs +118 -70
- package/package.json +2 -2
- package/src/__tests__/scoring.test.ts +1 -1
- package/src/analyzer.ts +29 -15
- package/src/analyzers/naming-ast.ts +18 -2
- package/src/analyzers/naming.ts +15 -6
- package/src/analyzers/patterns.ts +20 -10
- package/src/index.ts +7 -0
- package/src/provider.ts +48 -0
- package/src/scoring.ts +2 -2
- package/src/types.ts +7 -1
package/dist/cli.js
CHANGED
|
@@ -403,7 +403,18 @@ function checkVariableNaming(varInfo, file, issues, context) {
|
|
|
403
403
|
}
|
|
404
404
|
}
|
|
405
405
|
function isCommonAbbreviation(name) {
|
|
406
|
-
const common = [
|
|
406
|
+
const common = [
|
|
407
|
+
"id",
|
|
408
|
+
"db",
|
|
409
|
+
"fs",
|
|
410
|
+
"os",
|
|
411
|
+
"ip",
|
|
412
|
+
"ui",
|
|
413
|
+
"ux",
|
|
414
|
+
"api",
|
|
415
|
+
"env",
|
|
416
|
+
"url"
|
|
417
|
+
];
|
|
407
418
|
return common.includes(name.toLowerCase());
|
|
408
419
|
}
|
|
409
420
|
var ScopeTracker = class {
|
package/dist/cli.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import { Severity, Issue, IssueType, ScanOptions, AnalysisResult, CostConfig, ToolScoringOutput } from '@aiready/core';
|
|
1
|
+
import { ToolProvider, Severity, Issue, IssueType, ScanOptions, AnalysisResult, CostConfig, ToolScoringOutput } from '@aiready/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Consistency Tool Provider
|
|
5
|
+
*/
|
|
6
|
+
declare const ConsistencyProvider: ToolProvider;
|
|
2
7
|
|
|
3
8
|
interface ConsistencyOptions extends ScanOptions {
|
|
4
9
|
/** Check naming conventions and quality */
|
|
@@ -99,4 +104,4 @@ declare function analyzePatterns(filePaths: string[]): Promise<PatternIssue[]>;
|
|
|
99
104
|
*/
|
|
100
105
|
declare function calculateConsistencyScore(issues: ConsistencyIssue[], totalFilesAnalyzed: number, costConfig?: Partial<CostConfig>): ToolScoringOutput;
|
|
101
106
|
|
|
102
|
-
export { type ArchitectureIssue, type ConsistencyIssue, type ConsistencyOptions, type ConsistencyReport, type NamingIssue, type PatternIssue, analyzeConsistency, analyzeNaming, analyzeNamingAST, analyzePatterns, calculateConsistencyScore, detectNamingConventions };
|
|
107
|
+
export { type ArchitectureIssue, type ConsistencyIssue, type ConsistencyOptions, ConsistencyProvider, type ConsistencyReport, type NamingIssue, type PatternIssue, analyzeConsistency, analyzeNaming, analyzeNamingAST, analyzePatterns, calculateConsistencyScore, detectNamingConventions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import { Severity, Issue, IssueType, ScanOptions, AnalysisResult, CostConfig, ToolScoringOutput } from '@aiready/core';
|
|
1
|
+
import { ToolProvider, Severity, Issue, IssueType, ScanOptions, AnalysisResult, CostConfig, ToolScoringOutput } from '@aiready/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Consistency Tool Provider
|
|
5
|
+
*/
|
|
6
|
+
declare const ConsistencyProvider: ToolProvider;
|
|
2
7
|
|
|
3
8
|
interface ConsistencyOptions extends ScanOptions {
|
|
4
9
|
/** Check naming conventions and quality */
|
|
@@ -99,4 +104,4 @@ declare function analyzePatterns(filePaths: string[]): Promise<PatternIssue[]>;
|
|
|
99
104
|
*/
|
|
100
105
|
declare function calculateConsistencyScore(issues: ConsistencyIssue[], totalFilesAnalyzed: number, costConfig?: Partial<CostConfig>): ToolScoringOutput;
|
|
101
106
|
|
|
102
|
-
export { type ArchitectureIssue, type ConsistencyIssue, type ConsistencyOptions, type ConsistencyReport, type NamingIssue, type PatternIssue, analyzeConsistency, analyzeNaming, analyzeNamingAST, analyzePatterns, calculateConsistencyScore, detectNamingConventions };
|
|
107
|
+
export { type ArchitectureIssue, type ConsistencyIssue, type ConsistencyOptions, ConsistencyProvider, type ConsistencyReport, type NamingIssue, type PatternIssue, analyzeConsistency, analyzeNaming, analyzeNamingAST, analyzePatterns, calculateConsistencyScore, detectNamingConventions };
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
ConsistencyProvider: () => ConsistencyProvider,
|
|
33
34
|
analyzeConsistency: () => analyzeConsistency,
|
|
34
35
|
analyzeNaming: () => analyzeNaming,
|
|
35
36
|
analyzeNamingAST: () => analyzeNamingAST,
|
|
@@ -38,6 +39,10 @@ __export(index_exports, {
|
|
|
38
39
|
detectNamingConventions: () => detectNamingConventions
|
|
39
40
|
});
|
|
40
41
|
module.exports = __toCommonJS(index_exports);
|
|
42
|
+
var import_core9 = require("@aiready/core");
|
|
43
|
+
|
|
44
|
+
// src/provider.ts
|
|
45
|
+
var import_core7 = require("@aiready/core");
|
|
41
46
|
|
|
42
47
|
// src/analyzer.ts
|
|
43
48
|
var import_core5 = require("@aiready/core");
|
|
@@ -416,7 +421,18 @@ function checkVariableNaming(varInfo, file, issues, context) {
|
|
|
416
421
|
}
|
|
417
422
|
}
|
|
418
423
|
function isCommonAbbreviation(name) {
|
|
419
|
-
const common = [
|
|
424
|
+
const common = [
|
|
425
|
+
"id",
|
|
426
|
+
"db",
|
|
427
|
+
"fs",
|
|
428
|
+
"os",
|
|
429
|
+
"ip",
|
|
430
|
+
"ui",
|
|
431
|
+
"ux",
|
|
432
|
+
"api",
|
|
433
|
+
"env",
|
|
434
|
+
"url"
|
|
435
|
+
];
|
|
420
436
|
return common.includes(name.toLowerCase());
|
|
421
437
|
}
|
|
422
438
|
var ScopeTracker = class {
|
|
@@ -855,77 +871,8 @@ function generateRecommendations(namingIssues, patternIssues) {
|
|
|
855
871
|
return recommendations;
|
|
856
872
|
}
|
|
857
873
|
|
|
858
|
-
// src/analyzers/naming.ts
|
|
859
|
-
var import_fs3 = require("fs");
|
|
860
|
-
var import_core6 = require("@aiready/core");
|
|
861
|
-
async function analyzeNaming(filePaths) {
|
|
862
|
-
const issues = [];
|
|
863
|
-
for (const filePath of filePaths) {
|
|
864
|
-
try {
|
|
865
|
-
const content = (0, import_fs3.readFileSync)(filePath, "utf-8");
|
|
866
|
-
const lines = content.split("\n");
|
|
867
|
-
lines.forEach((line, index) => {
|
|
868
|
-
const singleLetterMatch = line.match(/\b(const|let|var)\s+([a-hj-km-np-zA-Z])\s*=/);
|
|
869
|
-
if (singleLetterMatch) {
|
|
870
|
-
issues.push({
|
|
871
|
-
file: filePath,
|
|
872
|
-
line: index + 1,
|
|
873
|
-
type: "poor-naming",
|
|
874
|
-
identifier: singleLetterMatch[2],
|
|
875
|
-
severity: import_core6.Severity.Minor,
|
|
876
|
-
suggestion: "Use a more descriptive name than a single letter"
|
|
877
|
-
});
|
|
878
|
-
}
|
|
879
|
-
if (filePath.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
880
|
-
const snakeCaseMatch = line.match(/\b(const|let|var|function)\s+([a-z]+_[a-z0-9_]+)\b/);
|
|
881
|
-
if (snakeCaseMatch) {
|
|
882
|
-
issues.push({
|
|
883
|
-
file: filePath,
|
|
884
|
-
line: index + 1,
|
|
885
|
-
type: "convention-mix",
|
|
886
|
-
identifier: snakeCaseMatch[2],
|
|
887
|
-
severity: import_core6.Severity.Info,
|
|
888
|
-
suggestion: "Use camelCase instead of snake_case in TypeScript/JavaScript"
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
const shortNameMatch = line.match(/\b(const|let|var)\s+([a-zA-Z0-9]{2,3})\s*=/);
|
|
893
|
-
if (shortNameMatch) {
|
|
894
|
-
const name = shortNameMatch[2].toLowerCase();
|
|
895
|
-
const vagueNames = ["obj", "val", "tmp", "res", "ret", "data"];
|
|
896
|
-
if (vagueNames.includes(name)) {
|
|
897
|
-
issues.push({
|
|
898
|
-
file: filePath,
|
|
899
|
-
line: index + 1,
|
|
900
|
-
type: "poor-naming",
|
|
901
|
-
identifier: name,
|
|
902
|
-
severity: import_core6.Severity.Minor,
|
|
903
|
-
suggestion: `Avoid vague names like '${name}'`
|
|
904
|
-
});
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
});
|
|
908
|
-
} catch (err) {
|
|
909
|
-
void err;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
return issues;
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
// src/analyzers/naming-constants.ts
|
|
916
|
-
function detectNamingConventions(files, allIssues) {
|
|
917
|
-
const camelCaseCount = allIssues.filter(
|
|
918
|
-
(i) => i.type === "convention-mix"
|
|
919
|
-
).length;
|
|
920
|
-
const totalChecks = files.length * 10;
|
|
921
|
-
if (camelCaseCount / totalChecks > 0.3) {
|
|
922
|
-
return { dominantConvention: "mixed", conventionScore: 0.5 };
|
|
923
|
-
}
|
|
924
|
-
return { dominantConvention: "camelCase", conventionScore: 0.9 };
|
|
925
|
-
}
|
|
926
|
-
|
|
927
874
|
// src/scoring.ts
|
|
928
|
-
var
|
|
875
|
+
var import_core6 = require("@aiready/core");
|
|
929
876
|
function calculateConsistencyScore2(issues, totalFilesAnalyzed, costConfig) {
|
|
930
877
|
void costConfig;
|
|
931
878
|
const criticalIssues = issues.filter((i) => i.severity === "critical").length;
|
|
@@ -1001,9 +948,9 @@ function calculateConsistencyScore2(issues, totalFilesAnalyzed, costConfig) {
|
|
|
1001
948
|
priority: "low"
|
|
1002
949
|
});
|
|
1003
950
|
}
|
|
1004
|
-
const productivityImpact = (0,
|
|
951
|
+
const productivityImpact = (0, import_core6.calculateProductivityImpact)(issues);
|
|
1005
952
|
return {
|
|
1006
|
-
toolName:
|
|
953
|
+
toolName: import_core6.ToolName.NamingConsistency,
|
|
1007
954
|
score,
|
|
1008
955
|
rawMetrics: {
|
|
1009
956
|
totalIssues,
|
|
@@ -1019,8 +966,116 @@ function calculateConsistencyScore2(issues, totalFilesAnalyzed, costConfig) {
|
|
|
1019
966
|
recommendations
|
|
1020
967
|
};
|
|
1021
968
|
}
|
|
969
|
+
|
|
970
|
+
// src/provider.ts
|
|
971
|
+
var ConsistencyProvider = {
|
|
972
|
+
id: import_core7.ToolName.NamingConsistency,
|
|
973
|
+
alias: ["consistency", "naming", "standards"],
|
|
974
|
+
async analyze(options) {
|
|
975
|
+
const report = await analyzeConsistency(options);
|
|
976
|
+
return import_core7.SpokeOutputSchema.parse({
|
|
977
|
+
results: report.results,
|
|
978
|
+
summary: report.summary,
|
|
979
|
+
metadata: {
|
|
980
|
+
toolName: import_core7.ToolName.NamingConsistency,
|
|
981
|
+
version: "0.16.5",
|
|
982
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
983
|
+
}
|
|
984
|
+
});
|
|
985
|
+
},
|
|
986
|
+
score(output, options) {
|
|
987
|
+
const results = output.results;
|
|
988
|
+
const allIssues = results.flatMap((r) => r.issues);
|
|
989
|
+
const totalFiles = output.summary.filesAnalyzed || results.length;
|
|
990
|
+
return calculateConsistencyScore2(
|
|
991
|
+
allIssues,
|
|
992
|
+
totalFiles,
|
|
993
|
+
options.costConfig
|
|
994
|
+
);
|
|
995
|
+
},
|
|
996
|
+
defaultWeight: 14
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
// src/analyzers/naming.ts
|
|
1000
|
+
var import_fs3 = require("fs");
|
|
1001
|
+
var import_core8 = require("@aiready/core");
|
|
1002
|
+
async function analyzeNaming(filePaths) {
|
|
1003
|
+
const issues = [];
|
|
1004
|
+
for (const filePath of filePaths) {
|
|
1005
|
+
try {
|
|
1006
|
+
const content = (0, import_fs3.readFileSync)(filePath, "utf-8");
|
|
1007
|
+
const lines = content.split("\n");
|
|
1008
|
+
lines.forEach((line, index) => {
|
|
1009
|
+
const singleLetterMatch = line.match(
|
|
1010
|
+
/\b(const|let|var)\s+([a-hj-km-np-zA-Z])\s*=/
|
|
1011
|
+
);
|
|
1012
|
+
if (singleLetterMatch) {
|
|
1013
|
+
issues.push({
|
|
1014
|
+
file: filePath,
|
|
1015
|
+
line: index + 1,
|
|
1016
|
+
type: "poor-naming",
|
|
1017
|
+
identifier: singleLetterMatch[2],
|
|
1018
|
+
severity: import_core8.Severity.Minor,
|
|
1019
|
+
suggestion: "Use a more descriptive name than a single letter"
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
if (filePath.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
1023
|
+
const snakeCaseMatch = line.match(
|
|
1024
|
+
/\b(const|let|var|function)\s+([a-z]+_[a-z0-9_]+)\b/
|
|
1025
|
+
);
|
|
1026
|
+
if (snakeCaseMatch) {
|
|
1027
|
+
issues.push({
|
|
1028
|
+
file: filePath,
|
|
1029
|
+
line: index + 1,
|
|
1030
|
+
type: "convention-mix",
|
|
1031
|
+
identifier: snakeCaseMatch[2],
|
|
1032
|
+
severity: import_core8.Severity.Info,
|
|
1033
|
+
suggestion: "Use camelCase instead of snake_case in TypeScript/JavaScript"
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
const shortNameMatch = line.match(
|
|
1038
|
+
/\b(const|let|var)\s+([a-zA-Z0-9]{2,3})\s*=/
|
|
1039
|
+
);
|
|
1040
|
+
if (shortNameMatch) {
|
|
1041
|
+
const name = shortNameMatch[2].toLowerCase();
|
|
1042
|
+
const vagueNames = ["obj", "val", "tmp", "res", "ret", "data"];
|
|
1043
|
+
if (vagueNames.includes(name)) {
|
|
1044
|
+
issues.push({
|
|
1045
|
+
file: filePath,
|
|
1046
|
+
line: index + 1,
|
|
1047
|
+
type: "poor-naming",
|
|
1048
|
+
identifier: name,
|
|
1049
|
+
severity: import_core8.Severity.Minor,
|
|
1050
|
+
suggestion: `Avoid vague names like '${name}'`
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
} catch (err) {
|
|
1056
|
+
void err;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
return issues;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// src/analyzers/naming-constants.ts
|
|
1063
|
+
function detectNamingConventions(files, allIssues) {
|
|
1064
|
+
const camelCaseCount = allIssues.filter(
|
|
1065
|
+
(i) => i.type === "convention-mix"
|
|
1066
|
+
).length;
|
|
1067
|
+
const totalChecks = files.length * 10;
|
|
1068
|
+
if (camelCaseCount / totalChecks > 0.3) {
|
|
1069
|
+
return { dominantConvention: "mixed", conventionScore: 0.5 };
|
|
1070
|
+
}
|
|
1071
|
+
return { dominantConvention: "camelCase", conventionScore: 0.9 };
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// src/index.ts
|
|
1075
|
+
import_core9.ToolRegistry.register(ConsistencyProvider);
|
|
1022
1076
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1023
1077
|
0 && (module.exports = {
|
|
1078
|
+
ConsistencyProvider,
|
|
1024
1079
|
analyzeConsistency,
|
|
1025
1080
|
analyzeNaming,
|
|
1026
1081
|
analyzeNamingAST,
|
package/dist/index.mjs
CHANGED
|
@@ -2,79 +2,19 @@ import {
|
|
|
2
2
|
analyzeConsistency,
|
|
3
3
|
analyzeNamingAST,
|
|
4
4
|
analyzePatterns
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-IXBC6GVT.mjs";
|
|
6
6
|
|
|
7
|
-
// src/
|
|
8
|
-
import {
|
|
9
|
-
import { Severity } from "@aiready/core";
|
|
10
|
-
async function analyzeNaming(filePaths) {
|
|
11
|
-
const issues = [];
|
|
12
|
-
for (const filePath of filePaths) {
|
|
13
|
-
try {
|
|
14
|
-
const content = readFileSync(filePath, "utf-8");
|
|
15
|
-
const lines = content.split("\n");
|
|
16
|
-
lines.forEach((line, index) => {
|
|
17
|
-
const singleLetterMatch = line.match(/\b(const|let|var)\s+([a-hj-km-np-zA-Z])\s*=/);
|
|
18
|
-
if (singleLetterMatch) {
|
|
19
|
-
issues.push({
|
|
20
|
-
file: filePath,
|
|
21
|
-
line: index + 1,
|
|
22
|
-
type: "poor-naming",
|
|
23
|
-
identifier: singleLetterMatch[2],
|
|
24
|
-
severity: Severity.Minor,
|
|
25
|
-
suggestion: "Use a more descriptive name than a single letter"
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
if (filePath.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
29
|
-
const snakeCaseMatch = line.match(/\b(const|let|var|function)\s+([a-z]+_[a-z0-9_]+)\b/);
|
|
30
|
-
if (snakeCaseMatch) {
|
|
31
|
-
issues.push({
|
|
32
|
-
file: filePath,
|
|
33
|
-
line: index + 1,
|
|
34
|
-
type: "convention-mix",
|
|
35
|
-
identifier: snakeCaseMatch[2],
|
|
36
|
-
severity: Severity.Info,
|
|
37
|
-
suggestion: "Use camelCase instead of snake_case in TypeScript/JavaScript"
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
const shortNameMatch = line.match(/\b(const|let|var)\s+([a-zA-Z0-9]{2,3})\s*=/);
|
|
42
|
-
if (shortNameMatch) {
|
|
43
|
-
const name = shortNameMatch[2].toLowerCase();
|
|
44
|
-
const vagueNames = ["obj", "val", "tmp", "res", "ret", "data"];
|
|
45
|
-
if (vagueNames.includes(name)) {
|
|
46
|
-
issues.push({
|
|
47
|
-
file: filePath,
|
|
48
|
-
line: index + 1,
|
|
49
|
-
type: "poor-naming",
|
|
50
|
-
identifier: name,
|
|
51
|
-
severity: Severity.Minor,
|
|
52
|
-
suggestion: `Avoid vague names like '${name}'`
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
} catch (err) {
|
|
58
|
-
void err;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return issues;
|
|
62
|
-
}
|
|
7
|
+
// src/index.ts
|
|
8
|
+
import { ToolRegistry } from "@aiready/core";
|
|
63
9
|
|
|
64
|
-
// src/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const totalChecks = files.length * 10;
|
|
70
|
-
if (camelCaseCount / totalChecks > 0.3) {
|
|
71
|
-
return { dominantConvention: "mixed", conventionScore: 0.5 };
|
|
72
|
-
}
|
|
73
|
-
return { dominantConvention: "camelCase", conventionScore: 0.9 };
|
|
74
|
-
}
|
|
10
|
+
// src/provider.ts
|
|
11
|
+
import {
|
|
12
|
+
ToolName as ToolName2,
|
|
13
|
+
SpokeOutputSchema
|
|
14
|
+
} from "@aiready/core";
|
|
75
15
|
|
|
76
16
|
// src/scoring.ts
|
|
77
|
-
import { calculateProductivityImpact } from "@aiready/core";
|
|
17
|
+
import { calculateProductivityImpact, ToolName } from "@aiready/core";
|
|
78
18
|
function calculateConsistencyScore(issues, totalFilesAnalyzed, costConfig) {
|
|
79
19
|
void costConfig;
|
|
80
20
|
const criticalIssues = issues.filter((i) => i.severity === "critical").length;
|
|
@@ -152,7 +92,7 @@ function calculateConsistencyScore(issues, totalFilesAnalyzed, costConfig) {
|
|
|
152
92
|
}
|
|
153
93
|
const productivityImpact = calculateProductivityImpact(issues);
|
|
154
94
|
return {
|
|
155
|
-
toolName:
|
|
95
|
+
toolName: ToolName.NamingConsistency,
|
|
156
96
|
score,
|
|
157
97
|
rawMetrics: {
|
|
158
98
|
totalIssues,
|
|
@@ -168,7 +108,115 @@ function calculateConsistencyScore(issues, totalFilesAnalyzed, costConfig) {
|
|
|
168
108
|
recommendations
|
|
169
109
|
};
|
|
170
110
|
}
|
|
111
|
+
|
|
112
|
+
// src/provider.ts
|
|
113
|
+
var ConsistencyProvider = {
|
|
114
|
+
id: ToolName2.NamingConsistency,
|
|
115
|
+
alias: ["consistency", "naming", "standards"],
|
|
116
|
+
async analyze(options) {
|
|
117
|
+
const report = await analyzeConsistency(options);
|
|
118
|
+
return SpokeOutputSchema.parse({
|
|
119
|
+
results: report.results,
|
|
120
|
+
summary: report.summary,
|
|
121
|
+
metadata: {
|
|
122
|
+
toolName: ToolName2.NamingConsistency,
|
|
123
|
+
version: "0.16.5",
|
|
124
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
score(output, options) {
|
|
129
|
+
const results = output.results;
|
|
130
|
+
const allIssues = results.flatMap((r) => r.issues);
|
|
131
|
+
const totalFiles = output.summary.filesAnalyzed || results.length;
|
|
132
|
+
return calculateConsistencyScore(
|
|
133
|
+
allIssues,
|
|
134
|
+
totalFiles,
|
|
135
|
+
options.costConfig
|
|
136
|
+
);
|
|
137
|
+
},
|
|
138
|
+
defaultWeight: 14
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// src/analyzers/naming.ts
|
|
142
|
+
import { readFileSync } from "fs";
|
|
143
|
+
import { Severity } from "@aiready/core";
|
|
144
|
+
async function analyzeNaming(filePaths) {
|
|
145
|
+
const issues = [];
|
|
146
|
+
for (const filePath of filePaths) {
|
|
147
|
+
try {
|
|
148
|
+
const content = readFileSync(filePath, "utf-8");
|
|
149
|
+
const lines = content.split("\n");
|
|
150
|
+
lines.forEach((line, index) => {
|
|
151
|
+
const singleLetterMatch = line.match(
|
|
152
|
+
/\b(const|let|var)\s+([a-hj-km-np-zA-Z])\s*=/
|
|
153
|
+
);
|
|
154
|
+
if (singleLetterMatch) {
|
|
155
|
+
issues.push({
|
|
156
|
+
file: filePath,
|
|
157
|
+
line: index + 1,
|
|
158
|
+
type: "poor-naming",
|
|
159
|
+
identifier: singleLetterMatch[2],
|
|
160
|
+
severity: Severity.Minor,
|
|
161
|
+
suggestion: "Use a more descriptive name than a single letter"
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (filePath.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
165
|
+
const snakeCaseMatch = line.match(
|
|
166
|
+
/\b(const|let|var|function)\s+([a-z]+_[a-z0-9_]+)\b/
|
|
167
|
+
);
|
|
168
|
+
if (snakeCaseMatch) {
|
|
169
|
+
issues.push({
|
|
170
|
+
file: filePath,
|
|
171
|
+
line: index + 1,
|
|
172
|
+
type: "convention-mix",
|
|
173
|
+
identifier: snakeCaseMatch[2],
|
|
174
|
+
severity: Severity.Info,
|
|
175
|
+
suggestion: "Use camelCase instead of snake_case in TypeScript/JavaScript"
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const shortNameMatch = line.match(
|
|
180
|
+
/\b(const|let|var)\s+([a-zA-Z0-9]{2,3})\s*=/
|
|
181
|
+
);
|
|
182
|
+
if (shortNameMatch) {
|
|
183
|
+
const name = shortNameMatch[2].toLowerCase();
|
|
184
|
+
const vagueNames = ["obj", "val", "tmp", "res", "ret", "data"];
|
|
185
|
+
if (vagueNames.includes(name)) {
|
|
186
|
+
issues.push({
|
|
187
|
+
file: filePath,
|
|
188
|
+
line: index + 1,
|
|
189
|
+
type: "poor-naming",
|
|
190
|
+
identifier: name,
|
|
191
|
+
severity: Severity.Minor,
|
|
192
|
+
suggestion: `Avoid vague names like '${name}'`
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
} catch (err) {
|
|
198
|
+
void err;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return issues;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/analyzers/naming-constants.ts
|
|
205
|
+
function detectNamingConventions(files, allIssues) {
|
|
206
|
+
const camelCaseCount = allIssues.filter(
|
|
207
|
+
(i) => i.type === "convention-mix"
|
|
208
|
+
).length;
|
|
209
|
+
const totalChecks = files.length * 10;
|
|
210
|
+
if (camelCaseCount / totalChecks > 0.3) {
|
|
211
|
+
return { dominantConvention: "mixed", conventionScore: 0.5 };
|
|
212
|
+
}
|
|
213
|
+
return { dominantConvention: "camelCase", conventionScore: 0.9 };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/index.ts
|
|
217
|
+
ToolRegistry.register(ConsistencyProvider);
|
|
171
218
|
export {
|
|
219
|
+
ConsistencyProvider,
|
|
172
220
|
analyzeConsistency,
|
|
173
221
|
analyzeNaming,
|
|
174
222
|
analyzeNamingAST,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiready/consistency",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Detects consistency issues in naming, patterns, and architecture that confuse AI models",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@typescript-eslint/typescript-estree": "^8.53.0",
|
|
44
44
|
"chalk": "^5.3.0",
|
|
45
45
|
"commander": "^14.0.0",
|
|
46
|
-
"@aiready/core": "0.
|
|
46
|
+
"@aiready/core": "0.21.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/node": "^24.0.0",
|
|
@@ -8,7 +8,7 @@ describe('Consistency Scoring', () => {
|
|
|
8
8
|
const result = calculateConsistencyScore([], 100);
|
|
9
9
|
|
|
10
10
|
expect(result.score).toBe(100);
|
|
11
|
-
expect(result.toolName).toBe('consistency');
|
|
11
|
+
expect(result.toolName).toBe('naming-consistency');
|
|
12
12
|
expect(result.rawMetrics.totalIssues).toBe(0);
|
|
13
13
|
expect(result.recommendations).toHaveLength(0);
|
|
14
14
|
});
|
package/src/analyzer.ts
CHANGED
|
@@ -123,15 +123,15 @@ export async function analyzeConsistency(
|
|
|
123
123
|
// Get highest severity in each file
|
|
124
124
|
const maxSeverityA = Math.min(
|
|
125
125
|
...fileResultA.issues.map((i) => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
const val = getSeverityLevel((i as ConsistencyIssue).severity);
|
|
127
|
+
// Map 4->0, 3->1, 2->2, 1->3
|
|
128
|
+
return val === 4 ? 0 : val === 3 ? 1 : val === 2 ? 2 : 3;
|
|
129
129
|
})
|
|
130
130
|
);
|
|
131
131
|
const maxSeverityB = Math.min(
|
|
132
132
|
...fileResultB.issues.map((i) => {
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
const val = getSeverityLevel((i as ConsistencyIssue).severity);
|
|
134
|
+
return val === 4 ? 0 : val === 3 ? 1 : val === 2 ? 2 : 3;
|
|
135
135
|
})
|
|
136
136
|
);
|
|
137
137
|
|
|
@@ -179,11 +179,16 @@ function getSeverityLevel(s: any): number {
|
|
|
179
179
|
function getSeverityEnum(s: any): Severity {
|
|
180
180
|
const val = getSeverityLevel(s);
|
|
181
181
|
switch (val) {
|
|
182
|
-
case 4:
|
|
183
|
-
|
|
184
|
-
case
|
|
185
|
-
|
|
186
|
-
|
|
182
|
+
case 4:
|
|
183
|
+
return Severity.Critical;
|
|
184
|
+
case 3:
|
|
185
|
+
return Severity.Major;
|
|
186
|
+
case 2:
|
|
187
|
+
return Severity.Minor;
|
|
188
|
+
case 1:
|
|
189
|
+
return Severity.Info;
|
|
190
|
+
default:
|
|
191
|
+
return Severity.Info;
|
|
187
192
|
}
|
|
188
193
|
}
|
|
189
194
|
|
|
@@ -199,11 +204,20 @@ function calculateConsistencyScore(issues: ConsistencyIssue[]): number {
|
|
|
199
204
|
for (const issue of issues) {
|
|
200
205
|
const val = getSeverityLevel(issue.severity);
|
|
201
206
|
switch (val) {
|
|
202
|
-
case 4:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
case
|
|
206
|
-
|
|
207
|
+
case 4:
|
|
208
|
+
totalWeight += 10;
|
|
209
|
+
break;
|
|
210
|
+
case 3:
|
|
211
|
+
totalWeight += 5;
|
|
212
|
+
break;
|
|
213
|
+
case 2:
|
|
214
|
+
totalWeight += 2;
|
|
215
|
+
break;
|
|
216
|
+
case 1:
|
|
217
|
+
totalWeight += 1;
|
|
218
|
+
break;
|
|
219
|
+
default:
|
|
220
|
+
totalWeight += 1;
|
|
207
221
|
}
|
|
208
222
|
}
|
|
209
223
|
// Score from 0-1, where 1 is perfect
|
|
@@ -218,7 +218,18 @@ function checkVariableNaming(
|
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
function isCommonAbbreviation(name: string): boolean {
|
|
221
|
-
const common = [
|
|
221
|
+
const common = [
|
|
222
|
+
'id',
|
|
223
|
+
'db',
|
|
224
|
+
'fs',
|
|
225
|
+
'os',
|
|
226
|
+
'ip',
|
|
227
|
+
'ui',
|
|
228
|
+
'ux',
|
|
229
|
+
'api',
|
|
230
|
+
'env',
|
|
231
|
+
'url',
|
|
232
|
+
];
|
|
222
233
|
return common.includes(name.toLowerCase());
|
|
223
234
|
}
|
|
224
235
|
|
|
@@ -228,7 +239,12 @@ function isCommonAbbreviation(name: string): boolean {
|
|
|
228
239
|
class ScopeTracker {
|
|
229
240
|
private variables: any[] = [];
|
|
230
241
|
|
|
231
|
-
declareVariable(
|
|
242
|
+
declareVariable(
|
|
243
|
+
name: string,
|
|
244
|
+
node: TSESTree.Node,
|
|
245
|
+
line: number,
|
|
246
|
+
options = {}
|
|
247
|
+
) {
|
|
232
248
|
this.variables.push({ name, node, line, options });
|
|
233
249
|
}
|
|
234
250
|
|