@aiready/pattern-detect 0.17.13 → 0.17.15

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.mjs CHANGED
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  analyzePatterns,
4
4
  generateSummary
5
- } from "./chunk-P3BOCGVV.mjs";
6
- import "./chunk-IPBGVPUX.mjs";
5
+ } from "./chunk-WQX7IHAN.mjs";
6
+ import "./chunk-JWP5TCDM.mjs";
7
7
  import {
8
8
  filterBySeverity
9
- } from "./chunk-2P7BQHGR.mjs";
10
- import "./chunk-PHJE6A3J.mjs";
9
+ } from "./chunk-KDXWIT6W.mjs";
10
+ import "./chunk-G3GZFYRI.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
  }
@@ -151,19 +151,91 @@ var INFRA_RULES = [
151
151
  {
152
152
  name: "cli-command-definitions",
153
153
  detect: (file, code) => {
154
- const isCliFile = file.includes("/commands/") || file.includes("/cli/") || file.endsWith(".command.ts");
154
+ const basename = file.split("/").pop() || "";
155
+ const isCliFile = file.includes("/commands/") || file.includes("/cli/") || file.endsWith(".command.ts") || basename === "cli.ts" || basename === "cli.js" || basename === "cli.tsx" || basename === "cli-action.ts";
155
156
  const hasCommandPattern = (code.includes(".command(") || code.includes("defineCommand")) && (code.includes(".description(") || code.includes(".option(")) && (code.includes(".action(") || code.includes("async"));
156
157
  return isCliFile && hasCommandPattern;
157
158
  },
158
159
  severity: import_core3.Severity.Info,
159
160
  reason: "CLI command definitions follow standard Commander.js patterns and are intentionally similar",
160
161
  suggestion: "Command boilerplate duplication is acceptable for CLI interfaces"
162
+ },
163
+ // DynamoDB Single-Table Design - Standard single-table patterns with prefixed keys
164
+ {
165
+ name: "dynamodb-single-table",
166
+ detect: (file, code) => {
167
+ 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");
168
+ const hasKeyPrefix = code.includes("userId:") && code.includes("#") || code.includes("pk:") && code.includes("#") || code.includes("Key:") && code.includes("#") || /[A-Z]+#/.test(code);
169
+ const hasSingleTablePattern = code.includes("KeyConditionExpression") || code.includes("pk =") || code.includes("sk =") || code.includes("userId") && code.includes("timestamp");
170
+ return hasDynamoDBPattern && (hasKeyPrefix || hasSingleTablePattern);
171
+ },
172
+ severity: import_core3.Severity.Info,
173
+ reason: "DynamoDB single-table design with prefixed keys is a standard pattern for efficient data access",
174
+ suggestion: "Single-table query patterns are intentionally similar and should not be refactored"
175
+ },
176
+ // CLI Main Function Boilerplate - Standard argument parsing patterns
177
+ {
178
+ name: "cli-main-boilerplate",
179
+ detect: (file, code) => {
180
+ const basename = file.split("/").pop() || "";
181
+ const isCliFile = file.includes("/cli/") || file.includes("/commands/") || basename.startsWith("cli") || basename.endsWith(".cli.ts") || basename.endsWith(".cli.js");
182
+ const hasMainFunction = code.includes("function main()") || code.includes("async function main()") || code.includes("const main =") || code.includes("main()");
183
+ const hasArgParsing = code.includes("process.argv") || code.includes("yargs") || code.includes("commander") || code.includes("minimist") || code.includes(".parse(") || code.includes("args") && code.includes("._");
184
+ return isCliFile && hasMainFunction && hasArgParsing;
185
+ },
186
+ severity: import_core3.Severity.Info,
187
+ reason: "CLI main functions with argument parsing follow standard boilerplate patterns",
188
+ suggestion: "CLI argument parsing boilerplate is acceptable and should not be flagged as duplication"
161
189
  }
162
190
  ];
163
191
 
164
192
  // src/rules/categories/logic-rules.ts
165
193
  var import_core4 = require("@aiready/core");
166
194
  var LOGIC_RULES = [
195
+ // Enum Semantic Difference - Different enum names indicate different semantic meanings
196
+ {
197
+ name: "enum-semantic-difference",
198
+ detect: (file, code) => {
199
+ const enumRegex = /(?:export\s+)?(?:const\s+)?enum\s+([A-Z][a-zA-Z0-9]*)/g;
200
+ const enums = [];
201
+ let match;
202
+ while ((match = enumRegex.exec(code)) !== null) {
203
+ enums.push(match[1]);
204
+ }
205
+ return enums.length > 0;
206
+ },
207
+ severity: import_core4.Severity.Info,
208
+ reason: "Enums with different names represent different semantic domain concepts, even if they share similar values",
209
+ suggestion: "Different enums (e.g., EscalationPriority vs HealthSeverity) serve different purposes and should not be merged"
210
+ },
211
+ // Enum Value Similarity - Common enum values like LOW, MEDIUM, HIGH are standard
212
+ {
213
+ name: "enum-value-similarity",
214
+ detect: (file, code) => {
215
+ const hasCommonEnumValues = (code.includes("LOW = 'low'") || code.includes("LOW = 0") || code.includes("LOW = 'LOW'")) && (code.includes("HIGH = 'high'") || code.includes("HIGH = 2") || code.includes("HIGH = 'HIGH'")) && (code.includes("MEDIUM = 'medium'") || code.includes("MEDIUM = 1") || code.includes("MEDIUM = 'MEDIUM'"));
216
+ const isEnumDefinition = /(?:export\s+)?(?:const\s+)?enum\s+/.test(code) || code.includes("enum ") && code.includes("{") && code.includes("}");
217
+ return hasCommonEnumValues && isEnumDefinition;
218
+ },
219
+ severity: import_core4.Severity.Info,
220
+ reason: "Common enum values (LOW, MEDIUM, HIGH, CRITICAL) are standard patterns used across different domain enums",
221
+ suggestion: "Enum value similarity is expected for severity/priority enums and should not be flagged as duplication"
222
+ },
223
+ // Re-export / Barrel files - Intentional API surface consolidation
224
+ {
225
+ name: "re-export-files",
226
+ detect: (file, code) => {
227
+ const isIndexFile = file.endsWith("/index.ts") || file.endsWith("/index.js") || file.endsWith("/index.tsx") || file.endsWith("/index.jsx");
228
+ const lines = code.split("\n").filter((l) => l.trim());
229
+ if (lines.length === 0) return false;
230
+ const reExportLines = lines.filter(
231
+ (l) => /^export\s+(\{[^}]+\}|\*)\s+from\s+/.test(l.trim()) || /^export\s+\*\s+as\s+\w+\s+from\s+/.test(l.trim())
232
+ ).length;
233
+ return isIndexFile && reExportLines > 0 && reExportLines / lines.length > 0.5;
234
+ },
235
+ severity: import_core4.Severity.Info,
236
+ reason: "Barrel/index files intentionally re-export for API surface consolidation",
237
+ suggestion: "Re-exports in barrel files are expected and not true duplication"
238
+ },
167
239
  // Type Definitions - Duplication for type safety and module independence
168
240
  {
169
241
  name: "type-definitions",
@@ -177,6 +249,20 @@ var LOGIC_RULES = [
177
249
  reason: "Type/interface definitions are intentionally duplicated for module independence",
178
250
  suggestion: "Extract to shared types package only if causing maintenance burden"
179
251
  },
252
+ // Cross-Package Type Definitions - Different packages may have similar types
253
+ {
254
+ name: "cross-package-types",
255
+ detect: (file, code) => {
256
+ const hasTypeDefinition = code.includes("interface ") || code.includes("type ") || code.includes("enum ");
257
+ const isPackageOrApp = file.includes("/packages/") || file.includes("/apps/") || file.includes("/core/");
258
+ const packageMatch = file.match(/\/(packages|apps|core)\/([^/]+)\//);
259
+ const hasPackageStructure = packageMatch !== null;
260
+ return hasTypeDefinition && isPackageOrApp && hasPackageStructure;
261
+ },
262
+ severity: import_core4.Severity.Info,
263
+ reason: "Types in different packages/modules are often intentionally similar for module independence",
264
+ suggestion: "Cross-package type duplication is acceptable for decoupled module architecture"
265
+ },
180
266
  // Utility Functions - Small helpers in dedicated utility files
181
267
  {
182
268
  name: "utility-functions",
@@ -259,6 +345,22 @@ var LOGIC_RULES = [
259
345
  severity: import_core4.Severity.Info,
260
346
  reason: "Validation functions are inherently similar and often intentionally duplicated for domain clarity",
261
347
  suggestion: "Consider extracting to shared validators only if validation logic becomes complex"
348
+ },
349
+ // Singleton Getter Pattern - Standard singleton initialization pattern
350
+ {
351
+ name: "singleton-getter",
352
+ detect: (file, code) => {
353
+ const hasSingletonGetter = /(?:export\s+)?(?:async\s+)?function\s+get[A-Z][a-zA-Z0-9]*\s*\(/.test(
354
+ code
355
+ ) || /(?:export\s+)?const\s+get[A-Z][a-zA-Z0-9]*\s*=\s*(?:async\s+)?\(\)\s*=>/.test(
356
+ code
357
+ );
358
+ const hasSingletonPattern = code.includes("if (!") && code.includes("instance") && code.includes(" = ") || code.includes("if (!_") && code.includes(" = new ") || code.includes("if (") && code.includes(" === null") && code.includes(" = new ");
359
+ return hasSingletonGetter && hasSingletonPattern;
360
+ },
361
+ severity: import_core4.Severity.Info,
362
+ reason: "Singleton getter functions follow standard initialization pattern and are intentionally similar",
363
+ suggestion: "Singleton getters are boilerplate and acceptable duplication for lazy initialization"
262
364
  }
263
365
  ];
264
366
 
@@ -3,7 +3,7 @@ import {
3
3
  filterBySeverity,
4
4
  getSeverityLabel,
5
5
  getSeverityThreshold
6
- } from "../chunk-2P7BQHGR.mjs";
6
+ } from "../chunk-KDXWIT6W.mjs";
7
7
  export {
8
8
  calculateSeverity,
9
9
  filterBySeverity,
@@ -151,19 +151,91 @@ var INFRA_RULES = [
151
151
  {
152
152
  name: "cli-command-definitions",
153
153
  detect: (file, code) => {
154
- const isCliFile = file.includes("/commands/") || file.includes("/cli/") || file.endsWith(".command.ts");
154
+ const basename = file.split("/").pop() || "";
155
+ const isCliFile = file.includes("/commands/") || file.includes("/cli/") || file.endsWith(".command.ts") || basename === "cli.ts" || basename === "cli.js" || basename === "cli.tsx" || basename === "cli-action.ts";
155
156
  const hasCommandPattern = (code.includes(".command(") || code.includes("defineCommand")) && (code.includes(".description(") || code.includes(".option(")) && (code.includes(".action(") || code.includes("async"));
156
157
  return isCliFile && hasCommandPattern;
157
158
  },
158
159
  severity: import_core3.Severity.Info,
159
160
  reason: "CLI command definitions follow standard Commander.js patterns and are intentionally similar",
160
161
  suggestion: "Command boilerplate duplication is acceptable for CLI interfaces"
162
+ },
163
+ // DynamoDB Single-Table Design - Standard single-table patterns with prefixed keys
164
+ {
165
+ name: "dynamodb-single-table",
166
+ detect: (file, code) => {
167
+ 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");
168
+ const hasKeyPrefix = code.includes("userId:") && code.includes("#") || code.includes("pk:") && code.includes("#") || code.includes("Key:") && code.includes("#") || /[A-Z]+#/.test(code);
169
+ const hasSingleTablePattern = code.includes("KeyConditionExpression") || code.includes("pk =") || code.includes("sk =") || code.includes("userId") && code.includes("timestamp");
170
+ return hasDynamoDBPattern && (hasKeyPrefix || hasSingleTablePattern);
171
+ },
172
+ severity: import_core3.Severity.Info,
173
+ reason: "DynamoDB single-table design with prefixed keys is a standard pattern for efficient data access",
174
+ suggestion: "Single-table query patterns are intentionally similar and should not be refactored"
175
+ },
176
+ // CLI Main Function Boilerplate - Standard argument parsing patterns
177
+ {
178
+ name: "cli-main-boilerplate",
179
+ detect: (file, code) => {
180
+ const basename = file.split("/").pop() || "";
181
+ const isCliFile = file.includes("/cli/") || file.includes("/commands/") || basename.startsWith("cli") || basename.endsWith(".cli.ts") || basename.endsWith(".cli.js");
182
+ const hasMainFunction = code.includes("function main()") || code.includes("async function main()") || code.includes("const main =") || code.includes("main()");
183
+ const hasArgParsing = code.includes("process.argv") || code.includes("yargs") || code.includes("commander") || code.includes("minimist") || code.includes(".parse(") || code.includes("args") && code.includes("._");
184
+ return isCliFile && hasMainFunction && hasArgParsing;
185
+ },
186
+ severity: import_core3.Severity.Info,
187
+ reason: "CLI main functions with argument parsing follow standard boilerplate patterns",
188
+ suggestion: "CLI argument parsing boilerplate is acceptable and should not be flagged as duplication"
161
189
  }
162
190
  ];
163
191
 
164
192
  // src/rules/categories/logic-rules.ts
165
193
  var import_core4 = require("@aiready/core");
166
194
  var LOGIC_RULES = [
195
+ // Enum Semantic Difference - Different enum names indicate different semantic meanings
196
+ {
197
+ name: "enum-semantic-difference",
198
+ detect: (file, code) => {
199
+ const enumRegex = /(?:export\s+)?(?:const\s+)?enum\s+([A-Z][a-zA-Z0-9]*)/g;
200
+ const enums = [];
201
+ let match;
202
+ while ((match = enumRegex.exec(code)) !== null) {
203
+ enums.push(match[1]);
204
+ }
205
+ return enums.length > 0;
206
+ },
207
+ severity: import_core4.Severity.Info,
208
+ reason: "Enums with different names represent different semantic domain concepts, even if they share similar values",
209
+ suggestion: "Different enums (e.g., EscalationPriority vs HealthSeverity) serve different purposes and should not be merged"
210
+ },
211
+ // Enum Value Similarity - Common enum values like LOW, MEDIUM, HIGH are standard
212
+ {
213
+ name: "enum-value-similarity",
214
+ detect: (file, code) => {
215
+ const hasCommonEnumValues = (code.includes("LOW = 'low'") || code.includes("LOW = 0") || code.includes("LOW = 'LOW'")) && (code.includes("HIGH = 'high'") || code.includes("HIGH = 2") || code.includes("HIGH = 'HIGH'")) && (code.includes("MEDIUM = 'medium'") || code.includes("MEDIUM = 1") || code.includes("MEDIUM = 'MEDIUM'"));
216
+ const isEnumDefinition = /(?:export\s+)?(?:const\s+)?enum\s+/.test(code) || code.includes("enum ") && code.includes("{") && code.includes("}");
217
+ return hasCommonEnumValues && isEnumDefinition;
218
+ },
219
+ severity: import_core4.Severity.Info,
220
+ reason: "Common enum values (LOW, MEDIUM, HIGH, CRITICAL) are standard patterns used across different domain enums",
221
+ suggestion: "Enum value similarity is expected for severity/priority enums and should not be flagged as duplication"
222
+ },
223
+ // Re-export / Barrel files - Intentional API surface consolidation
224
+ {
225
+ name: "re-export-files",
226
+ detect: (file, code) => {
227
+ const isIndexFile = file.endsWith("/index.ts") || file.endsWith("/index.js") || file.endsWith("/index.tsx") || file.endsWith("/index.jsx");
228
+ const lines = code.split("\n").filter((l) => l.trim());
229
+ if (lines.length === 0) return false;
230
+ const reExportLines = lines.filter(
231
+ (l) => /^export\s+(\{[^}]+\}|\*)\s+from\s+/.test(l.trim()) || /^export\s+\*\s+as\s+\w+\s+from\s+/.test(l.trim())
232
+ ).length;
233
+ return isIndexFile && reExportLines > 0 && reExportLines / lines.length > 0.5;
234
+ },
235
+ severity: import_core4.Severity.Info,
236
+ reason: "Barrel/index files intentionally re-export for API surface consolidation",
237
+ suggestion: "Re-exports in barrel files are expected and not true duplication"
238
+ },
167
239
  // Type Definitions - Duplication for type safety and module independence
168
240
  {
169
241
  name: "type-definitions",
@@ -177,6 +249,20 @@ var LOGIC_RULES = [
177
249
  reason: "Type/interface definitions are intentionally duplicated for module independence",
178
250
  suggestion: "Extract to shared types package only if causing maintenance burden"
179
251
  },
252
+ // Cross-Package Type Definitions - Different packages may have similar types
253
+ {
254
+ name: "cross-package-types",
255
+ detect: (file, code) => {
256
+ const hasTypeDefinition = code.includes("interface ") || code.includes("type ") || code.includes("enum ");
257
+ const isPackageOrApp = file.includes("/packages/") || file.includes("/apps/") || file.includes("/core/");
258
+ const packageMatch = file.match(/\/(packages|apps|core)\/([^/]+)\//);
259
+ const hasPackageStructure = packageMatch !== null;
260
+ return hasTypeDefinition && isPackageOrApp && hasPackageStructure;
261
+ },
262
+ severity: import_core4.Severity.Info,
263
+ reason: "Types in different packages/modules are often intentionally similar for module independence",
264
+ suggestion: "Cross-package type duplication is acceptable for decoupled module architecture"
265
+ },
180
266
  // Utility Functions - Small helpers in dedicated utility files
181
267
  {
182
268
  name: "utility-functions",
@@ -259,6 +345,22 @@ var LOGIC_RULES = [
259
345
  severity: import_core4.Severity.Info,
260
346
  reason: "Validation functions are inherently similar and often intentionally duplicated for domain clarity",
261
347
  suggestion: "Consider extracting to shared validators only if validation logic becomes complex"
348
+ },
349
+ // Singleton Getter Pattern - Standard singleton initialization pattern
350
+ {
351
+ name: "singleton-getter",
352
+ detect: (file, code) => {
353
+ const hasSingletonGetter = /(?:export\s+)?(?:async\s+)?function\s+get[A-Z][a-zA-Z0-9]*\s*\(/.test(
354
+ code
355
+ ) || /(?:export\s+)?const\s+get[A-Z][a-zA-Z0-9]*\s*=\s*(?:async\s+)?\(\)\s*=>/.test(
356
+ code
357
+ );
358
+ const hasSingletonPattern = code.includes("if (!") && code.includes("instance") && code.includes(" = ") || code.includes("if (!_") && code.includes(" = new ") || code.includes("if (") && code.includes(" === null") && code.includes(" = new ");
359
+ return hasSingletonGetter && hasSingletonPattern;
360
+ },
361
+ severity: import_core4.Severity.Info,
362
+ reason: "Singleton getter functions follow standard initialization pattern and are intentionally similar",
363
+ suggestion: "Singleton getters are boilerplate and acceptable duplication for lazy initialization"
262
364
  }
263
365
  ];
264
366
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  detectDuplicatePatterns
3
- } from "../chunk-IPBGVPUX.mjs";
4
- import "../chunk-2P7BQHGR.mjs";
3
+ } from "../chunk-JWP5TCDM.mjs";
4
+ import "../chunk-KDXWIT6W.mjs";
5
5
  export {
6
6
  detectDuplicatePatterns
7
7
  };
package/dist/index.js CHANGED
@@ -183,19 +183,91 @@ var INFRA_RULES = [
183
183
  {
184
184
  name: "cli-command-definitions",
185
185
  detect: (file, code) => {
186
- const isCliFile = file.includes("/commands/") || file.includes("/cli/") || file.endsWith(".command.ts");
186
+ const basename = file.split("/").pop() || "";
187
+ const isCliFile = file.includes("/commands/") || file.includes("/cli/") || file.endsWith(".command.ts") || basename === "cli.ts" || basename === "cli.js" || basename === "cli.tsx" || basename === "cli-action.ts";
187
188
  const hasCommandPattern = (code.includes(".command(") || code.includes("defineCommand")) && (code.includes(".description(") || code.includes(".option(")) && (code.includes(".action(") || code.includes("async"));
188
189
  return isCliFile && hasCommandPattern;
189
190
  },
190
191
  severity: import_core3.Severity.Info,
191
192
  reason: "CLI command definitions follow standard Commander.js patterns and are intentionally similar",
192
193
  suggestion: "Command boilerplate duplication is acceptable for CLI interfaces"
194
+ },
195
+ // DynamoDB Single-Table Design - Standard single-table patterns with prefixed keys
196
+ {
197
+ name: "dynamodb-single-table",
198
+ detect: (file, code) => {
199
+ 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");
200
+ const hasKeyPrefix = code.includes("userId:") && code.includes("#") || code.includes("pk:") && code.includes("#") || code.includes("Key:") && code.includes("#") || /[A-Z]+#/.test(code);
201
+ const hasSingleTablePattern = code.includes("KeyConditionExpression") || code.includes("pk =") || code.includes("sk =") || code.includes("userId") && code.includes("timestamp");
202
+ return hasDynamoDBPattern && (hasKeyPrefix || hasSingleTablePattern);
203
+ },
204
+ severity: import_core3.Severity.Info,
205
+ reason: "DynamoDB single-table design with prefixed keys is a standard pattern for efficient data access",
206
+ suggestion: "Single-table query patterns are intentionally similar and should not be refactored"
207
+ },
208
+ // CLI Main Function Boilerplate - Standard argument parsing patterns
209
+ {
210
+ name: "cli-main-boilerplate",
211
+ detect: (file, code) => {
212
+ const basename = file.split("/").pop() || "";
213
+ const isCliFile = file.includes("/cli/") || file.includes("/commands/") || basename.startsWith("cli") || basename.endsWith(".cli.ts") || basename.endsWith(".cli.js");
214
+ const hasMainFunction = code.includes("function main()") || code.includes("async function main()") || code.includes("const main =") || code.includes("main()");
215
+ const hasArgParsing = code.includes("process.argv") || code.includes("yargs") || code.includes("commander") || code.includes("minimist") || code.includes(".parse(") || code.includes("args") && code.includes("._");
216
+ return isCliFile && hasMainFunction && hasArgParsing;
217
+ },
218
+ severity: import_core3.Severity.Info,
219
+ reason: "CLI main functions with argument parsing follow standard boilerplate patterns",
220
+ suggestion: "CLI argument parsing boilerplate is acceptable and should not be flagged as duplication"
193
221
  }
194
222
  ];
195
223
 
196
224
  // src/rules/categories/logic-rules.ts
197
225
  var import_core4 = require("@aiready/core");
198
226
  var LOGIC_RULES = [
227
+ // Enum Semantic Difference - Different enum names indicate different semantic meanings
228
+ {
229
+ name: "enum-semantic-difference",
230
+ detect: (file, code) => {
231
+ const enumRegex = /(?:export\s+)?(?:const\s+)?enum\s+([A-Z][a-zA-Z0-9]*)/g;
232
+ const enums = [];
233
+ let match;
234
+ while ((match = enumRegex.exec(code)) !== null) {
235
+ enums.push(match[1]);
236
+ }
237
+ return enums.length > 0;
238
+ },
239
+ severity: import_core4.Severity.Info,
240
+ reason: "Enums with different names represent different semantic domain concepts, even if they share similar values",
241
+ suggestion: "Different enums (e.g., EscalationPriority vs HealthSeverity) serve different purposes and should not be merged"
242
+ },
243
+ // Enum Value Similarity - Common enum values like LOW, MEDIUM, HIGH are standard
244
+ {
245
+ name: "enum-value-similarity",
246
+ detect: (file, code) => {
247
+ const hasCommonEnumValues = (code.includes("LOW = 'low'") || code.includes("LOW = 0") || code.includes("LOW = 'LOW'")) && (code.includes("HIGH = 'high'") || code.includes("HIGH = 2") || code.includes("HIGH = 'HIGH'")) && (code.includes("MEDIUM = 'medium'") || code.includes("MEDIUM = 1") || code.includes("MEDIUM = 'MEDIUM'"));
248
+ const isEnumDefinition = /(?:export\s+)?(?:const\s+)?enum\s+/.test(code) || code.includes("enum ") && code.includes("{") && code.includes("}");
249
+ return hasCommonEnumValues && isEnumDefinition;
250
+ },
251
+ severity: import_core4.Severity.Info,
252
+ reason: "Common enum values (LOW, MEDIUM, HIGH, CRITICAL) are standard patterns used across different domain enums",
253
+ suggestion: "Enum value similarity is expected for severity/priority enums and should not be flagged as duplication"
254
+ },
255
+ // Re-export / Barrel files - Intentional API surface consolidation
256
+ {
257
+ name: "re-export-files",
258
+ detect: (file, code) => {
259
+ const isIndexFile = file.endsWith("/index.ts") || file.endsWith("/index.js") || file.endsWith("/index.tsx") || file.endsWith("/index.jsx");
260
+ const lines = code.split("\n").filter((l) => l.trim());
261
+ if (lines.length === 0) return false;
262
+ const reExportLines = lines.filter(
263
+ (l) => /^export\s+(\{[^}]+\}|\*)\s+from\s+/.test(l.trim()) || /^export\s+\*\s+as\s+\w+\s+from\s+/.test(l.trim())
264
+ ).length;
265
+ return isIndexFile && reExportLines > 0 && reExportLines / lines.length > 0.5;
266
+ },
267
+ severity: import_core4.Severity.Info,
268
+ reason: "Barrel/index files intentionally re-export for API surface consolidation",
269
+ suggestion: "Re-exports in barrel files are expected and not true duplication"
270
+ },
199
271
  // Type Definitions - Duplication for type safety and module independence
200
272
  {
201
273
  name: "type-definitions",
@@ -209,6 +281,20 @@ var LOGIC_RULES = [
209
281
  reason: "Type/interface definitions are intentionally duplicated for module independence",
210
282
  suggestion: "Extract to shared types package only if causing maintenance burden"
211
283
  },
284
+ // Cross-Package Type Definitions - Different packages may have similar types
285
+ {
286
+ name: "cross-package-types",
287
+ detect: (file, code) => {
288
+ const hasTypeDefinition = code.includes("interface ") || code.includes("type ") || code.includes("enum ");
289
+ const isPackageOrApp = file.includes("/packages/") || file.includes("/apps/") || file.includes("/core/");
290
+ const packageMatch = file.match(/\/(packages|apps|core)\/([^/]+)\//);
291
+ const hasPackageStructure = packageMatch !== null;
292
+ return hasTypeDefinition && isPackageOrApp && hasPackageStructure;
293
+ },
294
+ severity: import_core4.Severity.Info,
295
+ reason: "Types in different packages/modules are often intentionally similar for module independence",
296
+ suggestion: "Cross-package type duplication is acceptable for decoupled module architecture"
297
+ },
212
298
  // Utility Functions - Small helpers in dedicated utility files
213
299
  {
214
300
  name: "utility-functions",
@@ -291,6 +377,22 @@ var LOGIC_RULES = [
291
377
  severity: import_core4.Severity.Info,
292
378
  reason: "Validation functions are inherently similar and often intentionally duplicated for domain clarity",
293
379
  suggestion: "Consider extracting to shared validators only if validation logic becomes complex"
380
+ },
381
+ // Singleton Getter Pattern - Standard singleton initialization pattern
382
+ {
383
+ name: "singleton-getter",
384
+ detect: (file, code) => {
385
+ const hasSingletonGetter = /(?:export\s+)?(?:async\s+)?function\s+get[A-Z][a-zA-Z0-9]*\s*\(/.test(
386
+ code
387
+ ) || /(?:export\s+)?const\s+get[A-Z][a-zA-Z0-9]*\s*=\s*(?:async\s+)?\(\)\s*=>/.test(
388
+ code
389
+ );
390
+ const hasSingletonPattern = code.includes("if (!") && code.includes("instance") && code.includes(" = ") || code.includes("if (!_") && code.includes(" = new ") || code.includes("if (") && code.includes(" === null") && code.includes(" = new ");
391
+ return hasSingletonGetter && hasSingletonPattern;
392
+ },
393
+ severity: import_core4.Severity.Info,
394
+ reason: "Singleton getter functions follow standard initialization pattern and are intentionally similar",
395
+ suggestion: "Singleton getters are boilerplate and acceptable duplication for lazy initialization"
294
396
  }
295
397
  ];
296
398
 
@@ -759,6 +861,22 @@ function getRefactoringSuggestion(patternType, similarity) {
759
861
  return baseMessages[patternType] + urgency;
760
862
  }
761
863
  function generateSummary(results) {
864
+ if (!Array.isArray(results)) {
865
+ return {
866
+ totalPatterns: 0,
867
+ totalTokenCost: 0,
868
+ patternsByType: {
869
+ "api-handler": 0,
870
+ validator: 0,
871
+ utility: 0,
872
+ "class-method": 0,
873
+ component: 0,
874
+ function: 0,
875
+ unknown: 0
876
+ },
877
+ topDuplicates: []
878
+ };
879
+ }
762
880
  const allIssues = results.flatMap((r) => r.issues || []);
763
881
  const totalTokenCost = results.reduce(
764
882
  (sum, r) => sum + (r.metrics?.tokenCost || 0),
@@ -847,7 +965,35 @@ function calculateSeverity2(similarity) {
847
965
  // src/scoring.ts
848
966
  var import_core10 = require("@aiready/core");
849
967
  function calculatePatternScore(duplicates, totalFilesAnalyzed, costConfig) {
850
- const actionableDuplicates = duplicates.filter((d) => d.severity !== "info");
968
+ const actionableDuplicates = duplicates.filter((d) => {
969
+ if (d.severity === "info") return false;
970
+ const acceptableRules = [
971
+ // Logic rules (logic-rules.ts)
972
+ "type-definitions",
973
+ "utility-functions",
974
+ "shared-hooks",
975
+ "score-helpers",
976
+ "visualization-handlers",
977
+ "switch-helpers",
978
+ "common-api-functions",
979
+ "validation-functions",
980
+ // Infrastructure rules (infra-rules.ts)
981
+ "config-files",
982
+ "migration-scripts",
983
+ "tool-implementations",
984
+ "cli-command-definitions",
985
+ // Web rules (web-rules.ts)
986
+ "templates",
987
+ "common-ui-handlers",
988
+ "nextjs-route-handlers",
989
+ // Test rules (test-rules.ts)
990
+ "test-fixtures",
991
+ "e2e-page-objects",
992
+ "mock-data"
993
+ ];
994
+ if (d.matchedRule && acceptableRules.includes(d.matchedRule)) return false;
995
+ return true;
996
+ });
851
997
  const totalDuplicates = actionableDuplicates.length;
852
998
  const totalTokenCost = actionableDuplicates.reduce(
853
999
  (sum, d) => sum + d.tokenCost,
package/dist/index.mjs CHANGED
@@ -12,16 +12,16 @@ import {
12
12
  getSmartDefaults,
13
13
  groupDuplicatesByFilePair,
14
14
  logConfiguration
15
- } from "./chunk-P3BOCGVV.mjs";
15
+ } from "./chunk-WQX7IHAN.mjs";
16
16
  import {
17
17
  detectDuplicatePatterns
18
- } from "./chunk-IPBGVPUX.mjs";
18
+ } from "./chunk-JWP5TCDM.mjs";
19
19
  import {
20
20
  filterBySeverity
21
- } from "./chunk-2P7BQHGR.mjs";
21
+ } from "./chunk-KDXWIT6W.mjs";
22
22
  import {
23
23
  calculatePatternScore
24
- } from "./chunk-PHJE6A3J.mjs";
24
+ } from "./chunk-G3GZFYRI.mjs";
25
25
 
26
26
  // src/index.ts
27
27
  import { ToolRegistry } from "@aiready/core";
@@ -27,7 +27,35 @@ module.exports = __toCommonJS(scoring_entry_exports);
27
27
  // src/scoring.ts
28
28
  var import_core = require("@aiready/core");
29
29
  function calculatePatternScore(duplicates, totalFilesAnalyzed, costConfig) {
30
- const actionableDuplicates = duplicates.filter((d) => d.severity !== "info");
30
+ const actionableDuplicates = duplicates.filter((d) => {
31
+ if (d.severity === "info") return false;
32
+ const acceptableRules = [
33
+ // Logic rules (logic-rules.ts)
34
+ "type-definitions",
35
+ "utility-functions",
36
+ "shared-hooks",
37
+ "score-helpers",
38
+ "visualization-handlers",
39
+ "switch-helpers",
40
+ "common-api-functions",
41
+ "validation-functions",
42
+ // Infrastructure rules (infra-rules.ts)
43
+ "config-files",
44
+ "migration-scripts",
45
+ "tool-implementations",
46
+ "cli-command-definitions",
47
+ // Web rules (web-rules.ts)
48
+ "templates",
49
+ "common-ui-handlers",
50
+ "nextjs-route-handlers",
51
+ // Test rules (test-rules.ts)
52
+ "test-fixtures",
53
+ "e2e-page-objects",
54
+ "mock-data"
55
+ ];
56
+ if (d.matchedRule && acceptableRules.includes(d.matchedRule)) return false;
57
+ return true;
58
+ });
31
59
  const totalDuplicates = actionableDuplicates.length;
32
60
  const totalTokenCost = actionableDuplicates.reduce(
33
61
  (sum, d) => sum + d.tokenCost,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  calculatePatternScore
3
- } from "../chunk-PHJE6A3J.mjs";
3
+ } from "../chunk-G3GZFYRI.mjs";
4
4
  export {
5
5
  calculatePatternScore
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/pattern-detect",
3
- "version": "0.17.13",
3
+ "version": "0.17.15",
4
4
  "description": "Semantic duplicate pattern detection for AI-generated code - finds similar implementations that waste AI context tokens",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -56,16 +56,16 @@
56
56
  "license": "MIT",
57
57
  "repository": {
58
58
  "type": "git",
59
- "url": "https://github.com/caopengau/aiready-pattern-detect.git"
59
+ "url": "https://github.com/getaiready/aiready-pattern-detect.git"
60
60
  },
61
- "homepage": "https://github.com/caopengau/aiready-pattern-detect",
61
+ "homepage": "https://github.com/getaiready/aiready-pattern-detect",
62
62
  "bugs": {
63
- "url": "https://github.com/caopengau/aiready-pattern-detect/issues"
63
+ "url": "https://github.com/getaiready/aiready-pattern-detect/issues"
64
64
  },
65
65
  "dependencies": {
66
66
  "commander": "^14.0.0",
67
67
  "chalk": "^5.3.0",
68
- "@aiready/core": "0.24.14"
68
+ "@aiready/core": "0.24.16"
69
69
  },
70
70
  "devDependencies": {
71
71
  "tsup": "^8.3.5",