@loxia-labs/loxia-autopilot-one 1.0.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.
Files changed (80) hide show
  1. package/LICENSE +267 -0
  2. package/README.md +509 -0
  3. package/bin/cli.js +117 -0
  4. package/package.json +94 -0
  5. package/scripts/install-scanners.js +236 -0
  6. package/src/analyzers/CSSAnalyzer.js +297 -0
  7. package/src/analyzers/ConfigValidator.js +690 -0
  8. package/src/analyzers/ESLintAnalyzer.js +320 -0
  9. package/src/analyzers/JavaScriptAnalyzer.js +261 -0
  10. package/src/analyzers/PrettierFormatter.js +247 -0
  11. package/src/analyzers/PythonAnalyzer.js +266 -0
  12. package/src/analyzers/SecurityAnalyzer.js +729 -0
  13. package/src/analyzers/TypeScriptAnalyzer.js +247 -0
  14. package/src/analyzers/codeCloneDetector/analyzer.js +344 -0
  15. package/src/analyzers/codeCloneDetector/detector.js +203 -0
  16. package/src/analyzers/codeCloneDetector/index.js +160 -0
  17. package/src/analyzers/codeCloneDetector/parser.js +199 -0
  18. package/src/analyzers/codeCloneDetector/reporter.js +148 -0
  19. package/src/analyzers/codeCloneDetector/scanner.js +59 -0
  20. package/src/core/agentPool.js +1474 -0
  21. package/src/core/agentScheduler.js +2147 -0
  22. package/src/core/contextManager.js +709 -0
  23. package/src/core/messageProcessor.js +732 -0
  24. package/src/core/orchestrator.js +548 -0
  25. package/src/core/stateManager.js +877 -0
  26. package/src/index.js +631 -0
  27. package/src/interfaces/cli.js +549 -0
  28. package/src/interfaces/webServer.js +2162 -0
  29. package/src/modules/fileExplorer/controller.js +280 -0
  30. package/src/modules/fileExplorer/index.js +37 -0
  31. package/src/modules/fileExplorer/middleware.js +92 -0
  32. package/src/modules/fileExplorer/routes.js +125 -0
  33. package/src/modules/fileExplorer/types.js +44 -0
  34. package/src/services/aiService.js +1232 -0
  35. package/src/services/apiKeyManager.js +164 -0
  36. package/src/services/benchmarkService.js +366 -0
  37. package/src/services/budgetService.js +539 -0
  38. package/src/services/contextInjectionService.js +247 -0
  39. package/src/services/conversationCompactionService.js +637 -0
  40. package/src/services/errorHandler.js +810 -0
  41. package/src/services/fileAttachmentService.js +544 -0
  42. package/src/services/modelRouterService.js +366 -0
  43. package/src/services/modelsService.js +322 -0
  44. package/src/services/qualityInspector.js +796 -0
  45. package/src/services/tokenCountingService.js +536 -0
  46. package/src/tools/agentCommunicationTool.js +1344 -0
  47. package/src/tools/agentDelayTool.js +485 -0
  48. package/src/tools/asyncToolManager.js +604 -0
  49. package/src/tools/baseTool.js +800 -0
  50. package/src/tools/browserTool.js +920 -0
  51. package/src/tools/cloneDetectionTool.js +621 -0
  52. package/src/tools/dependencyResolverTool.js +1215 -0
  53. package/src/tools/fileContentReplaceTool.js +875 -0
  54. package/src/tools/fileSystemTool.js +1107 -0
  55. package/src/tools/fileTreeTool.js +853 -0
  56. package/src/tools/imageTool.js +901 -0
  57. package/src/tools/importAnalyzerTool.js +1060 -0
  58. package/src/tools/jobDoneTool.js +248 -0
  59. package/src/tools/seekTool.js +956 -0
  60. package/src/tools/staticAnalysisTool.js +1778 -0
  61. package/src/tools/taskManagerTool.js +2873 -0
  62. package/src/tools/terminalTool.js +2304 -0
  63. package/src/tools/webTool.js +1430 -0
  64. package/src/types/agent.js +519 -0
  65. package/src/types/contextReference.js +972 -0
  66. package/src/types/conversation.js +730 -0
  67. package/src/types/toolCommand.js +747 -0
  68. package/src/utilities/attachmentValidator.js +292 -0
  69. package/src/utilities/configManager.js +582 -0
  70. package/src/utilities/constants.js +722 -0
  71. package/src/utilities/directoryAccessManager.js +535 -0
  72. package/src/utilities/fileProcessor.js +307 -0
  73. package/src/utilities/logger.js +436 -0
  74. package/src/utilities/tagParser.js +1246 -0
  75. package/src/utilities/toolConstants.js +317 -0
  76. package/web-ui/build/index.html +15 -0
  77. package/web-ui/build/logo.png +0 -0
  78. package/web-ui/build/logo2.png +0 -0
  79. package/web-ui/build/static/index-CjkkcnFA.js +344 -0
  80. package/web-ui/build/static/index-Dy2bYbOa.css +1 -0
@@ -0,0 +1,621 @@
1
+ /**
2
+ * CloneDetectionTool - Detect duplicated code (code clones) for refactoring
3
+ *
4
+ * Purpose:
5
+ * - Identify exact and similar code clones across the codebase
6
+ * - Provide refactoring recommendations with priorities
7
+ * - Help reduce technical debt and improve maintainability
8
+ * - Support JavaScript, TypeScript, JSX, TSX, Vue files
9
+ */
10
+
11
+ import { BaseTool } from './baseTool.js';
12
+ import TagParser from '../utilities/tagParser.js';
13
+ import DirectoryAccessManager from '../utilities/directoryAccessManager.js';
14
+ import path from 'path';
15
+
16
+ import {
17
+ TOOL_STATUS,
18
+ SYSTEM_DEFAULTS
19
+ } from '../utilities/constants.js';
20
+
21
+ class CloneDetectionTool extends BaseTool {
22
+ constructor(config = {}, logger = null) {
23
+ super(config, logger);
24
+
25
+ // Tool metadata
26
+ this.requiresProject = true;
27
+ this.isAsync = false;
28
+ this.timeout = config.timeout || 120000; // 2 minutes default
29
+ this.maxConcurrentOperations = config.maxConcurrentOperations || 1;
30
+
31
+ // Clone detection settings
32
+ this.defaultMinTokens = config.defaultMinTokens || 50;
33
+ this.defaultMinLines = config.defaultMinLines || 5;
34
+ this.defaultSimilarityThreshold = config.defaultSimilarityThreshold || 0.85;
35
+ this.maxFileSize = config.maxFileSize || 500000; // 500KB per file
36
+
37
+ // Directory access manager
38
+ this.directoryAccessManager = new DirectoryAccessManager(config, logger);
39
+
40
+ // Clone detector will be initialized lazily when needed
41
+ this.cloneDetector = null;
42
+ }
43
+
44
+ /**
45
+ * Get tool description for LLM consumption
46
+ * @returns {string} Tool description
47
+ */
48
+ getDescription() {
49
+ return `
50
+ Code Clone Detection Tool: Find duplicated code for refactoring opportunities
51
+
52
+ This tool identifies exact and similar code patterns (code clones) across your codebase to help reduce duplication, improve maintainability, and identify refactoring opportunities.
53
+
54
+ WHAT IT DETECTS:
55
+ - Exact Clones (Type 1): Identical code with different formatting/comments
56
+ - Similar Clones (Type 2/3): Structurally similar code with minor variations
57
+
58
+ SUPPORTED LANGUAGES:
59
+ - JavaScript (.js, .jsx, .mjs, .cjs)
60
+ - TypeScript (.ts, .tsx)
61
+ - Vue (.vue)
62
+
63
+ USAGE - XML FORMAT:
64
+
65
+ Detect clones in entire directory:
66
+ [tool id="clonedetection"]
67
+ <detect-clones directory="." />
68
+ [/tool]
69
+
70
+ Detect clones in specific directory:
71
+ [tool id="clonedetection"]
72
+ <detect-clones directory="src/services" />
73
+ [/tool]
74
+
75
+ Custom sensitivity (lower minTokens = more sensitive):
76
+ [tool id="clonedetection"]
77
+ <detect-clones directory="src" min-tokens="30" min-lines="5" />
78
+ [/tool]
79
+
80
+ Filter by priority:
81
+ [tool id="clonedetection"]
82
+ <detect-clones directory="." priority-filter="high" max-results="5" />
83
+ [/tool]
84
+
85
+ Get summary only:
86
+ [tool id="clonedetection"]
87
+ <detect-clones directory="." output-mode="summary" />
88
+ [/tool]
89
+
90
+ USAGE - JSON FORMAT:
91
+
92
+ \`\`\`json
93
+ {
94
+ "toolId": "clonedetection",
95
+ "actions": [
96
+ {
97
+ "type": "detect-clones",
98
+ "directory": "src",
99
+ "minTokens": 50,
100
+ "minLines": 5,
101
+ "similarityThreshold": 0.85,
102
+ "priorityFilter": "high",
103
+ "maxResults": 10,
104
+ "outputMode": "summary"
105
+ }
106
+ ]
107
+ }
108
+ \`\`\`
109
+
110
+ PARAMETERS:
111
+ - directory: Directory to analyze (required)
112
+ - min-tokens: Minimum token count (default: 50, lower = more sensitive)
113
+ - min-lines: Minimum line count (default: 5)
114
+ - similarity-threshold: 0-1 similarity threshold (default: 0.85)
115
+ - priority-filter: Filter by priority (high/medium/low, optional)
116
+ - max-results: Maximum number of clones to return (optional)
117
+ - output-mode: summary|detailed|recommendations (default: detailed)
118
+
119
+ OUTPUT FORMAT:
120
+ Returns clone detection results with:
121
+ - summary: Overall statistics (total clones, duplication %, priority breakdown)
122
+ - clones: Array of detected clone groups
123
+ - id: Clone identifier
124
+ - type: exact | similar
125
+ - confidence: Detection confidence (0-1)
126
+ - instances: Array of code locations with file paths and line numbers
127
+ - metrics: tokenCount, lineCount, instanceCount, impactScore, filesCovered
128
+ - refactoringAdvice:
129
+ - priority: high | medium | low
130
+ - strategy: extract-function | extract-class | extract-module | extract-constant
131
+ - suggestedName: Recommended name for extracted code
132
+ - reasoning: Why this refactoring is recommended
133
+ - estimatedEffort: low | medium | high
134
+ - benefits: Array of benefits
135
+ - actionableSteps: Step-by-step refactoring instructions
136
+
137
+ OUTPUT MODES:
138
+ - summary: High-level overview only (duplication %, top 5 clones)
139
+ - detailed: Full clone details with code snippets (default)
140
+ - recommendations: Refactoring priorities with actionable steps
141
+
142
+ DETECTION ALGORITHM:
143
+ - Uses AST (Abstract Syntax Tree) parsing via Babel
144
+ - Token-based similarity with normalization
145
+ - Longest Common Subsequence (LCS) algorithm for similarity
146
+ - Configurable sensitivity and thresholds
147
+
148
+ EXAMPLES:
149
+
150
+ Find all clones in project:
151
+ [tool id="clonedetection"]
152
+ <detect-clones directory="." />
153
+ [/tool]
154
+
155
+ High-priority refactoring opportunities only:
156
+ [tool id="clonedetection"]
157
+ <detect-clones directory="src" priority-filter="high" max-results="10" />
158
+ [/tool]
159
+
160
+ More sensitive detection (finds smaller clones):
161
+ [tool id="clonedetection"]
162
+ <detect-clones directory="." min-tokens="30" similarity-threshold="0.80" />
163
+ [/tool]
164
+
165
+ Quick overview:
166
+ [tool id="clonedetection"]
167
+ <detect-clones directory="." output-mode="summary" />
168
+ [/tool]
169
+
170
+ LIMITATIONS:
171
+ - Analyzes JavaScript/TypeScript family only
172
+ - Performance depends on codebase size (500ms per file typical)
173
+ - Maximum file size: ${Math.round(this.maxFileSize / 1024)}KB per file
174
+ - Analysis timeout: ${this.timeout / 1000} seconds
175
+
176
+ USE CASES:
177
+ - Identify refactoring opportunities before code review
178
+ - Track technical debt and code duplication metrics
179
+ - Find candidates for DRY (Don't Repeat Yourself) refactoring
180
+ - Prioritize maintenance work by impact score
181
+ `;
182
+ }
183
+
184
+ /**
185
+ * Parse parameters from tool command content
186
+ * @param {string} content - Raw tool command content
187
+ * @returns {Object} Parsed parameters
188
+ */
189
+ parseParameters(content) {
190
+ try {
191
+ const params = {};
192
+ const actions = [];
193
+
194
+ this.logger?.debug('CloneDetection tool parsing parameters', {
195
+ contentLength: content.length,
196
+ contentPreview: content.substring(0, 200)
197
+ });
198
+
199
+ // Extract self-closing <detect-clones> tags
200
+ const detectClonesPattern = /<detect-clones\s+(.+?)\/>/g;
201
+ let match;
202
+
203
+ while ((match = detectClonesPattern.exec(content)) !== null) {
204
+ const attributeString = match[1].trim();
205
+ const parser = new TagParser();
206
+ const attributes = parser.parseAttributes(attributeString);
207
+
208
+ const action = {
209
+ type: 'detect-clones',
210
+ ...attributes
211
+ };
212
+
213
+ // Normalize attribute names
214
+ if (action['min-tokens']) {
215
+ action.minTokens = parseInt(action['min-tokens'], 10);
216
+ delete action['min-tokens'];
217
+ }
218
+ if (action['min-lines']) {
219
+ action.minLines = parseInt(action['min-lines'], 10);
220
+ delete action['min-lines'];
221
+ }
222
+ if (action['similarity-threshold']) {
223
+ action.similarityThreshold = parseFloat(action['similarity-threshold']);
224
+ delete action['similarity-threshold'];
225
+ }
226
+ if (action['priority-filter']) {
227
+ action.priorityFilter = action['priority-filter'];
228
+ delete action['priority-filter'];
229
+ }
230
+ if (action['max-results']) {
231
+ action.maxResults = parseInt(action['max-results'], 10);
232
+ delete action['max-results'];
233
+ }
234
+ if (action['output-mode']) {
235
+ action.outputMode = action['output-mode'];
236
+ delete action['output-mode'];
237
+ }
238
+
239
+ actions.push(action);
240
+ }
241
+
242
+ params.actions = actions;
243
+ params.rawContent = content.trim();
244
+
245
+ this.logger?.debug('Parsed CloneDetection tool parameters', {
246
+ totalActions: actions.length,
247
+ actionTypes: actions.map(a => a.type)
248
+ });
249
+
250
+ return params;
251
+
252
+ } catch (error) {
253
+ throw new Error(`Failed to parse clone detection parameters: ${error.message}`);
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Get required parameters
259
+ * @returns {Array<string>} Array of required parameter names
260
+ */
261
+ getRequiredParameters() {
262
+ return ['actions'];
263
+ }
264
+
265
+ /**
266
+ * Custom parameter validation
267
+ * @param {Object} params - Parameters to validate
268
+ * @returns {Object} Validation result
269
+ */
270
+ customValidateParameters(params) {
271
+ const errors = [];
272
+
273
+ if (!params.actions || !Array.isArray(params.actions) || params.actions.length === 0) {
274
+ errors.push('At least one action is required');
275
+ } else {
276
+ // Validate each action
277
+ for (const [index, action] of params.actions.entries()) {
278
+ if (!action.type) {
279
+ errors.push(`Action ${index + 1}: type is required`);
280
+ continue;
281
+ }
282
+
283
+ if (action.type === 'detect-clones') {
284
+ if (!action.directory) {
285
+ errors.push(`Action ${index + 1}: directory is required for detect-clones`);
286
+ }
287
+
288
+ // Validate numeric parameters
289
+ if (action.minTokens !== undefined && (action.minTokens < 10 || action.minTokens > 1000)) {
290
+ errors.push(`Action ${index + 1}: min-tokens must be between 10 and 1000`);
291
+ }
292
+
293
+ if (action.minLines !== undefined && (action.minLines < 1 || action.minLines > 100)) {
294
+ errors.push(`Action ${index + 1}: min-lines must be between 1 and 100`);
295
+ }
296
+
297
+ if (action.similarityThreshold !== undefined &&
298
+ (action.similarityThreshold < 0.5 || action.similarityThreshold > 1.0)) {
299
+ errors.push(`Action ${index + 1}: similarity-threshold must be between 0.5 and 1.0`);
300
+ }
301
+
302
+ // Validate enum parameters
303
+ if (action.priorityFilter && !['high', 'medium', 'low'].includes(action.priorityFilter)) {
304
+ errors.push(`Action ${index + 1}: priority-filter must be high, medium, or low`);
305
+ }
306
+
307
+ if (action.outputMode && !['summary', 'detailed', 'recommendations'].includes(action.outputMode)) {
308
+ errors.push(`Action ${index + 1}: output-mode must be summary, detailed, or recommendations`);
309
+ }
310
+ } else {
311
+ errors.push(`Action ${index + 1}: unknown action type: ${action.type}`);
312
+ }
313
+ }
314
+ }
315
+
316
+ return {
317
+ valid: errors.length === 0,
318
+ errors
319
+ };
320
+ }
321
+
322
+ /**
323
+ * Execute tool with parsed parameters
324
+ * @param {Object} params - Parsed parameters
325
+ * @param {Object} context - Execution context
326
+ * @returns {Promise<Object>} Execution result
327
+ */
328
+ async execute(params, context) {
329
+ const { actions } = params;
330
+ const { projectDir, agentId, directoryAccess } = context;
331
+
332
+ // Get directory access configuration
333
+ const accessConfig = directoryAccess ||
334
+ this.directoryAccessManager.createDirectoryAccess({
335
+ workingDirectory: projectDir || process.cwd(),
336
+ writeEnabledDirectories: [],
337
+ readOnlyDirectories: [projectDir || process.cwd()],
338
+ restrictToProject: true
339
+ });
340
+
341
+ const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
342
+ const results = [];
343
+
344
+ for (const action of actions) {
345
+ try {
346
+ if (action.type === 'detect-clones') {
347
+ const result = await this.detectClones(
348
+ action.directory,
349
+ workingDir,
350
+ accessConfig,
351
+ action
352
+ );
353
+ results.push(result);
354
+ } else {
355
+ throw new Error(`Unknown action type: ${action.type}`);
356
+ }
357
+
358
+ } catch (error) {
359
+ this.logger?.error('Clone detection action failed', {
360
+ action: action.type,
361
+ error: error.message
362
+ });
363
+
364
+ results.push({
365
+ directory: action.directory,
366
+ error: error.message,
367
+ success: false
368
+ });
369
+ }
370
+ }
371
+
372
+ return {
373
+ success: true,
374
+ results,
375
+ toolUsed: 'clonedetection'
376
+ };
377
+ }
378
+
379
+ /**
380
+ * Detect clones in directory
381
+ * @private
382
+ */
383
+ async detectClones(directory, workingDir, accessConfig, options = {}) {
384
+ const fullDir = path.isAbsolute(directory)
385
+ ? path.normalize(directory)
386
+ : path.resolve(workingDir, directory);
387
+
388
+ // Validate read access
389
+ const accessResult = this.directoryAccessManager.validateReadAccess(fullDir, accessConfig);
390
+ if (!accessResult.allowed) {
391
+ throw new Error(`Read access denied: ${accessResult.reason}`);
392
+ }
393
+
394
+ try {
395
+ // Get clone detector instance
396
+ const detector = await this.getCloneDetector();
397
+
398
+ // Prepare configuration
399
+ const config = {
400
+ minTokens: options.minTokens || this.defaultMinTokens,
401
+ minLines: options.minLines || this.defaultMinLines,
402
+ similarityThreshold: options.similarityThreshold || this.defaultSimilarityThreshold,
403
+ include: ['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs', '**/*.ts', '**/*.tsx', '**/*.vue'],
404
+ exclude: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/*.test.js', '**/*.spec.js'],
405
+ maxFileSize: this.maxFileSize
406
+ };
407
+
408
+ this.logger?.info('Starting clone detection', {
409
+ directory: fullDir,
410
+ config
411
+ });
412
+
413
+ // Run clone detection (without output file)
414
+ const report = await detector.run(fullDir, null);
415
+
416
+ if (!report) {
417
+ return {
418
+ directory: this.directoryAccessManager.createRelativePath(fullDir, accessConfig),
419
+ fullPath: fullDir,
420
+ success: true,
421
+ summary: {
422
+ totalFiles: 0,
423
+ totalClones: 0,
424
+ duplicationPercentage: 0
425
+ },
426
+ clones: [],
427
+ message: 'No files found or no clones detected'
428
+ };
429
+ }
430
+
431
+ // Apply filters
432
+ let filteredClones = report.clones;
433
+
434
+ // Filter by priority
435
+ if (options.priorityFilter) {
436
+ filteredClones = filteredClones.filter(
437
+ clone => clone.refactoringAdvice.priority === options.priorityFilter
438
+ );
439
+ }
440
+
441
+ // Limit results
442
+ if (options.maxResults) {
443
+ filteredClones = filteredClones.slice(0, options.maxResults);
444
+ }
445
+
446
+ // Format output based on mode
447
+ const outputMode = options.outputMode || 'detailed';
448
+
449
+ if (outputMode === 'summary') {
450
+ return this.formatSummaryOutput(report, filteredClones, fullDir, accessConfig);
451
+ } else if (outputMode === 'recommendations') {
452
+ return this.formatRecommendationsOutput(report, filteredClones, fullDir, accessConfig);
453
+ } else {
454
+ return this.formatDetailedOutput(report, filteredClones, fullDir, accessConfig);
455
+ }
456
+
457
+ } catch (error) {
458
+ throw new Error(`Failed to detect clones in ${directory}: ${error.message}`);
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Format summary output
464
+ * @private
465
+ */
466
+ formatSummaryOutput(report, clones, fullDir, accessConfig) {
467
+ return {
468
+ directory: this.directoryAccessManager.createRelativePath(fullDir, accessConfig),
469
+ fullPath: fullDir,
470
+ success: true,
471
+ outputMode: 'summary',
472
+ summary: {
473
+ totalFiles: report.summary.totalFiles,
474
+ totalClones: report.summary.totalClones,
475
+ duplicatedLines: report.summary.totalDuplicatedLines,
476
+ duplicationPercentage: report.summary.duplicationPercentage,
477
+ priorityCounts: report.summary.priorityCounts,
478
+ topClones: clones.slice(0, 5).map(clone => ({
479
+ id: clone.id,
480
+ type: clone.type,
481
+ confidence: clone.confidence,
482
+ instances: clone.metrics.instanceCount,
483
+ lines: clone.metrics.lineCount,
484
+ priority: clone.refactoringAdvice.priority,
485
+ strategy: clone.refactoringAdvice.strategy,
486
+ locations: clone.instances.map(i => `${i.file}:${i.startLine}-${i.endLine}`)
487
+ }))
488
+ }
489
+ };
490
+ }
491
+
492
+ /**
493
+ * Format recommendations output
494
+ * @private
495
+ */
496
+ formatRecommendationsOutput(report, clones, fullDir, accessConfig) {
497
+ return {
498
+ directory: this.directoryAccessManager.createRelativePath(fullDir, accessConfig),
499
+ fullPath: fullDir,
500
+ success: true,
501
+ outputMode: 'recommendations',
502
+ summary: {
503
+ totalFiles: report.summary.totalFiles,
504
+ totalClones: report.summary.totalClones,
505
+ duplicationPercentage: report.summary.duplicationPercentage
506
+ },
507
+ recommendations: clones.map(clone => ({
508
+ id: clone.id,
509
+ priority: clone.refactoringAdvice.priority,
510
+ strategy: clone.refactoringAdvice.strategy,
511
+ suggestedName: clone.refactoringAdvice.suggestedName,
512
+ reasoning: clone.refactoringAdvice.reasoning,
513
+ effort: clone.refactoringAdvice.estimatedEffort,
514
+ benefits: clone.refactoringAdvice.benefits,
515
+ steps: clone.refactoringAdvice.actionableSteps,
516
+ metrics: {
517
+ instances: clone.metrics.instanceCount,
518
+ lines: clone.metrics.lineCount,
519
+ files: clone.metrics.filesCovered,
520
+ impact: clone.metrics.impactScore
521
+ },
522
+ locations: clone.instances.map(i => ({
523
+ file: i.file,
524
+ startLine: i.startLine,
525
+ endLine: i.endLine
526
+ }))
527
+ }))
528
+ };
529
+ }
530
+
531
+ /**
532
+ * Format detailed output
533
+ * @private
534
+ */
535
+ formatDetailedOutput(report, clones, fullDir, accessConfig) {
536
+ return {
537
+ directory: this.directoryAccessManager.createRelativePath(fullDir, accessConfig),
538
+ fullPath: fullDir,
539
+ success: true,
540
+ outputMode: 'detailed',
541
+ summary: report.summary,
542
+ clones: clones.map(clone => ({
543
+ ...clone,
544
+ // Truncate code snippets for agent readability
545
+ instances: clone.instances.map(instance => ({
546
+ ...instance,
547
+ code: instance.code.split('\n').slice(0, 10).join('\n') +
548
+ (instance.code.split('\n').length > 10 ? '\n... (truncated)' : '')
549
+ }))
550
+ }))
551
+ };
552
+ }
553
+
554
+ /**
555
+ * Get clone detector instance (lazy initialization)
556
+ * @private
557
+ */
558
+ async getCloneDetector() {
559
+ if (!this.cloneDetector) {
560
+ const { CloneDetectionTool } = await import('../analyzers/codeCloneDetector/index.js');
561
+
562
+ const config = {
563
+ minTokens: this.defaultMinTokens,
564
+ minLines: this.defaultMinLines,
565
+ similarityThreshold: this.defaultSimilarityThreshold,
566
+ include: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx', '**/*.vue'],
567
+ exclude: ['**/node_modules/**', '**/dist/**', '**/build/**'],
568
+ maxFileSize: this.maxFileSize
569
+ };
570
+
571
+ this.cloneDetector = new CloneDetectionTool(config);
572
+ this.logger?.debug('Clone detector initialized', { config });
573
+ }
574
+
575
+ return this.cloneDetector;
576
+ }
577
+
578
+ /**
579
+ * Get supported actions for this tool
580
+ * @returns {Array<string>} Array of supported action names
581
+ */
582
+ getSupportedActions() {
583
+ return ['detect-clones'];
584
+ }
585
+
586
+ /**
587
+ * Get parameter schema for validation
588
+ * @returns {Object} Parameter schema
589
+ */
590
+ getParameterSchema() {
591
+ return {
592
+ type: 'object',
593
+ properties: {
594
+ actions: {
595
+ type: 'array',
596
+ minItems: 1,
597
+ items: {
598
+ type: 'object',
599
+ properties: {
600
+ type: {
601
+ type: 'string',
602
+ enum: this.getSupportedActions()
603
+ },
604
+ directory: { type: 'string' },
605
+ minTokens: { type: 'number', minimum: 10, maximum: 1000 },
606
+ minLines: { type: 'number', minimum: 1, maximum: 100 },
607
+ similarityThreshold: { type: 'number', minimum: 0.5, maximum: 1.0 },
608
+ priorityFilter: { type: 'string', enum: ['high', 'medium', 'low'] },
609
+ maxResults: { type: 'number', minimum: 1 },
610
+ outputMode: { type: 'string', enum: ['summary', 'detailed', 'recommendations'] }
611
+ },
612
+ required: ['type', 'directory']
613
+ }
614
+ }
615
+ },
616
+ required: ['actions']
617
+ };
618
+ }
619
+ }
620
+
621
+ export default CloneDetectionTool;