thailint 0.1.6__py3-none-any.whl → 0.2.1__py3-none-any.whl
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.
- src/__init__.py +7 -2
- src/analyzers/__init__.py +23 -0
- src/analyzers/typescript_base.py +148 -0
- src/api.py +1 -1
- src/cli.py +524 -141
- src/config.py +6 -31
- src/core/base.py +12 -0
- src/core/cli_utils.py +206 -0
- src/core/config_parser.py +99 -0
- src/core/linter_utils.py +168 -0
- src/core/registry.py +17 -92
- src/core/rule_discovery.py +132 -0
- src/core/violation_builder.py +122 -0
- src/linter_config/ignore.py +112 -40
- src/linter_config/loader.py +3 -13
- src/linters/dry/__init__.py +23 -0
- src/linters/dry/base_token_analyzer.py +76 -0
- src/linters/dry/block_filter.py +262 -0
- src/linters/dry/block_grouper.py +59 -0
- src/linters/dry/cache.py +218 -0
- src/linters/dry/cache_query.py +61 -0
- src/linters/dry/config.py +130 -0
- src/linters/dry/config_loader.py +44 -0
- src/linters/dry/deduplicator.py +120 -0
- src/linters/dry/duplicate_storage.py +126 -0
- src/linters/dry/file_analyzer.py +127 -0
- src/linters/dry/inline_ignore.py +140 -0
- src/linters/dry/linter.py +170 -0
- src/linters/dry/python_analyzer.py +517 -0
- src/linters/dry/storage_initializer.py +51 -0
- src/linters/dry/token_hasher.py +115 -0
- src/linters/dry/typescript_analyzer.py +590 -0
- src/linters/dry/violation_builder.py +74 -0
- src/linters/dry/violation_filter.py +91 -0
- src/linters/dry/violation_generator.py +174 -0
- src/linters/file_placement/config_loader.py +86 -0
- src/linters/file_placement/directory_matcher.py +80 -0
- src/linters/file_placement/linter.py +252 -472
- src/linters/file_placement/path_resolver.py +61 -0
- src/linters/file_placement/pattern_matcher.py +55 -0
- src/linters/file_placement/pattern_validator.py +106 -0
- src/linters/file_placement/rule_checker.py +229 -0
- src/linters/file_placement/violation_factory.py +177 -0
- src/linters/nesting/config.py +13 -3
- src/linters/nesting/linter.py +76 -152
- src/linters/nesting/typescript_analyzer.py +38 -102
- src/linters/nesting/typescript_function_extractor.py +130 -0
- src/linters/nesting/violation_builder.py +139 -0
- src/linters/srp/__init__.py +99 -0
- src/linters/srp/class_analyzer.py +113 -0
- src/linters/srp/config.py +76 -0
- src/linters/srp/heuristics.py +89 -0
- src/linters/srp/linter.py +225 -0
- src/linters/srp/metrics_evaluator.py +47 -0
- src/linters/srp/python_analyzer.py +72 -0
- src/linters/srp/typescript_analyzer.py +75 -0
- src/linters/srp/typescript_metrics_calculator.py +90 -0
- src/linters/srp/violation_builder.py +117 -0
- src/orchestrator/core.py +42 -7
- src/utils/__init__.py +4 -0
- src/utils/project_root.py +84 -0
- {thailint-0.1.6.dist-info → thailint-0.2.1.dist-info}/METADATA +414 -63
- thailint-0.2.1.dist-info/RECORD +75 -0
- src/.ai/layout.yaml +0 -48
- thailint-0.1.6.dist-info/RECORD +0 -28
- {thailint-0.1.6.dist-info → thailint-0.2.1.dist-info}/LICENSE +0 -0
- {thailint-0.1.6.dist-info → thailint-0.2.1.dist-info}/WHEEL +0 -0
- {thailint-0.1.6.dist-info → thailint-0.2.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: thailint
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: The AI Linter - Enterprise-grade linting and governance for AI-generated code across multiple languages
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: linter,ai,code-quality,static-analysis,file-placement,governance,multi-language,cli,docker,python
|
|
@@ -22,6 +22,7 @@ Classifier: Topic :: Software Development :: Testing
|
|
|
22
22
|
Classifier: Topic :: Utilities
|
|
23
23
|
Classifier: Typing :: Typed
|
|
24
24
|
Requires-Dist: click (>=8.1.0,<9.0.0)
|
|
25
|
+
Requires-Dist: pyprojroot (>=0.3.0,<0.4.0)
|
|
25
26
|
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
26
27
|
Requires-Dist: tree-sitter (>=0.25.2,<0.26.0)
|
|
27
28
|
Requires-Dist: tree-sitter-typescript (>=0.23.2,<0.24.0)
|
|
@@ -43,30 +44,40 @@ The AI Linter - Enterprise-ready linting and governance for AI-generated code ac
|
|
|
43
44
|
|
|
44
45
|
thailint is a modern, enterprise-ready multi-language linter designed specifically for AI-generated code. It enforces project structure, file placement rules, and coding standards across Python, TypeScript, and other languages.
|
|
45
46
|
|
|
46
|
-
##
|
|
47
|
+
## Features
|
|
47
48
|
|
|
48
49
|
### Core Capabilities
|
|
49
|
-
-
|
|
50
|
-
-
|
|
50
|
+
- **File Placement Linting** - Enforce project structure and organization
|
|
51
|
+
- **Nesting Depth Linting** - Detect excessive code nesting with AST analysis
|
|
51
52
|
- Python and TypeScript support with tree-sitter
|
|
52
53
|
- Configurable max depth (default: 4, recommended: 3)
|
|
53
54
|
- Helpful refactoring suggestions (guard clauses, extract method)
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
55
|
+
- **SRP Linting** - Detect Single Responsibility Principle violations
|
|
56
|
+
- Heuristic-based analysis (method count, LOC, keywords)
|
|
57
|
+
- Language-specific thresholds (Python, TypeScript, JavaScript)
|
|
58
|
+
- Refactoring patterns from real-world examples
|
|
59
|
+
- **DRY Linting** - Detect duplicate code across projects
|
|
60
|
+
- Token-based hash detection with SQLite caching
|
|
61
|
+
- Fast incremental scans (10-50x speedup with cache)
|
|
62
|
+
- Configurable thresholds (lines, tokens, occurrences)
|
|
63
|
+
- Language-specific detection (Python, TypeScript, JavaScript)
|
|
64
|
+
- False positive filtering (keyword args, imports)
|
|
65
|
+
- **Pluggable Architecture** - Easy to extend with custom linters
|
|
66
|
+
- **Multi-Language Support** - Python, TypeScript, JavaScript, and more
|
|
67
|
+
- **Flexible Configuration** - YAML/JSON configs with pattern matching
|
|
68
|
+
- **5-Level Ignore System** - Repo, directory, file, method, and line-level ignores
|
|
58
69
|
|
|
59
70
|
### Deployment Modes
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
71
|
+
- **CLI Mode** - Full-featured command-line interface
|
|
72
|
+
- **Library API** - Python library for programmatic integration
|
|
73
|
+
- **Docker Support** - Containerized deployment for CI/CD
|
|
63
74
|
|
|
64
75
|
### Enterprise Features
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
76
|
+
- **Performance** - <100ms for single files, <5s for 1000 files
|
|
77
|
+
- **Type Safety** - Full type hints and MyPy strict mode
|
|
78
|
+
- **Test Coverage** - 90% coverage with 317 tests
|
|
79
|
+
- **CI/CD Ready** - Proper exit codes and JSON output
|
|
80
|
+
- **Comprehensive Docs** - Complete documentation and examples
|
|
70
81
|
|
|
71
82
|
## Installation
|
|
72
83
|
|
|
@@ -105,14 +116,20 @@ docker run --rm washad/thailint:latest --help
|
|
|
105
116
|
# Check file placement
|
|
106
117
|
thailint file-placement .
|
|
107
118
|
|
|
108
|
-
# Check
|
|
119
|
+
# Check multiple files
|
|
120
|
+
thailint nesting file1.py file2.py file3.py
|
|
121
|
+
|
|
122
|
+
# Check specific directory
|
|
109
123
|
thailint nesting src/
|
|
110
124
|
|
|
125
|
+
# Check for duplicate code
|
|
126
|
+
thailint dry .
|
|
127
|
+
|
|
111
128
|
# With config file
|
|
112
|
-
thailint
|
|
129
|
+
thailint dry --config .thailint.yaml src/
|
|
113
130
|
|
|
114
131
|
# JSON output for CI/CD
|
|
115
|
-
thailint
|
|
132
|
+
thailint dry --format json src/
|
|
116
133
|
```
|
|
117
134
|
|
|
118
135
|
### Library Mode
|
|
@@ -135,13 +152,21 @@ if violations:
|
|
|
135
152
|
### Docker Mode
|
|
136
153
|
|
|
137
154
|
```bash
|
|
138
|
-
#
|
|
155
|
+
# Lint directory (recommended - lints all files inside)
|
|
139
156
|
docker run --rm -v $(pwd):/data \
|
|
140
157
|
washad/thailint:latest file-placement /data
|
|
141
158
|
|
|
142
|
-
#
|
|
159
|
+
# Lint single file
|
|
160
|
+
docker run --rm -v $(pwd):/data \
|
|
161
|
+
washad/thailint:latest file-placement /data/src/app.py
|
|
162
|
+
|
|
163
|
+
# Lint multiple specific files
|
|
164
|
+
docker run --rm -v $(pwd):/data \
|
|
165
|
+
washad/thailint:latest nesting /data/src/file1.py /data/src/file2.py
|
|
166
|
+
|
|
167
|
+
# Check nesting depth in subdirectory
|
|
143
168
|
docker run --rm -v $(pwd):/data \
|
|
144
|
-
washad/thailint:latest nesting /data
|
|
169
|
+
washad/thailint:latest nesting /data/src
|
|
145
170
|
```
|
|
146
171
|
|
|
147
172
|
## Configuration
|
|
@@ -191,6 +216,26 @@ nesting:
|
|
|
191
216
|
max_depth: 4
|
|
192
217
|
javascript:
|
|
193
218
|
max_depth: 4
|
|
219
|
+
|
|
220
|
+
# DRY linter configuration
|
|
221
|
+
dry:
|
|
222
|
+
enabled: true
|
|
223
|
+
min_duplicate_lines: 4 # Minimum lines to consider duplicate
|
|
224
|
+
min_duplicate_tokens: 30 # Minimum tokens to consider duplicate
|
|
225
|
+
min_occurrences: 2 # Report if appears 2+ times
|
|
226
|
+
|
|
227
|
+
# Language-specific thresholds
|
|
228
|
+
python:
|
|
229
|
+
min_occurrences: 3 # Python: require 3+ occurrences
|
|
230
|
+
|
|
231
|
+
# Cache settings (SQLite)
|
|
232
|
+
cache_enabled: true
|
|
233
|
+
cache_path: ".thailint-cache/dry.db"
|
|
234
|
+
|
|
235
|
+
# Ignore patterns
|
|
236
|
+
ignore:
|
|
237
|
+
- "tests/"
|
|
238
|
+
- "__init__.py"
|
|
194
239
|
```
|
|
195
240
|
|
|
196
241
|
**JSON format also supported** (`.thailint.json`):
|
|
@@ -214,6 +259,18 @@ nesting:
|
|
|
214
259
|
"python": { "max_depth": 4 },
|
|
215
260
|
"typescript": { "max_depth": 4 }
|
|
216
261
|
}
|
|
262
|
+
},
|
|
263
|
+
"dry": {
|
|
264
|
+
"enabled": true,
|
|
265
|
+
"min_duplicate_lines": 4,
|
|
266
|
+
"min_duplicate_tokens": 30,
|
|
267
|
+
"min_occurrences": 2,
|
|
268
|
+
"python": {
|
|
269
|
+
"min_occurrences": 3
|
|
270
|
+
},
|
|
271
|
+
"cache_enabled": true,
|
|
272
|
+
"cache_path": ".thailint-cache/dry.db",
|
|
273
|
+
"ignore": ["tests/", "__init__.py"]
|
|
217
274
|
}
|
|
218
275
|
}
|
|
219
276
|
```
|
|
@@ -280,7 +337,7 @@ def process_data(items):
|
|
|
280
337
|
|
|
281
338
|
### Refactoring Patterns
|
|
282
339
|
|
|
283
|
-
Common patterns to reduce nesting
|
|
340
|
+
Common patterns to reduce nesting:
|
|
284
341
|
|
|
285
342
|
1. **Guard Clauses (Early Returns)**
|
|
286
343
|
- Replace `if x: do_something()` with `if not x: return`
|
|
@@ -300,19 +357,300 @@ Common patterns to reduce nesting (used to fix 23 violations in thai-lint):
|
|
|
300
357
|
|
|
301
358
|
### Language Support
|
|
302
359
|
|
|
303
|
-
-
|
|
304
|
-
-
|
|
305
|
-
-
|
|
360
|
+
- **Python**: Full support (if/for/while/with/try/match)
|
|
361
|
+
- **TypeScript**: Full support (if/for/while/try/switch)
|
|
362
|
+
- **JavaScript**: Supported via TypeScript parser
|
|
306
363
|
|
|
307
|
-
|
|
364
|
+
See [Nesting Linter Guide](docs/nesting-linter.md) for comprehensive documentation and refactoring patterns.
|
|
308
365
|
|
|
309
|
-
|
|
310
|
-
- Identified: 23 functions requiring refactoring (depth 4 → depth ≤3)
|
|
311
|
-
- Refactored using documented patterns
|
|
312
|
-
- Time estimate: ~10 minutes per function
|
|
313
|
-
- Result: Zero violations, improved readability
|
|
366
|
+
## Single Responsibility Principle (SRP) Linter
|
|
314
367
|
|
|
315
|
-
|
|
368
|
+
### Overview
|
|
369
|
+
|
|
370
|
+
The SRP linter detects classes that violate the Single Responsibility Principle by having too many methods, too many lines of code, or generic naming patterns. It uses AST analysis with configurable heuristics to identify classes that likely handle multiple responsibilities.
|
|
371
|
+
|
|
372
|
+
### Quick Start
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
# Check SRP violations in current directory
|
|
376
|
+
thailint srp .
|
|
377
|
+
|
|
378
|
+
# Use custom thresholds
|
|
379
|
+
thailint srp --max-methods 10 --max-loc 300 src/
|
|
380
|
+
|
|
381
|
+
# Get JSON output
|
|
382
|
+
thailint srp --format json src/
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Configuration
|
|
386
|
+
|
|
387
|
+
Add to `.thailint.yaml`:
|
|
388
|
+
|
|
389
|
+
```yaml
|
|
390
|
+
srp:
|
|
391
|
+
enabled: true
|
|
392
|
+
max_methods: 7 # Maximum methods per class
|
|
393
|
+
max_loc: 200 # Maximum lines of code per class
|
|
394
|
+
|
|
395
|
+
# Language-specific thresholds
|
|
396
|
+
python:
|
|
397
|
+
max_methods: 8
|
|
398
|
+
max_loc: 200
|
|
399
|
+
|
|
400
|
+
typescript:
|
|
401
|
+
max_methods: 10 # TypeScript more verbose
|
|
402
|
+
max_loc: 250
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Detection Heuristics
|
|
406
|
+
|
|
407
|
+
The SRP linter uses three heuristics to detect violations:
|
|
408
|
+
|
|
409
|
+
1. **Method Count**: Classes with >7 methods (default) likely have multiple responsibilities
|
|
410
|
+
2. **Lines of Code**: Classes with >200 LOC (default) are often doing too much
|
|
411
|
+
3. **Responsibility Keywords**: Names containing "Manager", "Handler", "Processor", etc.
|
|
412
|
+
|
|
413
|
+
### Example Violation
|
|
414
|
+
|
|
415
|
+
**Code with SRP violation:**
|
|
416
|
+
```python
|
|
417
|
+
class UserManager: # 8 methods, contains "Manager" keyword
|
|
418
|
+
def create_user(self): pass
|
|
419
|
+
def update_user(self): pass
|
|
420
|
+
def delete_user(self): pass
|
|
421
|
+
def send_email(self): pass # ← Different responsibility
|
|
422
|
+
def log_action(self): pass # ← Different responsibility
|
|
423
|
+
def validate_data(self): pass # ← Different responsibility
|
|
424
|
+
def generate_report(self): pass # ← Different responsibility
|
|
425
|
+
def export_data(self): pass # ← Violation at method 8
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
**Refactored following SRP:**
|
|
429
|
+
```python
|
|
430
|
+
class UserRepository: # 3 methods ✓
|
|
431
|
+
def create(self, user): pass
|
|
432
|
+
def update(self, user): pass
|
|
433
|
+
def delete(self, user): pass
|
|
434
|
+
|
|
435
|
+
class EmailService: # 1 method ✓
|
|
436
|
+
def send(self, user, template): pass
|
|
437
|
+
|
|
438
|
+
class UserAuditLog: # 1 method ✓
|
|
439
|
+
def log(self, action, user): pass
|
|
440
|
+
|
|
441
|
+
class UserValidator: # 1 method ✓
|
|
442
|
+
def validate(self, data): pass
|
|
443
|
+
|
|
444
|
+
class ReportGenerator: # 1 method ✓
|
|
445
|
+
def generate(self, users): pass
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Refactoring Patterns
|
|
449
|
+
|
|
450
|
+
Common patterns to fix SRP violations (discovered during dogfooding):
|
|
451
|
+
|
|
452
|
+
1. **Extract Class**
|
|
453
|
+
- Split god classes into focused classes
|
|
454
|
+
- Each class handles one responsibility
|
|
455
|
+
|
|
456
|
+
2. **Split Configuration and Logic**
|
|
457
|
+
- Separate config loading from business logic
|
|
458
|
+
- Create dedicated ConfigLoader classes
|
|
459
|
+
|
|
460
|
+
3. **Extract Language-Specific Logic**
|
|
461
|
+
- Separate Python/TypeScript analysis
|
|
462
|
+
- Use analyzer classes per language
|
|
463
|
+
|
|
464
|
+
4. **Utility Module Pattern**
|
|
465
|
+
- Group related helper methods
|
|
466
|
+
- Create focused utility classes
|
|
467
|
+
|
|
468
|
+
### Language Support
|
|
469
|
+
|
|
470
|
+
- **Python**: Full support with method counting and LOC analysis
|
|
471
|
+
- **TypeScript**: Full support with tree-sitter parsing
|
|
472
|
+
- **JavaScript**: Supported via TypeScript parser
|
|
473
|
+
|
|
474
|
+
### Real-World Example
|
|
475
|
+
|
|
476
|
+
**Large class refactoring:**
|
|
477
|
+
- **Before**: FilePlacementLinter (33 methods, 382 LOC) - single class handling config, patterns, validation
|
|
478
|
+
- **After**: Extract Class pattern applied - 5 focused classes (ConfigLoader, PatternValidator, RuleChecker, PathResolver, FilePlacementLinter)
|
|
479
|
+
- **Result**: Each class ≤8 methods, ≤150 LOC, single responsibility
|
|
480
|
+
|
|
481
|
+
See [SRP Linter Guide](docs/srp-linter.md) for comprehensive documentation and refactoring patterns.
|
|
482
|
+
|
|
483
|
+
## DRY Linter (Don't Repeat Yourself)
|
|
484
|
+
|
|
485
|
+
### Overview
|
|
486
|
+
|
|
487
|
+
The DRY linter detects duplicate code blocks across your entire project using token-based hashing with SQLite caching. It identifies identical or near-identical code that violates the Don't Repeat Yourself (DRY) principle, helping maintain code quality at scale.
|
|
488
|
+
|
|
489
|
+
### Quick Start
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
# Check for duplicate code in current directory
|
|
493
|
+
thailint dry .
|
|
494
|
+
|
|
495
|
+
# Use custom thresholds
|
|
496
|
+
thailint dry --min-lines 5 src/
|
|
497
|
+
|
|
498
|
+
# Clear cache and re-scan
|
|
499
|
+
thailint dry --clear-cache src/
|
|
500
|
+
|
|
501
|
+
# Get JSON output
|
|
502
|
+
thailint dry --format json src/
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Configuration
|
|
506
|
+
|
|
507
|
+
Add to `.thailint.yaml`:
|
|
508
|
+
|
|
509
|
+
```yaml
|
|
510
|
+
dry:
|
|
511
|
+
enabled: true
|
|
512
|
+
min_duplicate_lines: 4 # Minimum lines to consider duplicate
|
|
513
|
+
min_duplicate_tokens: 30 # Minimum tokens to consider duplicate
|
|
514
|
+
min_occurrences: 2 # Report if appears 2+ times
|
|
515
|
+
|
|
516
|
+
# Language-specific thresholds
|
|
517
|
+
python:
|
|
518
|
+
min_occurrences: 3 # Python: require 3+ occurrences
|
|
519
|
+
typescript:
|
|
520
|
+
min_occurrences: 3 # TypeScript: require 3+ occurrences
|
|
521
|
+
|
|
522
|
+
# Cache settings (SQLite for fast incremental scans)
|
|
523
|
+
cache_enabled: true
|
|
524
|
+
cache_path: ".thailint-cache/dry.db"
|
|
525
|
+
cache_max_age_days: 30
|
|
526
|
+
|
|
527
|
+
# Ignore patterns
|
|
528
|
+
ignore:
|
|
529
|
+
- "tests/" # Test code often has acceptable duplication
|
|
530
|
+
- "__init__.py" # Import-only files exempt
|
|
531
|
+
|
|
532
|
+
# False positive filters
|
|
533
|
+
filters:
|
|
534
|
+
keyword_argument_filter: true # Filter function call kwargs
|
|
535
|
+
import_group_filter: true # Filter import groups
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### How It Works
|
|
539
|
+
|
|
540
|
+
**Token-Based Detection:**
|
|
541
|
+
1. Parse code into tokens (stripping comments, normalizing whitespace)
|
|
542
|
+
2. Create rolling hash windows of N lines
|
|
543
|
+
3. Store hashes in SQLite database with file locations
|
|
544
|
+
4. Query for hashes appearing 2+ times across project
|
|
545
|
+
|
|
546
|
+
**SQLite Caching:**
|
|
547
|
+
- First scan: Hash all files (~1-3s for 1000 files)
|
|
548
|
+
- Subsequent scans: Load cached hashes for unchanged files (~0.1-0.5s)
|
|
549
|
+
- 10-50x speedup for incremental scans
|
|
550
|
+
- Mtime-based cache invalidation (automatic and safe)
|
|
551
|
+
|
|
552
|
+
### Example Violation
|
|
553
|
+
|
|
554
|
+
**Code with duplication:**
|
|
555
|
+
```python
|
|
556
|
+
# src/auth.py
|
|
557
|
+
def validate_user(user_data):
|
|
558
|
+
if not user_data:
|
|
559
|
+
return False
|
|
560
|
+
if not user_data.get('email'):
|
|
561
|
+
return False
|
|
562
|
+
if not user_data.get('password'):
|
|
563
|
+
return False
|
|
564
|
+
return True
|
|
565
|
+
|
|
566
|
+
# src/admin.py
|
|
567
|
+
def validate_admin(admin_data):
|
|
568
|
+
if not admin_data:
|
|
569
|
+
return False
|
|
570
|
+
if not admin_data.get('email'):
|
|
571
|
+
return False
|
|
572
|
+
if not admin_data.get('password'):
|
|
573
|
+
return False
|
|
574
|
+
return True
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**Violation message:**
|
|
578
|
+
```
|
|
579
|
+
src/auth.py:3 - Duplicate code detected (4 lines, 2 occurrences)
|
|
580
|
+
Locations:
|
|
581
|
+
- src/auth.py:3-6
|
|
582
|
+
- src/admin.py:3-6
|
|
583
|
+
Consider extracting to shared function
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
**Refactored (DRY):**
|
|
587
|
+
```python
|
|
588
|
+
# src/validators.py
|
|
589
|
+
def validate_credentials(data):
|
|
590
|
+
if not data:
|
|
591
|
+
return False
|
|
592
|
+
if not data.get('email'):
|
|
593
|
+
return False
|
|
594
|
+
if not data.get('password'):
|
|
595
|
+
return False
|
|
596
|
+
return True
|
|
597
|
+
|
|
598
|
+
# src/auth.py & src/admin.py
|
|
599
|
+
from src.validators import validate_credentials
|
|
600
|
+
|
|
601
|
+
def validate_user(user_data):
|
|
602
|
+
return validate_credentials(user_data)
|
|
603
|
+
|
|
604
|
+
def validate_admin(admin_data):
|
|
605
|
+
return validate_credentials(admin_data)
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### Cache Management
|
|
609
|
+
|
|
610
|
+
```bash
|
|
611
|
+
# Normal cached run (default)
|
|
612
|
+
thailint dry src/
|
|
613
|
+
|
|
614
|
+
# Force re-analysis (ignore cache)
|
|
615
|
+
thailint dry --no-cache src/
|
|
616
|
+
|
|
617
|
+
# Clear cache before running
|
|
618
|
+
thailint dry --clear-cache src/
|
|
619
|
+
|
|
620
|
+
# Manual cache cleanup
|
|
621
|
+
rm -rf .thailint-cache/dry.db
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
### Performance
|
|
625
|
+
|
|
626
|
+
| Operation | Performance | Cache Status |
|
|
627
|
+
|-----------|-------------|--------------|
|
|
628
|
+
| First scan (1000 files) | 1-3s | Cache write |
|
|
629
|
+
| Unchanged files | 0.1-0.5s | Cache hit |
|
|
630
|
+
| 50 changed files | 0.5-1s | Partial cache |
|
|
631
|
+
|
|
632
|
+
**Speedup**: 3-10x for incremental scans with cache
|
|
633
|
+
|
|
634
|
+
### Language Support
|
|
635
|
+
|
|
636
|
+
- **Python**: Full support with AST-based tokenization
|
|
637
|
+
- **TypeScript**: Full support with tree-sitter parsing
|
|
638
|
+
- **JavaScript**: Supported via TypeScript parser
|
|
639
|
+
|
|
640
|
+
### False Positive Filtering
|
|
641
|
+
|
|
642
|
+
Built-in filters automatically exclude common non-duplication patterns:
|
|
643
|
+
- **keyword_argument_filter**: Excludes function calls with keyword arguments
|
|
644
|
+
- **import_group_filter**: Excludes import statement groups
|
|
645
|
+
|
|
646
|
+
### Refactoring Patterns
|
|
647
|
+
|
|
648
|
+
1. **Extract Function**: Move repeated logic to shared function
|
|
649
|
+
2. **Extract Base Class**: Create base class for similar implementations
|
|
650
|
+
3. **Extract Utility Module**: Move helper functions to shared utilities
|
|
651
|
+
4. **Template Method**: Use function parameters for variations
|
|
652
|
+
|
|
653
|
+
See [DRY Linter Guide](docs/dry-linter.md) for comprehensive documentation, cache management, and refactoring patterns.
|
|
316
654
|
|
|
317
655
|
## Pre-commit Hooks
|
|
318
656
|
|
|
@@ -335,13 +673,13 @@ pre-commit run --all-files
|
|
|
335
673
|
### What You Get
|
|
336
674
|
|
|
337
675
|
**On every commit:**
|
|
338
|
-
-
|
|
339
|
-
-
|
|
340
|
-
-
|
|
676
|
+
- Prevents commits to main/master branch
|
|
677
|
+
- Auto-fixes formatting issues
|
|
678
|
+
- Runs thailint on changed files (fast, uses pass_filenames: true)
|
|
341
679
|
|
|
342
680
|
**On every push:**
|
|
343
|
-
-
|
|
344
|
-
-
|
|
681
|
+
- Full linting on entire codebase
|
|
682
|
+
- Runs complete test suite
|
|
345
683
|
|
|
346
684
|
### Example Configuration
|
|
347
685
|
|
|
@@ -353,7 +691,7 @@ repos:
|
|
|
353
691
|
# Prevent commits to protected branches
|
|
354
692
|
- id: no-commit-to-main
|
|
355
693
|
name: Prevent commits to main branch
|
|
356
|
-
entry: bash -c 'branch=$(git rev-parse --abbrev-ref HEAD); if [ "$branch" = "main" ]; then echo "
|
|
694
|
+
entry: bash -c 'branch=$(git rev-parse --abbrev-ref HEAD); if [ "$branch" = "main" ]; then echo "ERROR: Use a feature branch!"; exit 1; fi'
|
|
357
695
|
language: system
|
|
358
696
|
pass_filenames: false
|
|
359
697
|
always_run: true
|
|
@@ -361,16 +699,17 @@ repos:
|
|
|
361
699
|
# Auto-format code
|
|
362
700
|
- id: format
|
|
363
701
|
name: Auto-fix formatting
|
|
364
|
-
entry:
|
|
702
|
+
entry: just format
|
|
365
703
|
language: system
|
|
366
704
|
pass_filenames: false
|
|
367
705
|
|
|
368
|
-
# Run thailint on changed files
|
|
369
|
-
- id:
|
|
706
|
+
# Run thailint on changed files (passes filenames directly)
|
|
707
|
+
- id: thailint-changed
|
|
370
708
|
name: Lint changed files
|
|
371
|
-
entry:
|
|
709
|
+
entry: thailint nesting
|
|
372
710
|
language: system
|
|
373
|
-
|
|
711
|
+
files: \.(py|ts|tsx|js|jsx)$
|
|
712
|
+
pass_filenames: true
|
|
374
713
|
```
|
|
375
714
|
|
|
376
715
|
See **[Pre-commit Hooks Guide](docs/pre-commit-hooks.md)** for complete documentation, troubleshooting, and advanced configuration.
|
|
@@ -478,31 +817,43 @@ docker pull washad/thailint:latest
|
|
|
478
817
|
# Run CLI help
|
|
479
818
|
docker run --rm washad/thailint:latest --help
|
|
480
819
|
|
|
481
|
-
#
|
|
820
|
+
# Lint entire directory (recommended)
|
|
482
821
|
docker run --rm -v $(pwd):/data washad/thailint:latest file-placement /data
|
|
483
822
|
|
|
484
|
-
#
|
|
485
|
-
docker run --rm -v $(pwd):/data washad/thailint:latest
|
|
823
|
+
# Lint single file
|
|
824
|
+
docker run --rm -v $(pwd):/data washad/thailint:latest file-placement /data/src/app.py
|
|
825
|
+
|
|
826
|
+
# Lint multiple specific files
|
|
827
|
+
docker run --rm -v $(pwd):/data washad/thailint:latest nesting /data/src/file1.py /data/src/file2.py
|
|
828
|
+
|
|
829
|
+
# Lint specific subdirectory
|
|
830
|
+
docker run --rm -v $(pwd):/data washad/thailint:latest nesting /data/src
|
|
486
831
|
|
|
487
832
|
# With custom config
|
|
488
833
|
docker run --rm -v $(pwd):/data \
|
|
489
834
|
washad/thailint:latest nesting --config /data/.thailint.yaml /data
|
|
835
|
+
|
|
836
|
+
# JSON output for CI/CD
|
|
837
|
+
docker run --rm -v $(pwd):/data \
|
|
838
|
+
washad/thailint:latest file-placement --format json /data
|
|
490
839
|
```
|
|
491
840
|
|
|
492
841
|
## Documentation
|
|
493
842
|
|
|
494
843
|
### Comprehensive Guides
|
|
495
844
|
|
|
496
|
-
-
|
|
497
|
-
-
|
|
498
|
-
-
|
|
499
|
-
-
|
|
500
|
-
-
|
|
501
|
-
-
|
|
502
|
-
-
|
|
503
|
-
-
|
|
504
|
-
-
|
|
505
|
-
-
|
|
845
|
+
- **[Getting Started](docs/getting-started.md)** - Installation, first lint, basic config
|
|
846
|
+
- **[Configuration Reference](docs/configuration.md)** - Complete config options (YAML/JSON)
|
|
847
|
+
- **[API Reference](docs/api-reference.md)** - Library API documentation
|
|
848
|
+
- **[CLI Reference](docs/cli-reference.md)** - All CLI commands and options
|
|
849
|
+
- **[Deployment Modes](docs/deployment-modes.md)** - CLI, Library, and Docker usage
|
|
850
|
+
- **[File Placement Linter](docs/file-placement-linter.md)** - Detailed linter guide
|
|
851
|
+
- **[Nesting Depth Linter](docs/nesting-linter.md)** - Nesting depth analysis guide
|
|
852
|
+
- **[SRP Linter](docs/srp-linter.md)** - Single Responsibility Principle guide
|
|
853
|
+
- **[DRY Linter](docs/dry-linter.md)** - Duplicate code detection guide
|
|
854
|
+
- **[Pre-commit Hooks](docs/pre-commit-hooks.md)** - Automated quality checks
|
|
855
|
+
- **[Publishing Guide](docs/releasing.md)** - Release and publishing workflow
|
|
856
|
+
- **[Publishing Checklist](docs/publishing-checklist.md)** - Post-publication validation
|
|
506
857
|
|
|
507
858
|
### Examples
|
|
508
859
|
|
|
@@ -578,10 +929,10 @@ thailint is designed for speed and efficiency:
|
|
|
578
929
|
|
|
579
930
|
| Operation | Performance | Target |
|
|
580
931
|
|-----------|-------------|--------|
|
|
581
|
-
| Single file lint | ~20ms | <100ms
|
|
582
|
-
| 100 files | ~300ms | <1s
|
|
583
|
-
| 1000 files | ~900ms | <5s
|
|
584
|
-
| Config loading | ~10ms | <100ms
|
|
932
|
+
| Single file lint | ~20ms | <100ms |
|
|
933
|
+
| 100 files | ~300ms | <1s |
|
|
934
|
+
| 1000 files | ~900ms | <5s |
|
|
935
|
+
| Config loading | ~10ms | <100ms |
|
|
585
936
|
|
|
586
937
|
*Performance benchmarks run on standard hardware, your results may vary.*
|
|
587
938
|
|
|
@@ -596,9 +947,9 @@ thailint uses standard exit codes for CI/CD integration:
|
|
|
596
947
|
```bash
|
|
597
948
|
thailint file-placement .
|
|
598
949
|
if [ $? -eq 0 ]; then
|
|
599
|
-
echo "
|
|
950
|
+
echo "Linting passed"
|
|
600
951
|
else
|
|
601
|
-
echo "
|
|
952
|
+
echo "Linting failed"
|
|
602
953
|
fi
|
|
603
954
|
```
|
|
604
955
|
|