@aiready/pattern-detect 0.17.8 → 0.17.12

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.
Files changed (190) hide show
  1. package/dist/analyzer-entry/index.d.mts +1 -1
  2. package/dist/analyzer-entry/index.d.ts +1 -1
  3. package/dist/analyzer-entry/index.js +370 -135
  4. package/dist/analyzer-entry/index.mjs +4 -3
  5. package/dist/chunk-2P7BQHGR.mjs +306 -0
  6. package/dist/{chunk-VGMM3L3O.mjs → chunk-3EORD7DC.mjs} +1 -1
  7. package/dist/{chunk-GREN7X5H.mjs → chunk-4PVPQMRT.mjs} +2 -2
  8. package/dist/{chunk-RS73WLNI.mjs → chunk-6VDL7TAS.mjs} +5 -113
  9. package/dist/chunk-AQIP4JGM.mjs +283 -0
  10. package/dist/{chunk-JBUZ6YHE.mjs → chunk-B4NLWKPZ.mjs} +85 -9
  11. package/dist/chunk-IPBGVPUX.mjs +143 -0
  12. package/dist/chunk-LUUJOUK5.mjs +283 -0
  13. package/dist/chunk-P3BOCGVV.mjs +498 -0
  14. package/dist/{scoring-entry.js → chunk-PHJE6A3J.mjs} +20 -37
  15. package/dist/chunk-PQXOORR4.mjs +234 -0
  16. package/dist/{chunk-GLKAGFKX.mjs → chunk-RDR75DVI.mjs} +85 -9
  17. package/dist/chunk-SXVLRPMF.mjs +143 -0
  18. package/dist/{chunk-DNZS4ESD.mjs → chunk-SY7RX5YQ.mjs} +85 -9
  19. package/dist/{context-rules-entry.js → chunk-TIBF7KST.mjs} +81 -78
  20. package/dist/chunk-WYYSQX5M.mjs +467 -0
  21. package/dist/{chunk-I6ETJC7L.mjs → chunk-X553BOMI.mjs} +56 -26
  22. package/dist/{chunk-K7BO57OO.mjs → chunk-Y6OB7K34.mjs} +80 -4
  23. package/dist/chunk-YLVV6YZ5.mjs +143 -0
  24. package/dist/chunk-ZUWPFVJV.mjs +115 -0
  25. package/dist/chunk-ZZMONVPE.mjs +467 -0
  26. package/dist/cli.js +402 -167
  27. package/dist/cli.mjs +4 -3
  28. package/dist/context-rules-entry/index.d.mts +35 -1
  29. package/dist/context-rules-entry/index.d.ts +35 -1
  30. package/dist/context-rules-entry/index.js +194 -48
  31. package/dist/context-rules-entry/index.mjs +1 -1
  32. package/dist/detector-entry/index.js +192 -46
  33. package/dist/detector-entry/index.mjs +2 -2
  34. package/dist/{analyzer-entry-BVz-HnZd.d.mts → index-B-pnXpgn.d.mts} +10 -1
  35. package/dist/{index-BwuoiCNm.d.ts → index-CWgYOKaK.d.ts} +35 -16
  36. package/dist/{index-BVz-HnZd.d.mts → index-Dl4BrGIT.d.mts} +35 -16
  37. package/dist/{analyzer-entry-BwuoiCNm.d.ts → index-DqS2e0kK.d.ts} +10 -1
  38. package/dist/index.d.mts +5 -6
  39. package/dist/index.d.ts +5 -6
  40. package/dist/index.js +467 -214
  41. package/dist/index.mjs +37 -22
  42. package/dist/scoring-entry/index.js +7 -3
  43. package/dist/scoring-entry/index.mjs +1 -1
  44. package/package.json +2 -2
  45. package/dist/analyzer-entry.d.mts +0 -100
  46. package/dist/analyzer-entry.d.ts +0 -100
  47. package/dist/analyzer-entry.js +0 -693
  48. package/dist/analyzer-entry.mjs +0 -12
  49. package/dist/chunk-262N2JB7.mjs +0 -497
  50. package/dist/chunk-2R7HOR5H.mjs +0 -777
  51. package/dist/chunk-3D7RVGHM.mjs +0 -64
  52. package/dist/chunk-3LS3E6MO.mjs +0 -508
  53. package/dist/chunk-3VRQYFW3.mjs +0 -782
  54. package/dist/chunk-3WK24ZOX.mjs +0 -860
  55. package/dist/chunk-3YYN6ZXN.mjs +0 -1038
  56. package/dist/chunk-4BPRGZRG.mjs +0 -1041
  57. package/dist/chunk-4UHDGB7U.mjs +0 -920
  58. package/dist/chunk-5LYDB7DY.mjs +0 -771
  59. package/dist/chunk-65G3HXLQ.mjs +0 -497
  60. package/dist/chunk-65UQ5J2J.mjs +0 -64
  61. package/dist/chunk-6JTVOBJX.mjs +0 -64
  62. package/dist/chunk-6OEHUI5J.mjs +0 -1045
  63. package/dist/chunk-6YUGU4P4.mjs +0 -914
  64. package/dist/chunk-7EJGNGXM.mjs +0 -771
  65. package/dist/chunk-7O2DUBSN.mjs +0 -1058
  66. package/dist/chunk-7S4AUL5S.mjs +0 -911
  67. package/dist/chunk-A76JUWER.mjs +0 -786
  68. package/dist/chunk-AJZUNNFH.mjs +0 -817
  69. package/dist/chunk-AXHGYYYZ.mjs +0 -404
  70. package/dist/chunk-BKRPSTT2.mjs +0 -64
  71. package/dist/chunk-BUBQ3W6W.mjs +0 -980
  72. package/dist/chunk-CCHM2VLK.mjs +0 -1051
  73. package/dist/chunk-CHFK6EBT.mjs +0 -419
  74. package/dist/chunk-CMT3MWWO.mjs +0 -948
  75. package/dist/chunk-CMWW24HW.mjs +0 -259
  76. package/dist/chunk-CTDBJP25.mjs +0 -1043
  77. package/dist/chunk-DGAKXYIP.mjs +0 -1041
  78. package/dist/chunk-DQSLTL7J.mjs +0 -788
  79. package/dist/chunk-DR5W7S3Z.mjs +0 -968
  80. package/dist/chunk-EFUKPMBE.mjs +0 -950
  81. package/dist/chunk-EVBFDILL.mjs +0 -927
  82. package/dist/chunk-EXORBAXR.mjs +0 -887
  83. package/dist/chunk-EZT3NZGB.mjs +0 -1057
  84. package/dist/chunk-FWUKMJEQ.mjs +0 -1133
  85. package/dist/chunk-GSJFORRO.mjs +0 -504
  86. package/dist/chunk-H4ADJYOG.mjs +0 -925
  87. package/dist/chunk-H5FB2USZ.mjs +0 -762
  88. package/dist/chunk-H73HEG7M.mjs +0 -670
  89. package/dist/chunk-HOS5Z2NC.mjs +0 -669
  90. package/dist/chunk-HXHQOQB5.mjs +0 -508
  91. package/dist/chunk-INEOYHUM.mjs +0 -911
  92. package/dist/chunk-INJ4SBTV.mjs +0 -754
  93. package/dist/chunk-J5CW6NYY.mjs +0 -64
  94. package/dist/chunk-JAFZCZAP.mjs +0 -776
  95. package/dist/chunk-JKVKOXYR.mjs +0 -407
  96. package/dist/chunk-JTHW7EYW.mjs +0 -1041
  97. package/dist/chunk-JWR3AHKO.mjs +0 -788
  98. package/dist/chunk-KC2CQMG2.mjs +0 -858
  99. package/dist/chunk-KDWGWBP5.mjs +0 -832
  100. package/dist/chunk-KPEK5REL.mjs +0 -919
  101. package/dist/chunk-KT6O2IAE.mjs +0 -861
  102. package/dist/chunk-KWMNN3TG.mjs +0 -391
  103. package/dist/chunk-LUA5FXSZ.mjs +0 -771
  104. package/dist/chunk-LYKRYBSM.mjs +0 -64
  105. package/dist/chunk-M4PQMW34.mjs +0 -480
  106. package/dist/chunk-MH6LBXZF.mjs +0 -816
  107. package/dist/chunk-MHU3CL4R.mjs +0 -64
  108. package/dist/chunk-MJWBS6SM.mjs +0 -1058
  109. package/dist/chunk-OFGMDX66.mjs +0 -402
  110. package/dist/chunk-P7B6Z4I2.mjs +0 -1043
  111. package/dist/chunk-PBCXSG7E.mjs +0 -658
  112. package/dist/chunk-PEEHSFDR.mjs +0 -1058
  113. package/dist/chunk-PSVG2NLH.mjs +0 -966
  114. package/dist/chunk-PWNQ6JZW.mjs +0 -508
  115. package/dist/chunk-QE4E3F7C.mjs +0 -410
  116. package/dist/chunk-QEP76HGK.mjs +0 -1039
  117. package/dist/chunk-QX2BQJEO.mjs +0 -1058
  118. package/dist/chunk-R2S73CVG.mjs +0 -503
  119. package/dist/chunk-RMGDSNLE.mjs +0 -770
  120. package/dist/chunk-S2KQFII2.mjs +0 -491
  121. package/dist/chunk-SLDK5PQK.mjs +0 -1129
  122. package/dist/chunk-SNSDVGWW.mjs +0 -783
  123. package/dist/chunk-SUUZMLPS.mjs +0 -391
  124. package/dist/chunk-SVCSIZ2A.mjs +0 -259
  125. package/dist/chunk-T2C6WS73.mjs +0 -670
  126. package/dist/chunk-TCG2G32F.mjs +0 -911
  127. package/dist/chunk-TGBZP7SB.mjs +0 -773
  128. package/dist/chunk-THF4RW63.mjs +0 -254
  129. package/dist/chunk-TJKDLVLN.mjs +0 -503
  130. package/dist/chunk-TXWPOVYU.mjs +0 -402
  131. package/dist/chunk-UB3CGOQ7.mjs +0 -64
  132. package/dist/chunk-UKIKN27B.mjs +0 -950
  133. package/dist/chunk-V5DP4FP6.mjs +0 -876
  134. package/dist/chunk-VRMXVYDZ.mjs +0 -419
  135. package/dist/chunk-WACZ5LFH.mjs +0 -1055
  136. package/dist/chunk-WC7CBAA7.mjs +0 -1058
  137. package/dist/chunk-WKBCNITM.mjs +0 -1072
  138. package/dist/chunk-WMOGJFME.mjs +0 -391
  139. package/dist/chunk-X4GR2N2M.mjs +0 -947
  140. package/dist/chunk-XCWY2DQY.mjs +0 -788
  141. package/dist/chunk-XJD35DS6.mjs +0 -1058
  142. package/dist/chunk-XNPID6FU.mjs +0 -391
  143. package/dist/chunk-XUUVS54V.mjs +0 -776
  144. package/dist/chunk-YCGV65F5.mjs +0 -508
  145. package/dist/chunk-YJYDBFT3.mjs +0 -780
  146. package/dist/chunk-YP3HEDQW.mjs +0 -859
  147. package/dist/chunk-YSDOUNJJ.mjs +0 -1142
  148. package/dist/chunk-Z6GBFFOV.mjs +0 -1040
  149. package/dist/cli.d.ts.map +0 -1
  150. package/dist/cli.js.map +0 -1
  151. package/dist/context-rules-entry-y2uJSngh.d.mts +0 -60
  152. package/dist/context-rules-entry-y2uJSngh.d.ts +0 -60
  153. package/dist/context-rules-entry.d.mts +0 -55
  154. package/dist/context-rules-entry.d.ts +0 -55
  155. package/dist/context-rules-entry.mjs +0 -12
  156. package/dist/context-rules.d.ts +0 -41
  157. package/dist/context-rules.d.ts.map +0 -1
  158. package/dist/context-rules.js +0 -225
  159. package/dist/context-rules.js.map +0 -1
  160. package/dist/detector-entry.d.mts +0 -14
  161. package/dist/detector-entry.d.ts +0 -14
  162. package/dist/detector-entry.js +0 -301
  163. package/dist/detector-entry.mjs +0 -7
  164. package/dist/detector.d.ts +0 -40
  165. package/dist/detector.d.ts.map +0 -1
  166. package/dist/detector.js +0 -385
  167. package/dist/detector.js.map +0 -1
  168. package/dist/extractors/python-extractor.d.ts +0 -19
  169. package/dist/extractors/python-extractor.d.ts.map +0 -1
  170. package/dist/extractors/python-extractor.js +0 -164
  171. package/dist/extractors/python-extractor.js.map +0 -1
  172. package/dist/grouping.d.ts +0 -54
  173. package/dist/grouping.d.ts.map +0 -1
  174. package/dist/grouping.js +0 -347
  175. package/dist/grouping.js.map +0 -1
  176. package/dist/index-y2uJSngh.d.mts +0 -60
  177. package/dist/index-y2uJSngh.d.ts +0 -60
  178. package/dist/index.d.ts.map +0 -1
  179. package/dist/index.js.map +0 -1
  180. package/dist/python-extractor-BGKGX6BK.mjs +0 -131
  181. package/dist/python-extractor-ELAKYK2W.mjs +0 -140
  182. package/dist/scoring-entry.d.mts +0 -23
  183. package/dist/scoring-entry.d.ts +0 -23
  184. package/dist/scoring-entry.mjs +0 -6
  185. package/dist/scoring.d.ts +0 -12
  186. package/dist/scoring.d.ts.map +0 -1
  187. package/dist/scoring.js +0 -116
  188. package/dist/scoring.js.map +0 -1
  189. package/dist/types-C4lmb2Yh.d.mts +0 -36
  190. package/dist/types-C4lmb2Yh.d.ts +0 -36
package/dist/index.js CHANGED
@@ -30,38 +30,41 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- CONTEXT_RULES: () => CONTEXT_RULES,
34
- IssueType: () => import_core.IssueType,
35
- PatternDetectProvider: () => PatternDetectProvider,
36
- Severity: () => import_core7.Severity,
33
+ PATTERN_DETECT_PROVIDER: () => PATTERN_DETECT_PROVIDER,
34
+ Severity: () => import_core11.Severity,
37
35
  analyzePatterns: () => analyzePatterns,
36
+ areBrandSpecificVariants: () => areBrandSpecificVariants,
38
37
  calculatePatternScore: () => calculatePatternScore,
39
- calculateSeverity: () => calculateSeverity,
38
+ calculateSeverity: () => calculateSeverity2,
40
39
  createRefactorClusters: () => createRefactorClusters,
41
40
  detectDuplicatePatterns: () => detectDuplicatePatterns,
42
- filterBySeverity: () => import_core.filterBySeverity,
41
+ filterBrandSpecificVariants: () => filterBrandSpecificVariants,
42
+ filterBySeverity: () => filterBySeverity2,
43
43
  filterClustersByImpact: () => filterClustersByImpact,
44
44
  generateSummary: () => generateSummary,
45
- getSeverityLabel: () => import_core.getSeverityLabel,
46
- getSeverityThreshold: () => getSeverityThreshold,
45
+ getSeverityLabel: () => getSeverityLabel2,
47
46
  getSmartDefaults: () => getSmartDefaults,
48
- groupDuplicatesByFilePair: () => groupDuplicatesByFilePair
47
+ groupDuplicatesByFilePair: () => groupDuplicatesByFilePair,
48
+ logConfiguration: () => logConfiguration
49
49
  });
50
50
  module.exports = __toCommonJS(index_exports);
51
- var import_core7 = require("@aiready/core");
51
+ var import_core13 = require("@aiready/core");
52
52
 
53
53
  // src/provider.ts
54
- var import_core6 = require("@aiready/core");
54
+ var import_core12 = require("@aiready/core");
55
55
 
56
56
  // src/analyzer.ts
57
- var import_core4 = require("@aiready/core");
57
+ var import_core11 = require("@aiready/core");
58
58
 
59
59
  // src/detector.ts
60
- var import_core2 = require("@aiready/core");
60
+ var import_core6 = require("@aiready/core");
61
61
 
62
62
  // src/context-rules.ts
63
+ var import_core5 = require("@aiready/core");
64
+
65
+ // src/rules/categories/test-rules.ts
63
66
  var import_core = require("@aiready/core");
64
- var CONTEXT_RULES = [
67
+ var TEST_RULES = [
65
68
  // Test Fixtures - Intentional duplication for test isolation
66
69
  {
67
70
  name: "test-fixtures",
@@ -74,6 +77,35 @@ var CONTEXT_RULES = [
74
77
  reason: "Test fixture duplication is intentional for test isolation",
75
78
  suggestion: "Consider if shared test setup would improve maintainability without coupling tests"
76
79
  },
80
+ // E2E/Integration Test Page Objects - Test independence
81
+ {
82
+ name: "e2e-page-objects",
83
+ detect: (file, code) => {
84
+ const isE2ETest = file.includes("e2e/") || file.includes("/e2e/") || file.includes(".e2e.") || file.includes("/playwright/") || file.includes("playwright/") || file.includes("/cypress/") || file.includes("cypress/") || file.includes("/integration/") || file.includes("integration/");
85
+ const hasPageObjectPatterns = code.includes("page.") || code.includes("await page") || code.includes("locator") || code.includes("getBy") || code.includes("selector") || code.includes("click(") || code.includes("fill(");
86
+ return isE2ETest && hasPageObjectPatterns;
87
+ },
88
+ severity: import_core.Severity.Info,
89
+ reason: "E2E test duplication ensures test independence and reduces coupling",
90
+ suggestion: "Consider page object pattern only if duplication causes maintenance issues"
91
+ },
92
+ // Mock Data - Test data intentionally duplicated
93
+ {
94
+ name: "mock-data",
95
+ detect: (file, code) => {
96
+ const isMockFile = file.includes("/mocks/") || file.includes("/__mocks__/") || file.includes("/fixtures/") || file.includes(".mock.") || file.includes(".fixture.");
97
+ const hasMockData = code.includes("mock") || code.includes("Mock") || code.includes("fixture") || code.includes("stub") || code.includes("export const");
98
+ return isMockFile && hasMockData;
99
+ },
100
+ severity: import_core.Severity.Info,
101
+ reason: "Mock data duplication is expected for comprehensive test coverage",
102
+ suggestion: "Consider shared factories only for complex mock generation"
103
+ }
104
+ ];
105
+
106
+ // src/rules/categories/web-rules.ts
107
+ var import_core2 = require("@aiready/core");
108
+ var WEB_RULES = [
77
109
  // Email/Document Templates - Often intentionally similar for consistency
78
110
  {
79
111
  name: "templates",
@@ -82,66 +114,59 @@ var CONTEXT_RULES = [
82
114
  const hasTemplateContent = (code.includes("return") || code.includes("export")) && (code.includes("html") || code.includes("subject") || code.includes("body"));
83
115
  return isTemplate && hasTemplateContent;
84
116
  },
85
- severity: import_core.Severity.Minor,
117
+ severity: import_core2.Severity.Info,
86
118
  reason: "Template duplication may be intentional for maintainability and branding consistency",
87
119
  suggestion: "Extract shared structure only if templates become hard to maintain"
88
120
  },
89
- // E2E/Integration Test Page Objects - Test independence
121
+ // Common UI Event Handlers - Very specific patterns only
90
122
  {
91
- name: "e2e-page-objects",
123
+ name: "common-ui-handlers",
92
124
  detect: (file, code) => {
93
- const isE2ETest = file.includes("e2e/") || file.includes("/e2e/") || file.includes(".e2e.") || file.includes("/playwright/") || file.includes("playwright/") || file.includes("/cypress/") || file.includes("cypress/") || file.includes("/integration/") || file.includes("integration/");
94
- const hasPageObjectPatterns = code.includes("page.") || code.includes("await page") || code.includes("locator") || code.includes("getBy") || code.includes("selector") || code.includes("click(") || code.includes("fill(");
95
- return isE2ETest && hasPageObjectPatterns;
125
+ const isUIFile = file.includes("/components/") || file.includes(".tsx") || file.includes(".jsx") || file.includes("/hooks/");
126
+ const hasCommonHandler = code.includes("handleClickOutside") && code.includes("dropdownRef.current") && code.includes("!dropdownRef.current.contains") || code.includes("handleEscape") && code.includes("event.key") && code.includes("=== 'Escape'") || code.includes("handleClickInside") && code.includes("event.stopPropagation");
127
+ return isUIFile && hasCommonHandler;
96
128
  },
97
- severity: import_core.Severity.Minor,
98
- reason: "E2E test duplication ensures test independence and reduces coupling",
99
- suggestion: "Consider page object pattern only if duplication causes maintenance issues"
129
+ severity: import_core2.Severity.Info,
130
+ reason: "Common UI event handlers are boilerplate patterns that repeat across components",
131
+ suggestion: "Consider extracting to shared hooks (useClickOutside, useEscapeKey) only if causing maintenance issues"
100
132
  },
133
+ // Next.js Route Handler Patterns - Boilerplate API route patterns
134
+ {
135
+ name: "nextjs-route-handlers",
136
+ detect: (file, code) => {
137
+ const isRouteFile = file.includes("/api/") && (file.endsWith("/route.ts") || file.endsWith("/route.js"));
138
+ const hasRoutePattern = code.includes("export async function POST") || code.includes("export async function GET") || code.includes("export async function PUT") || code.includes("export async function DELETE") || code.includes("NextResponse.json") || code.includes("NextRequest");
139
+ return isRouteFile && hasRoutePattern;
140
+ },
141
+ severity: import_core2.Severity.Info,
142
+ reason: "Next.js route handlers follow standard patterns and are intentionally similar across endpoints",
143
+ suggestion: "Route handler duplication is acceptable for API endpoint boilerplate"
144
+ }
145
+ ];
146
+
147
+ // src/rules/categories/infra-rules.ts
148
+ var import_core3 = require("@aiready/core");
149
+ var INFRA_RULES = [
101
150
  // Configuration Files - Often necessarily similar by design
102
151
  {
103
152
  name: "config-files",
104
153
  detect: (file) => {
105
154
  return file.endsWith(".config.ts") || file.endsWith(".config.js") || file.includes("jest.config") || file.includes("vite.config") || file.includes("webpack.config") || file.includes("rollup.config") || file.includes("tsconfig");
106
155
  },
107
- severity: import_core.Severity.Minor,
156
+ severity: import_core3.Severity.Info,
108
157
  reason: "Configuration files often have similar structure by design",
109
158
  suggestion: "Consider shared config base only if configurations become hard to maintain"
110
159
  },
111
- // Type Definitions - Duplication for type safety and module independence
112
- {
113
- name: "type-definitions",
114
- detect: (file, code) => {
115
- const isTypeFile = file.endsWith(".d.ts") || file.includes("/types/");
116
- const hasTypeDefinitions = code.includes("interface ") || code.includes("type ") || code.includes("enum ");
117
- return isTypeFile && hasTypeDefinitions;
118
- },
119
- severity: import_core.Severity.Info,
120
- reason: "Type duplication may be intentional for module independence and type safety",
121
- suggestion: "Extract to shared types package only if causing maintenance burden"
122
- },
123
160
  // Migration Scripts - One-off scripts that are similar by nature
124
161
  {
125
162
  name: "migration-scripts",
126
163
  detect: (file) => {
127
164
  return file.includes("/migrations/") || file.includes("/migrate/") || file.includes(".migration.");
128
165
  },
129
- severity: import_core.Severity.Info,
166
+ severity: import_core3.Severity.Info,
130
167
  reason: "Migration scripts are typically one-off and intentionally similar",
131
168
  suggestion: "Duplication is acceptable for migration scripts"
132
169
  },
133
- // Mock Data - Test data intentionally duplicated
134
- {
135
- name: "mock-data",
136
- detect: (file, code) => {
137
- const isMockFile = file.includes("/mocks/") || file.includes("/__mocks__/") || file.includes("/fixtures/") || file.includes(".mock.") || file.includes(".fixture.");
138
- const hasMockData = code.includes("mock") || code.includes("Mock") || code.includes("fixture") || code.includes("stub") || code.includes("export const");
139
- return isMockFile && hasMockData;
140
- },
141
- severity: import_core.Severity.Info,
142
- reason: "Mock data duplication is expected for comprehensive test coverage",
143
- suggestion: "Consider shared factories only for complex mock generation"
144
- },
145
170
  // Tool Implementations - Structural Boilerplate
146
171
  {
147
172
  name: "tool-implementations",
@@ -150,11 +175,132 @@ var CONTEXT_RULES = [
150
175
  const hasToolStructure = code.includes("execute") && (code.includes("try") || code.includes("catch"));
151
176
  return isToolFile && hasToolStructure;
152
177
  },
153
- severity: import_core.Severity.Info,
178
+ severity: import_core3.Severity.Info,
154
179
  reason: "Tool implementations share structural boilerplate but have distinct business logic",
155
180
  suggestion: "Tool duplication is acceptable for boilerplate interface wrappers"
181
+ },
182
+ // CLI Command Definitions - Commander.js boilerplate patterns
183
+ {
184
+ name: "cli-command-definitions",
185
+ detect: (file, code) => {
186
+ const isCliFile = file.includes("/commands/") || file.includes("/cli/") || file.endsWith(".command.ts");
187
+ const hasCommandPattern = (code.includes(".command(") || code.includes("defineCommand")) && (code.includes(".description(") || code.includes(".option(")) && (code.includes(".action(") || code.includes("async"));
188
+ return isCliFile && hasCommandPattern;
189
+ },
190
+ severity: import_core3.Severity.Info,
191
+ reason: "CLI command definitions follow standard Commander.js patterns and are intentionally similar",
192
+ suggestion: "Command boilerplate duplication is acceptable for CLI interfaces"
193
+ }
194
+ ];
195
+
196
+ // src/rules/categories/logic-rules.ts
197
+ var import_core4 = require("@aiready/core");
198
+ var LOGIC_RULES = [
199
+ // Type Definitions - Duplication for type safety and module independence
200
+ {
201
+ name: "type-definitions",
202
+ detect: (file, code) => {
203
+ const isTypeFile = file.endsWith(".d.ts") || file.includes("/types/");
204
+ const hasOnlyTypeDefinitions = (code.includes("interface ") || code.includes("type ") || code.includes("enum ")) && !code.includes("function ") && !code.includes("class ") && !code.includes("const ") && !code.includes("let ") && !code.includes("export default");
205
+ const isInterfaceOnlySnippet = code.trim().startsWith("interface ") && code.includes("{") && code.includes("}") && !code.includes("function ") && !code.includes("const ") && !code.includes("return ");
206
+ return isTypeFile && hasOnlyTypeDefinitions || isInterfaceOnlySnippet;
207
+ },
208
+ severity: import_core4.Severity.Info,
209
+ reason: "Type/interface definitions are intentionally duplicated for module independence",
210
+ suggestion: "Extract to shared types package only if causing maintenance burden"
211
+ },
212
+ // Utility Functions - Small helpers in dedicated utility files
213
+ {
214
+ name: "utility-functions",
215
+ detect: (file, code) => {
216
+ const isUtilFile = file.endsWith(".util.ts") || file.endsWith(".helper.ts") || file.endsWith(".utils.ts");
217
+ const hasUtilPattern = code.includes("function format") || code.includes("function parse") || code.includes("function sanitize") || code.includes("function normalize") || code.includes("function convert");
218
+ return isUtilFile && hasUtilPattern;
219
+ },
220
+ severity: import_core4.Severity.Info,
221
+ reason: "Utility functions in dedicated utility files may be intentionally similar",
222
+ suggestion: "Consider extracting to shared utilities only if causing significant duplication"
223
+ },
224
+ // React/Vue Hooks - Standard patterns
225
+ {
226
+ name: "shared-hooks",
227
+ detect: (file, code) => {
228
+ const isHookFile = file.includes("/hooks/") || file.endsWith(".hook.ts") || file.endsWith(".hook.tsx");
229
+ const hasHookPattern = code.includes("function use") || code.includes("export function use") || code.includes("const use") || code.includes("export const use");
230
+ return isHookFile && hasHookPattern;
231
+ },
232
+ severity: import_core4.Severity.Info,
233
+ reason: "Hooks follow standard patterns and are often intentionally similar across components",
234
+ suggestion: "Consider extracting common hook logic only if hooks become complex"
235
+ },
236
+ // Score/Rating Helper Functions - Common threshold patterns
237
+ {
238
+ name: "score-helpers",
239
+ detect: (file, code) => {
240
+ const isHelperFile = file.includes("/utils/") || file.includes("/helpers/") || file.endsWith(".util.ts");
241
+ const hasScorePattern = (code.includes("if (score >=") || code.includes("if (score >")) && code.includes("return") && code.includes("'") && code.split("if (score").length >= 3;
242
+ return isHelperFile && hasScorePattern;
243
+ },
244
+ severity: import_core4.Severity.Info,
245
+ reason: "Score/rating helper functions use common threshold patterns that are intentionally similar",
246
+ suggestion: "Score formatting duplication is acceptable for consistent UI display"
247
+ },
248
+ // D3/Canvas Event Handlers - Standard visualization patterns
249
+ {
250
+ name: "visualization-handlers",
251
+ detect: (file, code) => {
252
+ const isVizFile = file.includes("/visualizer/") || file.includes("/charts/") || file.includes("GraphCanvas") || file.includes("ForceDirected");
253
+ const hasVizPattern = (code.includes("dragstarted") || code.includes("dragged") || code.includes("dragended")) && (code.includes("simulation") || code.includes("d3.") || code.includes("alphaTarget"));
254
+ return isVizFile && hasVizPattern;
255
+ },
256
+ severity: import_core4.Severity.Info,
257
+ reason: "D3/visualization event handlers follow standard patterns and are intentionally similar",
258
+ suggestion: "Visualization boilerplate duplication is acceptable for interactive charts"
259
+ },
260
+ // Icon/Switch Statement Helpers - Common enum-to-value patterns
261
+ {
262
+ name: "switch-helpers",
263
+ detect: (file, code) => {
264
+ const hasSwitchPattern = code.includes("switch (") && code.includes("case '") && code.includes("return") && code.split("case ").length >= 4;
265
+ const hasIconPattern = code.includes("getIcon") || code.includes("getColor") || code.includes("getLabel") || code.includes("getRating");
266
+ return hasSwitchPattern && hasIconPattern;
267
+ },
268
+ severity: import_core4.Severity.Info,
269
+ reason: "Switch statement helpers for enum-to-value mapping are inherently similar",
270
+ suggestion: "Switch duplication is acceptable for mapping enums to display values"
271
+ },
272
+ // Common API/Utility Functions - Legitimate duplication across modules
273
+ {
274
+ name: "common-api-functions",
275
+ detect: (file, code) => {
276
+ const isApiFile = file.includes("/api/") || file.includes("/lib/") || file.includes("/utils/") || file.endsWith(".ts");
277
+ const hasCommonApiPattern = code.includes("getStripe") && code.includes("process.env.STRIPE_SECRET_KEY") || code.includes("getUserByEmail") && code.includes("queryItems") || code.includes("updateUser") && code.includes("buildUpdateExpression") || code.includes("listUserRepositories") && code.includes("queryItems") || code.includes("listTeamRepositories") && code.includes("queryItems") || code.includes("getRemediation") && code.includes("queryItems") || code.includes("formatBreakdownKey") && code.includes(".replace(/([A-Z])/g") || code.includes("queryItems") && code.includes("KeyConditionExpression") || code.includes("putItem") && code.includes("createdAt") || code.includes("updateItem") && code.includes("buildUpdateExpression");
278
+ return isApiFile && hasCommonApiPattern;
279
+ },
280
+ severity: import_core4.Severity.Info,
281
+ reason: "Common API/utility functions are legitimately duplicated across modules for clarity and independence",
282
+ suggestion: "Consider extracting to shared utilities only if causing significant duplication"
283
+ },
284
+ // Validation Functions - Inherently similar patterns
285
+ {
286
+ name: "validation-functions",
287
+ detect: (file, code) => {
288
+ const hasValidationPattern = code.includes("isValid") || code.includes("validate") || code.includes("checkValid") || code.includes("isEmail") || code.includes("isPhone") || code.includes("isUrl") || code.includes("isNumeric") || code.includes("isAlpha") || code.includes("isAlphanumeric") || code.includes("isEmpty") || code.includes("isNotEmpty") || code.includes("isRequired") || code.includes("isOptional");
289
+ return hasValidationPattern;
290
+ },
291
+ severity: import_core4.Severity.Info,
292
+ reason: "Validation functions are inherently similar and often intentionally duplicated for domain clarity",
293
+ suggestion: "Consider extracting to shared validators only if validation logic becomes complex"
156
294
  }
157
295
  ];
296
+
297
+ // src/context-rules.ts
298
+ var CONTEXT_RULES = [
299
+ ...TEST_RULES,
300
+ ...WEB_RULES,
301
+ ...INFRA_RULES,
302
+ ...LOGIC_RULES
303
+ ];
158
304
  function calculateSeverity(file1, file2, code, similarity, linesOfCode) {
159
305
  for (const rule of CONTEXT_RULES) {
160
306
  if (rule.detect(file1, code) || rule.detect(file2, code)) {
@@ -168,45 +314,36 @@ function calculateSeverity(file1, file2, code, similarity, linesOfCode) {
168
314
  }
169
315
  if (similarity >= 0.95 && linesOfCode >= 30) {
170
316
  return {
171
- severity: import_core.Severity.Critical,
317
+ severity: import_core5.Severity.Critical,
172
318
  reason: "Large nearly-identical code blocks waste tokens and create maintenance burden",
173
319
  suggestion: "Extract to shared utility module immediately"
174
320
  };
175
321
  } else if (similarity >= 0.95 && linesOfCode >= 15) {
176
322
  return {
177
- severity: import_core.Severity.Major,
323
+ severity: import_core5.Severity.Major,
178
324
  reason: "Nearly identical code should be consolidated",
179
325
  suggestion: "Move to shared utility file"
180
326
  };
181
327
  } else if (similarity >= 0.85) {
182
328
  return {
183
- severity: import_core.Severity.Major,
329
+ severity: import_core5.Severity.Major,
184
330
  reason: "High similarity indicates significant duplication",
185
331
  suggestion: "Extract common logic to shared function"
186
332
  };
187
333
  } else if (similarity >= 0.7) {
188
334
  return {
189
- severity: import_core.Severity.Minor,
335
+ severity: import_core5.Severity.Minor,
190
336
  reason: "Moderate similarity detected",
191
337
  suggestion: "Consider extracting shared patterns if code evolves together"
192
338
  };
193
339
  } else {
194
340
  return {
195
- severity: import_core.Severity.Minor,
341
+ severity: import_core5.Severity.Minor,
196
342
  reason: "Minor similarity detected",
197
343
  suggestion: "Monitor but refactoring may not be worthwhile"
198
344
  };
199
345
  }
200
346
  }
201
- function getSeverityThreshold(severity) {
202
- const thresholds = {
203
- [import_core.Severity.Critical]: 0.95,
204
- [import_core.Severity.Major]: 0.85,
205
- [import_core.Severity.Minor]: 0.5,
206
- [import_core.Severity.Info]: 0
207
- };
208
- return thresholds[severity] || 0;
209
- }
210
347
 
211
348
  // src/core/normalizer.ts
212
349
  function normalizeCode(code, isPython = false) {
@@ -222,13 +359,13 @@ function normalizeCode(code, isPython = false) {
222
359
 
223
360
  // src/detector.ts
224
361
  function extractBlocks(file, content) {
225
- return (0, import_core2.extractCodeBlocks)(file, content);
362
+ return (0, import_core6.extractCodeBlocks)(file, content);
226
363
  }
227
364
  function calculateSimilarity(a, b) {
228
- return (0, import_core2.calculateStringSimilarity)(a, b);
365
+ return (0, import_core6.calculateStringSimilarity)(a, b);
229
366
  }
230
367
  function calculateConfidence(similarity, tokens, lines) {
231
- return (0, import_core2.calculateHeuristicConfidence)(similarity, tokens, lines);
368
+ return (0, import_core6.calculateHeuristicConfidence)(similarity, tokens, lines);
232
369
  }
233
370
  async function detectDuplicatePatterns(fileContents, options) {
234
371
  const {
@@ -338,7 +475,7 @@ async function detectDuplicatePatterns(fileContents, options) {
338
475
  }
339
476
 
340
477
  // src/grouping.ts
341
- var import_core3 = require("@aiready/core");
478
+ var import_core7 = require("@aiready/core");
342
479
  var import_path = __toESM(require("path"));
343
480
  function groupDuplicatesByFilePair(duplicates) {
344
481
  const groups = /* @__PURE__ */ new Map();
@@ -366,7 +503,7 @@ function groupDuplicatesByFilePair(duplicates) {
366
503
  file2: { start: dup.line2, end: dup.endLine2 }
367
504
  });
368
505
  const currentSev = dup.severity;
369
- if ((0, import_core3.getSeverityLevel)(currentSev) > (0, import_core3.getSeverityLevel)(group.severity)) {
506
+ if ((0, import_core7.getSeverityLevel)(currentSev) > (0, import_core7.getSeverityLevel)(group.severity)) {
370
507
  group.severity = currentSev;
371
508
  }
372
509
  }
@@ -456,21 +593,77 @@ function filterClustersByImpact(clusters, minTokenCost = 1e3, minFiles = 3) {
456
593
  (c) => c.totalTokenCost >= minTokenCost && c.files.length >= minFiles
457
594
  );
458
595
  }
459
-
460
- // src/analyzer.ts
461
- function getRefactoringSuggestion(patternType, similarity) {
462
- const baseMessages = {
463
- "api-handler": "Extract common middleware or create a base handler class",
464
- validator: "Consolidate validation logic into shared schema validators (Zod/Yup)",
465
- utility: "Move to a shared utilities file and reuse across modules",
466
- "class-method": "Consider inheritance or composition to share behavior",
467
- component: "Extract shared logic into a custom hook or HOC",
468
- function: "Extract into a shared helper function",
469
- unknown: "Extract common logic into a reusable module"
470
- };
471
- const urgency = similarity > 0.95 ? " (CRITICAL: Nearly identical code)" : similarity > 0.9 ? " (HIGH: Very similar, refactor soon)" : "";
472
- return baseMessages[patternType] + urgency;
596
+ function isPureInterfaceDefinition(code) {
597
+ const trimmed = code.trim();
598
+ if (!trimmed.startsWith("interface ") && !trimmed.startsWith("type ") && !trimmed.startsWith("export interface ") && !trimmed.startsWith("export type ") && !trimmed.startsWith("enum ") && !trimmed.startsWith("export enum ")) {
599
+ return false;
600
+ }
601
+ if (trimmed.includes("={") || trimmed.includes("=> {") || trimmed.includes("function ") || trimmed.includes("() {") || trimmed.includes(" implements ")) {
602
+ return false;
603
+ }
604
+ if (trimmed.length > 200) return false;
605
+ return true;
606
+ }
607
+ var BRAND_INDICATORS = [
608
+ "cyberpunk",
609
+ "cyber-blue",
610
+ "cyber-purple",
611
+ "slate-900",
612
+ "slate-400",
613
+ "zinc-",
614
+ "indigo-",
615
+ "neon-",
616
+ "glassmorphism",
617
+ "backdrop-blur"
618
+ ];
619
+ function isBrandSpecificComponent(filePath) {
620
+ const lower = filePath.toLowerCase();
621
+ const brandingTerms = ["landing", "clawmore", "platform", "apps/"];
622
+ for (const term of brandingTerms) {
623
+ if (lower.includes(term)) return true;
624
+ }
625
+ return false;
626
+ }
627
+ function areBrandSpecificVariants(file1, file2, code1, code2) {
628
+ const f1IsBrand = isBrandSpecificComponent(file1);
629
+ const f2IsBrand = isBrandSpecificComponent(file2);
630
+ if (f1IsBrand && f2IsBrand && file1 !== file2) {
631
+ const hasBrandKeyword = (code) => {
632
+ const lowerCode = code.toLowerCase();
633
+ return BRAND_INDICATORS.some((ind) => lowerCode.includes(ind));
634
+ };
635
+ const code1Brand = hasBrandKeyword(code1);
636
+ const code2Brand = hasBrandKeyword(code2);
637
+ if (code1Brand && code2Brand) {
638
+ return true;
639
+ }
640
+ }
641
+ return false;
473
642
  }
643
+ function filterBrandSpecificVariants(duplicates) {
644
+ return duplicates.filter((dup) => {
645
+ if (dup.file1 === dup.file2) return true;
646
+ const isBrandVariant = areBrandSpecificVariants(
647
+ dup.file1,
648
+ dup.file2,
649
+ dup.code1,
650
+ dup.code2
651
+ );
652
+ if (isBrandVariant) {
653
+ dup.severity = import_core7.Severity.Info;
654
+ dup.suggestion = "Brand-specific themed component variant (intentional)";
655
+ }
656
+ const isInterfaceDef = isPureInterfaceDefinition(dup.code1) && isPureInterfaceDefinition(dup.code2);
657
+ if (isInterfaceDef) {
658
+ dup.severity = import_core7.Severity.Info;
659
+ dup.suggestion = "Pure interface/type definition - intentional for module independence";
660
+ }
661
+ return true;
662
+ });
663
+ }
664
+
665
+ // src/config.ts
666
+ var import_core8 = require("@aiready/core");
474
667
  async function getSmartDefaults(directory, userOptions) {
475
668
  if (userOptions.useSmartDefaults === false) {
476
669
  return {
@@ -491,7 +684,7 @@ async function getSmartDefaults(directory, userOptions) {
491
684
  include: userOptions.include || ["**/*.{ts,tsx,js,jsx,py,java}"],
492
685
  exclude: userOptions.exclude
493
686
  };
494
- const files = await (0, import_core4.scanFiles)(scanOptions);
687
+ const files = await (0, import_core8.scanFiles)(scanOptions);
495
688
  const fileCount = files.length;
496
689
  const estimatedBlocks = fileCount * 5;
497
690
  const minLines = Math.max(
@@ -549,113 +742,21 @@ function logConfiguration(config, estimatedBlocks) {
549
742
  }
550
743
  console.log("");
551
744
  }
552
- async function analyzePatterns(options) {
553
- const smartDefaults = await getSmartDefaults(options.rootDir || ".", options);
554
- const finalOptions = { ...smartDefaults, ...options };
555
- const {
556
- minSimilarity = 0.4,
557
- minLines = 5,
558
- batchSize = 100,
559
- approx = true,
560
- minSharedTokens = 8,
561
- maxCandidatesPerBlock = 100,
562
- streamResults = false,
563
- severity = "all",
564
- groupByFilePair = true,
565
- createClusters = true,
566
- minClusterTokenCost = 1e3,
567
- minClusterFiles = 3,
568
- excludePatterns = [],
569
- confidenceThreshold = 0,
570
- ignoreWhitelist = [],
571
- ...scanOptions
572
- } = finalOptions;
573
- const files = await (0, import_core4.scanFiles)(scanOptions);
574
- const estimatedBlocks = files.length * 3;
575
- logConfiguration(finalOptions, estimatedBlocks);
576
- const results = [];
577
- const READ_BATCH_SIZE = 50;
578
- const fileContents = [];
579
- for (let i = 0; i < files.length; i += READ_BATCH_SIZE) {
580
- const batch = files.slice(i, i + READ_BATCH_SIZE);
581
- const batchContents = await Promise.all(
582
- batch.map(async (file) => ({
583
- file,
584
- content: await (0, import_core4.readFileContent)(file)
585
- }))
586
- );
587
- fileContents.push(...batchContents);
588
- }
589
- const duplicates = await detectDuplicatePatterns(fileContents, {
590
- minSimilarity,
591
- minLines,
592
- batchSize,
593
- approx,
594
- minSharedTokens,
595
- maxCandidatesPerBlock,
596
- streamResults,
597
- excludePatterns,
598
- confidenceThreshold,
599
- ignoreWhitelist,
600
- onProgress: options.onProgress
601
- });
602
- for (const file of files) {
603
- const fileDuplicates = duplicates.filter(
604
- (dup) => dup.file1 === file || dup.file2 === file
605
- );
606
- const issues = fileDuplicates.map((dup) => {
607
- const otherFile = dup.file1 === file ? dup.file2 : dup.file1;
608
- const severity2 = dup.similarity > 0.95 ? import_core4.Severity.Critical : dup.similarity > 0.9 ? import_core4.Severity.Major : import_core4.Severity.Minor;
609
- return {
610
- type: import_core4.IssueType.DuplicatePattern,
611
- severity: severity2,
612
- message: `${dup.patternType} pattern ${Math.round(dup.similarity * 100)}% similar to ${otherFile} (${dup.tokenCost} tokens wasted)`,
613
- location: {
614
- file,
615
- line: dup.file1 === file ? dup.line1 : dup.line2
616
- },
617
- suggestion: getRefactoringSuggestion(dup.patternType, dup.similarity)
618
- };
619
- });
620
- let filteredIssues = issues;
621
- if (severity !== "all") {
622
- const severityMap = {
623
- critical: [import_core4.Severity.Critical],
624
- high: [import_core4.Severity.Critical, import_core4.Severity.Major],
625
- medium: [import_core4.Severity.Critical, import_core4.Severity.Major, import_core4.Severity.Minor]
626
- };
627
- const allowedSeverities = severityMap[severity] || [import_core4.Severity.Critical, import_core4.Severity.Major, import_core4.Severity.Minor];
628
- filteredIssues = issues.filter(
629
- (issue) => allowedSeverities.includes(issue.severity)
630
- );
631
- }
632
- const totalTokenCost = fileDuplicates.reduce(
633
- (sum, dup) => sum + dup.tokenCost,
634
- 0
635
- );
636
- results.push({
637
- fileName: file,
638
- issues: filteredIssues,
639
- metrics: {
640
- tokenCost: totalTokenCost,
641
- consistencyScore: Math.max(0, 1 - fileDuplicates.length * 0.1)
642
- }
643
- });
644
- }
645
- let groups;
646
- let clusters;
647
- if (groupByFilePair) {
648
- groups = groupDuplicatesByFilePair(duplicates);
649
- }
650
- if (createClusters) {
651
- const allClusters = createRefactorClusters(duplicates);
652
- clusters = filterClustersByImpact(
653
- allClusters,
654
- minClusterTokenCost,
655
- minClusterFiles
656
- );
657
- }
658
- return { results, duplicates, files, groups, clusters, config: finalOptions };
745
+
746
+ // src/summary.ts
747
+ var import_core9 = require("@aiready/core");
748
+ function getRefactoringSuggestion(patternType, similarity) {
749
+ const baseMessages = {
750
+ "api-handler": "Extract common middleware or create a base handler class",
751
+ validator: "Consolidate validation logic into shared schema validators (Zod/Yup)",
752
+ utility: "Move to a shared utilities file and reuse across modules",
753
+ "class-method": "Consider inheritance or composition to share behavior",
754
+ component: "Extract shared logic into a custom hook or HOC",
755
+ function: "Extract into a shared helper function",
756
+ unknown: "Extract common logic into a reusable module"
757
+ };
758
+ const urgency = similarity > 0.95 ? " (CRITICAL: Nearly identical code)" : similarity > 0.9 ? " (HIGH: Very similar, refactor soon)" : "";
759
+ return baseMessages[patternType] + urgency;
659
760
  }
660
761
  function generateSummary(results) {
661
762
  const allIssues = results.flatMap((r) => r.issues || []);
@@ -675,7 +776,7 @@ function generateSummary(results) {
675
776
  allIssues.forEach((issue) => {
676
777
  const match = issue.message.match(/^(\S+(?:-\S+)*) pattern/);
677
778
  if (match) {
678
- const type = match[1];
779
+ const type = match[1] || "unknown";
679
780
  patternsByType[type] = (patternsByType[type] || 0) + 1;
680
781
  }
681
782
  });
@@ -698,8 +799,6 @@ function generateSummary(results) {
698
799
  }
699
800
  ],
700
801
  similarity: similarityMatch ? parseInt(similarityMatch[1]) / 100 : 0,
701
- confidence: similarityMatch ? parseInt(similarityMatch[1]) / 100 : 0,
702
- // Fallback for summary
703
802
  patternType: typeMatch?.[1] || "unknown",
704
803
  tokenCost: tokenMatch ? parseInt(tokenMatch[1]) : 0
705
804
  };
@@ -711,18 +810,55 @@ function generateSummary(results) {
711
810
  topDuplicates
712
811
  };
713
812
  }
813
+ function filterBySeverity2(issues, severity) {
814
+ if (severity === "all") return issues;
815
+ const severityMap = {
816
+ critical: [import_core9.Severity.Critical],
817
+ high: [import_core9.Severity.Critical, import_core9.Severity.Major],
818
+ medium: [import_core9.Severity.Critical, import_core9.Severity.Major, import_core9.Severity.Minor]
819
+ };
820
+ const allowed = severityMap[severity] || [
821
+ import_core9.Severity.Critical,
822
+ import_core9.Severity.Major,
823
+ import_core9.Severity.Minor
824
+ ];
825
+ return issues.filter((issue) => allowed.includes(issue.severity));
826
+ }
827
+ function getSeverityLabel2(severity) {
828
+ switch (severity) {
829
+ case import_core9.Severity.Critical:
830
+ return "CRITICAL";
831
+ case import_core9.Severity.Major:
832
+ return "HIGH";
833
+ case import_core9.Severity.Minor:
834
+ return "MEDIUM";
835
+ case import_core9.Severity.Info:
836
+ return "LOW";
837
+ default:
838
+ return "UNKNOWN";
839
+ }
840
+ }
841
+ function calculateSeverity2(similarity) {
842
+ if (similarity > 0.95) return import_core9.Severity.Critical;
843
+ if (similarity > 0.9) return import_core9.Severity.Major;
844
+ return import_core9.Severity.Minor;
845
+ }
714
846
 
715
847
  // src/scoring.ts
716
- var import_core5 = require("@aiready/core");
848
+ var import_core10 = require("@aiready/core");
717
849
  function calculatePatternScore(duplicates, totalFilesAnalyzed, costConfig) {
718
- const totalDuplicates = duplicates.length;
719
- const totalTokenCost = duplicates.reduce((sum, d) => sum + d.tokenCost, 0);
720
- const highImpactDuplicates = duplicates.filter(
850
+ const actionableDuplicates = duplicates.filter((d) => d.severity !== "info");
851
+ const totalDuplicates = actionableDuplicates.length;
852
+ const totalTokenCost = actionableDuplicates.reduce(
853
+ (sum, d) => sum + d.tokenCost,
854
+ 0
855
+ );
856
+ const highImpactDuplicates = actionableDuplicates.filter(
721
857
  (d) => d.tokenCost > 1e3 || d.similarity > 0.7
722
858
  ).length;
723
859
  if (totalFilesAnalyzed === 0) {
724
860
  return {
725
- toolName: import_core5.ToolName.PatternDetect,
861
+ toolName: import_core10.ToolName.PatternDetect,
726
862
  score: 100,
727
863
  rawMetrics: {
728
864
  totalDuplicates: 0,
@@ -791,12 +927,12 @@ function calculatePatternScore(duplicates, totalFilesAnalyzed, costConfig) {
791
927
  priority: totalTokenCost > 1e4 ? "high" : "medium"
792
928
  });
793
929
  }
794
- const cfg = { ...import_core5.DEFAULT_COST_CONFIG, ...costConfig };
795
- const estimatedMonthlyCost = (0, import_core5.calculateMonthlyCost)(totalTokenCost, cfg);
930
+ const cfg = { ...import_core10.DEFAULT_COST_CONFIG, ...costConfig };
931
+ const estimatedMonthlyCost = (0, import_core10.calculateMonthlyCost)(totalTokenCost, cfg);
796
932
  const issues = duplicates.map((d) => ({
797
933
  severity: d.severity === "critical" ? "critical" : d.severity === "major" ? "major" : "minor"
798
934
  }));
799
- const productivityImpact = (0, import_core5.calculateProductivityImpact)(issues);
935
+ const productivityImpact = (0, import_core10.calculateProductivityImpact)(issues);
800
936
  return {
801
937
  toolName: "pattern-detect",
802
938
  score: finalScore,
@@ -816,13 +952,118 @@ function calculatePatternScore(duplicates, totalFilesAnalyzed, costConfig) {
816
952
  };
817
953
  }
818
954
 
955
+ // src/analyzer.ts
956
+ async function analyzePatterns(options) {
957
+ const smartDefaults = await getSmartDefaults(options.rootDir || ".", options);
958
+ const finalOptions = { ...smartDefaults, ...options };
959
+ const {
960
+ minSimilarity = 0.4,
961
+ minLines = 5,
962
+ batchSize = 100,
963
+ approx = true,
964
+ minSharedTokens = 8,
965
+ maxCandidatesPerBlock = 100,
966
+ streamResults = false,
967
+ severity = "all",
968
+ groupByFilePair = true,
969
+ createClusters = true,
970
+ minClusterTokenCost = 1e3,
971
+ minClusterFiles = 3,
972
+ excludePatterns = [],
973
+ confidenceThreshold = 0,
974
+ ignoreWhitelist = [],
975
+ ...scanOptions
976
+ } = finalOptions;
977
+ const files = await (0, import_core11.scanFiles)(scanOptions);
978
+ const estimatedBlocks = files.length * 3;
979
+ logConfiguration(finalOptions, estimatedBlocks);
980
+ const results = [];
981
+ const READ_BATCH_SIZE = 50;
982
+ const fileContents = [];
983
+ for (let i = 0; i < files.length; i += READ_BATCH_SIZE) {
984
+ const batch = files.slice(i, i + READ_BATCH_SIZE);
985
+ const batchContents = await Promise.all(
986
+ batch.map(async (file) => ({
987
+ file,
988
+ content: await (0, import_core11.readFileContent)(file)
989
+ }))
990
+ );
991
+ fileContents.push(...batchContents);
992
+ }
993
+ const duplicates = await detectDuplicatePatterns(fileContents, {
994
+ minSimilarity,
995
+ minLines,
996
+ batchSize,
997
+ approx,
998
+ minSharedTokens,
999
+ maxCandidatesPerBlock,
1000
+ streamResults,
1001
+ excludePatterns,
1002
+ confidenceThreshold,
1003
+ ignoreWhitelist,
1004
+ onProgress: options.onProgress
1005
+ });
1006
+ filterBrandSpecificVariants(duplicates);
1007
+ for (const file of files) {
1008
+ const fileDuplicates = duplicates.filter(
1009
+ (dup) => dup.file1 === file || dup.file2 === file
1010
+ );
1011
+ const issues = fileDuplicates.map((dup) => {
1012
+ const otherFile = dup.file1 === file ? dup.file2 : dup.file1;
1013
+ let severityLevel;
1014
+ if (dup.severity === "info" || dup.severity === "Info") {
1015
+ severityLevel = import_core11.Severity.Info;
1016
+ } else {
1017
+ severityLevel = calculateSeverity2(dup.similarity);
1018
+ }
1019
+ return {
1020
+ type: import_core11.IssueType.DuplicatePattern,
1021
+ severity: severityLevel,
1022
+ message: `${dup.patternType} pattern ${Math.round(dup.similarity * 100)}% similar to ${otherFile} (${dup.tokenCost} tokens wasted)`,
1023
+ location: {
1024
+ file,
1025
+ line: dup.file1 === file ? dup.line1 : dup.line2
1026
+ },
1027
+ suggestion: getRefactoringSuggestion(dup.patternType, dup.similarity)
1028
+ };
1029
+ });
1030
+ const filteredIssues = filterBySeverity2(issues, severity || "all");
1031
+ const totalTokenCost = fileDuplicates.reduce(
1032
+ (sum, dup) => sum + dup.tokenCost,
1033
+ 0
1034
+ );
1035
+ results.push({
1036
+ fileName: file,
1037
+ issues: filteredIssues,
1038
+ metrics: {
1039
+ tokenCost: totalTokenCost,
1040
+ consistencyScore: Math.max(0, 1 - fileDuplicates.length * 0.1)
1041
+ }
1042
+ });
1043
+ }
1044
+ let groups;
1045
+ let clusters;
1046
+ if (groupByFilePair) {
1047
+ groups = groupDuplicatesByFilePair(duplicates);
1048
+ }
1049
+ if (createClusters) {
1050
+ const allClusters = createRefactorClusters(duplicates);
1051
+ clusters = filterClustersByImpact(
1052
+ allClusters,
1053
+ minClusterTokenCost,
1054
+ minClusterFiles
1055
+ );
1056
+ }
1057
+ return { results, duplicates, files, groups, clusters, config: finalOptions };
1058
+ }
1059
+
819
1060
  // src/provider.ts
820
- var PatternDetectProvider = {
821
- id: import_core6.ToolName.PatternDetect,
1061
+ var PATTERN_DETECT_PROVIDER = {
1062
+ id: import_core12.ToolName.PatternDetect,
822
1063
  alias: ["patterns", "duplicates", "duplication"],
823
1064
  async analyze(options) {
824
1065
  const results = await analyzePatterns(options);
825
- return import_core6.SpokeOutputSchema.parse({
1066
+ return import_core12.SpokeOutputSchema.parse({
826
1067
  results: results.results,
827
1068
  summary: {
828
1069
  totalFiles: results.files.length,
@@ -835,12 +1076,12 @@ var PatternDetectProvider = {
835
1076
  clusters: results.clusters,
836
1077
  config: Object.fromEntries(
837
1078
  Object.entries(results.config).filter(
838
- ([key]) => !import_core6.GLOBAL_SCAN_OPTIONS.includes(key) || key === "rootDir"
1079
+ ([key]) => !import_core12.GLOBAL_SCAN_OPTIONS.includes(key) || key === "rootDir"
839
1080
  )
840
1081
  )
841
1082
  },
842
1083
  metadata: {
843
- toolName: import_core6.ToolName.PatternDetect,
1084
+ toolName: import_core12.ToolName.PatternDetect,
844
1085
  version: "0.12.5",
845
1086
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
846
1087
  }
@@ -848,31 +1089,43 @@ var PatternDetectProvider = {
848
1089
  },
849
1090
  score(output, options) {
850
1091
  const duplicates = output.summary.duplicates || [];
851
- const scoreData = duplicates;
852
1092
  const totalFiles = output.summary.totalFiles || output.results.length;
1093
+ let scoreData = duplicates;
1094
+ const patternOptions = options;
1095
+ if (patternOptions.severity && patternOptions.severity !== "all") {
1096
+ const severityMap = {
1097
+ critical: import_core12.Severity.Critical,
1098
+ high: import_core12.Severity.Major,
1099
+ // 'high' maps to Major and above
1100
+ medium: import_core12.Severity.Minor,
1101
+ all: import_core12.Severity.Info
1102
+ };
1103
+ const minSeverity = severityMap[patternOptions.severity] || import_core12.Severity.Info;
1104
+ scoreData = (0, import_core5.filterBySeverity)(duplicates, minSeverity);
1105
+ }
853
1106
  return calculatePatternScore(scoreData, totalFiles, options.costConfig);
854
1107
  },
855
1108
  defaultWeight: 22
856
1109
  };
857
1110
 
858
1111
  // src/index.ts
859
- import_core7.ToolRegistry.register(PatternDetectProvider);
1112
+ import_core13.ToolRegistry.register(PATTERN_DETECT_PROVIDER);
860
1113
  // Annotate the CommonJS export names for ESM import in node:
861
1114
  0 && (module.exports = {
862
- CONTEXT_RULES,
863
- IssueType,
864
- PatternDetectProvider,
1115
+ PATTERN_DETECT_PROVIDER,
865
1116
  Severity,
866
1117
  analyzePatterns,
1118
+ areBrandSpecificVariants,
867
1119
  calculatePatternScore,
868
1120
  calculateSeverity,
869
1121
  createRefactorClusters,
870
1122
  detectDuplicatePatterns,
1123
+ filterBrandSpecificVariants,
871
1124
  filterBySeverity,
872
1125
  filterClustersByImpact,
873
1126
  generateSummary,
874
1127
  getSeverityLabel,
875
- getSeverityThreshold,
876
1128
  getSmartDefaults,
877
- groupDuplicatesByFilePair
1129
+ groupDuplicatesByFilePair,
1130
+ logConfiguration
878
1131
  });