@aiready/pattern-detect 0.12.5 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_commander = require("commander");
28
28
 
29
29
  // src/index.ts
30
- var import_core5 = require("@aiready/core");
30
+ var import_core6 = require("@aiready/core");
31
31
 
32
32
  // src/detector.ts
33
33
  var import_core2 = require("@aiready/core");
@@ -256,7 +256,7 @@ function calculateSimilarity(a, b) {
256
256
  return intersection.size / union.size;
257
257
  }
258
258
  async function detectDuplicatePatterns(fileContents, options) {
259
- const { minSimilarity, minLines, streamResults } = options;
259
+ const { minSimilarity, minLines, streamResults, onProgress } = options;
260
260
  const allBlocks = [];
261
261
  for (const { file, content } of fileContents) {
262
262
  const blocks = extractBlocks(file, content);
@@ -265,8 +265,29 @@ async function detectDuplicatePatterns(fileContents, options) {
265
265
  );
266
266
  }
267
267
  const duplicates = [];
268
+ const totalBlocks = allBlocks.length;
269
+ let comparisons = 0;
270
+ const totalComparisons = totalBlocks * (totalBlocks - 1) / 2;
271
+ if (onProgress) {
272
+ onProgress(
273
+ 0,
274
+ totalComparisons,
275
+ `Starting duplicate detection on ${totalBlocks} blocks...`
276
+ );
277
+ }
268
278
  for (let i = 0; i < allBlocks.length; i++) {
279
+ if (i % 50 === 0 && i > 0) {
280
+ await new Promise((resolve) => setImmediate(resolve));
281
+ if (onProgress) {
282
+ onProgress(
283
+ comparisons,
284
+ totalComparisons,
285
+ `Analyzing blocks (${i}/${totalBlocks})...`
286
+ );
287
+ }
288
+ }
269
289
  for (let j = i + 1; j < allBlocks.length; j++) {
290
+ comparisons++;
270
291
  const b1 = allBlocks[i];
271
292
  const b2 = allBlocks[j];
272
293
  if (b1.file === b2.file) continue;
@@ -306,6 +327,13 @@ async function detectDuplicatePatterns(fileContents, options) {
306
327
  }
307
328
  }
308
329
  }
330
+ if (onProgress) {
331
+ onProgress(
332
+ totalComparisons,
333
+ totalComparisons,
334
+ `Duplicate detection complete. Found ${duplicates.length} patterns.`
335
+ );
336
+ }
309
337
  return duplicates.sort((a, b) => b.similarity - a.similarity);
310
338
  }
311
339
 
@@ -438,8 +466,148 @@ function filterClustersByImpact(clusters, minTokenCost = 1e3, minFiles = 3) {
438
466
 
439
467
  // src/scoring.ts
440
468
  var import_core4 = require("@aiready/core");
469
+ function calculatePatternScore(duplicates, totalFilesAnalyzed, costConfig) {
470
+ const totalDuplicates = duplicates.length;
471
+ const totalTokenCost = duplicates.reduce((sum, d) => sum + d.tokenCost, 0);
472
+ const highImpactDuplicates = duplicates.filter(
473
+ (d) => d.tokenCost > 1e3 || d.similarity > 0.7
474
+ ).length;
475
+ if (totalFilesAnalyzed === 0) {
476
+ return {
477
+ toolName: import_core4.ToolName.PatternDetect,
478
+ score: 100,
479
+ rawMetrics: {
480
+ totalDuplicates: 0,
481
+ totalTokenCost: 0,
482
+ highImpactDuplicates: 0,
483
+ totalFilesAnalyzed: 0
484
+ },
485
+ factors: [],
486
+ recommendations: []
487
+ };
488
+ }
489
+ const duplicatesPerFile = totalDuplicates / totalFilesAnalyzed * 100;
490
+ const tokenWastePerFile = totalTokenCost / totalFilesAnalyzed;
491
+ const duplicatesPenalty = Math.min(60, duplicatesPerFile * 0.6);
492
+ const tokenPenalty = Math.min(40, tokenWastePerFile / 125);
493
+ const highImpactPenalty = highImpactDuplicates > 0 ? Math.min(15, highImpactDuplicates * 2 - 5) : -5;
494
+ const score = 100 - duplicatesPenalty - tokenPenalty - highImpactPenalty;
495
+ const finalScore = Math.max(0, Math.min(100, Math.round(score)));
496
+ const factors = [
497
+ {
498
+ name: "Duplication Density",
499
+ impact: -Math.round(duplicatesPenalty),
500
+ description: `${duplicatesPerFile.toFixed(1)} duplicates per 100 files`
501
+ },
502
+ {
503
+ name: "Token Waste",
504
+ impact: -Math.round(tokenPenalty),
505
+ description: `${Math.round(tokenWastePerFile)} tokens wasted per file`
506
+ }
507
+ ];
508
+ if (highImpactDuplicates > 0) {
509
+ factors.push({
510
+ name: "High-Impact Patterns",
511
+ impact: -Math.round(highImpactPenalty),
512
+ description: `${highImpactDuplicates} high-impact duplicates (>1000 tokens or >70% similar)`
513
+ });
514
+ } else {
515
+ factors.push({
516
+ name: "No High-Impact Patterns",
517
+ impact: 5,
518
+ description: "No severe duplicates detected"
519
+ });
520
+ }
521
+ const recommendations = [];
522
+ if (highImpactDuplicates > 0) {
523
+ const estimatedImpact = Math.min(15, highImpactDuplicates * 3);
524
+ recommendations.push({
525
+ action: `Deduplicate ${highImpactDuplicates} high-impact pattern${highImpactDuplicates > 1 ? "s" : ""}`,
526
+ estimatedImpact,
527
+ priority: "high"
528
+ });
529
+ }
530
+ if (totalDuplicates > 10 && duplicatesPerFile > 20) {
531
+ const estimatedImpact = Math.min(10, Math.round(duplicatesPenalty * 0.3));
532
+ recommendations.push({
533
+ action: "Extract common patterns into shared utilities",
534
+ estimatedImpact,
535
+ priority: "medium"
536
+ });
537
+ }
538
+ if (tokenWastePerFile > 2e3) {
539
+ const estimatedImpact = Math.min(8, Math.round(tokenPenalty * 0.4));
540
+ recommendations.push({
541
+ action: "Consolidate duplicated logic to reduce AI context waste",
542
+ estimatedImpact,
543
+ priority: totalTokenCost > 1e4 ? "high" : "medium"
544
+ });
545
+ }
546
+ const cfg = { ...import_core4.DEFAULT_COST_CONFIG, ...costConfig };
547
+ const estimatedMonthlyCost = (0, import_core4.calculateMonthlyCost)(totalTokenCost, cfg);
548
+ const issues = duplicates.map((d) => ({
549
+ severity: d.severity === "critical" ? "critical" : d.severity === "major" ? "major" : "minor"
550
+ }));
551
+ const productivityImpact = (0, import_core4.calculateProductivityImpact)(issues);
552
+ return {
553
+ toolName: "pattern-detect",
554
+ score: finalScore,
555
+ rawMetrics: {
556
+ totalDuplicates,
557
+ totalTokenCost,
558
+ highImpactDuplicates,
559
+ totalFilesAnalyzed,
560
+ duplicatesPerFile: Math.round(duplicatesPerFile * 10) / 10,
561
+ tokenWastePerFile: Math.round(tokenWastePerFile),
562
+ // Business value metrics
563
+ estimatedMonthlyCost,
564
+ estimatedDeveloperHours: productivityImpact.totalHours
565
+ },
566
+ factors,
567
+ recommendations
568
+ };
569
+ }
570
+
571
+ // src/provider.ts
572
+ var import_core5 = require("@aiready/core");
573
+ var PatternDetectProvider = {
574
+ id: import_core5.ToolName.PatternDetect,
575
+ alias: ["patterns", "duplicates", "duplication"],
576
+ async analyze(options) {
577
+ const results = await analyzePatterns(options);
578
+ return import_core5.SpokeOutputSchema.parse({
579
+ results: results.results,
580
+ summary: {
581
+ totalFiles: results.files.length,
582
+ totalIssues: results.results.reduce(
583
+ (sum, r) => sum + r.issues.length,
584
+ 0
585
+ ),
586
+ duplicates: results.duplicates,
587
+ groups: results.groups,
588
+ clusters: results.clusters
589
+ },
590
+ metadata: {
591
+ toolName: import_core5.ToolName.PatternDetect,
592
+ version: "0.12.5",
593
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
594
+ }
595
+ });
596
+ },
597
+ score(output, options) {
598
+ const duplicates = output.summary.duplicates || [];
599
+ const totalFiles = output.summary.totalFiles || output.results.length;
600
+ return calculatePatternScore(
601
+ duplicates,
602
+ totalFiles,
603
+ options.costConfig
604
+ );
605
+ },
606
+ defaultWeight: 22
607
+ };
441
608
 
442
609
  // src/index.ts
610
+ import_core6.ToolRegistry.register(PatternDetectProvider);
443
611
  function getRefactoringSuggestion(patternType, similarity) {
444
612
  const baseMessages = {
445
613
  "api-handler": "Extract common middleware or create a base handler class",
@@ -555,7 +723,7 @@ async function analyzePatterns(options) {
555
723
  const batchContents = await Promise.all(
556
724
  batch.map(async (file) => ({
557
725
  file,
558
- content: await (0, import_core5.readFileContent)(file)
726
+ content: await (0, import_core6.readFileContent)(file)
559
727
  }))
560
728
  );
561
729
  fileContents.push(...batchContents);
@@ -576,9 +744,9 @@ async function analyzePatterns(options) {
576
744
  );
577
745
  const issues = fileDuplicates.map((dup) => {
578
746
  const otherFile = dup.file1 === file ? dup.file2 : dup.file1;
579
- const severity2 = dup.similarity > 0.95 ? import_core5.Severity.Critical : dup.similarity > 0.9 ? import_core5.Severity.Major : import_core5.Severity.Minor;
747
+ const severity2 = dup.similarity > 0.95 ? import_core6.Severity.Critical : dup.similarity > 0.9 ? import_core6.Severity.Major : import_core6.Severity.Minor;
580
748
  return {
581
- type: import_core5.IssueType.DuplicatePattern,
749
+ type: import_core6.IssueType.DuplicatePattern,
582
750
  severity: severity2,
583
751
  message: `${dup.patternType} pattern ${Math.round(dup.similarity * 100)}% similar to ${otherFile} (${dup.tokenCost} tokens wasted)`,
584
752
  location: {
@@ -591,11 +759,11 @@ async function analyzePatterns(options) {
591
759
  let filteredIssues = issues;
592
760
  if (severity !== "all") {
593
761
  const severityMap = {
594
- critical: [import_core5.Severity.Critical],
595
- high: [import_core5.Severity.Critical, import_core5.Severity.Major],
596
- medium: [import_core5.Severity.Critical, import_core5.Severity.Major, import_core5.Severity.Minor]
762
+ critical: [import_core6.Severity.Critical],
763
+ high: [import_core6.Severity.Critical, import_core6.Severity.Major],
764
+ medium: [import_core6.Severity.Critical, import_core6.Severity.Major, import_core6.Severity.Minor]
597
765
  };
598
- const allowedSeverities = severityMap[severity] || [import_core5.Severity.Critical, import_core5.Severity.Major, import_core5.Severity.Minor];
766
+ const allowedSeverities = severityMap[severity] || [import_core6.Severity.Critical, import_core6.Severity.Major, import_core6.Severity.Minor];
599
767
  filteredIssues = issues.filter(
600
768
  (issue) => allowedSeverities.includes(issue.severity)
601
769
  );
@@ -688,7 +856,7 @@ function generateSummary(results) {
688
856
  var import_chalk = __toESM(require("chalk"));
689
857
  var import_fs = require("fs");
690
858
  var import_path2 = require("path");
691
- var import_core6 = require("@aiready/core");
859
+ var import_core7 = require("@aiready/core");
692
860
  var program = new import_commander.Command();
693
861
  program.name("aiready-patterns").description("Detect duplicate patterns in your codebase").version("0.1.0").addHelpText(
694
862
  "after",
@@ -742,7 +910,7 @@ program.name("aiready-patterns").description("Detect duplicate patterns in your
742
910
  ).option("--output-file <path>", "Output file path (for json/html)").action(async (directory, options) => {
743
911
  console.log(import_chalk.default.blue("\u{1F50D} Analyzing patterns...\n"));
744
912
  const startTime = Date.now();
745
- const config = await (0, import_core6.loadConfig)(directory);
913
+ const config = await (0, import_core7.loadConfig)(directory);
746
914
  const defaults = {
747
915
  minSimilarity: 0.4,
748
916
  minLines: 5,
@@ -753,7 +921,7 @@ program.name("aiready-patterns").description("Detect duplicate patterns in your
753
921
  streamResults: true,
754
922
  include: void 0,
755
923
  exclude: void 0,
756
- minSeverity: import_core6.Severity.Minor,
924
+ minSeverity: import_core7.Severity.Minor,
757
925
  excludeTestFixtures: false,
758
926
  excludeTemplates: false,
759
927
  includeTests: false,
@@ -764,7 +932,7 @@ program.name("aiready-patterns").description("Detect duplicate patterns in your
764
932
  minClusterFiles: 3,
765
933
  showRawDuplicates: false
766
934
  };
767
- const mergedConfig = (0, import_core6.mergeConfigWithDefaults)(config, defaults);
935
+ const mergedConfig = (0, import_core7.mergeConfigWithDefaults)(config, defaults);
768
936
  const finalOptions = {
769
937
  rootDir: directory,
770
938
  minSimilarity: options.similarity ? parseFloat(options.similarity) : mergedConfig.minSimilarity,
@@ -836,7 +1004,7 @@ program.name("aiready-patterns").description("Detect duplicate patterns in your
836
1004
  clusters: clusters || [],
837
1005
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
838
1006
  };
839
- const outputPath = (0, import_core6.resolveOutputPath)(
1007
+ const outputPath = (0, import_core7.resolveOutputPath)(
840
1008
  options.outputFile,
841
1009
  `pattern-report-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`,
842
1010
  directory
@@ -852,7 +1020,7 @@ program.name("aiready-patterns").description("Detect duplicate patterns in your
852
1020
  }
853
1021
  if (options.output === "html") {
854
1022
  const html = generateHTMLReport(summary, results);
855
- const outputPath = (0, import_core6.resolveOutputPath)(
1023
+ const outputPath = (0, import_core7.resolveOutputPath)(
856
1024
  options.outputFile,
857
1025
  `pattern-report-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.html`,
858
1026
  directory
@@ -1182,10 +1350,10 @@ function generateHTMLReport(summary, results) {
1182
1350
  }
1183
1351
  program.parse();
1184
1352
  function getSeverityValue(s) {
1185
- if (s === import_core6.Severity.Critical || s === "critical") return 4;
1186
- if (s === import_core6.Severity.Major || s === "major") return 3;
1187
- if (s === import_core6.Severity.Minor || s === "minor") return 2;
1188
- if (s === import_core6.Severity.Info || s === "info") return 1;
1353
+ if (s === import_core7.Severity.Critical || s === "critical") return 4;
1354
+ if (s === import_core7.Severity.Major || s === "major") return 3;
1355
+ if (s === import_core7.Severity.Minor || s === "minor") return 2;
1356
+ if (s === import_core7.Severity.Info || s === "info") return 1;
1189
1357
  return 0;
1190
1358
  }
1191
1359
  function getSeverityBadge(severity) {
package/dist/cli.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  analyzePatterns,
4
4
  filterBySeverity,
5
5
  generateSummary
6
- } from "./chunk-SNSDVGWW.mjs";
6
+ } from "./chunk-3WK24ZOX.mjs";
7
7
 
8
8
  // src/cli.ts
9
9
  import { Command } from "commander";
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { Severity, CostConfig, ToolScoringOutput, ScanOptions, AnalysisResult } from '@aiready/core';
1
+ import { Severity, CostConfig, ToolScoringOutput, ToolProvider, ScanOptions, AnalysisResult } from '@aiready/core';
2
2
  export { Severity } from '@aiready/core';
3
3
 
4
4
  type PatternType = 'api-handler' | 'validator' | 'utility' | 'class-method' | 'component' | 'function' | 'unknown';
@@ -83,6 +83,11 @@ interface RefactorCluster {
83
83
  */
84
84
  declare function calculatePatternScore(duplicates: DuplicatePattern[], totalFilesAnalyzed: number, costConfig?: Partial<CostConfig>): ToolScoringOutput;
85
85
 
86
+ /**
87
+ * Pattern Detection Tool Provider
88
+ */
89
+ declare const PatternDetectProvider: ToolProvider;
90
+
86
91
  /**
87
92
  * Calculate severity based on context rules and code characteristics
88
93
  */
@@ -151,4 +156,4 @@ declare function analyzePatterns(options: PatternDetectOptions): Promise<{
151
156
  */
152
157
  declare function generateSummary(results: AnalysisResult[]): PatternSummary;
153
158
 
154
- export { type DuplicateGroup, type DuplicatePattern, type PatternDetectOptions, type PatternSummary, type PatternType, type RefactorCluster, analyzePatterns, calculatePatternScore, calculateSeverity, detectDuplicatePatterns, filterBySeverity, generateSummary, getSeverityLabel, getSmartDefaults };
159
+ export { type DuplicateGroup, type DuplicatePattern, type PatternDetectOptions, PatternDetectProvider, type PatternSummary, type PatternType, type RefactorCluster, analyzePatterns, calculatePatternScore, calculateSeverity, detectDuplicatePatterns, filterBySeverity, generateSummary, getSeverityLabel, getSmartDefaults };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Severity, CostConfig, ToolScoringOutput, ScanOptions, AnalysisResult } from '@aiready/core';
1
+ import { Severity, CostConfig, ToolScoringOutput, ToolProvider, ScanOptions, AnalysisResult } from '@aiready/core';
2
2
  export { Severity } from '@aiready/core';
3
3
 
4
4
  type PatternType = 'api-handler' | 'validator' | 'utility' | 'class-method' | 'component' | 'function' | 'unknown';
@@ -83,6 +83,11 @@ interface RefactorCluster {
83
83
  */
84
84
  declare function calculatePatternScore(duplicates: DuplicatePattern[], totalFilesAnalyzed: number, costConfig?: Partial<CostConfig>): ToolScoringOutput;
85
85
 
86
+ /**
87
+ * Pattern Detection Tool Provider
88
+ */
89
+ declare const PatternDetectProvider: ToolProvider;
90
+
86
91
  /**
87
92
  * Calculate severity based on context rules and code characteristics
88
93
  */
@@ -151,4 +156,4 @@ declare function analyzePatterns(options: PatternDetectOptions): Promise<{
151
156
  */
152
157
  declare function generateSummary(results: AnalysisResult[]): PatternSummary;
153
158
 
154
- export { type DuplicateGroup, type DuplicatePattern, type PatternDetectOptions, type PatternSummary, type PatternType, type RefactorCluster, analyzePatterns, calculatePatternScore, calculateSeverity, detectDuplicatePatterns, filterBySeverity, generateSummary, getSeverityLabel, getSmartDefaults };
159
+ export { type DuplicateGroup, type DuplicatePattern, type PatternDetectOptions, PatternDetectProvider, type PatternSummary, type PatternType, type RefactorCluster, analyzePatterns, calculatePatternScore, calculateSeverity, detectDuplicatePatterns, filterBySeverity, generateSummary, getSeverityLabel, getSmartDefaults };
package/dist/index.js CHANGED
@@ -30,7 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- Severity: () => import_core5.Severity,
33
+ PatternDetectProvider: () => PatternDetectProvider,
34
+ Severity: () => import_core6.Severity,
34
35
  analyzePatterns: () => analyzePatterns,
35
36
  calculatePatternScore: () => calculatePatternScore,
36
37
  calculateSeverity: () => calculateSeverity,
@@ -41,7 +42,7 @@ __export(index_exports, {
41
42
  getSmartDefaults: () => getSmartDefaults
42
43
  });
43
44
  module.exports = __toCommonJS(index_exports);
44
- var import_core5 = require("@aiready/core");
45
+ var import_core6 = require("@aiready/core");
45
46
 
46
47
  // src/detector.ts
47
48
  var import_core2 = require("@aiready/core");
@@ -279,7 +280,7 @@ function calculateSimilarity(a, b) {
279
280
  return intersection.size / union.size;
280
281
  }
281
282
  async function detectDuplicatePatterns(fileContents, options) {
282
- const { minSimilarity, minLines, streamResults } = options;
283
+ const { minSimilarity, minLines, streamResults, onProgress } = options;
283
284
  const allBlocks = [];
284
285
  for (const { file, content } of fileContents) {
285
286
  const blocks = extractBlocks(file, content);
@@ -288,8 +289,29 @@ async function detectDuplicatePatterns(fileContents, options) {
288
289
  );
289
290
  }
290
291
  const duplicates = [];
292
+ const totalBlocks = allBlocks.length;
293
+ let comparisons = 0;
294
+ const totalComparisons = totalBlocks * (totalBlocks - 1) / 2;
295
+ if (onProgress) {
296
+ onProgress(
297
+ 0,
298
+ totalComparisons,
299
+ `Starting duplicate detection on ${totalBlocks} blocks...`
300
+ );
301
+ }
291
302
  for (let i = 0; i < allBlocks.length; i++) {
303
+ if (i % 50 === 0 && i > 0) {
304
+ await new Promise((resolve) => setImmediate(resolve));
305
+ if (onProgress) {
306
+ onProgress(
307
+ comparisons,
308
+ totalComparisons,
309
+ `Analyzing blocks (${i}/${totalBlocks})...`
310
+ );
311
+ }
312
+ }
292
313
  for (let j = i + 1; j < allBlocks.length; j++) {
314
+ comparisons++;
293
315
  const b1 = allBlocks[i];
294
316
  const b2 = allBlocks[j];
295
317
  if (b1.file === b2.file) continue;
@@ -329,6 +351,13 @@ async function detectDuplicatePatterns(fileContents, options) {
329
351
  }
330
352
  }
331
353
  }
354
+ if (onProgress) {
355
+ onProgress(
356
+ totalComparisons,
357
+ totalComparisons,
358
+ `Duplicate detection complete. Found ${duplicates.length} patterns.`
359
+ );
360
+ }
332
361
  return duplicates.sort((a, b) => b.similarity - a.similarity);
333
362
  }
334
363
 
@@ -469,7 +498,7 @@ function calculatePatternScore(duplicates, totalFilesAnalyzed, costConfig) {
469
498
  ).length;
470
499
  if (totalFilesAnalyzed === 0) {
471
500
  return {
472
- toolName: "pattern-detect",
501
+ toolName: import_core4.ToolName.PatternDetect,
473
502
  score: 100,
474
503
  rawMetrics: {
475
504
  totalDuplicates: 0,
@@ -563,7 +592,46 @@ function calculatePatternScore(duplicates, totalFilesAnalyzed, costConfig) {
563
592
  };
564
593
  }
565
594
 
595
+ // src/provider.ts
596
+ var import_core5 = require("@aiready/core");
597
+ var PatternDetectProvider = {
598
+ id: import_core5.ToolName.PatternDetect,
599
+ alias: ["patterns", "duplicates", "duplication"],
600
+ async analyze(options) {
601
+ const results = await analyzePatterns(options);
602
+ return import_core5.SpokeOutputSchema.parse({
603
+ results: results.results,
604
+ summary: {
605
+ totalFiles: results.files.length,
606
+ totalIssues: results.results.reduce(
607
+ (sum, r) => sum + r.issues.length,
608
+ 0
609
+ ),
610
+ duplicates: results.duplicates,
611
+ groups: results.groups,
612
+ clusters: results.clusters
613
+ },
614
+ metadata: {
615
+ toolName: import_core5.ToolName.PatternDetect,
616
+ version: "0.12.5",
617
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
618
+ }
619
+ });
620
+ },
621
+ score(output, options) {
622
+ const duplicates = output.summary.duplicates || [];
623
+ const totalFiles = output.summary.totalFiles || output.results.length;
624
+ return calculatePatternScore(
625
+ duplicates,
626
+ totalFiles,
627
+ options.costConfig
628
+ );
629
+ },
630
+ defaultWeight: 22
631
+ };
632
+
566
633
  // src/index.ts
634
+ import_core6.ToolRegistry.register(PatternDetectProvider);
567
635
  function getRefactoringSuggestion(patternType, similarity) {
568
636
  const baseMessages = {
569
637
  "api-handler": "Extract common middleware or create a base handler class",
@@ -679,7 +747,7 @@ async function analyzePatterns(options) {
679
747
  const batchContents = await Promise.all(
680
748
  batch.map(async (file) => ({
681
749
  file,
682
- content: await (0, import_core5.readFileContent)(file)
750
+ content: await (0, import_core6.readFileContent)(file)
683
751
  }))
684
752
  );
685
753
  fileContents.push(...batchContents);
@@ -700,9 +768,9 @@ async function analyzePatterns(options) {
700
768
  );
701
769
  const issues = fileDuplicates.map((dup) => {
702
770
  const otherFile = dup.file1 === file ? dup.file2 : dup.file1;
703
- const severity2 = dup.similarity > 0.95 ? import_core5.Severity.Critical : dup.similarity > 0.9 ? import_core5.Severity.Major : import_core5.Severity.Minor;
771
+ const severity2 = dup.similarity > 0.95 ? import_core6.Severity.Critical : dup.similarity > 0.9 ? import_core6.Severity.Major : import_core6.Severity.Minor;
704
772
  return {
705
- type: import_core5.IssueType.DuplicatePattern,
773
+ type: import_core6.IssueType.DuplicatePattern,
706
774
  severity: severity2,
707
775
  message: `${dup.patternType} pattern ${Math.round(dup.similarity * 100)}% similar to ${otherFile} (${dup.tokenCost} tokens wasted)`,
708
776
  location: {
@@ -715,11 +783,11 @@ async function analyzePatterns(options) {
715
783
  let filteredIssues = issues;
716
784
  if (severity !== "all") {
717
785
  const severityMap = {
718
- critical: [import_core5.Severity.Critical],
719
- high: [import_core5.Severity.Critical, import_core5.Severity.Major],
720
- medium: [import_core5.Severity.Critical, import_core5.Severity.Major, import_core5.Severity.Minor]
786
+ critical: [import_core6.Severity.Critical],
787
+ high: [import_core6.Severity.Critical, import_core6.Severity.Major],
788
+ medium: [import_core6.Severity.Critical, import_core6.Severity.Major, import_core6.Severity.Minor]
721
789
  };
722
- const allowedSeverities = severityMap[severity] || [import_core5.Severity.Critical, import_core5.Severity.Major, import_core5.Severity.Minor];
790
+ const allowedSeverities = severityMap[severity] || [import_core6.Severity.Critical, import_core6.Severity.Major, import_core6.Severity.Minor];
723
791
  filteredIssues = issues.filter(
724
792
  (issue) => allowedSeverities.includes(issue.severity)
725
793
  );
@@ -809,6 +877,7 @@ function generateSummary(results) {
809
877
  }
810
878
  // Annotate the CommonJS export names for ESM import in node:
811
879
  0 && (module.exports = {
880
+ PatternDetectProvider,
812
881
  Severity,
813
882
  analyzePatterns,
814
883
  calculatePatternScore,
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ PatternDetectProvider,
2
3
  Severity,
3
4
  analyzePatterns,
4
5
  calculatePatternScore,
@@ -8,8 +9,9 @@ import {
8
9
  generateSummary,
9
10
  getSeverityLabel,
10
11
  getSmartDefaults
11
- } from "./chunk-SNSDVGWW.mjs";
12
+ } from "./chunk-3WK24ZOX.mjs";
12
13
  export {
14
+ PatternDetectProvider,
13
15
  Severity,
14
16
  analyzePatterns,
15
17
  calculatePatternScore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/pattern-detect",
3
- "version": "0.12.5",
3
+ "version": "0.14.1",
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",
@@ -45,7 +45,7 @@
45
45
  "dependencies": {
46
46
  "commander": "^14.0.0",
47
47
  "chalk": "^5.3.0",
48
- "@aiready/core": "0.19.5"
48
+ "@aiready/core": "0.21.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "tsup": "^8.3.5",