@aiready/consistency 0.16.2 → 0.16.5

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/dist/cli.js CHANGED
@@ -27,7 +27,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_commander = require("commander");
28
28
 
29
29
  // src/analyzer.ts
30
- var import_core4 = require("@aiready/core");
30
+ var import_core5 = require("@aiready/core");
31
+
32
+ // src/analyzers/naming-ast.ts
33
+ var import_core2 = require("@aiready/core");
31
34
 
32
35
  // src/utils/ast-parser.ts
33
36
  var import_typescript_estree = require("@typescript-eslint/typescript-estree");
@@ -79,214 +82,12 @@ function isLoopStatement(node) {
79
82
  "DoWhileStatement"
80
83
  ].includes(node.type);
81
84
  }
82
- function getFunctionName(node) {
83
- switch (node.type) {
84
- case "FunctionDeclaration":
85
- return node.id?.name ?? null;
86
- case "FunctionExpression":
87
- return node.id?.name ?? null;
88
- case "ArrowFunctionExpression":
89
- return null;
90
- // Arrow functions don't have names directly
91
- case "MethodDefinition":
92
- if (node.key.type === "Identifier") {
93
- return node.key.name;
94
- }
95
- return null;
96
- default:
97
- return null;
98
- }
99
- }
100
85
  function getLineNumber(node) {
101
86
  return node.loc?.start.line ?? 0;
102
87
  }
103
- function isCoverageContext(node, ancestors) {
104
- const coveragePatterns = /coverage|summary|metrics|pct|percent|statements|branches|functions|lines/i;
105
- if (node.type === "Identifier" && coveragePatterns.test(node.name)) {
106
- return true;
107
- }
108
- for (const ancestor of ancestors.slice(-3)) {
109
- if (ancestor.type === "MemberExpression") {
110
- const memberExpr = ancestor;
111
- if (memberExpr.object.type === "Identifier" && coveragePatterns.test(memberExpr.object.name)) {
112
- return true;
113
- }
114
- }
115
- if (ancestor.type === "ObjectPattern" || ancestor.type === "ObjectExpression") {
116
- const parent = ancestors[ancestors.indexOf(ancestor) - 1];
117
- if (parent?.type === "VariableDeclarator") {
118
- const varDecl = parent;
119
- if (varDecl.id.type === "Identifier" && coveragePatterns.test(varDecl.id.name)) {
120
- return true;
121
- }
122
- }
123
- }
124
- }
125
- return false;
126
- }
127
-
128
- // src/utils/scope-tracker.ts
129
- var ScopeTracker = class {
130
- constructor(rootNode) {
131
- this.allScopes = [];
132
- this.rootScope = {
133
- type: "global",
134
- node: rootNode,
135
- parent: null,
136
- children: [],
137
- variables: /* @__PURE__ */ new Map()
138
- };
139
- this.currentScope = this.rootScope;
140
- this.allScopes.push(this.rootScope);
141
- }
142
- /**
143
- * Enter a new scope
144
- */
145
- enterScope(type, node) {
146
- const newScope = {
147
- type,
148
- node,
149
- parent: this.currentScope,
150
- children: [],
151
- variables: /* @__PURE__ */ new Map()
152
- };
153
- this.currentScope.children.push(newScope);
154
- this.currentScope = newScope;
155
- this.allScopes.push(newScope);
156
- }
157
- /**
158
- * Exit current scope and return to parent
159
- */
160
- exitScope() {
161
- if (this.currentScope.parent) {
162
- this.currentScope = this.currentScope.parent;
163
- }
164
- }
165
- /**
166
- * Declare a variable in the current scope
167
- */
168
- declareVariable(name, node, line, options = {}) {
169
- const varInfo = {
170
- name,
171
- node,
172
- declarationLine: line,
173
- references: [],
174
- type: options.type,
175
- isParameter: options.isParameter ?? false,
176
- isDestructured: options.isDestructured ?? false,
177
- isLoopVariable: options.isLoopVariable ?? false
178
- };
179
- this.currentScope.variables.set(name, varInfo);
180
- }
181
- /**
182
- * Add a reference to a variable
183
- */
184
- addReference(name, node) {
185
- const varInfo = this.findVariable(name);
186
- if (varInfo) {
187
- varInfo.references.push(node);
188
- }
189
- }
190
- /**
191
- * Find a variable in current or parent scopes
192
- */
193
- findVariable(name) {
194
- let scope = this.currentScope;
195
- while (scope) {
196
- const varInfo = scope.variables.get(name);
197
- if (varInfo) {
198
- return varInfo;
199
- }
200
- scope = scope.parent;
201
- }
202
- return null;
203
- }
204
- /**
205
- * Get all variables in current scope (not including parent scopes)
206
- */
207
- getCurrentScopeVariables() {
208
- return Array.from(this.currentScope.variables.values());
209
- }
210
- /**
211
- * Get all variables across all scopes
212
- */
213
- getAllVariables() {
214
- const allVars = [];
215
- for (const scope of this.allScopes) {
216
- allVars.push(...Array.from(scope.variables.values()));
217
- }
218
- return allVars;
219
- }
220
- /**
221
- * Calculate actual usage count (references minus declaration)
222
- */
223
- getUsageCount(varInfo) {
224
- return varInfo.references.length;
225
- }
226
- /**
227
- * Check if a variable is short-lived (used within N lines)
228
- */
229
- isShortLived(varInfo, maxLines = 5) {
230
- if (varInfo.references.length === 0) {
231
- return false;
232
- }
233
- const declarationLine = varInfo.declarationLine;
234
- const maxUsageLine = Math.max(
235
- ...varInfo.references.map((ref) => ref.loc?.start.line ?? declarationLine)
236
- );
237
- return maxUsageLine - declarationLine <= maxLines;
238
- }
239
- /**
240
- * Check if a variable is used in a limited scope (e.g., only in one callback)
241
- */
242
- isLocallyScoped(varInfo) {
243
- if (varInfo.references.length === 0) return false;
244
- const lines = varInfo.references.map((ref) => ref.loc?.start.line ?? 0);
245
- const minLine = Math.min(...lines);
246
- const maxLine = Math.max(...lines);
247
- return maxLine - minLine <= 3;
248
- }
249
- /**
250
- * Get current scope type
251
- */
252
- getCurrentScopeType() {
253
- return this.currentScope.type;
254
- }
255
- /**
256
- * Check if currently in a loop scope
257
- */
258
- isInLoop() {
259
- let scope = this.currentScope;
260
- while (scope) {
261
- if (scope.type === "loop") {
262
- return true;
263
- }
264
- scope = scope.parent;
265
- }
266
- return false;
267
- }
268
- /**
269
- * Check if currently in a function scope
270
- */
271
- isInFunction() {
272
- let scope = this.currentScope;
273
- while (scope) {
274
- if (scope.type === "function") {
275
- return true;
276
- }
277
- scope = scope.parent;
278
- }
279
- return false;
280
- }
281
- /**
282
- * Get the root scope
283
- */
284
- getRootScope() {
285
- return this.rootScope;
286
- }
287
- };
288
88
 
289
89
  // src/utils/context-detector.ts
90
+ var import_core = require("@aiready/core");
290
91
  function detectFileType(filePath, ast) {
291
92
  void ast;
292
93
  const path = filePath.toLowerCase();
@@ -414,24 +215,34 @@ function buildCodeContext(filePath, ast) {
414
215
  };
415
216
  }
416
217
  function adjustSeverity(baseSeverity, context, issueType) {
218
+ const getEnum = (s) => {
219
+ if (s === import_core.Severity.Critical || s === "critical") return import_core.Severity.Critical;
220
+ if (s === import_core.Severity.Major || s === "major") return import_core.Severity.Major;
221
+ if (s === import_core.Severity.Minor || s === "minor") return import_core.Severity.Minor;
222
+ return import_core.Severity.Info;
223
+ };
224
+ let currentSev = getEnum(baseSeverity);
417
225
  if (context.isTestFile) {
418
- if (baseSeverity === "minor") return "info";
419
- if (baseSeverity === "major") return "minor";
226
+ if (currentSev === import_core.Severity.Minor) currentSev = import_core.Severity.Info;
227
+ if (currentSev === import_core.Severity.Major) currentSev = import_core.Severity.Minor;
420
228
  }
421
229
  if (context.isTypeDefinition) {
422
- if (baseSeverity === "minor") return "info";
230
+ if (currentSev === import_core.Severity.Minor) currentSev = import_core.Severity.Info;
423
231
  }
424
232
  if (context.codeLayer === "api") {
425
- if (baseSeverity === "info" && issueType === "unclear") return "minor";
426
- if (baseSeverity === "minor" && issueType === "unclear") return "major";
233
+ if (currentSev === import_core.Severity.Info && issueType === "unclear")
234
+ currentSev = import_core.Severity.Minor;
235
+ if (currentSev === import_core.Severity.Minor && issueType === "unclear")
236
+ currentSev = import_core.Severity.Major;
427
237
  }
428
238
  if (context.complexity > 10) {
429
- if (baseSeverity === "info") return "minor";
239
+ if (currentSev === import_core.Severity.Info) currentSev = import_core.Severity.Minor;
430
240
  }
431
241
  if (context.codeLayer === "utility") {
432
- if (baseSeverity === "minor" && issueType === "abbreviation") return "info";
242
+ if (currentSev === import_core.Severity.Minor && issueType === "abbreviation")
243
+ currentSev = import_core.Severity.Info;
433
244
  }
434
- return baseSeverity;
245
+ return currentSev;
435
246
  }
436
247
  function isAcceptableInContext(name, context, options) {
437
248
  if (options.isLoopVariable && ["i", "j", "k", "l", "n", "m"].includes(name)) {
@@ -458,638 +269,168 @@ function isAcceptableInContext(name, context, options) {
458
269
  return false;
459
270
  }
460
271
 
461
- // src/utils/config-loader.ts
462
- var import_core = require("@aiready/core");
463
- var import_path = require("path");
464
-
465
- // src/analyzers/naming-constants.ts
466
- var COMMON_SHORT_WORDS = /* @__PURE__ */ new Set([
467
- // Full English words (1-3 letters)
468
- "day",
469
- "key",
470
- "net",
471
- "to",
472
- "go",
473
- "for",
474
- "not",
475
- "new",
476
- "old",
477
- "top",
478
- "end",
479
- "run",
480
- "try",
481
- "use",
482
- "get",
483
- "set",
484
- "add",
485
- "put",
486
- "map",
487
- "log",
488
- "row",
489
- "col",
490
- "tab",
491
- "box",
492
- "div",
493
- "nav",
494
- "tag",
495
- "any",
496
- "all",
497
- "one",
498
- "two",
499
- "out",
500
- "off",
501
- "on",
502
- "yes",
503
- "no",
504
- "now",
505
- "max",
506
- "min",
507
- "sum",
508
- "avg",
509
- "ref",
510
- "src",
511
- "dst",
512
- "raw",
513
- "def",
514
- "sub",
515
- "pub",
516
- "pre",
517
- "mid",
518
- "alt",
519
- "opt",
520
- "tmp",
521
- "ext",
522
- "sep",
523
- // Prepositions and conjunctions
524
- "and",
525
- "from",
526
- "how",
527
- "pad",
528
- "bar",
529
- "non",
530
- // Additional full words commonly flagged
531
- "tax",
532
- "cat",
533
- "dog",
534
- "car",
535
- "bus",
536
- "web",
537
- "app",
538
- "war",
539
- "law",
540
- "pay",
541
- "buy",
542
- "win",
543
- "cut",
544
- "hit",
545
- "hot",
546
- "pop",
547
- "job",
548
- "age",
549
- "act",
550
- "let",
551
- "lot",
552
- "bad",
553
- "big",
554
- "far",
555
- "few",
556
- "own",
557
- "per",
558
- "red",
559
- "low",
560
- "see",
561
- "six",
562
- "ten",
563
- "way",
564
- "who",
565
- "why",
566
- "yet",
567
- "via",
568
- "due",
569
- "fee",
570
- "fun",
571
- "gas",
572
- "gay",
573
- "god",
574
- "gun",
575
- "guy",
576
- "ice",
577
- "ill",
578
- "kid",
579
- "mad",
580
- "man",
581
- "mix",
582
- "mom",
583
- "mrs",
584
- "nor",
585
- "odd",
586
- "oil",
587
- "pan",
588
- "pet",
589
- "pit",
590
- "pot",
591
- "pow",
592
- "pro",
593
- "raw",
594
- "rep",
595
- "rid",
596
- "sad",
597
- "sea",
598
- "sit",
599
- "sky",
600
- "son",
601
- "tea",
602
- "tie",
603
- "tip",
604
- "van",
605
- "war",
606
- "win",
607
- "won"
608
- ]);
609
- var ACCEPTABLE_ABBREVIATIONS = /* @__PURE__ */ new Set([
610
- // Standard identifiers
611
- "id",
612
- "uid",
613
- "gid",
614
- "pid",
615
- // Loop counters and iterators
616
- "i",
617
- "j",
618
- "k",
619
- "n",
620
- "m",
621
- // Web/Network
622
- "url",
623
- "uri",
624
- "api",
625
- "cdn",
626
- "dns",
627
- "ip",
628
- "tcp",
629
- "udp",
630
- "http",
631
- "ssl",
632
- "tls",
633
- "utm",
634
- "seo",
635
- "rss",
636
- "xhr",
637
- "ajax",
638
- "cors",
639
- "ws",
640
- "wss",
641
- // Data formats
642
- "json",
643
- "xml",
644
- "yaml",
645
- "csv",
646
- "html",
647
- "css",
648
- "svg",
649
- "pdf",
650
- // File types & extensions
651
- "img",
652
- "txt",
653
- "doc",
654
- "docx",
655
- "xlsx",
656
- "ppt",
657
- "md",
658
- "rst",
659
- "jpg",
660
- "png",
661
- "gif",
662
- // Databases
663
- "db",
664
- "sql",
665
- "orm",
666
- "dao",
667
- "dto",
668
- "ddb",
669
- "rds",
670
- "nosql",
671
- // File system
672
- "fs",
673
- "dir",
674
- "tmp",
675
- "src",
676
- "dst",
677
- "bin",
678
- "lib",
679
- "pkg",
680
- // Operating system
681
- "os",
682
- "env",
683
- "arg",
684
- "cli",
685
- "cmd",
686
- "exe",
687
- "cwd",
688
- "pwd",
689
- // UI/UX
690
- "ui",
691
- "ux",
692
- "gui",
693
- "dom",
694
- "ref",
695
- // Request/Response
696
- "req",
697
- "res",
698
- "ctx",
699
- "err",
700
- "msg",
701
- "auth",
702
- // Mathematics/Computing
703
- "max",
704
- "min",
705
- "avg",
706
- "sum",
707
- "abs",
708
- "cos",
709
- "sin",
710
- "tan",
711
- "log",
712
- "exp",
713
- "pow",
714
- "sqrt",
715
- "std",
716
- "var",
717
- "int",
718
- "num",
719
- "idx",
720
- // Time
721
- "now",
722
- "utc",
723
- "tz",
724
- "ms",
725
- "sec",
726
- "hr",
727
- "min",
728
- "yr",
729
- "mo",
730
- // Common patterns
731
- "app",
732
- "cfg",
733
- "config",
734
- "init",
735
- "len",
736
- "val",
737
- "str",
738
- "obj",
739
- "arr",
740
- "gen",
741
- "def",
742
- "raw",
743
- "new",
744
- "old",
745
- "pre",
746
- "post",
747
- "sub",
748
- "pub",
749
- // Programming/Framework specific
750
- "ts",
751
- "js",
752
- "jsx",
753
- "tsx",
754
- "py",
755
- "rb",
756
- "vue",
757
- "re",
758
- "fn",
759
- "fns",
760
- "mod",
761
- "opts",
762
- "dev",
763
- // Cloud/Infrastructure
764
- "s3",
765
- "ec2",
766
- "sqs",
767
- "sns",
768
- "vpc",
769
- "ami",
770
- "iam",
771
- "acl",
772
- "elb",
773
- "alb",
774
- "nlb",
775
- "aws",
776
- "ses",
777
- "gst",
778
- "cdk",
779
- "btn",
780
- "buf",
781
- "agg",
782
- "ocr",
783
- "ai",
784
- "cf",
785
- "cfn",
786
- "ga",
787
- // Metrics/Performance
788
- "fcp",
789
- "lcp",
790
- "cls",
791
- "ttfb",
792
- "tti",
793
- "fid",
794
- "fps",
795
- "qps",
796
- "rps",
797
- "tps",
798
- "wpm",
799
- // Testing & i18n
800
- "po",
801
- "e2e",
802
- "a11y",
803
- "i18n",
804
- "l10n",
805
- "spy",
806
- // Domain-specific abbreviations (context-aware)
807
- "sk",
808
- "fy",
809
- "faq",
810
- "og",
811
- "seo",
812
- "cta",
813
- "roi",
814
- "kpi",
815
- "ttl",
816
- "pct",
817
- // Technical abbreviations
818
- "mac",
819
- "hex",
820
- "esm",
821
- "git",
822
- "rec",
823
- "loc",
824
- "dup",
825
- // Boolean helpers (these are intentional short names)
826
- "is",
827
- "has",
828
- "can",
829
- "did",
830
- "was",
831
- "are",
832
- // Date/Time context (when in date contexts)
833
- "d",
834
- "t",
835
- "dt",
836
- // Coverage metrics (industry standard: statements/branches/functions/lines)
837
- "s",
838
- "b",
839
- "f",
840
- "l",
841
- // Common media/content abbreviations
842
- "vid",
843
- "pic",
844
- "img",
845
- "doc",
846
- "msg"
847
- ]);
848
-
849
- // src/utils/config-loader.ts
850
- async function loadNamingConfig(files) {
851
- const rootDir = files.length > 0 ? (0, import_path.dirname)(files[0]) : process.cwd();
852
- const config = await (0, import_core.loadConfig)(rootDir);
853
- const consistencyConfig = config?.tools?.["consistency"];
854
- const customAbbreviations = new Set(
855
- consistencyConfig?.acceptedAbbreviations || []
856
- );
857
- const customShortWords = new Set(consistencyConfig?.shortWords || []);
858
- const disabledChecks = new Set(consistencyConfig?.disableChecks || []);
859
- const allAbbreviations = /* @__PURE__ */ new Set([
860
- ...ACCEPTABLE_ABBREVIATIONS,
861
- ...customAbbreviations
862
- ]);
863
- const allShortWords = /* @__PURE__ */ new Set([...COMMON_SHORT_WORDS, ...customShortWords]);
864
- return {
865
- customAbbreviations,
866
- customShortWords,
867
- disabledChecks,
868
- allAbbreviations,
869
- allShortWords
870
- };
871
- }
872
-
873
272
  // src/analyzers/naming-ast.ts
874
- async function analyzeNamingAST(files) {
875
- const issues = [];
876
- const { allAbbreviations, allShortWords, disabledChecks } = await loadNamingConfig(files);
877
- const supportedFiles = files.filter(
878
- (file) => /\.(js|jsx|ts|tsx)$/i.test(file)
879
- );
880
- for (const file of supportedFiles) {
273
+ async function analyzeNamingAST(filePaths) {
274
+ const allIssues = [];
275
+ for (const filePath of filePaths) {
881
276
  try {
882
- const ast = parseFile(file);
277
+ const ast = parseFile(filePath);
883
278
  if (!ast) continue;
884
- const fileIssues = analyzeFileNamingAST(
885
- file,
886
- ast,
887
- allAbbreviations,
888
- allShortWords,
889
- disabledChecks
890
- );
891
- issues.push(...fileIssues);
892
- } catch (error) {
893
- console.warn(`Skipping ${file} due to parse error:`, error);
279
+ const context = buildCodeContext(filePath, ast);
280
+ const issues = analyzeIdentifiers(ast, filePath, context);
281
+ allIssues.push(...issues);
282
+ } catch (err) {
283
+ void err;
894
284
  }
895
285
  }
896
- return issues;
286
+ return allIssues;
897
287
  }
898
- function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabledChecks) {
288
+ function analyzeIdentifiers(ast, filePath, context) {
899
289
  const issues = [];
900
- const scopeTracker = new ScopeTracker(ast);
901
- const context = buildCodeContext(file, ast);
902
- const ancestors = [];
290
+ const scopeTracker = new ScopeTracker();
903
291
  traverseAST(ast, {
904
- enter: (node, parent) => {
905
- ancestors.push(node);
906
- if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
907
- scopeTracker.enterScope("function", node);
908
- if ("params" in node) {
909
- for (const param of node.params) {
910
- if (param.type === "Identifier") {
911
- scopeTracker.declareVariable(
912
- param.name,
913
- param,
914
- getLineNumber(param),
915
- {
916
- isParameter: true
917
- }
918
- );
919
- } else if (param.type === "ObjectPattern" || param.type === "ArrayPattern") {
920
- extractIdentifiersFromPattern(param, scopeTracker, true);
921
- }
922
- }
923
- }
924
- } else if (node.type === "BlockStatement") {
925
- scopeTracker.enterScope("block", node);
926
- } else if (isLoopStatement(node)) {
927
- scopeTracker.enterScope("loop", node);
928
- } else if (node.type === "ClassDeclaration") {
929
- scopeTracker.enterScope("class", node);
930
- }
931
- if (node.type === "VariableDeclarator") {
932
- if (node.id.type === "Identifier") {
933
- void isCoverageContext(node, ancestors);
934
- scopeTracker.declareVariable(
935
- node.id.name,
936
- node.id,
937
- getLineNumber(node.id),
938
- {
939
- type: "typeAnnotation" in node.id ? node.id.typeAnnotation : null,
940
- isDestructured: false,
941
- isLoopVariable: scopeTracker.getCurrentScopeType() === "loop"
942
- }
943
- );
944
- } else if (node.id.type === "ObjectPattern" || node.id.type === "ArrayPattern") {
945
- extractIdentifiersFromPattern(
946
- node.id,
947
- scopeTracker,
948
- false,
949
- ancestors
950
- );
951
- }
292
+ enter: (node) => {
293
+ if (node.type === "VariableDeclarator" && node.id.type === "Identifier") {
294
+ const isParameter = false;
295
+ const isLoopVariable = isLoopStatement(node.parent?.parent);
296
+ scopeTracker.declareVariable(
297
+ node.id.name,
298
+ node.id,
299
+ getLineNumber(node.id),
300
+ { isParameter, isLoopVariable }
301
+ );
952
302
  }
953
- if (node.type === "Identifier" && parent) {
954
- if (parent.type !== "VariableDeclarator" || parent.id !== node) {
955
- if (parent.type !== "FunctionDeclaration" || parent.id !== node) {
956
- scopeTracker.addReference(node.name, node);
303
+ if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
304
+ node.params.forEach((param) => {
305
+ if (param.type === "Identifier") {
306
+ scopeTracker.declareVariable(
307
+ param.name,
308
+ param,
309
+ getLineNumber(param),
310
+ { isParameter: true }
311
+ );
312
+ } else if (param.type === "ObjectPattern") {
313
+ extractDestructuredIdentifiers(param, true, scopeTracker);
957
314
  }
958
- }
959
- }
960
- },
961
- leave: (node) => {
962
- ancestors.pop();
963
- if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "BlockStatement" || isLoopStatement(node) || node.type === "ClassDeclaration") {
964
- scopeTracker.exitScope();
965
- }
966
- }
967
- });
968
- const allVariables = scopeTracker.getAllVariables();
969
- for (const varInfo of allVariables) {
970
- const name = varInfo.name;
971
- const line = varInfo.declarationLine;
972
- if (disabledChecks.has("single-letter") && name.length === 1) continue;
973
- if (disabledChecks.has("abbreviation") && name.length <= 3) continue;
974
- const isInCoverage = ["s", "b", "f", "l"].includes(name) && varInfo.isDestructured;
975
- if (isInCoverage) continue;
976
- const functionComplexity = varInfo.node.type === "Identifier" && "parent" in varInfo.node ? calculateComplexity(varInfo.node) : context.complexity;
977
- if (isAcceptableInContext(name, context, {
978
- isLoopVariable: varInfo.isLoopVariable || allAbbreviations.has(name),
979
- isParameter: varInfo.isParameter,
980
- isDestructured: varInfo.isDestructured,
981
- complexity: functionComplexity
982
- })) {
983
- continue;
984
- }
985
- if (name.length === 1 && !allAbbreviations.has(name) && !allShortWords.has(name)) {
986
- const isShortLived = scopeTracker.isShortLived(varInfo, 5);
987
- if (!isShortLived) {
988
- issues.push({
989
- file,
990
- line,
991
- type: "poor-naming",
992
- identifier: name,
993
- severity: adjustSeverity("minor", context, "poor-naming"),
994
- suggestion: `Use descriptive variable name instead of single letter '${name}'`
995
315
  });
996
316
  }
997
- continue;
998
- }
999
- if (name.length >= 2 && name.length <= 3) {
1000
- if (!allShortWords.has(name.toLowerCase()) && !allAbbreviations.has(name.toLowerCase())) {
1001
- const isShortLived = scopeTracker.isShortLived(varInfo, 5);
1002
- if (!isShortLived) {
1003
- issues.push({
1004
- file,
1005
- line,
1006
- type: "abbreviation",
1007
- identifier: name,
1008
- severity: adjustSeverity("info", context, "abbreviation"),
1009
- suggestion: `Consider using full word instead of abbreviation '${name}'`
1010
- });
1011
- }
1012
- }
1013
- continue;
1014
- }
1015
- if (!disabledChecks.has("convention-mix") && file.match(/\.(ts|tsx|js|jsx)$/)) {
1016
- if (name.includes("_") && !name.startsWith("_") && name.toLowerCase() === name) {
1017
- const camelCase = name.replace(
1018
- /_([a-z])/g,
1019
- (_, letter) => letter.toUpperCase()
317
+ if ((node.type === "ClassDeclaration" || node.type === "TSInterfaceDeclaration" || node.type === "TSTypeAliasDeclaration") && node.id) {
318
+ checkNamingConvention(
319
+ node.id.name,
320
+ "PascalCase",
321
+ node.id,
322
+ filePath,
323
+ issues,
324
+ context
1020
325
  );
1021
- issues.push({
1022
- file,
1023
- line,
1024
- type: "convention-mix",
1025
- identifier: name,
1026
- severity: adjustSeverity("minor", context, "convention-mix"),
1027
- suggestion: `Use camelCase '${camelCase}' instead of snake_case in TypeScript/JavaScript`
1028
- });
1029
326
  }
1030
327
  }
328
+ });
329
+ for (const varInfo of scopeTracker.getVariables()) {
330
+ checkVariableNaming(varInfo, filePath, issues, context);
1031
331
  }
1032
- if (!disabledChecks.has("unclear")) {
1033
- traverseAST(ast, {
1034
- enter: (node) => {
1035
- if (node.type === "FunctionDeclaration" || node.type === "MethodDefinition") {
1036
- const name = getFunctionName(node);
1037
- if (!name) return;
1038
- const line = getLineNumber(node);
1039
- if (["main", "init", "setup", "bootstrap"].includes(name)) return;
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
- );
1046
- const isEventHandler = name.match(/^on[A-Z]/);
1047
- const isDescriptiveLong = name.length > 15;
1048
- const isReactHook = name.match(/^use[A-Z]/);
1049
- const isHelperPattern = name.match(/^(to|from|with|without|for|as|into)\w+/) || name.match(/^\w+(To|From|With|Without|For|As|Into)\w*$/);
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
- );
1071
- const capitalCount = (name.match(/[A-Z]/g) || []).length;
1072
- const isCompoundWord = capitalCount >= 3;
1073
- if (!hasActionVerb && !isFactoryPattern && !isEventHandler && !isDescriptiveLong && !isReactHook && !isHelperPattern && !isUtilityName && !isDescriptivePattern && !isCompoundWord && !isLanguageKeyword && !isFrameworkPattern) {
1074
- issues.push({
1075
- file,
1076
- line,
1077
- type: "unclear",
1078
- identifier: name,
1079
- severity: adjustSeverity("info", context, "unclear"),
1080
- suggestion: `Function '${name}' should start with an action verb (get, set, create, etc.)`
1081
- });
1082
- }
1083
- }
1084
- }
332
+ return issues;
333
+ }
334
+ function checkNamingConvention(name, convention, node, file, issues, context) {
335
+ let isValid = true;
336
+ if (convention === "PascalCase") {
337
+ isValid = /^[A-Z][a-zA-Z0-9]*$/.test(name);
338
+ } else if (convention === "camelCase") {
339
+ isValid = /^[a-z][a-zA-Z0-9]*$/.test(name);
340
+ } else if (convention === "UPPER_CASE") {
341
+ isValid = /^[A-Z][A-Z0-9_]*$/.test(name);
342
+ }
343
+ if (!isValid) {
344
+ const severity = adjustSeverity(import_core2.Severity.Info, context, "convention-mix");
345
+ issues.push({
346
+ file,
347
+ line: getLineNumber(node),
348
+ type: "convention-mix",
349
+ identifier: name,
350
+ severity,
351
+ suggestion: `Follow ${convention} for this identifier`
1085
352
  });
1086
353
  }
1087
- return issues;
1088
354
  }
1089
- function extractIdentifiersFromPattern(pattern, scopeTracker, isParameter, ancestors) {
1090
- void ancestors;
1091
- if (pattern.type === "ObjectPattern") {
1092
- for (const prop of pattern.properties) {
355
+ function checkVariableNaming(varInfo, file, issues, context) {
356
+ const { name, node, line, options } = varInfo;
357
+ if (isAcceptableInContext(name, context, options)) {
358
+ return;
359
+ }
360
+ if (name.length === 1 && !options.isLoopVariable) {
361
+ const severity = adjustSeverity(import_core2.Severity.Minor, context, "poor-naming");
362
+ issues.push({
363
+ file,
364
+ line,
365
+ type: "poor-naming",
366
+ identifier: name,
367
+ severity,
368
+ suggestion: "Use a more descriptive name than a single letter"
369
+ });
370
+ }
371
+ const vagueNames = [
372
+ "data",
373
+ "info",
374
+ "item",
375
+ "obj",
376
+ "val",
377
+ "tmp",
378
+ "temp",
379
+ "thing",
380
+ "stuff"
381
+ ];
382
+ if (vagueNames.includes(name.toLowerCase())) {
383
+ const severity = adjustSeverity(import_core2.Severity.Minor, context, "poor-naming");
384
+ issues.push({
385
+ file,
386
+ line,
387
+ type: "poor-naming",
388
+ identifier: name,
389
+ severity,
390
+ suggestion: `Avoid vague names like '${name}'. What does this data represent?`
391
+ });
392
+ }
393
+ if (name.length > 1 && name.length <= 3 && !options.isLoopVariable && !isCommonAbbreviation(name)) {
394
+ const severity = adjustSeverity(import_core2.Severity.Info, context, "abbreviation");
395
+ issues.push({
396
+ file,
397
+ line,
398
+ type: "abbreviation",
399
+ identifier: name,
400
+ severity,
401
+ suggestion: "Avoid non-standard abbreviations"
402
+ });
403
+ }
404
+ }
405
+ function isCommonAbbreviation(name) {
406
+ const common = [
407
+ "id",
408
+ "db",
409
+ "fs",
410
+ "os",
411
+ "ip",
412
+ "ui",
413
+ "ux",
414
+ "api",
415
+ "env",
416
+ "url"
417
+ ];
418
+ return common.includes(name.toLowerCase());
419
+ }
420
+ var ScopeTracker = class {
421
+ constructor() {
422
+ this.variables = [];
423
+ }
424
+ declareVariable(name, node, line, options = {}) {
425
+ this.variables.push({ name, node, line, options });
426
+ }
427
+ getVariables() {
428
+ return this.variables;
429
+ }
430
+ };
431
+ function extractDestructuredIdentifiers(node, isParameter, scopeTracker) {
432
+ if (node.type === "ObjectPattern") {
433
+ node.properties.forEach((prop) => {
1093
434
  if (prop.type === "Property" && prop.value.type === "Identifier") {
1094
435
  scopeTracker.declareVariable(
1095
436
  prop.value.name,
@@ -1100,21 +441,11 @@ function extractIdentifiersFromPattern(pattern, scopeTracker, isParameter, ances
1100
441
  isDestructured: true
1101
442
  }
1102
443
  );
1103
- } else if (prop.type === "RestElement" && prop.argument.type === "Identifier") {
1104
- scopeTracker.declareVariable(
1105
- prop.argument.name,
1106
- prop.argument,
1107
- getLineNumber(prop.argument),
1108
- {
1109
- isParameter,
1110
- isDestructured: true
1111
- }
1112
- );
1113
444
  }
1114
- }
1115
- } else if (pattern.type === "ArrayPattern") {
1116
- for (const element of pattern.elements) {
1117
- if (element && element.type === "Identifier") {
445
+ });
446
+ } else if (node.type === "ArrayPattern") {
447
+ for (const element of node.elements) {
448
+ if (element?.type === "Identifier") {
1118
449
  scopeTracker.declareVariable(
1119
450
  element.name,
1120
451
  element,
@@ -1130,10 +461,10 @@ function extractIdentifiersFromPattern(pattern, scopeTracker, isParameter, ances
1130
461
  }
1131
462
 
1132
463
  // src/analyzers/naming-python.ts
1133
- var import_core2 = require("@aiready/core");
464
+ var import_core3 = require("@aiready/core");
1134
465
  async function analyzePythonNaming(files) {
1135
466
  const issues = [];
1136
- const parser = (0, import_core2.getParser)("dummy.py");
467
+ const parser = (0, import_core3.getParser)("dummy.py");
1137
468
  if (!parser) {
1138
469
  console.warn("Python parser not available");
1139
470
  return issues;
@@ -1177,7 +508,7 @@ async function analyzePythonNaming(files) {
1177
508
  return issues;
1178
509
  }
1179
510
  function checkPythonNaming(identifier, type, file, line) {
1180
- const parser = (0, import_core2.getParser)("dummy.py");
511
+ const parser = (0, import_core3.getParser)("dummy.py");
1181
512
  const conventions = parser?.getNamingConventions();
1182
513
  if (!conventions) return null;
1183
514
  if (conventions.exceptions?.includes(identifier)) {
@@ -1252,162 +583,63 @@ function toPascalCase(str) {
1252
583
  }
1253
584
 
1254
585
  // src/analyzers/patterns.ts
1255
- var import_core3 = require("@aiready/core");
1256
- async function analyzePatterns(files) {
586
+ var import_fs2 = require("fs");
587
+ var import_core4 = require("@aiready/core");
588
+ async function analyzePatterns(filePaths) {
1257
589
  const issues = [];
1258
- const errorHandlingIssues = await analyzeErrorHandling(files);
1259
- issues.push(...errorHandlingIssues);
1260
- const asyncIssues = await analyzeAsyncPatterns(files);
1261
- issues.push(...asyncIssues);
1262
- const importIssues = await analyzeImportStyles(files);
1263
- issues.push(...importIssues);
1264
- return issues;
1265
- }
1266
- async function analyzeErrorHandling(files) {
1267
- const patterns = {
1268
- tryCatch: [],
1269
- throwsError: [],
1270
- returnsNull: [],
1271
- returnsError: []
590
+ const contents = /* @__PURE__ */ new Map();
591
+ const tryCatchPattern = /try\s*\{/g;
592
+ const catchPattern = /catch\s*\(\s*(\w+)\s*\)/g;
593
+ const styleStats = {
594
+ tryCatch: 0,
595
+ thenCatch: 0,
596
+ asyncAwait: 0,
597
+ commonJs: 0,
598
+ esm: 0
1272
599
  };
1273
- for (const file of files) {
1274
- const content = await (0, import_core3.readFileContent)(file);
1275
- if (content.includes("try {") || content.includes("} catch")) {
1276
- patterns.tryCatch.push(file);
1277
- }
1278
- if (content.match(/throw new \w*Error/)) {
1279
- patterns.throwsError.push(file);
1280
- }
1281
- if (content.match(/return null/)) {
1282
- patterns.returnsNull.push(file);
1283
- }
1284
- if (content.match(/return \{ error:/)) {
1285
- patterns.returnsError.push(file);
600
+ for (const filePath of filePaths) {
601
+ try {
602
+ const content = (0, import_fs2.readFileSync)(filePath, "utf-8");
603
+ contents.set(filePath, content);
604
+ if (content.match(tryCatchPattern)) styleStats.tryCatch++;
605
+ if (content.match(/\.catch\s*\(/)) styleStats.thenCatch++;
606
+ if (content.match(/\bawait\b/)) styleStats.asyncAwait++;
607
+ if (content.match(/\brequire\s*\(/)) styleStats.commonJs++;
608
+ if (content.match(/\bimport\b.*\bfrom\b/)) styleStats.esm++;
609
+ } catch (err) {
610
+ void err;
1286
611
  }
1287
612
  }
1288
- const issues = [];
1289
- const strategiesUsed = Object.values(patterns).filter(
1290
- (p) => p.length > 0
1291
- ).length;
1292
- if (strategiesUsed > 2) {
613
+ const totalFiles = filePaths.length;
614
+ if (styleStats.tryCatch > 0 && styleStats.thenCatch > 0) {
615
+ const dominant = styleStats.tryCatch >= styleStats.thenCatch ? "try-catch" : ".catch()";
616
+ const minority = dominant === "try-catch" ? ".catch()" : "try-catch";
1293
617
  issues.push({
1294
- files: [
1295
- .../* @__PURE__ */ new Set([
1296
- ...patterns.tryCatch,
1297
- ...patterns.throwsError,
1298
- ...patterns.returnsNull,
1299
- ...patterns.returnsError
1300
- ])
1301
- ],
618
+ files: filePaths.filter((f) => {
619
+ const c = contents.get(f) || "";
620
+ return minority === "try-catch" ? c.match(tryCatchPattern) : c.match(/\.catch\s*\(/);
621
+ }),
1302
622
  type: "error-handling",
1303
- description: "Inconsistent error handling strategies across codebase",
1304
- examples: [
1305
- patterns.tryCatch.length > 0 ? `Try-catch used in ${patterns.tryCatch.length} files` : "",
1306
- patterns.throwsError.length > 0 ? `Throws errors in ${patterns.throwsError.length} files` : "",
1307
- patterns.returnsNull.length > 0 ? `Returns null in ${patterns.returnsNull.length} files` : "",
1308
- patterns.returnsError.length > 0 ? `Returns error objects in ${patterns.returnsError.length} files` : ""
1309
- ].filter((e) => e),
1310
- severity: "major"
623
+ description: `Mixed error handling styles: codebase primarily uses ${dominant}, but found ${minority} in some files.`,
624
+ examples: [dominant, minority],
625
+ severity: import_core4.Severity.Minor
1311
626
  });
1312
627
  }
1313
- return issues;
1314
- }
1315
- async function analyzeAsyncPatterns(files) {
1316
- const patterns = {
1317
- asyncAwait: [],
1318
- promises: [],
1319
- callbacks: []
1320
- };
1321
- for (const file of files) {
1322
- const content = await (0, import_core3.readFileContent)(file);
1323
- if (content.match(/async\s+(function|\(|[a-zA-Z])/)) {
1324
- patterns.asyncAwait.push(file);
1325
- }
1326
- if (content.match(/\.then\(/) || content.match(/\.catch\(/)) {
1327
- patterns.promises.push(file);
1328
- }
1329
- if (content.match(/callback\s*\(/) || content.match(/\(\s*err\s*,/)) {
1330
- patterns.callbacks.push(file);
1331
- }
1332
- }
1333
- const issues = [];
1334
- if (patterns.callbacks.length > 0 && patterns.asyncAwait.length > 0) {
1335
- issues.push({
1336
- files: [...patterns.callbacks, ...patterns.asyncAwait],
1337
- type: "async-style",
1338
- description: "Mixed async patterns: callbacks and async/await",
1339
- examples: [
1340
- `Callbacks found in: ${patterns.callbacks.slice(0, 3).join(", ")}`,
1341
- `Async/await used in: ${patterns.asyncAwait.slice(0, 3).join(", ")}`
1342
- ],
1343
- severity: "minor"
1344
- });
1345
- }
1346
- if (patterns.promises.length > patterns.asyncAwait.length * 0.3 && patterns.asyncAwait.length > 0) {
628
+ if (styleStats.commonJs > 0 && styleStats.esm > 0) {
629
+ const minority = styleStats.esm >= styleStats.commonJs ? "CommonJS (require)" : "ESM (import)";
1347
630
  issues.push({
1348
- files: patterns.promises,
1349
- type: "async-style",
1350
- description: "Consider using async/await instead of promise chains for consistency",
1351
- examples: patterns.promises.slice(0, 5),
1352
- severity: "info"
1353
- });
1354
- }
1355
- return issues;
1356
- }
1357
- async function analyzeImportStyles(files) {
1358
- const patterns = {
1359
- esModules: [],
1360
- commonJS: [],
1361
- mixed: []
1362
- };
1363
- for (const file of files) {
1364
- const content = await (0, import_core3.readFileContent)(file);
1365
- const hasESM = content.match(/^import\s+/m);
1366
- const hasCJS = hasActualRequireCalls(content);
1367
- if (hasESM && hasCJS) {
1368
- patterns.mixed.push(file);
1369
- } else if (hasESM) {
1370
- patterns.esModules.push(file);
1371
- } else if (hasCJS) {
1372
- patterns.commonJS.push(file);
1373
- }
1374
- }
1375
- const issues = [];
1376
- if (patterns.mixed.length > 0) {
1377
- issues.push({
1378
- files: patterns.mixed,
631
+ files: filePaths.filter((f) => {
632
+ const c = contents.get(f) || "";
633
+ return minority === "CommonJS (require)" ? c.match(/\brequire\s*\(/) : c.match(/\bimport\b/);
634
+ }),
1379
635
  type: "import-style",
1380
- description: "Mixed ES modules and CommonJS imports in same files",
1381
- examples: patterns.mixed.slice(0, 5),
1382
- severity: "major"
636
+ description: `Mixed module systems: found both ESM and CommonJS.`,
637
+ examples: ['import X from "y"', 'const X = require("y")'],
638
+ severity: import_core4.Severity.Major
1383
639
  });
1384
640
  }
1385
- if (patterns.esModules.length > 0 && patterns.commonJS.length > 0) {
1386
- const ratio = patterns.commonJS.length / (patterns.esModules.length + patterns.commonJS.length);
1387
- if (ratio > 0.2 && ratio < 0.8) {
1388
- issues.push({
1389
- files: [...patterns.esModules, ...patterns.commonJS],
1390
- type: "import-style",
1391
- description: "Inconsistent import styles across project",
1392
- examples: [
1393
- `ES modules: ${patterns.esModules.length} files`,
1394
- `CommonJS: ${patterns.commonJS.length} files`
1395
- ],
1396
- severity: "minor"
1397
- });
1398
- }
1399
- }
1400
641
  return issues;
1401
642
  }
1402
- function hasActualRequireCalls(content) {
1403
- let cleaned = content.replace(/\/\/.*$/gm, "");
1404
- cleaned = cleaned.replace(/\/\*[\s\S]*?\*\//g, "");
1405
- cleaned = cleaned.replace(/"[^"\n]*"/g, '""');
1406
- cleaned = cleaned.replace(/'[^'\n]*'/g, "''");
1407
- cleaned = cleaned.replace(/`[^`]*`/g, "``");
1408
- cleaned = cleaned.replace(/\/[^/\n]*require[^/\n]*\/[gimsuvy]*/g, "");
1409
- return /require\s*\(/.test(cleaned);
1410
- }
1411
643
 
1412
644
  // src/analyzer.ts
1413
645
  async function analyzeConsistency(options) {
@@ -1416,11 +648,11 @@ async function analyzeConsistency(options) {
1416
648
  checkPatterns = true,
1417
649
  checkArchitecture = false,
1418
650
  // Not implemented yet
1419
- minSeverity = "info",
651
+ minSeverity = import_core5.Severity.Info,
1420
652
  ...scanOptions
1421
653
  } = options;
1422
654
  void checkArchitecture;
1423
- const filePaths = await (0, import_core4.scanFiles)(scanOptions);
655
+ const filePaths = await (0, import_core5.scanFiles)(scanOptions);
1424
656
  const tsJsFiles = filePaths.filter((f) => /\.(ts|tsx|js|jsx)$/i.test(f));
1425
657
  const pythonFiles = filePaths.filter((f) => /\.py$/i.test(f));
1426
658
  let namingIssues = [];
@@ -1437,9 +669,9 @@ async function analyzeConsistency(options) {
1437
669
  continue;
1438
670
  }
1439
671
  const consistencyIssue = {
1440
- type: issue.type === "convention-mix" ? "naming-inconsistency" : "naming-quality",
672
+ type: issue.type === "convention-mix" ? import_core5.IssueType.NamingInconsistency : import_core5.IssueType.NamingQuality,
1441
673
  category: "naming",
1442
- severity: issue.severity,
674
+ severity: getSeverityEnum(issue.severity),
1443
675
  message: `${issue.type}: ${issue.identifier}`,
1444
676
  location: {
1445
677
  file: issue.file,
@@ -1458,9 +690,9 @@ async function analyzeConsistency(options) {
1458
690
  continue;
1459
691
  }
1460
692
  const consistencyIssue = {
1461
- type: "pattern-inconsistency",
693
+ type: import_core5.IssueType.PatternInconsistency,
1462
694
  category: "patterns",
1463
- severity: issue.severity,
695
+ severity: getSeverityEnum(issue.severity),
1464
696
  message: issue.description,
1465
697
  location: {
1466
698
  file: issue.files[0] || "multiple files",
@@ -1487,16 +719,17 @@ async function analyzeConsistency(options) {
1487
719
  });
1488
720
  }
1489
721
  results.sort((fileResultA, fileResultB) => {
1490
- const severityOrder = { critical: 0, major: 1, minor: 2, info: 3 };
1491
722
  const maxSeverityA = Math.min(
1492
- ...fileResultA.issues.map(
1493
- (i) => severityOrder[i.severity]
1494
- )
723
+ ...fileResultA.issues.map((i) => {
724
+ const val = getSeverityLevel(i.severity);
725
+ return val === 4 ? 0 : val === 3 ? 1 : val === 2 ? 2 : 3;
726
+ })
1495
727
  );
1496
728
  const maxSeverityB = Math.min(
1497
- ...fileResultB.issues.map(
1498
- (i) => severityOrder[i.severity]
1499
- )
729
+ ...fileResultB.issues.map((i) => {
730
+ const val = getSeverityLevel(i.severity);
731
+ return val === 4 ? 0 : val === 3 ? 1 : val === 2 ? 2 : 3;
732
+ })
1500
733
  );
1501
734
  if (maxSeverityA !== maxSeverityB) {
1502
735
  return maxSeverityA - maxSeverityB;
@@ -1522,16 +755,52 @@ async function analyzeConsistency(options) {
1522
755
  recommendations
1523
756
  };
1524
757
  }
758
+ function getSeverityLevel(s) {
759
+ if (s === import_core5.Severity.Critical || s === "critical") return 4;
760
+ if (s === import_core5.Severity.Major || s === "major") return 3;
761
+ if (s === import_core5.Severity.Minor || s === "minor") return 2;
762
+ if (s === import_core5.Severity.Info || s === "info") return 1;
763
+ return 0;
764
+ }
765
+ function getSeverityEnum(s) {
766
+ const val = getSeverityLevel(s);
767
+ switch (val) {
768
+ case 4:
769
+ return import_core5.Severity.Critical;
770
+ case 3:
771
+ return import_core5.Severity.Major;
772
+ case 2:
773
+ return import_core5.Severity.Minor;
774
+ case 1:
775
+ return import_core5.Severity.Info;
776
+ default:
777
+ return import_core5.Severity.Info;
778
+ }
779
+ }
1525
780
  function shouldIncludeSeverity(severity, minSeverity) {
1526
- const severityLevels = { info: 0, minor: 1, major: 2, critical: 3 };
1527
- return severityLevels[severity] >= severityLevels[minSeverity];
781
+ return getSeverityLevel(severity) >= getSeverityLevel(minSeverity);
1528
782
  }
1529
783
  function calculateConsistencyScore(issues) {
1530
- const weights = { critical: 10, major: 5, minor: 2, info: 1 };
1531
- const totalWeight = issues.reduce(
1532
- (sum, issue) => sum + weights[issue.severity],
1533
- 0
1534
- );
784
+ let totalWeight = 0;
785
+ for (const issue of issues) {
786
+ const val = getSeverityLevel(issue.severity);
787
+ switch (val) {
788
+ case 4:
789
+ totalWeight += 10;
790
+ break;
791
+ case 3:
792
+ totalWeight += 5;
793
+ break;
794
+ case 2:
795
+ totalWeight += 2;
796
+ break;
797
+ case 1:
798
+ totalWeight += 1;
799
+ break;
800
+ default:
801
+ totalWeight += 1;
802
+ }
803
+ }
1535
804
  return Math.max(0, 1 - totalWeight / 100);
1536
805
  }
1537
806
  function generateRecommendations(namingIssues, patternIssues) {
@@ -1586,9 +855,9 @@ function generateRecommendations(namingIssues, patternIssues) {
1586
855
 
1587
856
  // src/cli.ts
1588
857
  var import_chalk = __toESM(require("chalk"));
1589
- var import_fs2 = require("fs");
1590
- var import_path2 = require("path");
1591
- var import_core5 = require("@aiready/core");
858
+ var import_fs3 = require("fs");
859
+ var import_path = require("path");
860
+ var import_core6 = require("@aiready/core");
1592
861
  var program = new import_commander.Command();
1593
862
  program.name("aiready-consistency").description(
1594
863
  "Detect consistency patterns in naming, code structure, and architecture"
@@ -1627,7 +896,7 @@ EXAMPLES:
1627
896
  ).option("--output-file <path>", "Output file path (for json/markdown)").action(async (directory, options) => {
1628
897
  console.log(import_chalk.default.blue("\u{1F50D} Analyzing consistency...\n"));
1629
898
  const startTime = Date.now();
1630
- const config = await (0, import_core5.loadConfig)(directory);
899
+ const config = await (0, import_core6.loadConfig)(directory);
1631
900
  const defaults = {
1632
901
  checkNaming: true,
1633
902
  checkPatterns: true,
@@ -1636,7 +905,7 @@ EXAMPLES:
1636
905
  include: void 0,
1637
906
  exclude: void 0
1638
907
  };
1639
- const mergedConfig = (0, import_core5.mergeConfigWithDefaults)(config, defaults);
908
+ const mergedConfig = (0, import_core6.mergeConfigWithDefaults)(config, defaults);
1640
909
  const finalOptions = {
1641
910
  rootDir: directory,
1642
911
  checkNaming: options.naming !== false && mergedConfig.checkNaming,
@@ -1650,29 +919,29 @@ EXAMPLES:
1650
919
  const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
1651
920
  if (options.output === "json") {
1652
921
  const output = JSON.stringify(report, null, 2);
1653
- const outputPath = (0, import_core5.resolveOutputPath)(
922
+ const outputPath = (0, import_core6.resolveOutputPath)(
1654
923
  options.outputFile,
1655
924
  `consistency-report-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`,
1656
925
  directory
1657
926
  );
1658
- const dir = (0, import_path2.dirname)(outputPath);
1659
- if (!(0, import_fs2.existsSync)(dir)) {
1660
- (0, import_fs2.mkdirSync)(dir, { recursive: true });
927
+ const dir = (0, import_path.dirname)(outputPath);
928
+ if (!(0, import_fs3.existsSync)(dir)) {
929
+ (0, import_fs3.mkdirSync)(dir, { recursive: true });
1661
930
  }
1662
- (0, import_fs2.writeFileSync)(outputPath, output);
931
+ (0, import_fs3.writeFileSync)(outputPath, output);
1663
932
  console.log(import_chalk.default.green(`\u2713 Report saved to ${outputPath}`));
1664
933
  } else if (options.output === "markdown") {
1665
934
  const markdown = generateMarkdownReport(report, elapsedTime);
1666
- const outputPath = (0, import_core5.resolveOutputPath)(
935
+ const outputPath = (0, import_core6.resolveOutputPath)(
1667
936
  options.outputFile,
1668
937
  `consistency-report-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.md`,
1669
938
  directory
1670
939
  );
1671
- const dir = (0, import_path2.dirname)(outputPath);
1672
- if (!(0, import_fs2.existsSync)(dir)) {
1673
- (0, import_fs2.mkdirSync)(dir, { recursive: true });
940
+ const dir = (0, import_path.dirname)(outputPath);
941
+ if (!(0, import_fs3.existsSync)(dir)) {
942
+ (0, import_fs3.mkdirSync)(dir, { recursive: true });
1674
943
  }
1675
- (0, import_fs2.writeFileSync)(outputPath, markdown);
944
+ (0, import_fs3.writeFileSync)(outputPath, markdown);
1676
945
  console.log(import_chalk.default.green(`\u2713 Report saved to ${outputPath}`));
1677
946
  } else {
1678
947
  displayConsoleReport(report, elapsedTime);