thailint 0.5.0__py3-none-any.whl → 0.15.3__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.
Files changed (204) hide show
  1. src/__init__.py +1 -0
  2. src/analyzers/__init__.py +4 -3
  3. src/analyzers/ast_utils.py +54 -0
  4. src/analyzers/rust_base.py +155 -0
  5. src/analyzers/rust_context.py +141 -0
  6. src/analyzers/typescript_base.py +4 -0
  7. src/cli/__init__.py +30 -0
  8. src/cli/__main__.py +22 -0
  9. src/cli/config.py +480 -0
  10. src/cli/config_merge.py +241 -0
  11. src/cli/linters/__init__.py +67 -0
  12. src/cli/linters/code_patterns.py +270 -0
  13. src/cli/linters/code_smells.py +342 -0
  14. src/cli/linters/documentation.py +83 -0
  15. src/cli/linters/performance.py +287 -0
  16. src/cli/linters/shared.py +331 -0
  17. src/cli/linters/structure.py +327 -0
  18. src/cli/linters/structure_quality.py +328 -0
  19. src/cli/main.py +120 -0
  20. src/cli/utils.py +395 -0
  21. src/cli_main.py +37 -0
  22. src/config.py +38 -25
  23. src/core/base.py +7 -2
  24. src/core/cli_utils.py +19 -2
  25. src/core/config_parser.py +5 -2
  26. src/core/constants.py +54 -0
  27. src/core/linter_utils.py +95 -6
  28. src/core/python_lint_rule.py +101 -0
  29. src/core/registry.py +1 -1
  30. src/core/rule_discovery.py +147 -84
  31. src/core/types.py +13 -0
  32. src/core/violation_builder.py +78 -15
  33. src/core/violation_utils.py +69 -0
  34. src/formatters/__init__.py +22 -0
  35. src/formatters/sarif.py +202 -0
  36. src/linter_config/directive_markers.py +109 -0
  37. src/linter_config/ignore.py +254 -395
  38. src/linter_config/loader.py +45 -12
  39. src/linter_config/pattern_utils.py +65 -0
  40. src/linter_config/rule_matcher.py +89 -0
  41. src/linters/collection_pipeline/__init__.py +90 -0
  42. src/linters/collection_pipeline/any_all_analyzer.py +281 -0
  43. src/linters/collection_pipeline/ast_utils.py +40 -0
  44. src/linters/collection_pipeline/config.py +75 -0
  45. src/linters/collection_pipeline/continue_analyzer.py +94 -0
  46. src/linters/collection_pipeline/detector.py +360 -0
  47. src/linters/collection_pipeline/filter_map_analyzer.py +402 -0
  48. src/linters/collection_pipeline/linter.py +420 -0
  49. src/linters/collection_pipeline/suggestion_builder.py +130 -0
  50. src/linters/cqs/__init__.py +54 -0
  51. src/linters/cqs/config.py +55 -0
  52. src/linters/cqs/function_analyzer.py +201 -0
  53. src/linters/cqs/input_detector.py +139 -0
  54. src/linters/cqs/linter.py +159 -0
  55. src/linters/cqs/output_detector.py +84 -0
  56. src/linters/cqs/python_analyzer.py +54 -0
  57. src/linters/cqs/types.py +82 -0
  58. src/linters/cqs/typescript_cqs_analyzer.py +61 -0
  59. src/linters/cqs/typescript_function_analyzer.py +192 -0
  60. src/linters/cqs/typescript_input_detector.py +203 -0
  61. src/linters/cqs/typescript_output_detector.py +117 -0
  62. src/linters/cqs/violation_builder.py +94 -0
  63. src/linters/dry/base_token_analyzer.py +16 -9
  64. src/linters/dry/block_filter.py +120 -20
  65. src/linters/dry/block_grouper.py +4 -0
  66. src/linters/dry/cache.py +104 -10
  67. src/linters/dry/cache_query.py +4 -0
  68. src/linters/dry/config.py +54 -11
  69. src/linters/dry/constant.py +92 -0
  70. src/linters/dry/constant_matcher.py +223 -0
  71. src/linters/dry/constant_violation_builder.py +98 -0
  72. src/linters/dry/duplicate_storage.py +5 -4
  73. src/linters/dry/file_analyzer.py +4 -2
  74. src/linters/dry/inline_ignore.py +7 -16
  75. src/linters/dry/linter.py +183 -48
  76. src/linters/dry/python_analyzer.py +60 -439
  77. src/linters/dry/python_constant_extractor.py +100 -0
  78. src/linters/dry/single_statement_detector.py +417 -0
  79. src/linters/dry/token_hasher.py +116 -112
  80. src/linters/dry/typescript_analyzer.py +68 -382
  81. src/linters/dry/typescript_constant_extractor.py +138 -0
  82. src/linters/dry/typescript_statement_detector.py +255 -0
  83. src/linters/dry/typescript_value_extractor.py +70 -0
  84. src/linters/dry/violation_builder.py +4 -0
  85. src/linters/dry/violation_filter.py +5 -4
  86. src/linters/dry/violation_generator.py +71 -14
  87. src/linters/file_header/atemporal_detector.py +68 -50
  88. src/linters/file_header/base_parser.py +93 -0
  89. src/linters/file_header/bash_parser.py +66 -0
  90. src/linters/file_header/config.py +90 -16
  91. src/linters/file_header/css_parser.py +70 -0
  92. src/linters/file_header/field_validator.py +36 -33
  93. src/linters/file_header/linter.py +140 -144
  94. src/linters/file_header/markdown_parser.py +130 -0
  95. src/linters/file_header/python_parser.py +14 -58
  96. src/linters/file_header/typescript_parser.py +73 -0
  97. src/linters/file_header/violation_builder.py +13 -12
  98. src/linters/file_placement/config_loader.py +3 -1
  99. src/linters/file_placement/directory_matcher.py +4 -0
  100. src/linters/file_placement/linter.py +66 -34
  101. src/linters/file_placement/pattern_matcher.py +41 -6
  102. src/linters/file_placement/pattern_validator.py +31 -12
  103. src/linters/file_placement/rule_checker.py +12 -7
  104. src/linters/lazy_ignores/__init__.py +43 -0
  105. src/linters/lazy_ignores/config.py +74 -0
  106. src/linters/lazy_ignores/directive_utils.py +164 -0
  107. src/linters/lazy_ignores/header_parser.py +177 -0
  108. src/linters/lazy_ignores/linter.py +158 -0
  109. src/linters/lazy_ignores/matcher.py +168 -0
  110. src/linters/lazy_ignores/python_analyzer.py +209 -0
  111. src/linters/lazy_ignores/rule_id_utils.py +180 -0
  112. src/linters/lazy_ignores/skip_detector.py +298 -0
  113. src/linters/lazy_ignores/types.py +71 -0
  114. src/linters/lazy_ignores/typescript_analyzer.py +146 -0
  115. src/linters/lazy_ignores/violation_builder.py +135 -0
  116. src/linters/lbyl/__init__.py +31 -0
  117. src/linters/lbyl/config.py +63 -0
  118. src/linters/lbyl/linter.py +67 -0
  119. src/linters/lbyl/pattern_detectors/__init__.py +53 -0
  120. src/linters/lbyl/pattern_detectors/base.py +63 -0
  121. src/linters/lbyl/pattern_detectors/dict_key_detector.py +107 -0
  122. src/linters/lbyl/pattern_detectors/division_check_detector.py +232 -0
  123. src/linters/lbyl/pattern_detectors/file_exists_detector.py +220 -0
  124. src/linters/lbyl/pattern_detectors/hasattr_detector.py +119 -0
  125. src/linters/lbyl/pattern_detectors/isinstance_detector.py +119 -0
  126. src/linters/lbyl/pattern_detectors/len_check_detector.py +173 -0
  127. src/linters/lbyl/pattern_detectors/none_check_detector.py +146 -0
  128. src/linters/lbyl/pattern_detectors/string_validator_detector.py +145 -0
  129. src/linters/lbyl/python_analyzer.py +215 -0
  130. src/linters/lbyl/violation_builder.py +354 -0
  131. src/linters/magic_numbers/context_analyzer.py +227 -225
  132. src/linters/magic_numbers/linter.py +28 -82
  133. src/linters/magic_numbers/python_analyzer.py +4 -16
  134. src/linters/magic_numbers/typescript_analyzer.py +9 -12
  135. src/linters/magic_numbers/typescript_ignore_checker.py +81 -0
  136. src/linters/method_property/__init__.py +49 -0
  137. src/linters/method_property/config.py +138 -0
  138. src/linters/method_property/linter.py +414 -0
  139. src/linters/method_property/python_analyzer.py +473 -0
  140. src/linters/method_property/violation_builder.py +119 -0
  141. src/linters/nesting/linter.py +24 -16
  142. src/linters/nesting/python_analyzer.py +4 -0
  143. src/linters/nesting/typescript_analyzer.py +6 -12
  144. src/linters/nesting/violation_builder.py +1 -0
  145. src/linters/performance/__init__.py +91 -0
  146. src/linters/performance/config.py +43 -0
  147. src/linters/performance/constants.py +49 -0
  148. src/linters/performance/linter.py +149 -0
  149. src/linters/performance/python_analyzer.py +365 -0
  150. src/linters/performance/regex_analyzer.py +312 -0
  151. src/linters/performance/regex_linter.py +139 -0
  152. src/linters/performance/typescript_analyzer.py +236 -0
  153. src/linters/performance/violation_builder.py +160 -0
  154. src/linters/print_statements/config.py +7 -12
  155. src/linters/print_statements/linter.py +26 -43
  156. src/linters/print_statements/python_analyzer.py +91 -93
  157. src/linters/print_statements/typescript_analyzer.py +15 -25
  158. src/linters/print_statements/violation_builder.py +12 -14
  159. src/linters/srp/class_analyzer.py +11 -7
  160. src/linters/srp/heuristics.py +56 -22
  161. src/linters/srp/linter.py +15 -16
  162. src/linters/srp/python_analyzer.py +55 -20
  163. src/linters/srp/typescript_metrics_calculator.py +110 -50
  164. src/linters/stateless_class/__init__.py +25 -0
  165. src/linters/stateless_class/config.py +58 -0
  166. src/linters/stateless_class/linter.py +349 -0
  167. src/linters/stateless_class/python_analyzer.py +290 -0
  168. src/linters/stringly_typed/__init__.py +36 -0
  169. src/linters/stringly_typed/config.py +189 -0
  170. src/linters/stringly_typed/context_filter.py +451 -0
  171. src/linters/stringly_typed/function_call_violation_builder.py +135 -0
  172. src/linters/stringly_typed/ignore_checker.py +100 -0
  173. src/linters/stringly_typed/ignore_utils.py +51 -0
  174. src/linters/stringly_typed/linter.py +376 -0
  175. src/linters/stringly_typed/python/__init__.py +33 -0
  176. src/linters/stringly_typed/python/analyzer.py +348 -0
  177. src/linters/stringly_typed/python/call_tracker.py +175 -0
  178. src/linters/stringly_typed/python/comparison_tracker.py +257 -0
  179. src/linters/stringly_typed/python/condition_extractor.py +134 -0
  180. src/linters/stringly_typed/python/conditional_detector.py +179 -0
  181. src/linters/stringly_typed/python/constants.py +21 -0
  182. src/linters/stringly_typed/python/match_analyzer.py +94 -0
  183. src/linters/stringly_typed/python/validation_detector.py +189 -0
  184. src/linters/stringly_typed/python/variable_extractor.py +96 -0
  185. src/linters/stringly_typed/storage.py +620 -0
  186. src/linters/stringly_typed/storage_initializer.py +45 -0
  187. src/linters/stringly_typed/typescript/__init__.py +28 -0
  188. src/linters/stringly_typed/typescript/analyzer.py +157 -0
  189. src/linters/stringly_typed/typescript/call_tracker.py +335 -0
  190. src/linters/stringly_typed/typescript/comparison_tracker.py +378 -0
  191. src/linters/stringly_typed/violation_generator.py +419 -0
  192. src/orchestrator/core.py +252 -14
  193. src/orchestrator/language_detector.py +5 -3
  194. src/templates/thailint_config_template.yaml +196 -0
  195. src/utils/project_root.py +3 -0
  196. thailint-0.15.3.dist-info/METADATA +187 -0
  197. thailint-0.15.3.dist-info/RECORD +226 -0
  198. thailint-0.15.3.dist-info/entry_points.txt +4 -0
  199. src/cli.py +0 -1665
  200. thailint-0.5.0.dist-info/METADATA +0 -1286
  201. thailint-0.5.0.dist-info/RECORD +0 -96
  202. thailint-0.5.0.dist-info/entry_points.txt +0 -4
  203. {thailint-0.5.0.dist-info → thailint-0.15.3.dist-info}/WHEEL +0 -0
  204. {thailint-0.5.0.dist-info → thailint-0.15.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,1286 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: thailint
3
- Version: 0.5.0
4
- Summary: The AI Linter - Enterprise-grade linting and governance for AI-generated code across multiple languages
5
- License: MIT
6
- License-File: LICENSE
7
- Keywords: linter,ai,code-quality,static-analysis,file-placement,governance,multi-language,cli,docker,python
8
- Author: Steve Jackson
9
- Requires-Python: >=3.11,<4.0
10
- Classifier: Development Status :: 3 - Alpha
11
- Classifier: Environment :: Console
12
- Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: MIT License
14
- Classifier: Operating System :: OS Independent
15
- Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.11
17
- Classifier: Programming Language :: Python :: 3.12
18
- Classifier: Programming Language :: Python :: 3.13
19
- Classifier: Programming Language :: Python :: 3.14
20
- Classifier: Programming Language :: Python :: 3 :: Only
21
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
- Classifier: Topic :: Software Development :: Quality Assurance
23
- Classifier: Topic :: Software Development :: Testing
24
- Classifier: Topic :: Utilities
25
- Classifier: Typing :: Typed
26
- Requires-Dist: click (>=8.1.0,<9.0.0)
27
- Requires-Dist: pyprojroot (>=0.3.0,<0.4.0)
28
- Requires-Dist: pyyaml (>=6.0,<7.0)
29
- Requires-Dist: tree-sitter (>=0.25.2,<0.26.0)
30
- Requires-Dist: tree-sitter-typescript (>=0.23.2,<0.24.0)
31
- Project-URL: Documentation, https://thai-lint.readthedocs.io/
32
- Project-URL: Homepage, https://github.com/be-wise-be-kind/thai-lint
33
- Project-URL: Repository, https://github.com/be-wise-be-kind/thai-lint
34
- Description-Content-Type: text/markdown
35
-
36
- # thai-lint
37
-
38
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
39
- [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
40
- [![Tests](https://img.shields.io/badge/tests-393%2F393%20passing-brightgreen.svg)](tests/)
41
- [![Coverage](https://img.shields.io/badge/coverage-87%25-brightgreen.svg)](htmlcov/)
42
- [![Documentation Status](https://readthedocs.org/projects/thai-lint/badge/?version=latest)](https://thai-lint.readthedocs.io/en/latest/?badge=latest)
43
-
44
- The AI Linter - Enterprise-ready linting and governance for AI-generated code across multiple languages.
45
-
46
- ## Documentation
47
-
48
- **New to thailint?** Start here:
49
- - **[Quick Start Guide](https://thai-lint.readthedocs.io/en/latest/quick-start/)** - Get running in 5 minutes
50
- - **[Configuration Reference](https://thai-lint.readthedocs.io/en/latest/configuration/)** - Complete config options for all linters
51
- - **[Troubleshooting Guide](https://thai-lint.readthedocs.io/en/latest/troubleshooting/)** - Common issues and solutions
52
-
53
- **Full Documentation:** Browse the **[documentation site](https://thai-lint.readthedocs.io/)** for comprehensive guides covering installation, all linters, configuration patterns, and integration examples.
54
-
55
- ## Overview
56
-
57
- thailint is a modern, enterprise-ready multi-language linter designed specifically for AI-generated code. It focuses on common mistakes and anti-patterns that AI coding assistants frequently introduce—issues that existing linters don't catch or don't handle consistently across languages.
58
-
59
- **Why thailint?**
60
-
61
- We're not trying to replace the wonderful existing linters like Pylint, ESLint, or Ruff. Instead, thailint fills critical gaps:
62
-
63
- - **AI-Specific Patterns**: AI assistants have predictable blind spots (excessive nesting, magic numbers, SRP violations) that traditional linters miss
64
- - **Cross-Language Consistency**: Detects the same anti-patterns across Python, TypeScript, and JavaScript with unified rules
65
- - **No Existing Solutions**: Issues like excessive nesting depth, file placement violations, and cross-project code duplication lack comprehensive multi-language detection
66
- - **Governance Layer**: Enforces project-wide structure and organization patterns that AI can't infer from local context
67
-
68
- thailint complements your existing linting stack by catching the patterns AI tools repeatedly miss.
69
-
70
- **Complete documentation available at [thai-lint.readthedocs.io](https://thai-lint.readthedocs.io/)** covering installation, configuration, all linters, and troubleshooting.
71
-
72
- ## Features
73
-
74
- ### Core Capabilities
75
- - **File Placement Linting** - Enforce project structure and organization
76
- - **Magic Numbers Linting** - Detect unnamed numeric literals that should be constants
77
- - Python and TypeScript support with AST analysis
78
- - Context-aware detection (ignores constants, test files, range() usage)
79
- - Configurable allowed numbers and thresholds
80
- - Helpful suggestions for extracting to named constants
81
- - **Nesting Depth Linting** - Detect excessive code nesting with AST analysis
82
- - Python and TypeScript support with tree-sitter
83
- - Configurable max depth (default: 4, recommended: 3)
84
- - Helpful refactoring suggestions (guard clauses, extract method)
85
- - **SRP Linting** - Detect Single Responsibility Principle violations
86
- - Heuristic-based analysis (method count, LOC, keywords)
87
- - Language-specific thresholds (Python, TypeScript, JavaScript)
88
- - Refactoring patterns from real-world examples
89
- - **DRY Linting** - Detect duplicate code across projects
90
- - Token-based hash detection with SQLite storage
91
- - Fast duplicate detection (in-memory or disk-backed)
92
- - Configurable thresholds (lines, tokens, occurrences)
93
- - Language-specific detection (Python, TypeScript, JavaScript)
94
- - False positive filtering (keyword args, imports)
95
- - **Pluggable Architecture** - Easy to extend with custom linters
96
- - **Multi-Language Support** - Python, TypeScript, JavaScript, and more
97
- - **Flexible Configuration** - YAML/JSON configs with pattern matching
98
- - **5-Level Ignore System** - Repo, directory, file, method, and line-level ignores
99
-
100
- ### Deployment Modes
101
- - **CLI Mode** - Full-featured command-line interface
102
- - **Library API** - Python library for programmatic integration
103
- - **Docker Support** - Containerized deployment for CI/CD
104
-
105
- ### Enterprise Features
106
- - **Performance** - <100ms for single files, <5s for 1000 files
107
- - **Type Safety** - Full type hints and MyPy strict mode
108
- - **Test Coverage** - 90% coverage with 317 tests
109
- - **CI/CD Ready** - Proper exit codes and JSON output
110
- - **Comprehensive Docs** - Complete documentation and examples
111
-
112
- ## Installation
113
-
114
- ### From Source
115
-
116
- ```bash
117
- # Clone repository
118
- git clone https://github.com/be-wise-be-kind/thai-lint.git
119
- cd thai-lint
120
-
121
- # Install dependencies
122
- pip install -e ".[dev]"
123
- ```
124
-
125
- ### From PyPI
126
-
127
- ```bash
128
- pip install thai-lint
129
- ```
130
-
131
- ### With Docker
132
-
133
- ```bash
134
- # Pull from Docker Hub
135
- docker pull washad/thailint:latest
136
-
137
- # Run CLI
138
- docker run --rm washad/thailint:latest --help
139
- ```
140
-
141
- ## Quick Start
142
-
143
- ### CLI Mode
144
-
145
- ```bash
146
- # Check file placement
147
- thailint file-placement .
148
-
149
- # Check multiple files
150
- thailint nesting file1.py file2.py file3.py
151
-
152
- # Check specific directory
153
- thailint nesting src/
154
-
155
- # Check for duplicate code
156
- thailint dry .
157
-
158
- # Check for magic numbers
159
- thailint magic-numbers src/
160
-
161
- # With config file
162
- thailint dry --config .thailint.yaml src/
163
-
164
- # JSON output for CI/CD
165
- thailint dry --format json src/
166
- ```
167
-
168
- **New to thailint?** See the **[Quick Start Guide](https://thai-lint.readthedocs.io/en/latest/quick-start/)** for a complete walkthrough including config generation, understanding output, and next steps.
169
-
170
- ### Library Mode
171
-
172
- ```python
173
- from src import Linter
174
-
175
- # Initialize linter
176
- linter = Linter(config_file='.thailint.yaml')
177
-
178
- # Lint directory
179
- violations = linter.lint('src/', rules=['file-placement'])
180
-
181
- # Process results
182
- if violations:
183
- for v in violations:
184
- print(f"{v.file_path}: {v.message}")
185
- ```
186
-
187
- ### Docker Mode
188
-
189
- ```bash
190
- # Lint directory (recommended - lints all files inside)
191
- docker run --rm -v $(pwd):/data \
192
- washad/thailint:latest file-placement /data
193
-
194
- # Lint single file
195
- docker run --rm -v $(pwd):/data \
196
- washad/thailint:latest file-placement /data/src/app.py
197
-
198
- # Lint multiple specific files
199
- docker run --rm -v $(pwd):/data \
200
- washad/thailint:latest nesting /data/src/file1.py /data/src/file2.py
201
-
202
- # Check nesting depth in subdirectory
203
- docker run --rm -v $(pwd):/data \
204
- washad/thailint:latest nesting /data/src
205
- ```
206
-
207
- ### Docker with Sibling Directories
208
-
209
- For Docker environments with sibling directories (e.g., separate config and source directories), use `--project-root` or config path inference:
210
-
211
- ```bash
212
- # Directory structure:
213
- # /workspace/
214
- # ├── root/ # Contains .thailint.yaml and .git
215
- # ├── backend/ # Code to lint
216
- # └── tools/
217
-
218
- # Option 1: Explicit project root (recommended)
219
- docker run --rm -v $(pwd):/data \
220
- washad/thailint:latest \
221
- --project-root /data/root \
222
- magic-numbers /data/backend/
223
-
224
- # Option 2: Config path inference (automatic)
225
- docker run --rm -v $(pwd):/data \
226
- washad/thailint:latest \
227
- --config /data/root/.thailint.yaml \
228
- magic-numbers /data/backend/
229
-
230
- # With ignore patterns resolving from project root
231
- docker run --rm -v $(pwd):/data \
232
- washad/thailint:latest \
233
- --project-root /data/root \
234
- --config /data/root/.thailint.yaml \
235
- magic-numbers /data/backend/
236
- ```
237
-
238
- **Priority order:**
239
- 1. `--project-root` (highest priority - explicit specification)
240
- 2. Inferred from `--config` path directory
241
- 3. Auto-detection from file location (fallback)
242
-
243
- See **[Docker Usage](#docker-usage)** section below for more examples.
244
-
245
- ## Configuration
246
-
247
- Create `.thailint.yaml` in your project root:
248
-
249
- ```yaml
250
- # File placement linter configuration
251
- file-placement:
252
- enabled: true
253
-
254
- # Global patterns apply to entire project
255
- global_patterns:
256
- deny:
257
- - pattern: "^(?!src/|tests/).*\\.py$"
258
- message: "Python files must be in src/ or tests/"
259
-
260
- # Directory-specific rules
261
- directories:
262
- src:
263
- allow:
264
- - ".*\\.py$"
265
- deny:
266
- - "test_.*\\.py$"
267
-
268
- tests:
269
- allow:
270
- - "test_.*\\.py$"
271
- - "conftest\\.py$"
272
-
273
- # Files/directories to ignore
274
- ignore:
275
- - "__pycache__/"
276
- - "*.pyc"
277
- - ".venv/"
278
-
279
- # Nesting depth linter configuration
280
- nesting:
281
- enabled: true
282
- max_nesting_depth: 4 # Maximum allowed nesting depth
283
-
284
- # Language-specific settings (optional)
285
- languages:
286
- python:
287
- max_depth: 4
288
- typescript:
289
- max_depth: 4
290
- javascript:
291
- max_depth: 4
292
-
293
- # DRY linter configuration
294
- dry:
295
- enabled: true
296
- min_duplicate_lines: 4 # Minimum lines to consider duplicate
297
- min_duplicate_tokens: 30 # Minimum tokens to consider duplicate
298
- min_occurrences: 2 # Report if appears 2+ times
299
-
300
- # Language-specific thresholds
301
- python:
302
- min_occurrences: 3 # Python: require 3+ occurrences
303
-
304
- # Storage settings (SQLite)
305
- storage_mode: "memory" # Options: "memory" (default) or "tempfile"
306
-
307
- # Ignore patterns
308
- ignore:
309
- - "tests/"
310
- - "__init__.py"
311
-
312
- # Magic numbers linter configuration
313
- magic-numbers:
314
- enabled: true
315
- allowed_numbers: [-1, 0, 1, 2, 10, 100, 1000] # Numbers allowed without constants
316
- max_small_integer: 10 # Max value allowed in range() or enumerate()
317
- ```
318
-
319
- **JSON format also supported** (`.thailint.json`):
320
-
321
- ```json
322
- {
323
- "file-placement": {
324
- "enabled": true,
325
- "directories": {
326
- "src": {
327
- "allow": [".*\\.py$"],
328
- "deny": ["test_.*\\.py$"]
329
- }
330
- },
331
- "ignore": ["__pycache__/", "*.pyc"]
332
- },
333
- "nesting": {
334
- "enabled": true,
335
- "max_nesting_depth": 4,
336
- "languages": {
337
- "python": { "max_depth": 4 },
338
- "typescript": { "max_depth": 4 }
339
- }
340
- },
341
- "dry": {
342
- "enabled": true,
343
- "min_duplicate_lines": 4,
344
- "min_duplicate_tokens": 30,
345
- "min_occurrences": 2,
346
- "python": {
347
- "min_occurrences": 3
348
- },
349
- "storage_mode": "memory",
350
- "ignore": ["tests/", "__init__.py"]
351
- },
352
- "magic-numbers": {
353
- "enabled": true,
354
- "allowed_numbers": [-1, 0, 1, 2, 10, 100, 1000],
355
- "max_small_integer": 10
356
- }
357
- }
358
- ```
359
-
360
- See [Configuration Guide](https://thai-lint.readthedocs.io/en/latest/configuration/) for complete reference.
361
-
362
- **Need help with ignores?** See **[How to Ignore Violations](https://thai-lint.readthedocs.io/en/latest/how-to-ignore-violations/)** for complete guide to all ignore levels (line, method, class, file, repository).
363
-
364
- ## Nesting Depth Linter
365
-
366
- ### Overview
367
-
368
- The nesting depth linter detects deeply nested code (if/for/while/try statements) that reduces readability and maintainability. It uses AST analysis to accurately calculate nesting depth.
369
-
370
- ### Quick Start
371
-
372
- ```bash
373
- # Check nesting depth in current directory
374
- thailint nesting .
375
-
376
- # Use strict limit (max depth 3)
377
- thailint nesting --max-depth 3 src/
378
-
379
- # Get JSON output
380
- thailint nesting --format json src/
381
- ```
382
-
383
- ### Configuration
384
-
385
- Add to `.thailint.yaml`:
386
-
387
- ```yaml
388
- nesting:
389
- enabled: true
390
- max_nesting_depth: 3 # Default: 4, recommended: 3
391
- ```
392
-
393
- ### Example Violation
394
-
395
- **Code with excessive nesting:**
396
- ```python
397
- def process_data(items):
398
- for item in items: # Depth 2
399
- if item.is_valid(): # Depth 3
400
- try: # Depth 4 ← VIOLATION (max=3)
401
- if item.process():
402
- return True
403
- except Exception:
404
- pass
405
- return False
406
- ```
407
-
408
- **Refactored with guard clauses:**
409
- ```python
410
- def process_data(items):
411
- for item in items: # Depth 2
412
- if not item.is_valid():
413
- continue
414
- try: # Depth 3 ✓
415
- if item.process():
416
- return True
417
- except Exception:
418
- pass
419
- return False
420
- ```
421
-
422
- ### Refactoring Patterns
423
-
424
- Common patterns to reduce nesting:
425
-
426
- 1. **Guard Clauses (Early Returns)**
427
- - Replace `if x: do_something()` with `if not x: return`
428
- - Exit early, reduce nesting
429
-
430
- 2. **Extract Method**
431
- - Move nested logic to separate functions
432
- - Improves readability and testability
433
-
434
- 3. **Dispatch Pattern**
435
- - Replace if-elif-else chains with dictionary dispatch
436
- - More extensible and cleaner
437
-
438
- 4. **Flatten Error Handling**
439
- - Combine multiple try-except blocks
440
- - Use tuple of exception types
441
-
442
- ### Language Support
443
-
444
- - **Python**: Full support (if/for/while/with/try/match)
445
- - **TypeScript**: Full support (if/for/while/try/switch)
446
- - **JavaScript**: Supported via TypeScript parser
447
-
448
- See [Nesting Linter Guide](https://thai-lint.readthedocs.io/en/latest/nesting-linter/) for comprehensive documentation and refactoring patterns.
449
-
450
- ## Single Responsibility Principle (SRP) Linter
451
-
452
- ### Overview
453
-
454
- 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.
455
-
456
- ### Quick Start
457
-
458
- ```bash
459
- # Check SRP violations in current directory
460
- thailint srp .
461
-
462
- # Use custom thresholds
463
- thailint srp --max-methods 10 --max-loc 300 src/
464
-
465
- # Get JSON output
466
- thailint srp --format json src/
467
- ```
468
-
469
- ### Configuration
470
-
471
- Add to `.thailint.yaml`:
472
-
473
- ```yaml
474
- srp:
475
- enabled: true
476
- max_methods: 7 # Maximum methods per class
477
- max_loc: 200 # Maximum lines of code per class
478
-
479
- # Language-specific thresholds
480
- python:
481
- max_methods: 8
482
- max_loc: 200
483
-
484
- typescript:
485
- max_methods: 10 # TypeScript more verbose
486
- max_loc: 250
487
- ```
488
-
489
- ### Detection Heuristics
490
-
491
- The SRP linter uses three heuristics to detect violations:
492
-
493
- 1. **Method Count**: Classes with >7 methods (default) likely have multiple responsibilities
494
- 2. **Lines of Code**: Classes with >200 LOC (default) are often doing too much
495
- 3. **Responsibility Keywords**: Names containing "Manager", "Handler", "Processor", etc.
496
-
497
- ### Example Violation
498
-
499
- **Code with SRP violation:**
500
- ```python
501
- class UserManager: # 8 methods, contains "Manager" keyword
502
- def create_user(self): pass
503
- def update_user(self): pass
504
- def delete_user(self): pass
505
- def send_email(self): pass # ← Different responsibility
506
- def log_action(self): pass # ← Different responsibility
507
- def validate_data(self): pass # ← Different responsibility
508
- def generate_report(self): pass # ← Different responsibility
509
- def export_data(self): pass # ← Violation at method 8
510
- ```
511
-
512
- **Refactored following SRP:**
513
- ```python
514
- class UserRepository: # 3 methods ✓
515
- def create(self, user): pass
516
- def update(self, user): pass
517
- def delete(self, user): pass
518
-
519
- class EmailService: # 1 method ✓
520
- def send(self, user, template): pass
521
-
522
- class UserAuditLog: # 1 method ✓
523
- def log(self, action, user): pass
524
-
525
- class UserValidator: # 1 method ✓
526
- def validate(self, data): pass
527
-
528
- class ReportGenerator: # 1 method ✓
529
- def generate(self, users): pass
530
- ```
531
-
532
- ### Refactoring Patterns
533
-
534
- Common patterns to fix SRP violations (discovered during dogfooding):
535
-
536
- 1. **Extract Class**
537
- - Split god classes into focused classes
538
- - Each class handles one responsibility
539
-
540
- 2. **Split Configuration and Logic**
541
- - Separate config loading from business logic
542
- - Create dedicated ConfigLoader classes
543
-
544
- 3. **Extract Language-Specific Logic**
545
- - Separate Python/TypeScript analysis
546
- - Use analyzer classes per language
547
-
548
- 4. **Utility Module Pattern**
549
- - Group related helper methods
550
- - Create focused utility classes
551
-
552
- ### Language Support
553
-
554
- - **Python**: Full support with method counting and LOC analysis
555
- - **TypeScript**: Full support with tree-sitter parsing
556
- - **JavaScript**: Supported via TypeScript parser
557
-
558
- ### Real-World Example
559
-
560
- **Large class refactoring:**
561
- - **Before**: FilePlacementLinter (33 methods, 382 LOC) - single class handling config, patterns, validation
562
- - **After**: Extract Class pattern applied - 5 focused classes (ConfigLoader, PatternValidator, RuleChecker, PathResolver, FilePlacementLinter)
563
- - **Result**: Each class ≤8 methods, ≤150 LOC, single responsibility
564
-
565
- See [SRP Linter Guide](https://thai-lint.readthedocs.io/en/latest/srp-linter/) for comprehensive documentation and refactoring patterns.
566
-
567
- ## DRY Linter (Don't Repeat Yourself)
568
-
569
- ### Overview
570
-
571
- The DRY linter detects duplicate code blocks across your entire project using token-based hashing with SQLite storage. It identifies identical or near-identical code that violates the Don't Repeat Yourself (DRY) principle, helping maintain code quality at scale.
572
-
573
- ### Quick Start
574
-
575
- ```bash
576
- # Check for duplicate code in current directory
577
- thailint dry .
578
-
579
- # Use custom thresholds
580
- thailint dry --min-lines 5 src/
581
-
582
- # Use tempfile storage for large projects
583
- thailint dry --storage-mode tempfile src/
584
-
585
- # Get JSON output
586
- thailint dry --format json src/
587
- ```
588
-
589
- ### Configuration
590
-
591
- Add to `.thailint.yaml`:
592
-
593
- ```yaml
594
- dry:
595
- enabled: true
596
- min_duplicate_lines: 4 # Minimum lines to consider duplicate
597
- min_duplicate_tokens: 30 # Minimum tokens to consider duplicate
598
- min_occurrences: 2 # Report if appears 2+ times
599
-
600
- # Language-specific thresholds
601
- python:
602
- min_occurrences: 3 # Python: require 3+ occurrences
603
- typescript:
604
- min_occurrences: 3 # TypeScript: require 3+ occurrences
605
-
606
- # Storage settings
607
- storage_mode: "memory" # Options: "memory" (default) or "tempfile"
608
-
609
- # Ignore patterns
610
- ignore:
611
- - "tests/" # Test code often has acceptable duplication
612
- - "__init__.py" # Import-only files exempt
613
-
614
- # False positive filters
615
- filters:
616
- keyword_argument_filter: true # Filter function call kwargs
617
- import_group_filter: true # Filter import groups
618
- ```
619
-
620
- ### How It Works
621
-
622
- **Token-Based Detection:**
623
- 1. Parse code into tokens (stripping comments, normalizing whitespace)
624
- 2. Create rolling hash windows of N lines
625
- 3. Store hashes in SQLite database with file locations
626
- 4. Query for hashes appearing 2+ times across project
627
-
628
- **SQLite Storage:**
629
- - In-memory mode (default): Stores in RAM for best performance
630
- - Tempfile mode: Stores in temporary disk file for large projects
631
- - Fresh analysis on every run (no persistence between runs)
632
- - Fast duplicate detection using B-tree indexes
633
-
634
- ### Example Violation
635
-
636
- **Code with duplication:**
637
- ```python
638
- # src/auth.py
639
- def validate_user(user_data):
640
- if not user_data:
641
- return False
642
- if not user_data.get('email'):
643
- return False
644
- if not user_data.get('password'):
645
- return False
646
- return True
647
-
648
- # src/admin.py
649
- def validate_admin(admin_data):
650
- if not admin_data:
651
- return False
652
- if not admin_data.get('email'):
653
- return False
654
- if not admin_data.get('password'):
655
- return False
656
- return True
657
- ```
658
-
659
- **Violation message:**
660
- ```
661
- src/auth.py:3 - Duplicate code detected (4 lines, 2 occurrences)
662
- Locations:
663
- - src/auth.py:3-6
664
- - src/admin.py:3-6
665
- Consider extracting to shared function
666
- ```
667
-
668
- **Refactored (DRY):**
669
- ```python
670
- # src/validators.py
671
- def validate_credentials(data):
672
- if not data:
673
- return False
674
- if not data.get('email'):
675
- return False
676
- if not data.get('password'):
677
- return False
678
- return True
679
-
680
- # src/auth.py & src/admin.py
681
- from src.validators import validate_credentials
682
-
683
- def validate_user(user_data):
684
- return validate_credentials(user_data)
685
-
686
- def validate_admin(admin_data):
687
- return validate_credentials(admin_data)
688
- ```
689
-
690
- ### Performance
691
-
692
- | Operation | Performance | Storage Mode |
693
- |-----------|-------------|--------------|
694
- | Scan (1000 files) | 1-3s | Memory (default) |
695
- | Large project (5000+ files) | Use tempfile mode | Tempfile |
696
-
697
- **Note**: Every run analyzes files fresh - no persistence between runs ensures accurate results
698
-
699
- ### Language Support
700
-
701
- - **Python**: Full support with AST-based tokenization
702
- - **TypeScript**: Full support with tree-sitter parsing
703
- - **JavaScript**: Supported via TypeScript parser
704
-
705
- ### False Positive Filtering
706
-
707
- Built-in filters automatically exclude common non-duplication patterns:
708
- - **keyword_argument_filter**: Excludes function calls with keyword arguments
709
- - **import_group_filter**: Excludes import statement groups
710
-
711
- ### Refactoring Patterns
712
-
713
- 1. **Extract Function**: Move repeated logic to shared function
714
- 2. **Extract Base Class**: Create base class for similar implementations
715
- 3. **Extract Utility Module**: Move helper functions to shared utilities
716
- 4. **Template Method**: Use function parameters for variations
717
-
718
- See [DRY Linter Guide](https://thai-lint.readthedocs.io/en/latest/dry-linter/) for comprehensive documentation, storage modes, and refactoring patterns.
719
-
720
- ## Magic Numbers Linter
721
-
722
- ### Overview
723
-
724
- The magic numbers linter detects unnamed numeric literals (magic numbers) that should be extracted to named constants. It uses AST analysis to identify numeric literals that lack meaningful context.
725
-
726
- ### What are Magic Numbers?
727
-
728
- **Magic numbers** are unnamed numeric literals in code without explanation:
729
-
730
- ```python
731
- # Bad - Magic numbers
732
- timeout = 3600 # What is 3600?
733
- max_retries = 5 # Why 5?
734
-
735
- # Good - Named constants
736
- TIMEOUT_SECONDS = 3600
737
- MAX_RETRY_ATTEMPTS = 5
738
- ```
739
-
740
- ### Quick Start
741
-
742
- ```bash
743
- # Check for magic numbers in current directory
744
- thailint magic-numbers .
745
-
746
- # Check specific directory
747
- thailint magic-numbers src/
748
-
749
- # Get JSON output
750
- thailint magic-numbers --format json src/
751
- ```
752
-
753
- ### Configuration
754
-
755
- Add to `.thailint.yaml`:
756
-
757
- ```yaml
758
- magic-numbers:
759
- enabled: true
760
- allowed_numbers: [-1, 0, 1, 2, 10, 100, 1000]
761
- max_small_integer: 10 # Max for range() to be acceptable
762
- ```
763
-
764
- ### Example Violation
765
-
766
- **Code with magic numbers:**
767
- ```python
768
- def calculate_timeout():
769
- return 3600 # Magic number - what is 3600?
770
-
771
- def process_items(items):
772
- for i in range(100): # Magic number - why 100?
773
- items[i] *= 1.5 # Magic number - what is 1.5?
774
- ```
775
-
776
- **Violation messages:**
777
- ```
778
- src/example.py:2 - Magic number 3600 should be a named constant
779
- src/example.py:5 - Magic number 100 should be a named constant
780
- src/example.py:6 - Magic number 1.5 should be a named constant
781
- ```
782
-
783
- **Refactored code:**
784
- ```python
785
- TIMEOUT_SECONDS = 3600
786
- MAX_ITEMS = 100
787
- PRICE_MULTIPLIER = 1.5
788
-
789
- def calculate_timeout():
790
- return TIMEOUT_SECONDS
791
-
792
- def process_items(items):
793
- for i in range(MAX_ITEMS):
794
- items[i] *= PRICE_MULTIPLIER
795
- ```
796
-
797
- ### Acceptable Contexts
798
-
799
- The linter **does not** flag numbers in these contexts:
800
-
801
- | Context | Example | Why Acceptable |
802
- |---------|---------|----------------|
803
- | Constants | `MAX_SIZE = 100` | UPPERCASE name provides context |
804
- | Small `range()` | `range(5)` | Small loop bounds are clear |
805
- | Test files | `test_*.py` | Test data can be literal |
806
- | Allowed numbers | `-1, 0, 1, 2, 10` | Common values are self-explanatory |
807
-
808
- ### Refactoring Patterns
809
-
810
- **Pattern 1: Extract to Module Constants**
811
- ```python
812
- # Before
813
- def connect():
814
- timeout = 30
815
- retries = 3
816
-
817
- # After
818
- DEFAULT_TIMEOUT_SECONDS = 30
819
- DEFAULT_MAX_RETRIES = 3
820
-
821
- def connect():
822
- timeout = DEFAULT_TIMEOUT_SECONDS
823
- retries = DEFAULT_MAX_RETRIES
824
- ```
825
-
826
- **Pattern 2: Extract with Units in Name**
827
- ```python
828
- # Before
829
- delay = 3600 # Is this seconds? Minutes?
830
-
831
- # After
832
- TASK_DELAY_SECONDS = 3600 # Clear unit
833
-
834
- delay = TASK_DELAY_SECONDS
835
- ```
836
-
837
- **Pattern 3: Use Standard Library**
838
- ```python
839
- # Before
840
- if status == 200:
841
- return "success"
842
-
843
- # After
844
- from http import HTTPStatus
845
-
846
- if status == HTTPStatus.OK:
847
- return "success"
848
- ```
849
-
850
- ### Language Support
851
-
852
- - **Python**: Full support (int, float, scientific notation)
853
- - **TypeScript**: Full support (int, float, scientific notation)
854
- - **JavaScript**: Supported via TypeScript parser
855
-
856
- ### Ignoring Violations
857
-
858
- ```python
859
- # Line-level ignore
860
- timeout = 3600 # thailint: ignore[magic-numbers] - Industry standard
861
-
862
- # Method-level ignore
863
- def get_ports(): # thailint: ignore[magic-numbers] - Standard ports
864
- return {80: "HTTP", 443: "HTTPS"}
865
-
866
- # File-level ignore
867
- # thailint: ignore-file[magic-numbers]
868
- ```
869
-
870
- See **[How to Ignore Violations](https://thai-lint.readthedocs.io/en/latest/how-to-ignore-violations/)** and **[Magic Numbers Linter Guide](https://thai-lint.readthedocs.io/en/latest/magic-numbers-linter/)** for complete documentation.
871
-
872
- ## Pre-commit Hooks
873
-
874
- Automate code quality checks before every commit and push with pre-commit hooks.
875
-
876
- ### Quick Setup
877
-
878
- ```bash
879
- # 1. Install pre-commit framework
880
- pip install pre-commit
881
-
882
- # 2. Install git hooks
883
- pre-commit install
884
- pre-commit install --hook-type pre-push
885
-
886
- # 3. Test it works
887
- pre-commit run --all-files
888
- ```
889
-
890
- ### What You Get
891
-
892
- **On every commit:**
893
- - Prevents commits to main/master branch
894
- - Auto-fixes formatting issues
895
- - Runs thailint on changed files (fast, uses pass_filenames: true)
896
-
897
- **On every push:**
898
- - Full linting on entire codebase
899
- - Runs complete test suite
900
-
901
- ### Example Configuration
902
-
903
- ```yaml
904
- # .pre-commit-config.yaml
905
- repos:
906
- - repo: local
907
- hooks:
908
- # Prevent commits to protected branches
909
- - id: no-commit-to-main
910
- name: Prevent commits to main branch
911
- entry: bash -c 'branch=$(git rev-parse --abbrev-ref HEAD); if [ "$branch" = "main" ]; then echo "ERROR: Use a feature branch!"; exit 1; fi'
912
- language: system
913
- pass_filenames: false
914
- always_run: true
915
-
916
- # Auto-format code
917
- - id: format
918
- name: Auto-fix formatting
919
- entry: just format
920
- language: system
921
- pass_filenames: false
922
-
923
- # Run thailint on changed files (passes filenames directly)
924
- - id: thailint-changed
925
- name: Lint changed files
926
- entry: thailint nesting
927
- language: system
928
- files: \.(py|ts|tsx|js|jsx)$
929
- pass_filenames: true
930
- ```
931
-
932
- See **[Pre-commit Hooks Guide](https://thai-lint.readthedocs.io/en/latest/pre-commit-hooks/)** for complete documentation, troubleshooting, and advanced configuration.
933
-
934
- ## Common Use Cases
935
-
936
- ### CI/CD Integration
937
-
938
- ```yaml
939
- # GitHub Actions example
940
- name: Lint
941
-
942
- on: [push, pull_request]
943
-
944
- jobs:
945
- lint:
946
- runs-on: ubuntu-latest
947
- steps:
948
- - uses: actions/checkout@v3
949
- - name: Install thailint
950
- run: pip install thailint
951
- - name: Run file placement linter
952
- run: thailint file-placement .
953
- - name: Run nesting linter
954
- run: thailint nesting src/ --config .thailint.yaml
955
- ```
956
-
957
- ### Editor Integration
958
-
959
- ```python
960
- # VS Code extension example
961
- from src import Linter
962
-
963
- linter = Linter(config_file='.thailint.yaml')
964
- violations = linter.lint(file_path)
965
- ```
966
-
967
- ### Test Suite
968
-
969
- ```python
970
- # pytest integration
971
- import pytest
972
- from src import Linter
973
-
974
- def test_no_violations():
975
- linter = Linter()
976
- violations = linter.lint('src/')
977
- assert len(violations) == 0
978
- ```
979
-
980
- ## Development
981
-
982
- ### Setup Development Environment
983
-
984
- ```bash
985
- # Install dependencies and activate virtualenv
986
- just init
987
-
988
- # Or manually:
989
- poetry install
990
- source $(poetry env info --path)/bin/activate
991
- ```
992
-
993
- ### Running Tests
994
-
995
- ```bash
996
- # Run all tests (parallel mode - fast)
997
- just test
998
-
999
- # Run with coverage (serial mode)
1000
- just test-coverage
1001
-
1002
- # Run specific test
1003
- poetry run pytest tests/test_cli.py::test_hello_command -v
1004
- ```
1005
-
1006
- ### Code Quality
1007
-
1008
- ```bash
1009
- # Fast linting (Ruff only - use during development)
1010
- just lint
1011
-
1012
- # Comprehensive linting (Ruff + Pylint + Flake8 + MyPy)
1013
- just lint-all
1014
-
1015
- # Security scanning
1016
- just lint-security
1017
-
1018
- # Complexity analysis (Radon + Xenon + Nesting)
1019
- just lint-complexity
1020
-
1021
- # SOLID principles (SRP)
1022
- just lint-solid
1023
-
1024
- # DRY principles (duplicate code detection)
1025
- just lint-dry
1026
-
1027
- # ALL quality checks (runs everything)
1028
- just lint-full
1029
-
1030
- # Auto-fix formatting issues
1031
- just format
1032
- ```
1033
-
1034
- ### Dogfooding (Lint Our Own Code)
1035
-
1036
- ```bash
1037
- # Lint file placement
1038
- just lint-placement
1039
-
1040
- # Check nesting depth
1041
- just lint-nesting
1042
-
1043
- # Check for magic numbers
1044
- poetry run thai-lint magic-numbers src/
1045
- ```
1046
-
1047
- ### Building and Publishing
1048
-
1049
- ```bash
1050
- # Build Python package
1051
- poetry build
1052
-
1053
- # Build Docker image locally
1054
- docker build -t washad/thailint:latest .
1055
-
1056
- # Publish to PyPI and Docker Hub (runs tests + linting + version bump)
1057
- just publish
1058
- ```
1059
-
1060
- ### Quick Development Workflows
1061
-
1062
- ```bash
1063
- # Make changes, then run quality checks
1064
- just lint-full
1065
-
1066
- # Share changes for collaboration (skips hooks)
1067
- just share "WIP: feature description"
1068
-
1069
- # Clean up cache and artifacts
1070
- just clean
1071
- ```
1072
-
1073
- See `just --list` or `just help` for all available commands.
1074
-
1075
- ## Docker Usage
1076
-
1077
- ### Basic Docker Commands
1078
-
1079
- ```bash
1080
- # Pull published image
1081
- docker pull washad/thailint:latest
1082
-
1083
- # Run CLI help
1084
- docker run --rm washad/thailint:latest --help
1085
-
1086
- # Lint entire directory (recommended)
1087
- docker run --rm -v $(pwd):/data washad/thailint:latest file-placement /data
1088
-
1089
- # Lint single file
1090
- docker run --rm -v $(pwd):/data washad/thailint:latest file-placement /data/src/app.py
1091
-
1092
- # Lint multiple specific files
1093
- docker run --rm -v $(pwd):/data washad/thailint:latest nesting /data/src/file1.py /data/src/file2.py
1094
-
1095
- # Lint specific subdirectory
1096
- docker run --rm -v $(pwd):/data washad/thailint:latest nesting /data/src
1097
-
1098
- # With custom config
1099
- docker run --rm -v $(pwd):/data \
1100
- washad/thailint:latest nesting --config /data/.thailint.yaml /data
1101
-
1102
- # JSON output for CI/CD
1103
- docker run --rm -v $(pwd):/data \
1104
- washad/thailint:latest file-placement --format json /data
1105
- ```
1106
-
1107
- ### Docker with Sibling Directories (Advanced)
1108
-
1109
- For complex Docker setups with sibling directories, use `--project-root` for explicit control:
1110
-
1111
- ```bash
1112
- # Scenario: Monorepo with separate config and code directories
1113
- # Directory structure:
1114
- # /workspace/
1115
- # ├── config/ # Contains .thailint.yaml
1116
- # ├── backend/app/ # Python backend code
1117
- # ├── frontend/ # TypeScript frontend
1118
- # └── tools/ # Build tools
1119
-
1120
- # Explicit project root (recommended for Docker)
1121
- docker run --rm -v /path/to/workspace:/workspace \
1122
- washad/thailint:latest \
1123
- --project-root /workspace/config \
1124
- magic-numbers /workspace/backend/
1125
-
1126
- # Config path inference (automatic - no --project-root needed)
1127
- docker run --rm -v /path/to/workspace:/workspace \
1128
- washad/thailint:latest \
1129
- --config /workspace/config/.thailint.yaml \
1130
- magic-numbers /workspace/backend/
1131
-
1132
- # Lint multiple sibling directories with shared config
1133
- docker run --rm -v /path/to/workspace:/workspace \
1134
- washad/thailint:latest \
1135
- --project-root /workspace/config \
1136
- nesting /workspace/backend/ /workspace/frontend/
1137
- ```
1138
-
1139
- **When to use `--project-root` in Docker:**
1140
- - **Sibling directory structures** - When config/code aren't nested
1141
- - **Monorepos** - Multiple projects sharing one config
1142
- - **CI/CD** - Explicit paths prevent auto-detection issues
1143
- - **Ignore patterns** - Ensures patterns resolve from correct base directory
1144
-
1145
- ## Documentation
1146
-
1147
- ### Comprehensive Guides
1148
-
1149
- - **[Getting Started](https://thai-lint.readthedocs.io/en/latest/getting-started/)** - Installation, first lint, basic config
1150
- - **[Configuration Reference](https://thai-lint.readthedocs.io/en/latest/configuration/)** - Complete config options (YAML/JSON)
1151
- - **[How to Ignore Violations](https://thai-lint.readthedocs.io/en/latest/how-to-ignore-violations/)** - Complete guide to all ignore levels
1152
- - **[API Reference](https://thai-lint.readthedocs.io/en/latest/api-reference/)** - Library API documentation
1153
- - **[CLI Reference](https://thai-lint.readthedocs.io/en/latest/cli-reference/)** - All CLI commands and options
1154
- - **[Deployment Modes](https://thai-lint.readthedocs.io/en/latest/deployment-modes/)** - CLI, Library, and Docker usage
1155
- - **[File Placement Linter](https://thai-lint.readthedocs.io/en/latest/file-placement-linter/)** - Detailed linter guide
1156
- - **[Magic Numbers Linter](https://thai-lint.readthedocs.io/en/latest/magic-numbers-linter/)** - Magic numbers detection guide
1157
- - **[Nesting Depth Linter](https://thai-lint.readthedocs.io/en/latest/nesting-linter/)** - Nesting depth analysis guide
1158
- - **[SRP Linter](https://thai-lint.readthedocs.io/en/latest/srp-linter/)** - Single Responsibility Principle guide
1159
- - **[DRY Linter](https://thai-lint.readthedocs.io/en/latest/dry-linter/)** - Duplicate code detection guide
1160
- - **[Pre-commit Hooks](https://thai-lint.readthedocs.io/en/latest/pre-commit-hooks/)** - Automated quality checks
1161
- - **[Publishing Guide](https://thai-lint.readthedocs.io/en/latest/releasing/)** - Release and publishing workflow
1162
- - **[Publishing Checklist](https://thai-lint.readthedocs.io/en/latest/publishing-checklist/)** - Post-publication validation
1163
-
1164
- ### Examples
1165
-
1166
- See [`examples/`](examples/) directory for working code:
1167
-
1168
- - **[basic_usage.py](examples/basic_usage.py)** - Simple library API usage
1169
- - **[advanced_usage.py](examples/advanced_usage.py)** - Advanced patterns and workflows
1170
- - **[ci_integration.py](examples/ci_integration.py)** - CI/CD integration example
1171
-
1172
- ## Project Structure
1173
-
1174
- ```
1175
- thai-lint/
1176
- ├── src/ # Application source code
1177
- │ ├── api.py # High-level Library API
1178
- │ ├── cli.py # CLI commands
1179
- │ ├── core/ # Core abstractions
1180
- │ │ ├── base.py # Base linter interfaces
1181
- │ │ ├── registry.py # Rule registry
1182
- │ │ └── types.py # Core types (Violation, Severity)
1183
- │ ├── linters/ # Linter implementations
1184
- │ │ └── file_placement/ # File placement linter
1185
- │ ├── linter_config/ # Configuration system
1186
- │ │ ├── loader.py # Config loader (YAML/JSON)
1187
- │ │ └── ignore.py # Ignore directives
1188
- │ └── orchestrator/ # Multi-language orchestrator
1189
- │ ├── core.py # Main orchestrator
1190
- │ └── language_detector.py
1191
- ├── tests/ # Test suite (221 tests, 87% coverage)
1192
- │ ├── unit/ # Unit tests
1193
- │ ├── integration/ # Integration tests
1194
- │ └── conftest.py # Pytest fixtures
1195
- ├── docs/ # Documentation
1196
- │ ├── getting-started.md
1197
- │ ├── configuration.md
1198
- │ ├── api-reference.md
1199
- │ ├── cli-reference.md
1200
- │ ├── deployment-modes.md
1201
- │ └── file-placement-linter.md
1202
- ├── examples/ # Working examples
1203
- │ ├── basic_usage.py
1204
- │ ├── advanced_usage.py
1205
- │ └── ci_integration.py
1206
- ├── .ai/ # AI agent documentation
1207
- ├── Dockerfile # Multi-stage Docker build
1208
- ├── docker-compose.yml # Docker orchestration
1209
- └── pyproject.toml # Project configuration
1210
- ```
1211
-
1212
- ## Contributing
1213
-
1214
- Contributions are welcome! Please follow these steps:
1215
-
1216
- 1. Fork the repository
1217
- 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
1218
- 3. Make your changes
1219
- 4. Run tests and linting
1220
- 5. Commit your changes (`git commit -m 'Add amazing feature'`)
1221
- 6. Push to the branch (`git push origin feature/amazing-feature`)
1222
- 7. Open a Pull Request
1223
-
1224
- ### Development Guidelines
1225
-
1226
- - Write tests for new features
1227
- - Follow existing code style (enforced by Ruff)
1228
- - Add type hints to all functions
1229
- - Update documentation for user-facing changes
1230
- - Run `pytest` and `ruff check` before committing
1231
-
1232
- ## Performance
1233
-
1234
- thailint is designed for speed and efficiency:
1235
-
1236
- | Operation | Performance | Target |
1237
- |-----------|-------------|--------|
1238
- | Single file lint | ~20ms | <100ms |
1239
- | 100 files | ~300ms | <1s |
1240
- | 1000 files | ~900ms | <5s |
1241
- | Config loading | ~10ms | <100ms |
1242
-
1243
- *Performance benchmarks run on standard hardware, your results may vary.*
1244
-
1245
- ## Exit Codes
1246
-
1247
- thailint uses standard exit codes for CI/CD integration:
1248
-
1249
- - **0** - Success (no violations)
1250
- - **1** - Violations found
1251
- - **2** - Error occurred (invalid config, file not found, etc.)
1252
-
1253
- ```bash
1254
- thailint file-placement .
1255
- if [ $? -eq 0 ]; then
1256
- echo "Linting passed"
1257
- else
1258
- echo "Linting failed"
1259
- fi
1260
- ```
1261
-
1262
- ## Architecture
1263
-
1264
- See [`.ai/docs/`](.ai/docs/) for detailed architecture documentation and [`.ai/howtos/`](.ai/howtos/) for development guides.
1265
-
1266
- ## License
1267
-
1268
- MIT License - see LICENSE file for details.
1269
-
1270
- ## Support
1271
-
1272
- - **Issues**: https://github.com/be-wise-be-kind/thai-lint/issues
1273
- - **Documentation**: `.ai/docs/` and `.ai/howtos/`
1274
-
1275
- ## Acknowledgments
1276
-
1277
- Built with:
1278
- - [Click](https://click.palletsprojects.com/) - CLI framework
1279
- - [pytest](https://pytest.org/) - Testing framework
1280
- - [Ruff](https://docs.astral.sh/ruff/) - Linting and formatting
1281
- - [Docker](https://www.docker.com/) - Containerization
1282
-
1283
- ## Changelog
1284
-
1285
- See [CHANGELOG.md](CHANGELOG.md) for version history.
1286
-