@aiready/pattern-detect 0.17.14 → 0.17.16

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 (74) hide show
  1. package/dist/analyzer-entry/index.d.mts +2 -2
  2. package/dist/analyzer-entry/index.d.ts +2 -2
  3. package/dist/analyzer-entry/index.js +391 -89
  4. package/dist/analyzer-entry/index.mjs +4 -4
  5. package/dist/chunk-3LMYFYWG.mjs +514 -0
  6. package/dist/chunk-4SKGAZEW.mjs +514 -0
  7. package/dist/chunk-4YXKUW4P.mjs +143 -0
  8. package/dist/chunk-5A3ULAQ5.mjs +571 -0
  9. package/dist/chunk-5FACKJ7M.mjs +519 -0
  10. package/dist/chunk-6B72OWZA.mjs +143 -0
  11. package/dist/chunk-6SHBBRHF.mjs +600 -0
  12. package/dist/chunk-ATXO4JL7.mjs +404 -0
  13. package/dist/chunk-BKSIA7A2.mjs +516 -0
  14. package/dist/chunk-CM5YJR7G.mjs +516 -0
  15. package/dist/chunk-F42Q2M4O.mjs +143 -0
  16. package/dist/chunk-FSXOU23F.mjs +620 -0
  17. package/dist/chunk-GUYQI3AF.mjs +514 -0
  18. package/dist/chunk-H2TGXGMX.mjs +587 -0
  19. package/dist/chunk-JWP5TCDM.mjs +143 -0
  20. package/dist/chunk-KDXWIT6W.mjs +408 -0
  21. package/dist/chunk-KMAOEVRS.mjs +150 -0
  22. package/dist/chunk-KZQXBBR3.mjs +143 -0
  23. package/dist/chunk-NVV4UFIV.mjs +514 -0
  24. package/dist/chunk-NWG2ZIGX.mjs +146 -0
  25. package/dist/chunk-OFVJFGQW.mjs +514 -0
  26. package/dist/chunk-PCCZREHY.mjs +143 -0
  27. package/dist/chunk-PFA2DO73.mjs +392 -0
  28. package/dist/chunk-PQS5ACTN.mjs +516 -0
  29. package/dist/chunk-TVE75IDM.mjs +143 -0
  30. package/dist/chunk-UDOGQ42Q.mjs +603 -0
  31. package/dist/chunk-UFI4UDQI.mjs +514 -0
  32. package/dist/chunk-UXV57HN3.mjs +144 -0
  33. package/dist/chunk-VC2BOV6R.mjs +143 -0
  34. package/dist/chunk-VI2OVG73.mjs +514 -0
  35. package/dist/chunk-VKGYNHFY.mjs +514 -0
  36. package/dist/chunk-WBLZYAQ2.mjs +518 -0
  37. package/dist/chunk-WFVXMMB3.mjs +143 -0
  38. package/dist/chunk-WQC43BIO.mjs +516 -0
  39. package/dist/chunk-WQX7IHAN.mjs +514 -0
  40. package/dist/chunk-WTAIM3SG.mjs +146 -0
  41. package/dist/chunk-XC7U55PE.mjs +514 -0
  42. package/dist/chunk-XR373Q6G.mjs +146 -0
  43. package/dist/chunk-XWIBTD67.mjs +620 -0
  44. package/dist/chunk-YUQ2VQVJ.mjs +514 -0
  45. package/dist/chunk-Z4NOH52X.mjs +143 -0
  46. package/dist/cli.js +395 -93
  47. package/dist/cli.mjs +8 -8
  48. package/dist/context-rules-entry/index.js +385 -88
  49. package/dist/context-rules-entry/index.mjs +1 -1
  50. package/dist/detector-entry/index.d.mts +2 -2
  51. package/dist/detector-entry/index.d.ts +2 -2
  52. package/dist/detector-entry/index.js +389 -89
  53. package/dist/detector-entry/index.mjs +2 -2
  54. package/dist/index-BGvkJ9j1.d.mts +136 -0
  55. package/dist/index-BJq32qmj.d.mts +137 -0
  56. package/dist/index-BpoJSgX-.d.mts +136 -0
  57. package/dist/index-C7qLPKmH.d.ts +150 -0
  58. package/dist/index-CThnG9hv.d.ts +155 -0
  59. package/dist/index-D0Hpg9nN.d.mts +150 -0
  60. package/dist/index-DN6XpBOW.d.mts +155 -0
  61. package/dist/index-F8xqZ2PS.d.ts +136 -0
  62. package/dist/index-HNhDr6CV.d.ts +137 -0
  63. package/dist/index-ux0Wo8Ps.d.ts +136 -0
  64. package/dist/index.d.mts +2 -2
  65. package/dist/index.d.ts +2 -2
  66. package/dist/index.js +393 -91
  67. package/dist/index.mjs +6 -6
  68. package/dist/scoring-entry/index.d.mts +1 -1
  69. package/dist/scoring-entry/index.d.ts +1 -1
  70. package/dist/scoring-entry/index.js +2 -2
  71. package/dist/scoring-entry/index.mjs +1 -1
  72. package/dist/types-tgrmUrHE.d.mts +37 -0
  73. package/dist/types-tgrmUrHE.d.ts +37 -0
  74. package/package.json +8 -6
package/dist/cli.js CHANGED
@@ -164,124 +164,421 @@ var INFRA_RULES = [
164
164
  severity: import_core3.Severity.Info,
165
165
  reason: "CLI command definitions follow standard Commander.js patterns and are intentionally similar",
166
166
  suggestion: "Command boilerplate duplication is acceptable for CLI interfaces"
167
- }
168
- ];
169
-
170
- // src/rules/categories/logic-rules.ts
171
- var import_core4 = require("@aiready/core");
172
- var LOGIC_RULES = [
173
- // Re-export / Barrel files - Intentional API surface consolidation
167
+ },
168
+ // DynamoDB Single-Table Design - Standard single-table patterns with prefixed keys
174
169
  {
175
- name: "re-export-files",
170
+ name: "dynamodb-single-table",
176
171
  detect: (file, code) => {
177
- const isIndexFile = file.endsWith("/index.ts") || file.endsWith("/index.js") || file.endsWith("/index.tsx") || file.endsWith("/index.jsx");
178
- const lines = code.split("\n").filter((l) => l.trim());
179
- if (lines.length === 0) return false;
180
- const reExportLines = lines.filter(
181
- (l) => /^export\s+(\{[^}]+\}|\*)\s+from\s+/.test(l.trim()) || /^export\s+\*\s+as\s+\w+\s+from\s+/.test(l.trim())
182
- ).length;
183
- return isIndexFile && reExportLines > 0 && reExportLines / lines.length > 0.5;
172
+ const hasDynamoDBPattern = code.includes("docClient") || code.includes("dynamodb") || code.includes("DynamoDB") || code.includes("queryItems") || code.includes("putItem") || code.includes("getItem") || code.includes("updateItem") || code.includes("deleteItem");
173
+ const hasKeyPrefix = code.includes("userId:") && code.includes("#") || code.includes("pk:") && code.includes("#") || code.includes("Key:") && code.includes("#") || /[A-Z]+#/.test(code);
174
+ const hasSingleTablePattern = code.includes("KeyConditionExpression") || code.includes("pk =") || code.includes("sk =") || code.includes("userId") && code.includes("timestamp");
175
+ return hasDynamoDBPattern && (hasKeyPrefix || hasSingleTablePattern);
184
176
  },
185
- severity: import_core4.Severity.Info,
186
- reason: "Barrel/index files intentionally re-export for API surface consolidation",
187
- suggestion: "Re-exports in barrel files are expected and not true duplication"
177
+ severity: import_core3.Severity.Info,
178
+ reason: "DynamoDB single-table design with prefixed keys is a standard pattern for efficient data access",
179
+ suggestion: "Single-table query patterns are intentionally similar and should not be refactored"
188
180
  },
189
- // Type Definitions - Duplication for type safety and module independence
181
+ // CLI Main Function Boilerplate - Standard argument parsing patterns
190
182
  {
191
- name: "type-definitions",
183
+ name: "cli-main-boilerplate",
192
184
  detect: (file, code) => {
193
- const isTypeFile = file.endsWith(".d.ts") || file.includes("/types/");
194
- 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");
195
- const isInterfaceOnlySnippet = code.trim().startsWith("interface ") && code.includes("{") && code.includes("}") && !code.includes("function ") && !code.includes("const ") && !code.includes("return ");
196
- return isTypeFile && hasOnlyTypeDefinitions || isInterfaceOnlySnippet;
185
+ const basename = file.split("/").pop() || "";
186
+ const isCliFile = file.includes("/cli/") || file.includes("/commands/") || basename.startsWith("cli") || basename.endsWith(".cli.ts") || basename.endsWith(".cli.js");
187
+ const hasMainFunction = code.includes("function main()") || code.includes("async function main()") || code.includes("const main =") || code.includes("main()");
188
+ const hasArgParsing = code.includes("process.argv") || code.includes("yargs") || code.includes("commander") || code.includes("minimist") || code.includes(".parse(") || code.includes("args") && code.includes("._");
189
+ return isCliFile && hasMainFunction && hasArgParsing;
197
190
  },
191
+ severity: import_core3.Severity.Info,
192
+ reason: "CLI main functions with argument parsing follow standard boilerplate patterns",
193
+ suggestion: "CLI argument parsing boilerplate is acceptable and should not be flagged as duplication"
194
+ }
195
+ ];
196
+
197
+ // src/rules/categories/logic/file-detectors.ts
198
+ var FileDetectors = {
199
+ isIndexFile: (file) => file.endsWith("/index.ts") || file.endsWith("/index.js") || file.endsWith("/index.tsx") || file.endsWith("/index.jsx"),
200
+ isTypeFile: (file) => file.endsWith(".d.ts") || file.includes("/types/"),
201
+ isUtilFile: (file) => file.endsWith(".util.ts") || file.endsWith(".helper.ts") || file.endsWith(".utils.ts"),
202
+ isHookFile: (file) => file.includes("/hooks/") || file.endsWith(".hook.ts") || file.endsWith(".hook.tsx"),
203
+ isHelperFile: (file) => file.includes("/utils/") || file.includes("/helpers/") || file.endsWith(".util.ts"),
204
+ isVizFile: (file) => file.includes("/visualizer/") || file.includes("/charts/") || file.includes("GraphCanvas") || file.includes("ForceDirected"),
205
+ isApiFile: (file) => file.includes("/api/") || file.includes("/lib/") || file.includes("/utils/") || file.endsWith(".ts"),
206
+ isPackageOrApp: (file) => file.includes("/packages/") || file.includes("/apps/") || file.includes("/core/"),
207
+ getPackageName: (file) => {
208
+ const match = file.match(/\/(packages|apps|core)\/([^/]+)\//);
209
+ return match?.[2] || null;
210
+ }
211
+ };
212
+
213
+ // src/rules/categories/logic/keyword-lists.ts
214
+ var VALIDATION_KEYWORDS = [
215
+ "isValid",
216
+ "validate",
217
+ "checkValid",
218
+ "isEmail",
219
+ "isPhone",
220
+ "isUrl",
221
+ "isNumeric",
222
+ "isAlpha",
223
+ "isAlphanumeric",
224
+ "isEmpty",
225
+ "isNotEmpty",
226
+ "isRequired",
227
+ "isOptional"
228
+ ];
229
+ var HOOK_KEYWORDS = [
230
+ "function use",
231
+ "export function use",
232
+ "const use",
233
+ "export const use"
234
+ ];
235
+ var VIZ_EVENT_KEYWORDS = ["dragstarted", "dragged", "dragended"];
236
+ var VIZ_LIB_KEYWORDS = ["simulation", "d3.", "alphaTarget"];
237
+ var ICON_HELPER_KEYWORDS = [
238
+ "getIcon",
239
+ "getColor",
240
+ "getLabel",
241
+ "getRating"
242
+ ];
243
+ var UTIL_KEYWORDS = [
244
+ "format",
245
+ "parse",
246
+ "sanitize",
247
+ "normalize",
248
+ "convert",
249
+ "utility",
250
+ "helper"
251
+ ];
252
+
253
+ // src/rules/categories/logic/detectors.ts
254
+ var isEnumDefinition = (code) => /(?:export\s+)?(?:const\s+)?enum\s+/.test(code) || code.includes("enum ") && code.includes("{") && code.includes("}");
255
+ var hasSingletonGetter = (code) => /(?:export\s+)?(?:async\s+)?function\s+get[A-Z][a-zA-Z0-9]*\s*\(/.test(
256
+ code
257
+ ) || /(?:export\s+)?const\s+get[A-Z][a-zA-Z0-9]*\s*=\s*(?:async\s+)?\(\)\s*=>/.test(
258
+ code
259
+ );
260
+ var hasSingletonPattern = (code) => code.includes("if (!") && code.includes("instance") && code.includes(" = ") || code.includes("if (!_") && code.includes(" = new ");
261
+ var hasReExportPattern = (code) => {
262
+ const lines = code.split("\n").filter((l) => l.trim());
263
+ if (lines.length === 0) return false;
264
+ const reExportLines = lines.filter(
265
+ (l) => /^export\s+(\{[^}]+\}|\*)\s+from\s+/.test(l.trim()) || /^export\s+\*\s+as\s+\w+\s+from\s+/.test(l.trim())
266
+ ).length;
267
+ return reExportLines > 0 && reExportLines / lines.length > 0.5;
268
+ };
269
+ var isInterfaceOnlySnippet = (code) => {
270
+ const hasInterface = code.includes("interface ");
271
+ const hasType = code.includes("type ");
272
+ const hasEnum = code.includes("enum ");
273
+ const hasNoImpl = ![
274
+ "function ",
275
+ "class ",
276
+ "const ",
277
+ "let ",
278
+ "var ",
279
+ "export default",
280
+ "export {"
281
+ ].some((kw) => code.includes(kw));
282
+ return (hasInterface || hasType || hasEnum) && hasNoImpl;
283
+ };
284
+
285
+ // src/rules/categories/logic/code-patterns.ts
286
+ var COMMON_ENUM_PATTERNS = [
287
+ ["LOW", ["'low'", "0", "'LOW'"]],
288
+ ["HIGH", ["'high'", "2", "'HIGH'"]],
289
+ ["MEDIUM", ["'medium'", "1", "'MEDIUM'"]]
290
+ ];
291
+ var hasEnumValue = (code, enumName, variants) => variants.some((v) => code.includes(`${enumName} = ${v}`));
292
+ var CodePatterns = {
293
+ // Enum patterns
294
+ hasCommonEnumValues: (code) => COMMON_ENUM_PATTERNS.every(
295
+ ([name, variants]) => hasEnumValue(code, name, variants)
296
+ ),
297
+ isEnumDefinition,
298
+ // Type patterns - helpers for checking type-only definitions
299
+ hasTypeDefinition: (code) => code.includes("interface ") || code.includes("type ") || code.includes("enum "),
300
+ isTypeOnlyFile: (code) => {
301
+ const hasTypes = CodePatterns.hasTypeDefinition(code);
302
+ const hasNoImpl = ![
303
+ "function ",
304
+ "class ",
305
+ "const ",
306
+ "let ",
307
+ "export default"
308
+ ].some((kw) => code.includes(kw));
309
+ return hasTypes && hasNoImpl;
310
+ },
311
+ hasOnlyTypeDefinitions: (code) => {
312
+ const hasTypes = CodePatterns.hasTypeDefinition(code);
313
+ const hasNoImpl = ![
314
+ "function ",
315
+ "class ",
316
+ "const ",
317
+ "let ",
318
+ "var ",
319
+ "export default",
320
+ "export {"
321
+ ].some((kw) => code.includes(kw));
322
+ return hasTypes && hasNoImpl;
323
+ },
324
+ // Utility patterns - function group patterns
325
+ hasUtilPattern: (code) => CodePatterns.hasFunctionGroup(code, "") || CodePatterns.hasKeywordGroup(code, UTIL_KEYWORDS),
326
+ hasFunctionGroup: (code, prefix) => {
327
+ const regex = new RegExp(
328
+ `${prefix}(format|parse|sanitize|normalize|convert)`,
329
+ "i"
330
+ );
331
+ return regex.test(code);
332
+ },
333
+ // Pattern checkers using keyword lists
334
+ hasKeywordGroup: (code, keywords) => keywords.some((kw) => code.includes(kw)),
335
+ // Validation patterns - unified keyword check
336
+ hasValidationPattern: (code) => CodePatterns.hasKeywordGroup(code, VALIDATION_KEYWORDS),
337
+ // Hook patterns - React/Vue hooks
338
+ hasHookPattern: (code) => CodePatterns.hasKeywordGroup(code, HOOK_KEYWORDS),
339
+ // Score/Rating patterns
340
+ hasScorePattern: (code) => (code.includes("if (score >=") || code.includes("if (score >")) && code.includes("return") && code.includes("'") && code.split("if (score").length >= 3,
341
+ // Visualization patterns - D3/Canvas
342
+ hasVizPattern: (code) => CodePatterns.hasKeywordGroup(code, VIZ_EVENT_KEYWORDS) && CodePatterns.hasKeywordGroup(code, VIZ_LIB_KEYWORDS),
343
+ // Switch/Icon patterns
344
+ hasSwitchPattern: (code) => code.includes("switch (") && code.includes("case '") && code.includes("return") && code.split("case ").length >= 4,
345
+ hasIconPattern: (code) => CodePatterns.hasKeywordGroup(code, ICON_HELPER_KEYWORDS),
346
+ // Singleton patterns
347
+ hasSingletonGetter,
348
+ hasSingletonPattern,
349
+ // Re-export patterns
350
+ hasReExportPattern,
351
+ // Interface-only snippets
352
+ isInterfaceOnlySnippet
353
+ };
354
+
355
+ // src/rules/categories/logic/api-patterns.ts
356
+ var API_PATTERNS = {
357
+ stripe: {
358
+ functions: ["getStripe"],
359
+ keywords: ["process.env.STRIPE_SECRET_KEY"]
360
+ },
361
+ userManagement: {
362
+ functions: ["getUserByEmail"],
363
+ keywords: ["queryItems"]
364
+ },
365
+ userUpdate: {
366
+ functions: ["updateUser"],
367
+ keywords: ["buildUpdateExpression"]
368
+ },
369
+ repositoryListing: {
370
+ functions: ["listUserRepositories", "listTeamRepositories"],
371
+ keywords: ["queryItems"]
372
+ },
373
+ remediationQueries: {
374
+ functions: ["getRemediation"],
375
+ keywords: ["queryItems"]
376
+ },
377
+ keyFormatting: {
378
+ functions: ["formatBreakdownKey"],
379
+ keywords: [".replace(/([A-Z])/g"]
380
+ },
381
+ dynamoDbQueries: {
382
+ functions: ["queryItems"],
383
+ keywords: ["KeyConditionExpression"]
384
+ },
385
+ itemOperations: {
386
+ functions: ["putItem"],
387
+ keywords: ["createdAt"]
388
+ },
389
+ itemUpdates: {
390
+ functions: ["updateItem"],
391
+ keywords: ["buildUpdateExpression"]
392
+ }
393
+ };
394
+ var ApiPatterns = {
395
+ /**
396
+ * Check if code contains a common API pattern
397
+ * Reduces from 11 separate || conditions to single call
398
+ */
399
+ hasCommonApiPattern: (code) => {
400
+ for (const pattern of Object.values(API_PATTERNS)) {
401
+ const hasFunctions = pattern.functions.some((fn) => code.includes(fn));
402
+ const hasKeywords = pattern.keywords.some((kw) => code.includes(kw));
403
+ if (hasFunctions && hasKeywords) {
404
+ return true;
405
+ }
406
+ }
407
+ return false;
408
+ },
409
+ /**
410
+ * Get all API patterns (useful for documentation/analysis)
411
+ */
412
+ getPatterns: () => API_PATTERNS,
413
+ /**
414
+ * Check if code matches a specific API pattern by name
415
+ */
416
+ matchesPattern: (code, patternName) => {
417
+ const pattern = API_PATTERNS[patternName];
418
+ if (!pattern) return false;
419
+ const hasFunctions = pattern.functions.some((fn) => code.includes(fn));
420
+ const hasKeywords = pattern.keywords.some((kw) => code.includes(kw));
421
+ return hasFunctions && hasKeywords;
422
+ }
423
+ };
424
+
425
+ // src/rules/categories/logic/rule-builders.ts
426
+ var import_core4 = require("@aiready/core");
427
+ function createRule(config) {
428
+ return {
429
+ name: config.name,
430
+ detect: config.detect,
431
+ severity: config.severity || import_core4.Severity.Info,
432
+ reason: config.reason,
433
+ suggestion: config.suggestion
434
+ };
435
+ }
436
+ var RuleTemplates = {
437
+ // Type-related rules
438
+ typeDefinition: {
198
439
  severity: import_core4.Severity.Info,
199
440
  reason: "Type/interface definitions are intentionally duplicated for module independence",
200
441
  suggestion: "Extract to shared types package only if causing maintenance burden"
201
442
  },
202
- // Utility Functions - Small helpers in dedicated utility files
203
- {
204
- name: "utility-functions",
205
- detect: (file, code) => {
206
- const isUtilFile = file.endsWith(".util.ts") || file.endsWith(".helper.ts") || file.endsWith(".utils.ts");
207
- const hasUtilPattern = code.includes("function format") || code.includes("function parse") || code.includes("function sanitize") || code.includes("function normalize") || code.includes("function convert");
208
- return isUtilFile && hasUtilPattern;
209
- },
443
+ crossPackageType: {
210
444
  severity: import_core4.Severity.Info,
211
- reason: "Utility functions in dedicated utility files may be intentionally similar",
445
+ reason: "Types in different packages/modules are often intentionally similar for module independence",
446
+ suggestion: "Cross-package type duplication is acceptable for decoupled module architecture"
447
+ },
448
+ // Pattern rules
449
+ standardPatterns: {
450
+ severity: import_core4.Severity.Info,
451
+ reason: "This pattern is inherently similar and intentionally duplicated across modules",
452
+ suggestion: "Pattern duplication is acceptable for domain clarity and independence"
453
+ },
454
+ // API rules
455
+ commonApiFunction: {
456
+ severity: import_core4.Severity.Info,
457
+ reason: "Common API/utility functions are legitimately duplicated across modules for clarity and independence",
212
458
  suggestion: "Consider extracting to shared utilities only if causing significant duplication"
213
459
  },
214
- // React/Vue Hooks - Standard patterns
215
- {
216
- name: "shared-hooks",
460
+ // Utility rules
461
+ utilityFunction: {
462
+ severity: import_core4.Severity.Info,
463
+ reason: "Utility functions may be intentionally similar across modules",
464
+ suggestion: "Consider extracting to shared utilities only if causing significant duplication"
465
+ },
466
+ // Boilerplate rules
467
+ boilerplate: {
468
+ severity: import_core4.Severity.Info,
469
+ reason: "This pattern is boilerplate and acceptable duplication",
470
+ suggestion: "Boilerplate reduction is acceptable for standard patterns"
471
+ },
472
+ // Enum rules
473
+ enumSemantic: {
474
+ severity: import_core4.Severity.Info,
475
+ reason: "Enums with different names represent different semantic domain concepts, even if they share similar values",
476
+ suggestion: "Different enums (e.g., EscalationPriority vs HealthSeverity) serve different purposes and should not be merged"
477
+ },
478
+ enumValue: {
479
+ severity: import_core4.Severity.Info,
480
+ reason: "Common enum values (LOW, MEDIUM, HIGH, CRITICAL) are standard patterns used across different domain enums",
481
+ suggestion: "Enum value similarity is expected for severity/priority enums and should not be flagged as duplication"
482
+ },
483
+ // Barrel file rules
484
+ barrelFile: {
485
+ severity: import_core4.Severity.Info,
486
+ reason: "Barrel/index files intentionally re-export for API surface consolidation",
487
+ suggestion: "Re-exports in barrel files are expected and not true duplication"
488
+ }
489
+ };
490
+
491
+ // src/rules/categories/logic-rules.ts
492
+ var LOGIC_RULES = [
493
+ // Enum patterns - identify by semantic meaning and value patterns
494
+ createRule({
495
+ name: "enum-semantic-difference",
496
+ detect: (file, code) => CodePatterns.isEnumDefinition(code),
497
+ ...RuleTemplates.enumSemantic
498
+ }),
499
+ createRule({
500
+ name: "enum-value-similarity",
501
+ detect: (file, code) => CodePatterns.hasCommonEnumValues(code) && CodePatterns.isEnumDefinition(code),
502
+ ...RuleTemplates.enumValue
503
+ }),
504
+ // Barrel/re-export files - intentional API consolidation
505
+ createRule({
506
+ name: "re-export-files",
507
+ detect: (file, code) => FileDetectors.isIndexFile(file) && CodePatterns.hasReExportPattern(code),
508
+ ...RuleTemplates.barrelFile
509
+ }),
510
+ // Type definitions - consolidated rule for both local and cross-package types
511
+ // PHASE 2 CONSOLIDATION: merged type-definitions + cross-package-types
512
+ createRule({
513
+ name: "type-definitions",
217
514
  detect: (file, code) => {
218
- const isHookFile = file.includes("/hooks/") || file.endsWith(".hook.ts") || file.endsWith(".hook.tsx");
219
- const hasHookPattern = code.includes("function use") || code.includes("export function use") || code.includes("const use") || code.includes("export const use");
220
- return isHookFile && hasHookPattern;
515
+ if (FileDetectors.isTypeFile(file)) {
516
+ if (CodePatterns.hasOnlyTypeDefinitions(code)) return true;
517
+ }
518
+ if (CodePatterns.isInterfaceOnlySnippet(code)) return true;
519
+ if (FileDetectors.isPackageOrApp(file) && FileDetectors.getPackageName(file) !== null && CodePatterns.hasTypeDefinition(code)) {
520
+ return true;
521
+ }
522
+ return false;
221
523
  },
222
- severity: import_core4.Severity.Info,
524
+ reason: "Type/interface definitions are intentionally duplicated for module independence and decoupled module architecture",
525
+ suggestion: "Extract to shared types package only if causing significant maintenance burden or cross-package conflicts"
526
+ }),
527
+ // Utility functions - dedicated utility file patterns
528
+ createRule({
529
+ name: "utility-functions",
530
+ detect: (file, code) => FileDetectors.isUtilFile(file) && CodePatterns.hasUtilPattern(code),
531
+ ...RuleTemplates.utilityFunction
532
+ }),
533
+ // React/Vue Hooks - standard hook patterns
534
+ createRule({
535
+ name: "shared-hooks",
536
+ detect: (file, code) => FileDetectors.isHookFile(file) && CodePatterns.hasHookPattern(code),
223
537
  reason: "Hooks follow standard patterns and are often intentionally similar across components",
224
538
  suggestion: "Consider extracting common hook logic only if hooks become complex"
225
- },
226
- // Score/Rating Helper Functions - Common threshold patterns
227
- {
539
+ }),
540
+ // Score/Rating helpers - common threshold patterns
541
+ createRule({
228
542
  name: "score-helpers",
229
- detect: (file, code) => {
230
- const isHelperFile = file.includes("/utils/") || file.includes("/helpers/") || file.endsWith(".util.ts");
231
- const hasScorePattern = (code.includes("if (score >=") || code.includes("if (score >")) && code.includes("return") && code.includes("'") && code.split("if (score").length >= 3;
232
- return isHelperFile && hasScorePattern;
233
- },
234
- severity: import_core4.Severity.Info,
543
+ detect: (file, code) => (FileDetectors.isHelperFile(file) || file.includes("/utils/")) && CodePatterns.hasScorePattern(code),
235
544
  reason: "Score/rating helper functions use common threshold patterns that are intentionally similar",
236
545
  suggestion: "Score formatting duplication is acceptable for consistent UI display"
237
- },
238
- // D3/Canvas Event Handlers - Standard visualization patterns
239
- {
546
+ }),
547
+ // D3/Canvas event handlers - visualization patterns
548
+ createRule({
240
549
  name: "visualization-handlers",
241
- detect: (file, code) => {
242
- const isVizFile = file.includes("/visualizer/") || file.includes("/charts/") || file.includes("GraphCanvas") || file.includes("ForceDirected");
243
- const hasVizPattern = (code.includes("dragstarted") || code.includes("dragged") || code.includes("dragended")) && (code.includes("simulation") || code.includes("d3.") || code.includes("alphaTarget"));
244
- return isVizFile && hasVizPattern;
245
- },
246
- severity: import_core4.Severity.Info,
550
+ detect: (file, code) => FileDetectors.isVizFile(file) && CodePatterns.hasVizPattern(code),
247
551
  reason: "D3/visualization event handlers follow standard patterns and are intentionally similar",
248
552
  suggestion: "Visualization boilerplate duplication is acceptable for interactive charts"
249
- },
250
- // Icon/Switch Statement Helpers - Common enum-to-value patterns
251
- {
553
+ }),
554
+ // Switch statement helpers - enum-to-value mapping
555
+ createRule({
252
556
  name: "switch-helpers",
253
- detect: (file, code) => {
254
- const hasSwitchPattern = code.includes("switch (") && code.includes("case '") && code.includes("return") && code.split("case ").length >= 4;
255
- const hasIconPattern = code.includes("getIcon") || code.includes("getColor") || code.includes("getLabel") || code.includes("getRating");
256
- return hasSwitchPattern && hasIconPattern;
257
- },
258
- severity: import_core4.Severity.Info,
557
+ detect: (file, code) => CodePatterns.hasSwitchPattern(code) && CodePatterns.hasIconPattern(code),
259
558
  reason: "Switch statement helpers for enum-to-value mapping are inherently similar",
260
559
  suggestion: "Switch duplication is acceptable for mapping enums to display values"
261
- },
262
- // Common API/Utility Functions - Legitimate duplication across modules
263
- {
560
+ }),
561
+ // Common API/Utility functions - legitimately duplicated across modules
562
+ createRule({
264
563
  name: "common-api-functions",
265
- detect: (file, code) => {
266
- const isApiFile = file.includes("/api/") || file.includes("/lib/") || file.includes("/utils/") || file.endsWith(".ts");
267
- 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");
268
- return isApiFile && hasCommonApiPattern;
269
- },
270
- severity: import_core4.Severity.Info,
271
- reason: "Common API/utility functions are legitimately duplicated across modules for clarity and independence",
272
- suggestion: "Consider extracting to shared utilities only if causing significant duplication"
273
- },
274
- // Validation Functions - Inherently similar patterns
275
- {
564
+ detect: (file, code) => FileDetectors.isApiFile(file) && ApiPatterns.hasCommonApiPattern(code),
565
+ ...RuleTemplates.commonApiFunction
566
+ }),
567
+ // Validation functions - inherently similar patterns
568
+ // PHASE 2: Consolidated validation-function branches into single rule
569
+ createRule({
276
570
  name: "validation-functions",
277
- detect: (file, code) => {
278
- 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");
279
- return hasValidationPattern;
280
- },
281
- severity: import_core4.Severity.Info,
571
+ detect: (file, code) => CodePatterns.hasValidationPattern(code),
282
572
  reason: "Validation functions are inherently similar and often intentionally duplicated for domain clarity",
283
573
  suggestion: "Consider extracting to shared validators only if validation logic becomes complex"
284
- }
574
+ }),
575
+ // Singleton getter pattern - standard initialization
576
+ createRule({
577
+ name: "singleton-getter",
578
+ detect: (file, code) => CodePatterns.hasSingletonGetter(code) && CodePatterns.hasSingletonPattern(code),
579
+ reason: "Singleton getter functions follow standard initialization pattern and are intentionally similar",
580
+ suggestion: "Singleton getters are boilerplate and acceptable duplication for lazy initialization"
581
+ })
285
582
  ];
286
583
 
287
584
  // src/context-rules.ts
@@ -314,7 +611,7 @@ function calculateSeverity(file1, file2, code, similarity, linesOfCode) {
314
611
  reason: "Nearly identical code should be consolidated",
315
612
  suggestion: "Move to shared utility file"
316
613
  };
317
- } else if (similarity >= 0.85) {
614
+ } else if (similarity >= 0.85 && linesOfCode >= 10) {
318
615
  return {
319
616
  severity: import_core5.Severity.Major,
320
617
  reason: "High similarity indicates significant duplication",
@@ -368,7 +665,10 @@ async function detectDuplicatePatterns(fileContents, options) {
368
665
  ignoreWhitelist = []
369
666
  } = options;
370
667
  const allBlocks = [];
371
- const excludeRegexes = excludePatterns.map((p) => new RegExp(p, "i"));
668
+ const regexStr = (f) => f.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, "[^/]*").replace(/\[\^\/\]\*\[\^\/\]\*/g, ".*");
669
+ const excludeRegexes = excludePatterns.map(
670
+ (p) => new RegExp(`${regexStr(p)}$`, "i")
671
+ );
372
672
  for (const { file, content } of fileContents) {
373
673
  const blocks = extractBlocks(file, content);
374
674
  for (const b of blocks) {
@@ -857,6 +1157,7 @@ async function analyzePatterns(options) {
857
1157
  minClusterTokenCost = 1e3,
858
1158
  minClusterFiles = 3,
859
1159
  excludePatterns = [],
1160
+ excludeFiles = [],
860
1161
  confidenceThreshold = 0,
861
1162
  ignoreWhitelist = [],
862
1163
  ...scanOptions
@@ -886,6 +1187,7 @@ async function analyzePatterns(options) {
886
1187
  maxCandidatesPerBlock,
887
1188
  streamResults,
888
1189
  excludePatterns,
1190
+ excludeFiles,
889
1191
  confidenceThreshold,
890
1192
  ignoreWhitelist,
891
1193
  onProgress: options.onProgress
@@ -1080,8 +1382,8 @@ function generateHTMLReport(results, summary) {
1080
1382
  {
1081
1383
  title: "Pattern Detection Report",
1082
1384
  packageName: "pattern-detect",
1083
- packageUrl: "https://github.com/caopengau/aiready-pattern-detect",
1084
- bugUrl: "https://github.com/caopengau/aiready-pattern-detect/issues",
1385
+ packageUrl: "https://github.com/getaiready/aiready-pattern-detect",
1386
+ bugUrl: "https://github.com/getaiready/aiready-pattern-detect/issues",
1085
1387
  version: metadata.version,
1086
1388
  emoji: "\u{1F50D}"
1087
1389
  },
@@ -1427,12 +1729,12 @@ function printGuidance() {
1427
1729
  function printFooter() {
1428
1730
  console.log(
1429
1731
  import_chalk2.default.dim(
1430
- "\n\u2B50 Like AIReady? Star us on GitHub: https://github.com/caopengau/aiready-pattern-detect"
1732
+ "\n\u2B50 Like AIReady? Star us on GitHub: https://github.com/getaiready/aiready-pattern-detect"
1431
1733
  )
1432
1734
  );
1433
1735
  console.log(
1434
1736
  import_chalk2.default.dim(
1435
- "\u{1F41B} Found a bug? Report it: https://github.com/caopengau/aiready-pattern-detect/issues\n"
1737
+ "\u{1F41B} Found a bug? Report it: https://github.com/getaiready/aiready-pattern-detect/issues\n"
1436
1738
  )
1437
1739
  );
1438
1740
  }
package/dist/cli.mjs CHANGED
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  analyzePatterns,
4
4
  generateSummary
5
- } from "./chunk-C4ZGC4KA.mjs";
6
- import "./chunk-RH5JPWEC.mjs";
7
- import "./chunk-G3GZFYRI.mjs";
5
+ } from "./chunk-WQC43BIO.mjs";
6
+ import "./chunk-XR373Q6G.mjs";
8
7
  import {
9
8
  filterBySeverity
10
- } from "./chunk-UKQFCUQA.mjs";
9
+ } from "./chunk-XWIBTD67.mjs";
10
+ import "./chunk-UXV57HN3.mjs";
11
11
 
12
12
  // src/cli.ts
13
13
  import { Command } from "commander";
@@ -157,8 +157,8 @@ function generateHTMLReport(results, summary) {
157
157
  {
158
158
  title: "Pattern Detection Report",
159
159
  packageName: "pattern-detect",
160
- packageUrl: "https://github.com/caopengau/aiready-pattern-detect",
161
- bugUrl: "https://github.com/caopengau/aiready-pattern-detect/issues",
160
+ packageUrl: "https://github.com/getaiready/aiready-pattern-detect",
161
+ bugUrl: "https://github.com/getaiready/aiready-pattern-detect/issues",
162
162
  version: metadata.version,
163
163
  emoji: "\u{1F50D}"
164
164
  },
@@ -509,12 +509,12 @@ function printGuidance() {
509
509
  function printFooter() {
510
510
  console.log(
511
511
  chalk2.dim(
512
- "\n\u2B50 Like AIReady? Star us on GitHub: https://github.com/caopengau/aiready-pattern-detect"
512
+ "\n\u2B50 Like AIReady? Star us on GitHub: https://github.com/getaiready/aiready-pattern-detect"
513
513
  )
514
514
  );
515
515
  console.log(
516
516
  chalk2.dim(
517
- "\u{1F41B} Found a bug? Report it: https://github.com/caopengau/aiready-pattern-detect/issues\n"
517
+ "\u{1F41B} Found a bug? Report it: https://github.com/getaiready/aiready-pattern-detect/issues\n"
518
518
  )
519
519
  );
520
520
  }