repotoire 0.3.112

Graph-powered code analysis CLI. 114 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
//! Pure Rust code analysis detectors — zero external dependencies.
#![allow(unused_imports)]
//!
//! All 112+ detectors are built-in Rust. No shelling out to Python, Node, or any external tool.
//!
//! # Architecture
//!
//! ```text
//! DetectorEngine → Detector trait → detect(graph) → Vec<Finding>
//! ```
//!
//! Detectors run in parallel via rayon. Each receives the code graph
//! and returns findings. Security detectors also use SSA-based
//! intra-function taint analysis via tree-sitter ASTs.

pub mod base;
pub mod content_classifier;
mod engine;
pub mod streaming_engine;

// Context classification using HMM
pub mod context_hmm;

// 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;

// ML/Data Science detectors (PyTorch, TensorFlow, Scikit-Learn, Pandas, NumPy)
mod ml_smells;
mod rust_smells;

// 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 data_flow;
pub mod ssa_flow;
pub mod taint;

// Function context and role inference
pub mod function_context;

// Class context and role inference
pub mod class_context;

// 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;

mod boolean_trap;
mod broad_exception;
mod callback_hell;
mod cleartext_credentials;
mod command_injection;
mod commented_code;
mod cors_misconfig;
mod dead_store;
mod debug_code;
mod deep_nesting;
mod dep_audit;
mod django_security;
mod duplicate_code;
mod empty_catch;
mod express_security;
mod gh_actions;
mod global_variables;
mod hardcoded_ips;
mod hardcoded_timeout;
mod implicit_coercion;
mod inconsistent_returns;
mod insecure_cookie;
mod insecure_crypto;
mod insecure_deserialize;
mod insecure_random;
mod insecure_tls;
mod jwt_weak;
mod large_files;
mod log_injection;
mod long_methods;
mod magic_numbers;
mod missing_await;
mod missing_docstrings;
mod mutable_default_args;
mod n_plus_one;
mod nosql_injection;
mod path_traversal;
mod prototype_pollution;
mod react_hooks;
mod regex_dos;
mod regex_in_loop;
mod secrets;
mod single_char_names;
mod ssrf;
mod string_concat_loop;
mod sync_in_async;
mod test_in_production;
mod todo_scanner;
mod unhandled_promise;
mod unreachable_code;
mod wildcard_imports;
mod xss;
mod xxe;

// Re-export base types
pub use base::{
    DetectionSummary, Detector, DetectorConfig, DetectorResult, DetectorScope, 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 ML/Data Science detectors
pub use ml_smells::{
    ChainIndexingDetector, DeprecatedTorchApiDetector, ForwardMethodDetector,
    MissingRandomSeedDetector, MissingZeroGradDetector, NanEqualityDetector,
    RequireGradTypoDetector, TorchLoadUnsafeDetector,
};

// Re-export Rust-specific detectors
pub use rust_smells::{
    BoxDynTraitDetector, CloneInHotPathDetector, MissingMustUseDetector,
    MutexPoisoningRiskDetector, UnsafeWithoutSafetyCommentDetector, UnwrapWithoutContextDetector,
};

// 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, CachedScoreResult, 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,
};

pub use gh_actions::GHActionsInjectionDetector;

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

// Re-export function context types
pub use function_context::{
    FunctionContext, FunctionContextBuilder, FunctionContextMap, FunctionRole,
};

// External tool wrappers removed — pure Rust detectors only
// External tool wrappers removed — pure Rust detectors only

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

/// 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>> {
    default_detectors_with_profile(repository_path, project_config, None)
}

pub fn default_detectors_with_profile(
    repository_path: &Path,
    project_config: &ProjectConfig,
    style_profile: Option<&crate::calibrate::StyleProfile>,
) -> Vec<Arc<dyn Detector>> {
    // Get project type for coupling/complexity multipliers
    let project_type = project_config.get_project_type(repository_path);
    tracing::info!(
        "Detected project type: {:?} (coupling multiplier: {:.1}x)",
        project_type,
        project_type.coupling_multiplier()
    );

    // Build adaptive threshold resolver
    let resolver = crate::calibrate::ThresholdResolver::new(style_profile.cloned());

    // Helper to build detector config with adaptive resolver
    let make_config = |name: &str| -> DetectorConfig {
        DetectorConfig::from_project_config_with_type(name, project_config, repository_path)
            .with_adaptive(resolver.clone())
    };

    vec![
        // Core detectors (with project config support)
        Arc::new(CircularDependencyDetector::new()),
        Arc::new(GodClassDetector::with_config(make_config("GodClassDetector"))),
        Arc::new(LongParameterListDetector::with_config(make_config("LongParameterListDetector"))),
        // Code smell detectors
        Arc::new(DataClumpsDetector::with_config(make_config("DataClumpsDetector"))),
        Arc::new(DeadCodeDetector::new()),
        Arc::new(FeatureEnvyDetector::with_config(make_config("FeatureEnvyDetector"))),
        Arc::new(InappropriateIntimacyDetector::new()),
        Arc::new(LazyClassDetector::new()),
        Arc::new(MessageChainDetector::new(repository_path)),
        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()),
        // ML/Data Science detectors (PyTorch, TensorFlow, Scikit-Learn, Pandas, NumPy)
        Arc::new(TorchLoadUnsafeDetector::new(repository_path)),
        Arc::new(NanEqualityDetector::new(repository_path)),
        Arc::new(MissingZeroGradDetector::new(repository_path)),
        Arc::new(ForwardMethodDetector::new(repository_path)),
        Arc::new(MissingRandomSeedDetector::new(repository_path)),
        Arc::new(ChainIndexingDetector::new(repository_path)),
        Arc::new(RequireGradTypoDetector::new(repository_path)),
        Arc::new(DeprecatedTorchApiDetector::new(repository_path)),
        // Graph/architecture detectors
        Arc::new(ArchitecturalBottleneckDetector::with_config(make_config("ArchitecturalBottleneckDetector"))),
        Arc::new(CoreUtilityDetector::new()),
        Arc::new(DegreeCentralityDetector::with_config(make_config("DegreeCentralityDetector"))),
        Arc::new(InfluentialCodeDetector::with_config(make_config("InfluentialCodeDetector"))),
        Arc::new(ModuleCohesionDetector::with_config(make_config("ModuleCohesionDetector"))),
        Arc::new(ShotgunSurgeryDetector::with_config(make_config("ShotgunSurgeryDetector"))),
        // 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(),
        )),
        // TaintDetector disabled - naive file-based analysis, replaced by graph-based detectors:
        // PathTraversalDetector, CommandInjectionDetector, SqlInjectionDetector, etc.
        // 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::with_path(repository_path)),
        Arc::new(InfiniteLoopDetector::with_path(repository_path)),
        Arc::new(UnusedImportsDetector::new(repository_path)),
        // 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::with_resolver(repository_path, &resolver)),
        Arc::new(MagicNumbersDetector::new(repository_path)),
        Arc::new(LargeFilesDetector::with_resolver(repository_path, &resolver)),
        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)),
        // Rust-specific detectors
        Arc::new(UnwrapWithoutContextDetector::new(repository_path)),
        Arc::new(UnsafeWithoutSafetyCommentDetector::new(repository_path)),
        Arc::new(CloneInHotPathDetector::new(repository_path)),
        Arc::new(MissingMustUseDetector::new(repository_path)),
        Arc::new(BoxDynTraitDetector::new(repository_path)),
        Arc::new(MutexPoisoningRiskDetector::new(repository_path)),
        // CI/CD security
        Arc::new(GHActionsInjectionDetector::new(repository_path)),
        // TLS/Certificate validation
        Arc::new(InsecureTlsDetector::new(repository_path)),
        // Dependency vulnerability auditing
        Arc::new(DepAuditDetector::new(repository_path)),
    ]
}

/// Create a detector engine with all default detectors
///
/// Convenience function for quickly setting up detection.
#[allow(dead_code)] // Public API - may be used by external callers
pub fn create_default_engine(workers: usize, repository_path: &Path) -> DetectorEngine {
    DetectorEngineBuilder::new()
        .workers(workers)
        .detectors(default_detectors(repository_path))
        .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
}