repotoire 0.3.47

Graph-powered code analysis CLI. 81 detectors for security, architecture, and code quality.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
//! Code smell detectors
#![allow(unused_imports)]
//!
//! This module provides the detector framework and implementations for
//! finding code smells in the code graph.
//!
//! # Architecture
//!
//! ```text
//! ┌─────────────────────────────────────────────────────────────┐
//! │                     DetectorEngine                          │
//! │  - Registers detectors                                      │
//! │  - Runs independent detectors in parallel (rayon)          │
//! │  - Runs dependent detectors sequentially                    │
//! │  - Collects and reports findings                           │
//! └─────────────────────────────────────────────────────────────┘
//!//!//! ┌─────────────────────────────────────────────────────────────┐
//! │                      Detector Trait                         │
//! │  - name(): Unique identifier                                │
//! │  - description(): Human-readable description                │
//! │  - detect(graph): Run detection, return findings            │
//! │  - is_dependent(): Whether depends on other detectors       │
//! └─────────────────────────────────────────────────────────────┘
//!//!              ┌───────────────┼───────────────┐
//!              ▼               ▼               ▼
//! ┌──────────────────┐ ┌──────────────┐ ┌──────────────────┐
//! │ Graph-based      │ │ External     │ │ Hybrid           │
//! │ (CircularDep,    │ │ Tool-based   │ │ (graph + tool)   │
//! │  GodClass, etc.) │ │ (Bandit,     │ │                  │
//! │                  │ │  Ruff, etc.) │ │                  │
//! └──────────────────┘ └──────────────┘ └──────────────────┘
//! ```
//!
//! # Detector Categories
//!
//! ## Graph-based detectors (fast, query the code graph)
//! - `CircularDependencyDetector` - Circular imports/dependencies
//! - `GodClassDetector` - Classes with too many responsibilities
//! - `LongParameterListDetector` - Functions with too many parameters
//!
//! ## External tool detectors (run external tools via subprocess)
//! - `BanditDetector` - Python security vulnerabilities
//! - `RuffLintDetector` - Python code quality (100x faster than Pylint)
//! - `RuffImportDetector` - Unused Python imports
//! - `MypyDetector` - Python type checking
//! - `PylintDetector` - Python code quality (comprehensive)
//! - `ESLintDetector` - JavaScript/TypeScript code quality
//! - `TscDetector` - TypeScript type checking
//! - `NpmAuditDetector` - npm dependency vulnerabilities
//! - `SemgrepDetector` - Security pattern matching
//! - `RadonDetector` - Python complexity metrics
//! - `JscpdDetector` - Duplicate code detection
//! - `VultureDetector` - Dead Python code detection
//! - `GHActionsInjectionDetector` - GitHub Actions command injection
//!
//! # Usage
//!
//! ```ignore
//! use repotoire_cli::detectors::{
//!     DetectorEngine, DetectorEngineBuilder,
//!     CircularDependencyDetector, GodClassDetector, BanditDetector,
//! };
//! use std::sync::Arc;
//!
//! // Build engine with detectors
//! let engine = DetectorEngineBuilder::new()
//!     .workers(4)
//!     .detector(Arc::new(CircularDependencyDetector::new()))
//!     .detector(Arc::new(GodClassDetector::new()))
//!     .detector(Arc::new(BanditDetector::new("/path/to/repo")))
//!     .build();
//!
//! // Run detection
//! let findings = engine.run(&graph_client)?;
//! ```

mod base;
mod engine;

// Graph-based detector implementations
mod circular_dependency;
mod god_class;
mod long_parameter;

// Code smell detectors
mod data_clumps;
mod dead_code;
mod feature_envy;
mod inappropriate_intimacy;
mod lazy_class;
mod message_chain;
mod middle_man;
mod refused_bequest;

// AI-specific detectors
mod ai_boilerplate;
mod ai_churn;
mod ai_complexity_spike;
mod ai_duplicate_block;
mod ai_missing_tests;
mod ai_naming_pattern;

// Graph/architecture detectors
mod architectural_bottleneck;
mod core_utility;
mod degree_centrality;
mod influential_code;
mod module_cohesion;
mod shotgun_surgery;

// Security detectors
mod eval_detector;
mod pickle_detector;
mod sql_injection;
mod taint_detector;
mod unsafe_template;

// Taint analysis module (graph-based data flow tracking)
pub mod taint;

// Framework/ORM detection for reducing false positives
pub mod framework_detection;

// Misc detectors
mod generator_misuse;
mod infinite_loop;
mod unused_imports;

// Cross-detector analysis (ported from Python)
mod health_delta;
mod incremental_cache;
mod query_cache;
mod risk_analyzer;
mod root_cause_analyzer;
mod voting_engine;

// External tool utilities
pub mod external_tool;

// External tool-based detector implementations
mod bandit;
mod eslint;
mod gh_actions;
// mod jscpd;
mod mypy;
mod npm_audit;
// mod pylint;
mod radon;
mod ruff;
mod semgrep;
mod tsc;
mod vulture;
mod secrets;
mod empty_catch;
mod todo_scanner;
mod deep_nesting;
mod magic_numbers;
mod large_files;
mod path_traversal;
mod command_injection;
mod ssrf;
mod missing_docstrings;
mod regex_dos;
mod sync_in_async;
mod n_plus_one;
mod insecure_crypto;
mod xss;
mod hardcoded_ips;
mod insecure_random;
mod cors_misconfig;
mod debug_code;
mod commented_code;
mod long_methods;
mod duplicate_code;
mod unreachable_code;
mod string_concat_loop;
mod xxe;
mod insecure_deserialize;
mod cleartext_credentials;
mod wildcard_imports;
mod mutable_default_args;
mod global_variables;
mod implicit_coercion;
mod single_char_names;
mod missing_await;
mod unhandled_promise;
mod callback_hell;
mod test_in_production;
mod insecure_cookie;
mod jwt_weak;
mod prototype_pollution;
mod nosql_injection;
mod log_injection;
mod broad_exception;
mod boolean_trap;
mod inconsistent_returns;
mod dead_store;
mod hardcoded_timeout;
mod regex_in_loop;
mod react_hooks;
mod django_security;
mod express_security;

// Re-export base types
pub use base::{
    DetectionSummary,
    Detector,
    DetectorConfig,
    DetectorResult,
    ProgressCallback,
};

// Re-export engine
pub use engine::{
    DetectorEngine,
    DetectorEngineBuilder,
};

// Re-export graph-based detector implementations
pub use circular_dependency::CircularDependencyDetector;
pub use god_class::{GodClassDetector, GodClassThresholds};
pub use long_parameter::{LongParameterListDetector, LongParameterThresholds};

// Re-export code smell detectors
pub use data_clumps::DataClumpsDetector;
pub use dead_code::DeadCodeDetector;
pub use feature_envy::FeatureEnvyDetector;
pub use inappropriate_intimacy::InappropriateIntimacyDetector;
pub use lazy_class::LazyClassDetector;
pub use message_chain::MessageChainDetector;
pub use middle_man::MiddleManDetector;
pub use refused_bequest::RefusedBequestDetector;

// Re-export AI detectors
pub use ai_boilerplate::AIBoilerplateDetector;
pub use ai_churn::AIChurnDetector;
pub use ai_complexity_spike::AIComplexitySpikeDetector;
pub use ai_duplicate_block::AIDuplicateBlockDetector;
pub use ai_missing_tests::AIMissingTestsDetector;
pub use ai_naming_pattern::AINamingPatternDetector;

// Re-export graph/architecture detectors
pub use architectural_bottleneck::ArchitecturalBottleneckDetector;
pub use core_utility::CoreUtilityDetector;
pub use degree_centrality::DegreeCentralityDetector;
pub use influential_code::InfluentialCodeDetector;
pub use module_cohesion::ModuleCohesionDetector;
pub use shotgun_surgery::ShotgunSurgeryDetector;

// Re-export security detectors
pub use eval_detector::EvalDetector;
pub use pickle_detector::PickleDeserializationDetector;
pub use sql_injection::SQLInjectionDetector;
pub use taint_detector::TaintDetector;
pub use unsafe_template::UnsafeTemplateDetector;

// Re-export misc detectors
pub use generator_misuse::GeneratorMisuseDetector;
pub use infinite_loop::InfiniteLoopDetector;
pub use unused_imports::UnusedImportsDetector;

// Re-export cross-detector analysis utilities
pub use health_delta::{
    estimate_batch_fix_impact, estimate_fix_impact, BatchHealthScoreDelta, HealthScoreDelta,
    HealthScoreDeltaCalculator, ImpactLevel, MetricsBreakdown,
};
pub use incremental_cache::{CacheStats, IncrementalCache};
pub use query_cache::{ClassData, FileData, FunctionData, QueryCache};
pub use risk_analyzer::{
    analyze_compound_risks, RiskAnalyzer, RiskAssessment, RiskFactor,
};
pub use root_cause_analyzer::{RootCauseAnalysis, RootCauseAnalyzer, RootCauseSummary};
pub use voting_engine::{
    ConfidenceMethod, ConsensusResult, DetectorWeight, SeverityResolution, VotingEngine,
    VotingStats, VotingStrategy,
};

// Re-export external tool-based detector implementations
pub use bandit::BanditDetector;
pub use eslint::ESLintDetector;
pub use gh_actions::GHActionsInjectionDetector;
// pub use jscpd::JscpdDetector;
pub use mypy::MypyDetector;
pub use npm_audit::NpmAuditDetector;
// pub use pylint::PylintDetector;
pub use radon::RadonDetector;
pub use ruff::{RuffImportDetector, RuffLintDetector};
pub use semgrep::SemgrepDetector;
pub use tsc::TscDetector;
pub use vulture::VultureDetector;

// New detectors
pub use secrets::SecretDetector;
pub use empty_catch::EmptyCatchDetector;
pub use todo_scanner::TodoScanner;
pub use deep_nesting::DeepNestingDetector;
pub use magic_numbers::MagicNumbersDetector;
pub use large_files::LargeFilesDetector;
pub use path_traversal::PathTraversalDetector;
pub use command_injection::CommandInjectionDetector;
pub use ssrf::SsrfDetector;
pub use missing_docstrings::MissingDocstringsDetector;
pub use regex_dos::RegexDosDetector;
pub use sync_in_async::SyncInAsyncDetector;
pub use n_plus_one::NPlusOneDetector;
pub use insecure_crypto::InsecureCryptoDetector;
pub use xss::XssDetector;
pub use hardcoded_ips::HardcodedIpsDetector;
pub use insecure_random::InsecureRandomDetector;
pub use cors_misconfig::CorsMisconfigDetector;
pub use debug_code::DebugCodeDetector;
pub use commented_code::CommentedCodeDetector;
pub use long_methods::LongMethodsDetector;
pub use duplicate_code::DuplicateCodeDetector;
pub use unreachable_code::UnreachableCodeDetector;
pub use string_concat_loop::StringConcatLoopDetector;
pub use xxe::XxeDetector;
pub use insecure_deserialize::InsecureDeserializeDetector;
pub use cleartext_credentials::CleartextCredentialsDetector;
pub use wildcard_imports::WildcardImportsDetector;
pub use mutable_default_args::MutableDefaultArgsDetector;
pub use global_variables::GlobalVariablesDetector;
pub use implicit_coercion::ImplicitCoercionDetector;
pub use single_char_names::SingleCharNamesDetector;
pub use missing_await::MissingAwaitDetector;
pub use unhandled_promise::UnhandledPromiseDetector;
pub use callback_hell::CallbackHellDetector;
pub use test_in_production::TestInProductionDetector;
pub use insecure_cookie::InsecureCookieDetector;
pub use jwt_weak::JwtWeakDetector;
pub use prototype_pollution::PrototypePollutionDetector;
pub use nosql_injection::NosqlInjectionDetector;
pub use log_injection::LogInjectionDetector;
pub use broad_exception::BroadExceptionDetector;
pub use boolean_trap::BooleanTrapDetector;
pub use inconsistent_returns::InconsistentReturnsDetector;
pub use dead_store::DeadStoreDetector;
pub use hardcoded_timeout::HardcodedTimeoutDetector;
pub use regex_in_loop::RegexInLoopDetector;
pub use react_hooks::ReactHooksDetector;
pub use django_security::DjangoSecurityDetector;
pub use express_security::ExpressSecurityDetector;

// Re-export external tool utilities
pub use external_tool::{
    ExternalToolResult,
    GraphContext,
    JsRuntime,
    batch_get_graph_context,
    get_graph_context,
    get_js_exec_command,
    get_js_runtime,
    is_python_tool_installed,
    is_tool_installed,
    run_external_tool,
    run_js_tool,
};

use std::path::Path;
use std::sync::Arc;
use crate::config::ProjectConfig;

/// Create a default set of graph-based detectors
///
/// Returns detectors that only query the graph (no external tools required).
/// The `repository_path` is used by file-scanning detectors (security, etc.)
/// The `project_config` is used to apply per-project threshold overrides.
pub fn default_detectors(repository_path: &Path) -> Vec<Arc<dyn Detector>> {
    default_detectors_with_config(repository_path, &ProjectConfig::default())
}

/// Create a default set of graph-based detectors with project configuration
///
/// This variant allows passing project-level configuration for threshold overrides.
pub fn default_detectors_with_config(repository_path: &Path, project_config: &ProjectConfig) -> Vec<Arc<dyn Detector>> {
    vec![
        // Core detectors (with project config support)
        Arc::new(CircularDependencyDetector::new()),
        Arc::new(GodClassDetector::with_config(
            DetectorConfig::from_project_config("GodClassDetector", project_config)
        )),
        Arc::new(LongParameterListDetector::with_config(
            DetectorConfig::from_project_config("LongParameterListDetector", project_config)
        )),
        // Code smell detectors
        Arc::new(DataClumpsDetector::new()),
        Arc::new(DeadCodeDetector::new()),
        Arc::new(FeatureEnvyDetector::new()),
        Arc::new(InappropriateIntimacyDetector::new()),
        Arc::new(LazyClassDetector::new()),
        Arc::new(MessageChainDetector::new()),
        Arc::new(MiddleManDetector::new()),
        Arc::new(RefusedBequestDetector::new()),
        // AI detectors
        Arc::new(AIBoilerplateDetector::new()),
        Arc::new(AIChurnDetector::new()),
        Arc::new(AIComplexitySpikeDetector::new()),
        Arc::new(AIDuplicateBlockDetector::new()),
        Arc::new(AIMissingTestsDetector::new()),
        Arc::new(AINamingPatternDetector::new()),
        // Graph/architecture detectors
        Arc::new(ArchitecturalBottleneckDetector::new()),
        Arc::new(CoreUtilityDetector::new()),
        Arc::new(DegreeCentralityDetector::new()),
        Arc::new(InfluentialCodeDetector::new()),
        Arc::new(ModuleCohesionDetector::new()),
        Arc::new(ShotgunSurgeryDetector::new()),
        // Security detectors (need repository path for file scanning)
        Arc::new(EvalDetector::with_repository_path(repository_path.to_path_buf())),
        Arc::new(PickleDeserializationDetector::with_repository_path(repository_path.to_path_buf())),
        Arc::new(SQLInjectionDetector::with_repository_path(repository_path.to_path_buf())),
        Arc::new(TaintDetector::with_repository_path(repository_path.to_path_buf())),
        Arc::new(UnsafeTemplateDetector::with_repository_path(repository_path.to_path_buf())),
        // Misc detectors
        Arc::new(GeneratorMisuseDetector::new()),
        Arc::new(InfiniteLoopDetector::new()),
        Arc::new(UnusedImportsDetector::new()),
        // New security detectors
        Arc::new(SecretDetector::new(repository_path)),
        Arc::new(PathTraversalDetector::new(repository_path)),
        Arc::new(CommandInjectionDetector::new(repository_path)),
        Arc::new(SsrfDetector::new(repository_path)),
        Arc::new(RegexDosDetector::new(repository_path)),
        // New code quality detectors
        Arc::new(EmptyCatchDetector::new(repository_path)),
        Arc::new(TodoScanner::new(repository_path)),
        Arc::new(DeepNestingDetector::new(repository_path)),
        Arc::new(MagicNumbersDetector::new(repository_path)),
        Arc::new(LargeFilesDetector::new(repository_path)),
        Arc::new(MissingDocstringsDetector::new(repository_path)),
        // New performance detectors
        Arc::new(SyncInAsyncDetector::new(repository_path)),
        Arc::new(NPlusOneDetector::new(repository_path)),
        // More security detectors
        Arc::new(InsecureCryptoDetector::new(repository_path)),
        Arc::new(XssDetector::new(repository_path)),
        Arc::new(HardcodedIpsDetector::new(repository_path)),
        Arc::new(InsecureRandomDetector::new(repository_path)),
        Arc::new(CorsMisconfigDetector::new(repository_path)),
        // More code quality detectors
        Arc::new(DebugCodeDetector::new(repository_path)),
        Arc::new(CommentedCodeDetector::new(repository_path)),
        Arc::new(LongMethodsDetector::with_config(
            repository_path,
            DetectorConfig::from_project_config("long-methods", project_config)
        )),
        Arc::new(DuplicateCodeDetector::new(repository_path)),
        Arc::new(UnreachableCodeDetector::new(repository_path)),
        Arc::new(StringConcatLoopDetector::new(repository_path)),
        // Additional security
        Arc::new(XxeDetector::new(repository_path)),
        Arc::new(InsecureDeserializeDetector::new(repository_path)),
        Arc::new(CleartextCredentialsDetector::new(repository_path)),
        // Code quality
        Arc::new(WildcardImportsDetector::new(repository_path)),
        Arc::new(MutableDefaultArgsDetector::new(repository_path)),
        Arc::new(GlobalVariablesDetector::new(repository_path)),
        Arc::new(ImplicitCoercionDetector::new(repository_path)),
        Arc::new(SingleCharNamesDetector::new(repository_path)),
        // Async issues
        Arc::new(MissingAwaitDetector::new(repository_path)),
        Arc::new(UnhandledPromiseDetector::new(repository_path)),
        Arc::new(CallbackHellDetector::new(repository_path)),
        // Testing
        Arc::new(TestInProductionDetector::new(repository_path)),
        // More security
        Arc::new(InsecureCookieDetector::new(repository_path)),
        Arc::new(JwtWeakDetector::new(repository_path)),
        Arc::new(PrototypePollutionDetector::new(repository_path)),
        Arc::new(NosqlInjectionDetector::new(repository_path)),
        Arc::new(LogInjectionDetector::new(repository_path)),
        // More quality
        Arc::new(BroadExceptionDetector::new(repository_path)),
        Arc::new(BooleanTrapDetector::new(repository_path)),
        Arc::new(InconsistentReturnsDetector::new(repository_path)),
        Arc::new(DeadStoreDetector::new(repository_path)),
        Arc::new(HardcodedTimeoutDetector::new(repository_path)),
        // Performance
        Arc::new(RegexInLoopDetector::new(repository_path)),
        // Framework-specific
        Arc::new(ReactHooksDetector::new(repository_path)),
        Arc::new(DjangoSecurityDetector::new(repository_path)),
        Arc::new(ExpressSecurityDetector::new(repository_path)),
    ]
}

/// Create all Python detectors for a repository
///
/// Includes: Bandit, Ruff, Mypy, Pylint, Radon, Vulture
pub fn python_detectors(repository_path: &Path) -> Vec<Arc<dyn Detector>> {
    vec![
        Arc::new(BanditDetector::new(repository_path)),
        Arc::new(RuffLintDetector::new(repository_path)),
        Arc::new(RuffImportDetector::new(repository_path)),
        Arc::new(MypyDetector::new(repository_path)),
        // Arc::new(PylintDetector::new(repository_path)),
        Arc::new(RadonDetector::new(repository_path)),
        Arc::new(VultureDetector::new(repository_path)),
    ]
}

/// Create all JavaScript/TypeScript detectors for a repository
///
/// Includes: ESLint, tsc, npm audit
pub fn javascript_detectors(repository_path: &Path) -> Vec<Arc<dyn Detector>> {
    vec![
        Arc::new(ESLintDetector::new(repository_path)),
        Arc::new(TscDetector::new(repository_path)),
        Arc::new(NpmAuditDetector::new(repository_path)),
    ]
}

/// Create security-focused detectors for a repository
///
/// Includes: Bandit, Semgrep, npm audit, GitHub Actions injection, secrets, path traversal, etc.
pub fn security_detectors(repository_path: &Path) -> Vec<Arc<dyn Detector>> {
    vec![
        Arc::new(BanditDetector::new(repository_path)),
        Arc::new(SemgrepDetector::new(repository_path)),
        Arc::new(NpmAuditDetector::new(repository_path)),
        Arc::new(GHActionsInjectionDetector::new(repository_path)),
        Arc::new(SecretDetector::new(repository_path)),
        Arc::new(PathTraversalDetector::new(repository_path)),
        Arc::new(CommandInjectionDetector::new(repository_path)),
        Arc::new(SsrfDetector::new(repository_path)),
        Arc::new(RegexDosDetector::new(repository_path)),
    ]
}

/// Create all external tool detectors for a repository
///
/// Includes all language-specific and cross-language detectors.
pub fn all_external_detectors(repository_path: &Path) -> Vec<Arc<dyn Detector>> {
    vec![
        // Python
        Arc::new(BanditDetector::new(repository_path)),
        Arc::new(RuffLintDetector::new(repository_path)),
        Arc::new(RuffImportDetector::new(repository_path)),
        Arc::new(MypyDetector::new(repository_path)),
        // Arc::new(PylintDetector::new(repository_path)),
        Arc::new(RadonDetector::new(repository_path)),
        Arc::new(VultureDetector::new(repository_path)),
        // JavaScript/TypeScript
        Arc::new(ESLintDetector::new(repository_path)),
        Arc::new(TscDetector::new(repository_path)),
        Arc::new(NpmAuditDetector::new(repository_path)),
        // Cross-language
        Arc::new(SemgrepDetector::new(repository_path)),
        // Arc::new(JscpdDetector::new(repository_path)),
        Arc::new(GHActionsInjectionDetector::new(repository_path)),
    ]
}

/// Create a detector engine with all default detectors
///
/// Convenience function for quickly setting up detection.
pub fn create_default_engine(workers: usize, repository_path: &Path) -> DetectorEngine {
    DetectorEngineBuilder::new()
        .workers(workers)
        .detectors(default_detectors(repository_path))
        .build()
}

/// Create a detector engine with all detectors for a repository
pub fn create_full_engine(workers: usize, repository_path: &Path) -> DetectorEngine {
    let mut detectors = default_detectors(repository_path);
    detectors.extend(all_external_detectors(repository_path));

    DetectorEngineBuilder::new()
        .workers(workers)
        .detectors(detectors)
        .build()
}

/// Create a file walker that respects .gitignore and .repotoireignore
/// 
/// Use this instead of `walkdir::WalkDir` to ensure ignored files are skipped.
/// 
/// # Arguments
/// * `repository_path` - Path to the repository root
/// * `extensions` - Optional list of file extensions to filter (e.g., &["py", "pyi"])
/// 
/// # Returns
/// Iterator over file paths (not directories)
/// 
/// # Example
/// ```rust,ignore
/// for path in walk_source_files(&repo_path, Some(&["py"])) {
///     // Process Python file
/// }
/// ```
pub fn walk_source_files<'a>(
    repository_path: &'a Path,
    extensions: Option<&'a [&'a str]>,
) -> impl Iterator<Item = std::path::PathBuf> + 'a {
    use ignore::WalkBuilder;
    
    let mut builder = WalkBuilder::new(repository_path);
    builder
        .hidden(true) // Respect hidden files setting
        .git_ignore(true) // Respect .gitignore
        .git_global(true) // Respect global gitignore
        .git_exclude(true) // Respect .git/info/exclude
        .require_git(false) // Work even if not a git repo
        .add_custom_ignore_filename(".repotoireignore"); // Support .repotoireignore files
    
    builder.build().filter_map(move |entry| {
        let entry = entry.ok()?;
        let path = entry.path();
        
        // Skip directories
        if !path.is_file() {
            return None;
        }
        
        // Filter by extension if specified
        if let Some(exts) = extensions {
            let ext = path.extension()?.to_str()?;
            if !exts.contains(&ext) {
                return None;
            }
        }
        
        Some(path.to_path_buf())
    })
}

/// Check if a line has a repotoire suppression comment
/// 
/// Supports multiple comment styles:
/// - `# repotoire: ignore` (Python, Shell)
/// - `// repotoire: ignore` (JS, Rust, Go, etc.)
/// - `/* repotoire: ignore */` (C-style)
/// - `-- repotoire: ignore` (SQL)
/// 
/// Also checks the previous line for standalone suppression comments.
/// 
/// # Arguments
/// * `line` - The current line to check
/// * `prev_line` - Optional previous line (for standalone comments)
/// 
/// # Returns
/// `true` if the line should be suppressed
pub fn is_line_suppressed(line: &str, prev_line: Option<&str>) -> bool {
    let suppression_pattern = "repotoire: ignore";
    let suppression_pattern_alt = "repotoire:ignore";
    
    // Check current line for inline suppression
    let line_lower = line.to_lowercase();
    if line_lower.contains(suppression_pattern) || line_lower.contains(suppression_pattern_alt) {
        return true;
    }
    
    // Check previous line for standalone suppression comment
    if let Some(prev) = prev_line {
        let prev_lower = prev.trim().to_lowercase();
        // Only count if previous line is just a comment (not code + comment)
        if (prev_lower.starts_with('#') || prev_lower.starts_with("//") || 
            prev_lower.starts_with("--") || prev_lower.starts_with("/*")) &&
           (prev_lower.contains(suppression_pattern) || prev_lower.contains(suppression_pattern_alt)) {
            return true;
        }
    }
    
    false
}