thailint 0.12.0__py3-none-any.whl → 0.14.0__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 (135) hide show
  1. src/analyzers/__init__.py +4 -3
  2. src/analyzers/ast_utils.py +54 -0
  3. src/analyzers/typescript_base.py +4 -0
  4. src/cli/__init__.py +3 -0
  5. src/cli/config.py +12 -12
  6. src/cli/config_merge.py +241 -0
  7. src/cli/linters/__init__.py +9 -0
  8. src/cli/linters/code_patterns.py +107 -257
  9. src/cli/linters/code_smells.py +48 -165
  10. src/cli/linters/documentation.py +21 -95
  11. src/cli/linters/performance.py +274 -0
  12. src/cli/linters/shared.py +232 -6
  13. src/cli/linters/structure.py +26 -21
  14. src/cli/linters/structure_quality.py +28 -21
  15. src/cli_main.py +3 -0
  16. src/config.py +2 -1
  17. src/core/base.py +3 -2
  18. src/core/cli_utils.py +3 -1
  19. src/core/config_parser.py +5 -2
  20. src/core/constants.py +54 -0
  21. src/core/linter_utils.py +95 -6
  22. src/core/rule_discovery.py +5 -1
  23. src/core/violation_builder.py +3 -0
  24. src/linter_config/directive_markers.py +109 -0
  25. src/linter_config/ignore.py +225 -383
  26. src/linter_config/pattern_utils.py +65 -0
  27. src/linter_config/rule_matcher.py +89 -0
  28. src/linters/collection_pipeline/any_all_analyzer.py +281 -0
  29. src/linters/collection_pipeline/ast_utils.py +40 -0
  30. src/linters/collection_pipeline/config.py +12 -0
  31. src/linters/collection_pipeline/continue_analyzer.py +2 -8
  32. src/linters/collection_pipeline/detector.py +262 -32
  33. src/linters/collection_pipeline/filter_map_analyzer.py +402 -0
  34. src/linters/collection_pipeline/linter.py +18 -35
  35. src/linters/collection_pipeline/suggestion_builder.py +68 -1
  36. src/linters/dry/base_token_analyzer.py +16 -9
  37. src/linters/dry/block_filter.py +7 -4
  38. src/linters/dry/cache.py +7 -2
  39. src/linters/dry/config.py +7 -1
  40. src/linters/dry/constant_matcher.py +34 -25
  41. src/linters/dry/file_analyzer.py +4 -2
  42. src/linters/dry/inline_ignore.py +7 -16
  43. src/linters/dry/linter.py +48 -25
  44. src/linters/dry/python_analyzer.py +18 -10
  45. src/linters/dry/python_constant_extractor.py +51 -52
  46. src/linters/dry/single_statement_detector.py +14 -12
  47. src/linters/dry/token_hasher.py +115 -115
  48. src/linters/dry/typescript_analyzer.py +11 -6
  49. src/linters/dry/typescript_constant_extractor.py +4 -0
  50. src/linters/dry/typescript_statement_detector.py +208 -208
  51. src/linters/dry/typescript_value_extractor.py +3 -0
  52. src/linters/dry/violation_filter.py +1 -4
  53. src/linters/dry/violation_generator.py +1 -4
  54. src/linters/file_header/atemporal_detector.py +58 -40
  55. src/linters/file_header/base_parser.py +4 -0
  56. src/linters/file_header/bash_parser.py +4 -0
  57. src/linters/file_header/config.py +14 -0
  58. src/linters/file_header/field_validator.py +5 -8
  59. src/linters/file_header/linter.py +19 -12
  60. src/linters/file_header/markdown_parser.py +6 -0
  61. src/linters/file_placement/config_loader.py +3 -1
  62. src/linters/file_placement/linter.py +22 -8
  63. src/linters/file_placement/pattern_matcher.py +21 -4
  64. src/linters/file_placement/pattern_validator.py +21 -7
  65. src/linters/file_placement/rule_checker.py +2 -2
  66. src/linters/lazy_ignores/__init__.py +43 -0
  67. src/linters/lazy_ignores/config.py +66 -0
  68. src/linters/lazy_ignores/directive_utils.py +121 -0
  69. src/linters/lazy_ignores/header_parser.py +177 -0
  70. src/linters/lazy_ignores/linter.py +158 -0
  71. src/linters/lazy_ignores/matcher.py +135 -0
  72. src/linters/lazy_ignores/python_analyzer.py +205 -0
  73. src/linters/lazy_ignores/rule_id_utils.py +180 -0
  74. src/linters/lazy_ignores/skip_detector.py +298 -0
  75. src/linters/lazy_ignores/types.py +69 -0
  76. src/linters/lazy_ignores/typescript_analyzer.py +146 -0
  77. src/linters/lazy_ignores/violation_builder.py +131 -0
  78. src/linters/lbyl/__init__.py +29 -0
  79. src/linters/lbyl/config.py +63 -0
  80. src/linters/lbyl/pattern_detectors/__init__.py +25 -0
  81. src/linters/lbyl/pattern_detectors/base.py +46 -0
  82. src/linters/magic_numbers/context_analyzer.py +227 -229
  83. src/linters/magic_numbers/linter.py +20 -15
  84. src/linters/magic_numbers/python_analyzer.py +4 -16
  85. src/linters/magic_numbers/typescript_analyzer.py +9 -16
  86. src/linters/method_property/config.py +4 -1
  87. src/linters/method_property/linter.py +5 -10
  88. src/linters/method_property/python_analyzer.py +5 -4
  89. src/linters/method_property/violation_builder.py +3 -0
  90. src/linters/nesting/linter.py +11 -6
  91. src/linters/nesting/typescript_analyzer.py +6 -12
  92. src/linters/nesting/typescript_function_extractor.py +0 -4
  93. src/linters/nesting/violation_builder.py +1 -0
  94. src/linters/performance/__init__.py +91 -0
  95. src/linters/performance/config.py +43 -0
  96. src/linters/performance/constants.py +49 -0
  97. src/linters/performance/linter.py +149 -0
  98. src/linters/performance/python_analyzer.py +365 -0
  99. src/linters/performance/regex_analyzer.py +312 -0
  100. src/linters/performance/regex_linter.py +139 -0
  101. src/linters/performance/typescript_analyzer.py +236 -0
  102. src/linters/performance/violation_builder.py +160 -0
  103. src/linters/print_statements/linter.py +6 -4
  104. src/linters/print_statements/python_analyzer.py +85 -81
  105. src/linters/print_statements/typescript_analyzer.py +6 -15
  106. src/linters/srp/heuristics.py +4 -4
  107. src/linters/srp/linter.py +12 -12
  108. src/linters/srp/violation_builder.py +0 -4
  109. src/linters/stateless_class/linter.py +30 -36
  110. src/linters/stateless_class/python_analyzer.py +11 -20
  111. src/linters/stringly_typed/config.py +4 -5
  112. src/linters/stringly_typed/context_filter.py +410 -410
  113. src/linters/stringly_typed/function_call_violation_builder.py +93 -95
  114. src/linters/stringly_typed/linter.py +48 -16
  115. src/linters/stringly_typed/python/analyzer.py +5 -1
  116. src/linters/stringly_typed/python/call_tracker.py +8 -5
  117. src/linters/stringly_typed/python/comparison_tracker.py +10 -5
  118. src/linters/stringly_typed/python/condition_extractor.py +3 -0
  119. src/linters/stringly_typed/python/conditional_detector.py +4 -1
  120. src/linters/stringly_typed/python/match_analyzer.py +8 -2
  121. src/linters/stringly_typed/python/validation_detector.py +3 -0
  122. src/linters/stringly_typed/storage.py +14 -14
  123. src/linters/stringly_typed/typescript/call_tracker.py +9 -3
  124. src/linters/stringly_typed/typescript/comparison_tracker.py +9 -3
  125. src/linters/stringly_typed/violation_generator.py +288 -259
  126. src/orchestrator/core.py +13 -4
  127. src/templates/thailint_config_template.yaml +196 -0
  128. src/utils/project_root.py +3 -0
  129. thailint-0.14.0.dist-info/METADATA +185 -0
  130. thailint-0.14.0.dist-info/RECORD +199 -0
  131. thailint-0.12.0.dist-info/METADATA +0 -1667
  132. thailint-0.12.0.dist-info/RECORD +0 -164
  133. {thailint-0.12.0.dist-info → thailint-0.14.0.dist-info}/WHEEL +0 -0
  134. {thailint-0.12.0.dist-info → thailint-0.14.0.dist-info}/entry_points.txt +0 -0
  135. {thailint-0.12.0.dist-info → thailint-0.14.0.dist-info}/licenses/LICENSE +0 -0
@@ -132,6 +132,202 @@ print-statements:
132
132
  # - "scripts/**"
133
133
  # - "**/debug.py"
134
134
 
135
+ # ============================================================================
136
+ # STRINGLY-TYPED LINTER
137
+ # ============================================================================
138
+ # Detects "stringly typed" code patterns where strings are used instead of
139
+ # proper enums - e.g., if env == "production": ... repeated across files
140
+ #
141
+ stringly-typed:
142
+ enabled: true
143
+
144
+ # Minimum occurrences across files to flag a violation
145
+ # Default: 2
146
+ min_occurrences: 2
147
+
148
+ # Minimum unique string values to suggest creating an enum
149
+ # Default: 2
150
+ min_values_for_enum: 2
151
+
152
+ # Maximum unique string values to suggest an enum (above this, probably not enum-worthy)
153
+ # Default: 6
154
+ max_values_for_enum: 6
155
+
156
+ # Whether to require cross-file occurrences to flag violations
157
+ # Default: true
158
+ require_cross_file: true
159
+
160
+ # -------------------------------------------------------------------------
161
+ # OPTIONAL: String value sets that are acceptable (won't be flagged)
162
+ # -------------------------------------------------------------------------
163
+ # allowed_string_sets:
164
+ # - ["debug", "info", "warning", "error"] # Log levels
165
+ # - ["ASC", "DESC"] # Sort directions
166
+
167
+ # -------------------------------------------------------------------------
168
+ # OPTIONAL: Variable names to exclude from analysis
169
+ # -------------------------------------------------------------------------
170
+ # exclude_variables:
171
+ # - log_level
172
+ # - severity
173
+
174
+ # ============================================================================
175
+ # FILE HEADER LINTER
176
+ # ============================================================================
177
+ # Validates that files have proper documentation headers
178
+ #
179
+ file-header:
180
+ enabled: true
181
+
182
+ # Enforce atemporal language (no "currently", "now", dates)
183
+ # Default: true
184
+ enforce_atemporal: true
185
+
186
+ # -------------------------------------------------------------------------
187
+ # OPTIONAL: Override required fields by language
188
+ # -------------------------------------------------------------------------
189
+ # required_fields:
190
+ # python: [Purpose, Scope, Overview, Dependencies, Exports, Interfaces, Implementation]
191
+ # typescript: [Purpose, Scope, Overview, Dependencies, Exports, Props/Interfaces, State/Behavior]
192
+ # bash: [Purpose, Scope, Overview, Dependencies, Exports, Usage, Environment]
193
+ # markdown: [purpose, scope, overview, audience, status]
194
+ # css: [Purpose, Scope, Overview, Dependencies, Exports, Interfaces, Environment]
195
+
196
+ # -------------------------------------------------------------------------
197
+ # OPTIONAL: File patterns to ignore
198
+ # -------------------------------------------------------------------------
199
+ # ignore:
200
+ # - "test/**"
201
+ # - "**/migrations/**"
202
+ # - "**/__init__.py"
203
+
204
+ # ============================================================================
205
+ # METHOD PROPERTY LINTER
206
+ # ============================================================================
207
+ # Detects methods that should be @property (no args, simple return)
208
+ #
209
+ method-property:
210
+ enabled: true
211
+
212
+ # Maximum statements in method body to suggest @property
213
+ # Default: 3
214
+ max_body_statements: 3
215
+
216
+ # -------------------------------------------------------------------------
217
+ # OPTIONAL: Methods to ignore (exact names)
218
+ # -------------------------------------------------------------------------
219
+ # ignore_methods:
220
+ # - "__str__"
221
+ # - "__repr__"
222
+
223
+ # -------------------------------------------------------------------------
224
+ # OPTIONAL: Additional action verb prefixes to exclude
225
+ # -------------------------------------------------------------------------
226
+ # exclude_prefixes:
227
+ # - "fetch_"
228
+ # - "load_"
229
+
230
+ # -------------------------------------------------------------------------
231
+ # OPTIONAL: File patterns to ignore
232
+ # -------------------------------------------------------------------------
233
+ # ignore:
234
+ # - "tests/**"
235
+
236
+ # ============================================================================
237
+ # STATELESS CLASS LINTER
238
+ # ============================================================================
239
+ # Detects classes with no instance state (should be modules or functions)
240
+ #
241
+ stateless-class:
242
+ enabled: true
243
+
244
+ # Minimum methods to flag a stateless class
245
+ # Default: 2
246
+ min_methods: 2
247
+
248
+ # -------------------------------------------------------------------------
249
+ # OPTIONAL: File patterns to ignore
250
+ # -------------------------------------------------------------------------
251
+ # ignore:
252
+ # - "tests/**"
253
+
254
+ # ============================================================================
255
+ # COLLECTION PIPELINE LINTER
256
+ # ============================================================================
257
+ # Detects "embedded loop filtering" anti-pattern (if/continue in loops)
258
+ # Suggests using filter(), list comprehensions, or generator expressions
259
+ #
260
+ pipeline:
261
+ enabled: true
262
+
263
+ # Minimum if/continue patterns in a loop to flag
264
+ # Default: 1
265
+ min_continues: 1
266
+
267
+ # -------------------------------------------------------------------------
268
+ # OPTIONAL: File patterns to ignore
269
+ # -------------------------------------------------------------------------
270
+ # ignore:
271
+ # - "tests/**"
272
+
273
+ # ============================================================================
274
+ # LAZY IGNORES LINTER
275
+ # ============================================================================
276
+ # Detects unjustified linting suppressions (noqa, type: ignore, etc.)
277
+ # without proper documentation in file headers
278
+ #
279
+ lazy-ignores:
280
+ enabled: true
281
+
282
+ # Pattern-specific toggles
283
+ check_noqa: true
284
+ check_type_ignore: true
285
+ check_pylint_disable: true
286
+ check_nosec: true
287
+ check_ts_ignore: true
288
+ check_eslint_disable: true
289
+ check_thailint_ignore: true
290
+ check_test_skips: true
291
+
292
+ # Check for orphaned suppressions (documented but not used)
293
+ check_orphaned: true
294
+
295
+ # -------------------------------------------------------------------------
296
+ # OPTIONAL: File patterns to ignore
297
+ # -------------------------------------------------------------------------
298
+ # ignore_patterns:
299
+ # - "tests/**"
300
+
301
+ # ============================================================================
302
+ # PERFORMANCE LINTER
303
+ # ============================================================================
304
+ # Detects performance anti-patterns in loops that cause O(n²) behavior
305
+ #
306
+ performance:
307
+ enabled: true
308
+
309
+ # String concatenation in loops (O(n²) pattern)
310
+ # Detects: result += str in for/while loops
311
+ # Suggests: Use "".join() or list append + join
312
+ string-concat-loop:
313
+ enabled: true
314
+ # Report each += separately, or one violation per loop
315
+ # Default: false (one per loop)
316
+ report_each_concat: false
317
+
318
+ # Regex compilation in loops
319
+ # Detects: re.match(), re.search(), re.sub() etc. in loops
320
+ # Suggests: Use re.compile() outside loop
321
+ regex-in-loop:
322
+ enabled: true
323
+
324
+ # -------------------------------------------------------------------------
325
+ # OPTIONAL: File patterns to ignore
326
+ # -------------------------------------------------------------------------
327
+ # ignore:
328
+ # - "tests/**"
329
+ # - "scripts/**"
330
+
135
331
  # ============================================================================
136
332
  # GLOBAL SETTINGS
137
333
  # ============================================================================
src/utils/project_root.py CHANGED
@@ -15,6 +15,9 @@ Exports: is_project_root(), get_project_root()
15
15
  Interfaces: Path-based functions for checking and finding project roots
16
16
 
17
17
  Implementation: pyprojroot delegation with manual fallback for test environments
18
+
19
+ Suppressions:
20
+ - type:ignore[arg-type]: pyprojroot external library typing issue with Path conversion
18
21
  """
19
22
 
20
23
  from pathlib import Path
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.4
2
+ Name: thailint
3
+ Version: 0.14.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,performance,typescript
8
+ Author: Steve Jackson
9
+ Requires-Python: >=3.11,<4.0
10
+ Classifier: Development Status :: 4 - Beta
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
+ [![PyPI](https://img.shields.io/pypi/v/thai-lint)](https://pypi.org/project/thai-lint/)
41
+ [![Documentation](https://readthedocs.org/projects/thai-lint/badge/?version=latest)](https://thai-lint.readthedocs.io/)
42
+
43
+ **The AI Linter** - Catch the mistakes AI coding assistants keep making.
44
+
45
+ thailint detects anti-patterns that AI tools frequently introduce: duplicate code, excessive nesting, magic numbers, SRP violations, and more. It works across Python, TypeScript, and JavaScript with unified rules - filling gaps that existing linters miss.
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ pip install thai-lint
51
+ ```
52
+
53
+ Or with Docker:
54
+ ```bash
55
+ docker run --rm -v $(pwd):/data washad/thailint:latest --help
56
+ ```
57
+
58
+ ## Quick Start
59
+
60
+ ```bash
61
+ # Generate a config file (optional)
62
+ thailint init-config
63
+
64
+ # Run any linter
65
+ thailint dry src/
66
+ ```
67
+
68
+ That's it. See violations, fix them, ship better code.
69
+
70
+ ## Available Linters
71
+
72
+ | Linter | What It Catches | Command | Docs |
73
+ |--------|-----------------|---------|------|
74
+ | **DRY** | Duplicate code across files | `thailint dry src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/dry-linter/) |
75
+ | **Nesting** | Deeply nested if/for/while blocks | `thailint nesting src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/nesting-linter/) |
76
+ | **Magic Numbers** | Unnamed numeric literals | `thailint magic-numbers src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/magic-numbers-linter/) |
77
+ | **Performance** | O(n²) patterns: string += in loops, regex in loops | `thailint perf src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/performance-linter/) |
78
+ | **SRP** | Classes doing too much | `thailint srp src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/srp-linter/) |
79
+ | **File Header** | Missing documentation headers | `thailint file-header src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/file-header-linter/) |
80
+ | **Stateless Class** | Classes that should be functions | `thailint stateless-class src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/stateless-class-linter/) |
81
+ | **Collection Pipeline** | Loops with embedded filtering | `thailint pipeline src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/collection-pipeline-linter/) |
82
+ | **Method Property** | Methods that should be @property | `thailint method-property src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/method-property-linter/) |
83
+ | **File Placement** | Files in wrong directories | `thailint file-placement src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/file-placement-linter/) |
84
+ | **Lazy Ignores** | Unjustified linting suppressions | `thailint lazy-ignores src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/lazy-ignores-linter/) |
85
+ | **Print Statements** | Debug prints left in code | `thailint print-statements src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/print-statements-linter/) |
86
+ | **Stringly Typed** | Strings that should be enums | `thailint stringly-typed src/` | [Guide](https://thai-lint.readthedocs.io/en/latest/stringly-typed-linter/) |
87
+
88
+ ## Configuration
89
+
90
+ Create `.thailint.yaml` in your project root:
91
+
92
+ ```yaml
93
+ dry:
94
+ enabled: true
95
+ min_duplicate_lines: 4
96
+
97
+ nesting:
98
+ enabled: true
99
+ max_nesting_depth: 3
100
+
101
+ magic-numbers:
102
+ enabled: true
103
+ allowed_numbers: [-1, 0, 1, 2, 10, 100]
104
+ ```
105
+
106
+ Or generate one automatically:
107
+ ```bash
108
+ thailint init-config --preset lenient # or: strict, standard
109
+ ```
110
+
111
+ See [Configuration Reference](https://thai-lint.readthedocs.io/en/latest/configuration/) for all options.
112
+
113
+ ## Output Formats
114
+
115
+ ```bash
116
+ # Human-readable (default)
117
+ thailint dry src/
118
+
119
+ # JSON for CI/CD
120
+ thailint dry --format json src/
121
+
122
+ # SARIF for GitHub Code Scanning
123
+ thailint dry --format sarif src/ > results.sarif
124
+ ```
125
+
126
+ ## Ignoring Violations
127
+
128
+ ```python
129
+ # Line-level
130
+ timeout = 3600 # thailint: ignore[magic-numbers]
131
+
132
+ # File-level
133
+ # thailint: ignore-file[dry]
134
+ ```
135
+
136
+ Or in config:
137
+ ```yaml
138
+ dry:
139
+ ignore:
140
+ - "tests/"
141
+ - "**/generated/**"
142
+ ```
143
+
144
+ See [How to Ignore Violations](https://thai-lint.readthedocs.io/en/latest/how-to-ignore-violations/) for all 5 ignore levels.
145
+
146
+ ## CI/CD Integration
147
+
148
+ ```yaml
149
+ # GitHub Actions
150
+ - name: Run thailint
151
+ run: |
152
+ pip install thai-lint
153
+ thailint dry src/
154
+ thailint nesting src/
155
+ ```
156
+
157
+ Exit codes: `0` = success, `1` = violations found, `2` = error.
158
+
159
+ ## Documentation
160
+
161
+ - **[Quick Start Guide](https://thai-lint.readthedocs.io/en/latest/quick-start/)** - Get running in 5 minutes
162
+ - **[Configuration Reference](https://thai-lint.readthedocs.io/en/latest/configuration/)** - All config options
163
+ - **[Troubleshooting](https://thai-lint.readthedocs.io/en/latest/troubleshooting/)** - Common issues
164
+ - **[Full Documentation](https://thai-lint.readthedocs.io/)** - Everything else
165
+
166
+ ## Contributing
167
+
168
+ Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
169
+
170
+ ```bash
171
+ git clone https://github.com/be-wise-be-kind/thai-lint.git
172
+ cd thai-lint
173
+ poetry install
174
+ just test
175
+ ```
176
+
177
+ ## License
178
+
179
+ MIT License - see [LICENSE](LICENSE) for details.
180
+
181
+ ## Support
182
+
183
+ - **Issues**: [github.com/be-wise-be-kind/thai-lint/issues](https://github.com/be-wise-be-kind/thai-lint/issues)
184
+ - **Docs**: [thai-lint.readthedocs.io](https://thai-lint.readthedocs.io/)
185
+
@@ -0,0 +1,199 @@
1
+ src/__init__.py,sha256=0IT3HnAnSBEfos4G_27cflJiaoWWfeSEfHxsUr53OsM,2192
2
+ src/analyzers/__init__.py,sha256=hCN7ugG_Xs2N_WfWO7ZId-Cv4kvN1AxhIYTA4SD2fJ4,1062
3
+ src/analyzers/ast_utils.py,sha256=iWCRzlTw2QF5wWzQx1OmKshx7TU3pHxC8_omv9XUInw,1630
4
+ src/analyzers/typescript_base.py,sha256=Dtc_2jLSNoadh53MyeW2syrJMBIMVPvotcM8SeDEXH8,5244
5
+ src/api.py,sha256=pJ5l3qxccKBEY-BkANwzTgLAl1ZFq7OP6hx6LSxbhDw,4664
6
+ src/cli/__init__.py,sha256=nMxKv4D0t09LwUm4Z5w-b7vIuyn24SFzv78BKzE3AOQ,1272
7
+ src/cli/__main__.py,sha256=xIKI57yqB1NOw9eXnGXfU8rC2UwcAJYjDxlZbt9eP0w,671
8
+ src/cli/config.py,sha256=qbBX8LQrlPcOrvPYcZw9Hobv9FqSI7T3xy2HUITRqAU,14426
9
+ src/cli/config_merge.py,sha256=A1eCthiLwjj0SEhOcxa6t2hYwWMapGJpL9sCmYvUFw4,8464
10
+ src/cli/linters/__init__.py,sha256=Dlx2CRHT5_QeIeCJ0horPXci9zRP_KzSz_-mg0VMbOw,2343
11
+ src/cli/linters/code_patterns.py,sha256=3mi6tRJiNAN2DBdQLcawJhn5pSBDIN1-pLBD_1HNb90,9059
12
+ src/cli/linters/code_smells.py,sha256=a5nMke9-6jPmkyG9RrgfuiOYfJuuUsmkujgAdsvnVOs,11457
13
+ src/cli/linters/documentation.py,sha256=T2HeyQim4m_aE3f1pNnsCW9rDnTQf_YETcHnExXum4k,3224
14
+ src/cli/linters/performance.py,sha256=toQqOPOxiJ5D5TvXv6pFoIibfbquOyrnQVrZUmrz8Zc,9530
15
+ src/cli/linters/shared.py,sha256=f3WsTgLyetndI9li9W-q_-MvOXjahNyJGJVCEoUQ6QI,9489
16
+ src/cli/linters/structure.py,sha256=77JdZmjp7pBNoMP1SKkj4EQa4z4IZ_260TonvQ__IgE,10515
17
+ src/cli/linters/structure_quality.py,sha256=T1uKtDnKLz_YnHe_MP6lB8mvfxdaJNP-XQ8_IWUpTt0,10586
18
+ src/cli/main.py,sha256=mRyRN_IQI2WyqDCxI8vuzdhbkCkVrK6INfJPjU8ayIU,3844
19
+ src/cli/utils.py,sha256=L1q2i6NBkWoQZLYnkeNNdfzHYRhaUobbzqSr2w8JUFY,12595
20
+ src/cli_main.py,sha256=C0Ey7YNlG3ipqb3KsJZ8rL8PJ4ueVp_45IUirGidvHI,1618
21
+ src/config.py,sha256=6yQyfvYizffV6GQrnAlL30bKR4iKROPoxoe4al42Pqg,12531
22
+ src/core/__init__.py,sha256=5FtsDvhMt4SNRx3pbcGURrxn135XRbeRrjSUxiXwkNc,381
23
+ src/core/base.py,sha256=u5A8geprlKnsJk4ShiLHTKXRekZUB4I6rPQWxgiFeto,8019
24
+ src/core/cli_utils.py,sha256=ZdFSPrZ4WfpTMh-mc_Z3u5OYidE1YyPRKflMynPosa8,6552
25
+ src/core/config_parser.py,sha256=CRHV2-csxag6yQzx_4IYYz57QSUYjPkeSb0XvOyshRI,4272
26
+ src/core/constants.py,sha256=PKtPDqk6k9VuOSgjq1FAdi2CTvlnhdXvLj91dNaMDTA,1584
27
+ src/core/linter_utils.py,sha256=StnKFzJgSvLyao1S0LpTKhsXo8nOwpdKpxo7mXl5PIg,8594
28
+ src/core/registry.py,sha256=yRA8mQLiZwjmgxl1wSTgdj1cuo_QXuRdrXt3NpCBUgE,3285
29
+ src/core/rule_discovery.py,sha256=tgRH-BJGKsQTxfa249yrY7UJuonRjobMCENqmhcbAeY,5496
30
+ src/core/types.py,sha256=SElFzf_VSrAMsoiE0aU8ZYXuvKqdfwfM5umUHx4eT8w,3342
31
+ src/core/violation_builder.py,sha256=dOPFfZx6U5_TMgaTop-4SUV2jHOZjBo67uwgiS_s-uE,6694
32
+ src/core/violation_utils.py,sha256=hSTSfQaaB7038G2Au4vqBLYTnkoaSN3k70IxWacjbl8,1993
33
+ src/formatters/__init__.py,sha256=yE1yIL8lplTMEjsmQm7F-kOMaYq7OjmbFuiwwK0D-gM,815
34
+ src/formatters/sarif.py,sha256=gGOwb_v7j4mx4bpvV1NNDd-JyHH8i8XX89iQ6uRSvG4,7050
35
+ src/linter_config/__init__.py,sha256=_I2VVlZlfKyT-tKukuUA5-aVcHLOe3m6C2cev43AiEc,298
36
+ src/linter_config/directive_markers.py,sha256=nRc2Mp3B1mn6tu1XH88ugMxWffk1k8OUNohepaRQ0S0,3245
37
+ src/linter_config/ignore.py,sha256=lSt_2nGHiGe06Ha1pl2nZTbAVUjwhKtS2b3UHIZuA1I,13089
38
+ src/linter_config/loader.py,sha256=K6mKRkP2jgwar-pwBoJGWgwynLVjqdez-l3Nd6bUCMk,3363
39
+ src/linter_config/pattern_utils.py,sha256=BjV95SySST3HqZBwF1Og8yoHqFxuqQ16VPCacE21ks0,2056
40
+ src/linter_config/rule_matcher.py,sha256=EWqSv4UY90fWps3AzDCSF7PZCbYyFTEBZT2h_txcRms,2779
41
+ src/linters/__init__.py,sha256=-nnNsL8E5-2p9qlLKp_TaShHAjPH-NacOEU1sXmAR9k,77
42
+ src/linters/collection_pipeline/__init__.py,sha256=BcnbY3wgJB1XLfZ9J9qfUJQ1_yCo_THjGDTppxJEMZY,3231
43
+ src/linters/collection_pipeline/any_all_analyzer.py,sha256=u8NGIYrJTuP3U6je5rkeEUV2Bh1yePC12vl-czAU5GU,7554
44
+ src/linters/collection_pipeline/ast_utils.py,sha256=JZsJeel9BzVIOfnENgVcZR8wMNoQ-RJoxuBx-J7w9Uk,1227
45
+ src/linters/collection_pipeline/config.py,sha256=EDlPcYEsECfAhZu9h1z5MVszRdr9Okdp3Ea8bf8c2mI,2782
46
+ src/linters/collection_pipeline/continue_analyzer.py,sha256=Azn7-2MLpi0M68dXDLgXCbIBpCT1i3GSUGE13U2Tl4U,2728
47
+ src/linters/collection_pipeline/detector.py,sha256=7_keKR5lGo5rvOVCAvV_6-bG29I_SYAhkkxP6ES3wKg,11696
48
+ src/linters/collection_pipeline/filter_map_analyzer.py,sha256=QbTjObryaLVB71MD4b4SyoOMEO_EgMNedNZLVm05QCQ,11829
49
+ src/linters/collection_pipeline/linter.py,sha256=53pkC1a6mvNTzjK_LDV_JmMysbZBLUsM_NjavvAZZBQ,13552
50
+ src/linters/collection_pipeline/suggestion_builder.py,sha256=4-RHBw95u7gPKpoN1xZlpSOkqzXY7_TRrB_otUXsdDE,4357
51
+ src/linters/dry/__init__.py,sha256=p58tN3z_VbulfTkRm1kLZJ43Bemt66T2sro1teirUY8,826
52
+ src/linters/dry/base_token_analyzer.py,sha256=hkR3MI6UYwQ7PNJiyGiIPiX7uMrDRHr0mzI-aG8wVCM,3199
53
+ src/linters/dry/block_filter.py,sha256=3RgmRSqYFk2eqATLOWN3hET09JuaPEFux3ResA0ltqo,11432
54
+ src/linters/dry/block_grouper.py,sha256=NP66BlofaY7HVXcWwmK5lyiNXbaTlU1V3IbcZubIq_I,1937
55
+ src/linters/dry/cache.py,sha256=909Va6bsq_DREooZGE1VDisefN_QqfPTL1ZKPi7k7Bk,8911
56
+ src/linters/dry/cache_query.py,sha256=qu_uHe360ZvKmFTBvfREjjPMGbJgLQsFTKPVIA2jQJ0,1949
57
+ src/linters/dry/config.py,sha256=3l1Ly3JbeYLvBbpyBsti2hGAKvxDShZ6d4uc8FEMB0I,7283
58
+ src/linters/dry/config_loader.py,sha256=wikqnigOp6p1h9jaAATV_3bDXSiaIUFaf9xg1jQMDpo,1313
59
+ src/linters/dry/constant.py,sha256=n9cNwa-1GQPISGZ3dgGcpLv7tc-uy56ravHiFsIwoRk,3249
60
+ src/linters/dry/constant_matcher.py,sha256=GcfCRKNvUchandcqM8lz54BpEpSkdxr4K2Vb1aTAlcc,8074
61
+ src/linters/dry/constant_violation_builder.py,sha256=F88aLlgscWmyUeIr_EgF4CeheXEc6Iv2pBG2eKJTmts,4048
62
+ src/linters/dry/deduplicator.py,sha256=a1TRvldxCszf5QByo1ihXF3W98dpGuyaRT74jPfQftM,3988
63
+ src/linters/dry/duplicate_storage.py,sha256=9pIALnwAuz5BJUYNXrPbObbP932CE9x0vgUkICryT_s,1970
64
+ src/linters/dry/file_analyzer.py,sha256=3uO2fy8HvxFiRCtYaBs9LztkzvDynqmy7INYRaJjK-g,2990
65
+ src/linters/dry/inline_ignore.py,sha256=3fgPsn_kXeF7kVy_9FL8xYwSUA9cSmgUR3F0tOwzjuY,4275
66
+ src/linters/dry/linter.py,sha256=-8MiprIs2MsAj2fAmg-hCbasDVBkLLgMP9BR2_ZcDv0,9304
67
+ src/linters/dry/python_analyzer.py,sha256=b7n7u3NbOW3DFZHeC-bG3N_6VgW2NU2easG8pIHYaZc,10962
68
+ src/linters/dry/python_constant_extractor.py,sha256=v9-3NDCq9CKT6uvti_PRIC3DMEESHW2g3i2IfFy0hw8,3469
69
+ src/linters/dry/single_statement_detector.py,sha256=ZmMo_tsvVxugFmnNzWtGc-4e5sGOw18HHxHre3lRUUA,18220
70
+ src/linters/dry/storage_initializer.py,sha256=ykMALFs4uMUrN0_skEwySDl_t5Dm_LGHllF0OxDhiUI,1366
71
+ src/linters/dry/token_hasher.py,sha256=RoUXByVHwf9TZjRqXB3aI1htNZS0pX41oOsUxvlsF00,4951
72
+ src/linters/dry/typescript_analyzer.py,sha256=xGUcQO8MvJnAVgn5GZRwQAc5xZsD0T-qOQlFuJcvfZM,10777
73
+ src/linters/dry/typescript_constant_extractor.py,sha256=ri5NivpcxLAxwdMJvbeTF4Vu0WS_Fgf7FydQGoVNgm0,5130
74
+ src/linters/dry/typescript_statement_detector.py,sha256=8WiwcjLs8j8_wp0UTsoXN0vVr1mNa562O1CB-FtaQR4,8848
75
+ src/linters/dry/typescript_value_extractor.py,sha256=Wi6Yy0yklQSDpeA6FRCsquXILHD8RjRFKJI5Nsg3f70,2506
76
+ src/linters/dry/violation_builder.py,sha256=WkCibSNytoqMHGC-3GrVff4PD7-SOnVzzZgkMeqmzco,2952
77
+ src/linters/dry/violation_filter.py,sha256=2e6NHN7GYadt27Pz5kiXhttKPJu1loiXhOi0G3J3Epk,3211
78
+ src/linters/dry/violation_generator.py,sha256=7jkRfauwAEdipFuDhoy82eIeXS1ECN4W_3jspMlnFaU,6068
79
+ src/linters/file_header/__init__.py,sha256=S3a2xrOlxnNWD02To5K2ZwILsNEvSj1IvUAH8RjgOV4,791
80
+ src/linters/file_header/atemporal_detector.py,sha256=lerkiMwiUhYv3X3vxpm_88otZhSMG3DBgryhFyxe3As,3934
81
+ src/linters/file_header/base_parser.py,sha256=k6ymg1ocuesA6PH7NMDQOy0LTgSglu0wXed68fPaHxM,3382
82
+ src/linters/file_header/bash_parser.py,sha256=aRlIbR6x8IeYAj8w6a3eQzdZZivHB0oPg843CTnQm-Y,2481
83
+ src/linters/file_header/config.py,sha256=gdnZoJ-lEq8DACr6C2UKLorHiFCNdQspP_88FQBtoyc,4755
84
+ src/linters/file_header/css_parser.py,sha256=ijpGMixg2ZqNWWdiZjSNtMXCOhm6XDfSY7OU68B9fS8,2332
85
+ src/linters/file_header/field_validator.py,sha256=owA-ahjx0cUWBIqCxT0dMyGTABQA0b8HbdWbZPQk7pw,2769
86
+ src/linters/file_header/linter.py,sha256=9XFk9H-0QtXvMBgHfVM5PcI1igDcE8ylLARr0547XZ4,12532
87
+ src/linters/file_header/markdown_parser.py,sha256=4rNYrxuZbJz4LoSmv0U741Cv7wP9jftTl0Ty7mBDHRI,5323
88
+ src/linters/file_header/python_parser.py,sha256=RTOeEt1b3tCvFWbZIt89awQA37CUOSBIGagEYnayn-M,1432
89
+ src/linters/file_header/typescript_parser.py,sha256=R11Vkr6dUVaU8t90m8rrkMzODtBYk7u-TYFsMDRwzX8,2532
90
+ src/linters/file_header/violation_builder.py,sha256=HPYTmrcCmcO6Dx5dhmj85zZgEBM5EZqTgql-0CA0A0k,2745
91
+ src/linters/file_placement/__init__.py,sha256=vJ43GZujcbAk-K3DwfsQZ0J3yP_5G35CKssatLyntXk,862
92
+ src/linters/file_placement/config_loader.py,sha256=tLBeP9njYmtD0FNQsKkywMQJWrZaDBl7z_5sqVLzndc,2690
93
+ src/linters/file_placement/directory_matcher.py,sha256=1rxJtCEzqDYDQnscVX6pzk7gxCMD11pVIGaWcli-tHY,2742
94
+ src/linters/file_placement/linter.py,sha256=8mKCs20iEyUs3GaguHSHvMYdXJhXkoPTOfACM3kAFLs,15310
95
+ src/linters/file_placement/path_resolver.py,sha256=S6g7xOYsoSc0O_RDJh8j4Z2klcwzp16rSUfEAErGOTI,1972
96
+ src/linters/file_placement/pattern_matcher.py,sha256=56PCVL_4ajpTCnebHNUZKMJyAWeUOUHkeEwd4o3ofXQ,3183
97
+ src/linters/file_placement/pattern_validator.py,sha256=kg96qbN62kL0cTMncIFsoofiEJl64jWW4-0eK9_mD98,4247
98
+ src/linters/file_placement/rule_checker.py,sha256=OIN8v6uwn6nFiKBKCs2IRgczNt8e5ZyxCvYgkH2KB9Q,7846
99
+ src/linters/file_placement/violation_factory.py,sha256=NkQmBcgpa3g3W2ZdFZNQ5djLVP4x9OKs65d7F1rCKvM,6040
100
+ src/linters/lazy_ignores/__init__.py,sha256=qPwCC1Y-TPn6tNLTO4X6QsACaAiPMBpsIKlKh_dSz5k,1656
101
+ src/linters/lazy_ignores/config.py,sha256=ZM1W9L_pJtirmafGuwiKJyD29dAQIoiNOEMMXnyMATA,2464
102
+ src/linters/lazy_ignores/directive_utils.py,sha256=6Mc56hrcFe21LG7PMmIVEPDGKUF6F9dffhHkwixj1R8,3432
103
+ src/linters/lazy_ignores/header_parser.py,sha256=ADtVJUoJfoVx_zehoPpo6YLws0N-7c2PLK1PhNqn5Uk,6030
104
+ src/linters/lazy_ignores/linter.py,sha256=1bl3b2NliVurlb9bUcZUzd8sBZG6KJhRODD-Mb46HnE,6052
105
+ src/linters/lazy_ignores/matcher.py,sha256=jLE12aPPEz6tBejFrmXGXdCb7kzeFh8IYH5b_UjHqpQ,5201
106
+ src/linters/lazy_ignores/python_analyzer.py,sha256=JlblemVtYPPdcl9l654WSHIjy0HusXfA5TidzAm5hdc,7211
107
+ src/linters/lazy_ignores/rule_id_utils.py,sha256=sE7kAQFO6zGAR5JQN2OLLVjAdiVNPZONTb0sY01ri9w,5809
108
+ src/linters/lazy_ignores/skip_detector.py,sha256=9RK5uD4b2pAfdJsK1dHRTAWG4kKfAf1yfjc1OBsI14M,10461
109
+ src/linters/lazy_ignores/types.py,sha256=8i6mMXRXQkC8M8pH3uH63G-0x0PYWQvz0Ikb0F6cSx4,2204
110
+ src/linters/lazy_ignores/typescript_analyzer.py,sha256=k8R60Mcw9OxvHFFUPhUErrb-tbek7Q7PXXZDq_H0ioM,5322
111
+ src/linters/lazy_ignores/violation_builder.py,sha256=Z5RlCRJKkTfetkRVqsu1rfJRgBeiE9RTEu1djr-nmto,4203
112
+ src/linters/lbyl/__init__.py,sha256=rV7NcrP32ku1jp9kABWuvrstJNk4vfoH2IjWEwuwfos,1069
113
+ src/linters/lbyl/config.py,sha256=kWCjBRs1HEVf9oK4dHKHfjQX8KU-o5i-Jc_94ULDD4Y,2434
114
+ src/linters/lbyl/pattern_detectors/__init__.py,sha256=HXxpEmEiVD2K-IhDZ2qbR1mGfaxzOUj9Y3NFGSqcn_w,763
115
+ src/linters/lbyl/pattern_detectors/base.py,sha256=4LZYORaYFK0aj4xs4035hegj7cXJtaao0M95kM1QYTk,1272
116
+ src/linters/magic_numbers/__init__.py,sha256=17dkCUf0uiYLvpOZF01VDojj92NzxXZMtRhrSBUzsdc,1689
117
+ src/linters/magic_numbers/config.py,sha256=3zV6ZNezouBWUYy4kMw5PUlPNvIWXVwOxTz1moZfRoI,3270
118
+ src/linters/magic_numbers/context_analyzer.py,sha256=EgDyxxjvEqyD3FX0Fnxj5RcOPyvyVs_rYFxj2HOxYdg,7309
119
+ src/linters/magic_numbers/linter.py,sha256=CGo_35ujoCbNXbb0XI4KGCm5C9PCe_LzvXrgmvYN-I4,16736
120
+ src/linters/magic_numbers/python_analyzer.py,sha256=Ba-EODvAkUIOhqMFv86MxMlXqF20ngvgubiWN_U_IUk,2446
121
+ src/linters/magic_numbers/typescript_analyzer.py,sha256=-2YPmNWXHJN8R2siV3pJk_3Baj-A9nnvQRpU35YBKgs,7519
122
+ src/linters/magic_numbers/typescript_ignore_checker.py,sha256=9JWqtXd8KU_GCc_66KSZT2X7uQhNGpxE2ikOyjcLyao,2847
123
+ src/linters/magic_numbers/violation_builder.py,sha256=SqIQv3N9lpP2GRC1TC5InrvaEdrAq24V7Ec2Xj5olb0,3308
124
+ src/linters/method_property/__init__.py,sha256=t0C6zD5WLm-McgmvVajQJg4HQfOi7_4YzNLhKNA484w,1415
125
+ src/linters/method_property/config.py,sha256=_TbUc0piC1FeW3qsw4hYryzWPUOq_laia_QNLe1Y0aw,5529
126
+ src/linters/method_property/linter.py,sha256=9pgUEIK7ASZVwtsRVOg3U0k-GR0lHSLpUv_1TnIcZPA,12700
127
+ src/linters/method_property/python_analyzer.py,sha256=uyNUJHxACw01Z1Uz6lRUPe753MpzUOYuQ999rRt5ocE,15386
128
+ src/linters/method_property/violation_builder.py,sha256=A7SwZWlVG_7W5pJiHOvIroI2q4UuOQNryOYvmX3APLs,4251
129
+ src/linters/nesting/__init__.py,sha256=tszmyCEQMpEwB5H84WcAUfRYDQl7jpsn04es5DtAHsM,3200
130
+ src/linters/nesting/config.py,sha256=PfPA2wJn3i6HHXeM0qu6Qx-v1KJdRwlRkFOdpf7NhS8,2405
131
+ src/linters/nesting/linter.py,sha256=bn5aPlxKZNw3T2LsOSfZUK_shkxcsdUS_LtFVsGJexk,6622
132
+ src/linters/nesting/python_analyzer.py,sha256=__fs_NE9xA4NM1MDOHBGdrI0zICkTcgbVZtfT03cxF0,3230
133
+ src/linters/nesting/typescript_analyzer.py,sha256=70TsjP3EJWiHJ1ncMaveFE0e9_HdukWZr9LM0_MDXr8,3639
134
+ src/linters/nesting/typescript_function_extractor.py,sha256=dDB1otJnFMCo-Pj4mTr4gekKe7V4ArOAtX6gV0dBDc4,4494
135
+ src/linters/nesting/violation_builder.py,sha256=WwgR_Q9pfPJOoVuNZQL4MU3-Wc6RX_GGL5Rc2-RVlbI,4829
136
+ src/linters/performance/__init__.py,sha256=UXJwfTk2ZCBqdy0Rtqcn2rMffWXXauq14oNMPtJDO3o,3118
137
+ src/linters/performance/config.py,sha256=TmOdKtbrYx8POzFx_7fkgWfHcri2oBIXsm4V9FqkAek,1458
138
+ src/linters/performance/constants.py,sha256=WBiSCOMGNd01o5D0M95Lyx_liZAd3zBkIPu7ZXzcW3M,1127
139
+ src/linters/performance/linter.py,sha256=PrzHt5Y83eUlLWnZol7PPJsYRTPlIPv8-2FDJ-u88j8,5719
140
+ src/linters/performance/python_analyzer.py,sha256=EgRBzk-0CHS48RM2IlqilKkDsMadyjf1IumMUvfw2zs,13808
141
+ src/linters/performance/regex_analyzer.py,sha256=GZKf3jWWH28TxGlTqDeyd97JDbxjIT1pPO2jcgZwofY,10520
142
+ src/linters/performance/regex_linter.py,sha256=velv84oQ3TxP9cJlYmSts3_DjS1h-RINuZDmQx-YatU,5078
143
+ src/linters/performance/typescript_analyzer.py,sha256=t0jvLvVG97ffgC-4KhXobKa9iG9iGREOV5Kta7XsbFw,8740
144
+ src/linters/performance/violation_builder.py,sha256=q4fy3SVVrveyYYg9O2-MZfWjlHlKRP3llH1-8VQ6sUU,5752
145
+ src/linters/print_statements/__init__.py,sha256=yhvdTFSqBB4UDreeadTHKFzhayxeT6JkF3yxUHMgn1g,1893
146
+ src/linters/print_statements/config.py,sha256=rth3XmzqZGzXkRXDIVZswdtNOXIe1vIRaF46tVLKnyQ,3041
147
+ src/linters/print_statements/linter.py,sha256=CcKolaaHYJzhpxWXthYZ7xXhfTnxmyOIhuE40Ly3ofA,14351
148
+ src/linters/print_statements/python_analyzer.py,sha256=48IDRQEv861B90qCl5w8ASxcXR7juZ429YX2ST9n2ic,5028
149
+ src/linters/print_statements/typescript_analyzer.py,sha256=EFE3bjRENvCPEYmNNxZ4jiq1VCA-rEUAJ_VFWJApLqY,4935
150
+ src/linters/print_statements/violation_builder.py,sha256=Vs5m3AnWjrQqQHf6JJDaPP5B1V3YNl5pepG_oiTJnx4,3333
151
+ src/linters/srp/__init__.py,sha256=GbhaSB2_AYY-mWgG_ThbyAcDXoVZuB5eLzguoShf38w,3367
152
+ src/linters/srp/class_analyzer.py,sha256=tZ6xAT0Y7LkzeVlGHckQRhFdnssuetUYxTQdUHwBoCo,4045
153
+ src/linters/srp/config.py,sha256=hTxrM21HIOmg0sM6eJ_h3hRnuxqRZEgs13Ie97-PDr4,3397
154
+ src/linters/srp/heuristics.py,sha256=u2TTQS25hkVGcsFyJPFvmSqs-GpjL9ojsuVAUIgnSP0,3222
155
+ src/linters/srp/linter.py,sha256=ut6k8QDb4P2CXHl5Jnem0eFBN2lxcvsRyGnnQqMZL8w,7660
156
+ src/linters/srp/metrics_evaluator.py,sha256=Prk_dPacas_dX7spAzV0g734srmzT5u0t5d4mTG9g2o,1606
157
+ src/linters/srp/python_analyzer.py,sha256=PH27l38BFPNmj22Z10QDBioLDCZ4xpJFzBfTh_4XMZ4,3585
158
+ src/linters/srp/typescript_analyzer.py,sha256=Wi0P_G1v5AnZYtMN3sNm1iHva84-8Kep2LZ5RmAS4c4,2885
159
+ src/linters/srp/typescript_metrics_calculator.py,sha256=0RyHTWzBJbLUT3BqFuNc1wJVoS5nNI1Ph-tF_cp4EEI,3499
160
+ src/linters/srp/violation_builder.py,sha256=jaIjVtRYWUTs1SVJVwd0FxCojo0DxhPzfhyfMKmAroM,3881
161
+ src/linters/stateless_class/__init__.py,sha256=8ePpinmCD27PCz7ukwUWcNwo-ZgyvhOquns-U51MyiQ,1063
162
+ src/linters/stateless_class/config.py,sha256=u8Jt_xygIkuxZx2o0Uw_XFatOh11QhC9aN8lB_vfnLk,1993
163
+ src/linters/stateless_class/linter.py,sha256=Rm3fZfkyUOYeBodcLPUcMNKUHPuc5NgKOeDioGXyu_M,11338
164
+ src/linters/stateless_class/python_analyzer.py,sha256=psEx2pG-eZJfK9ViX4YaNCLFEXEqUoViA3rc32o_sVQ,7623
165
+ src/linters/stringly_typed/__init__.py,sha256=6r4IIykZ6mm551KQpRTSDp418EFqJQbuzjSfLHcwyBc,1511
166
+ src/linters/stringly_typed/config.py,sha256=-M7fwwr9axQsQcGtowVINC9Bh1cS1b2-KPxFb2GtL3M,7500
167
+ src/linters/stringly_typed/context_filter.py,sha256=JohTFvXiHKfVzUowRbsDrY37QngJDmhFfoxyoTzKriY,11422
168
+ src/linters/stringly_typed/function_call_violation_builder.py,sha256=RiuzeKmUzb6Fzdc4j8lXl4V-jf-0xae-5t7YcIaKTMY,4234
169
+ src/linters/stringly_typed/ignore_checker.py,sha256=sFV9NzsIhUWZe59h2X9JJv8yE3PWQLWAbhOG7Sl1Cs8,3438
170
+ src/linters/stringly_typed/ignore_utils.py,sha256=hw0wfnGFJQkysr1qi_vmykZPr02SNBElwVHFu55tB6M,1531
171
+ src/linters/stringly_typed/linter.py,sha256=mKokag3XCQl4QuhT25sekuiX2bMERDUdq9SNxqBpNCw,13440
172
+ src/linters/stringly_typed/python/__init__.py,sha256=y1ELj3We0_VeA0ygXd1DxudSWrZE5OhLGtZNkKwuomA,1359
173
+ src/linters/stringly_typed/python/analyzer.py,sha256=HAhSAMIXMr4FoSKE9ovrWuB0f3Zjp9Py3AoruArmaOU,12247
174
+ src/linters/stringly_typed/python/call_tracker.py,sha256=Re_BgUQQhWNTg9jM6XsyyqYxmeVhoqVJXPe2ITcNhhA,6046
175
+ src/linters/stringly_typed/python/comparison_tracker.py,sha256=MAYQF-IKYaJLV4DYwbAPz8E2fWFBoSMTxhCwnGFP5Jw,8384
176
+ src/linters/stringly_typed/python/condition_extractor.py,sha256=_Y-lSqmqfUYbJjmSjOcQDetCNwWvW6bAAdyHjRSVAek,4222
177
+ src/linters/stringly_typed/python/conditional_detector.py,sha256=js1wTcXwLOrHVAJY9dq0xqXVZVNXh5FSFQNvIZ4SUtg,6006
178
+ src/linters/stringly_typed/python/constants.py,sha256=IF3Y2W96hihHlr5HMenq5Q93uOo7KHzNazVVvhq3E58,671
179
+ src/linters/stringly_typed/python/match_analyzer.py,sha256=mgarAtnL79iOrK6xuiRE2Hw-9tR8ocrIRza6g0SorY8,2719
180
+ src/linters/stringly_typed/python/validation_detector.py,sha256=jzcowBcA7R_aKeXFf2sxI2yGGUoT1lGj1y7DTnmO88M,6288
181
+ src/linters/stringly_typed/python/variable_extractor.py,sha256=yYJQ5jTSMz94SD_0IMfCHMWcw1F57GmRuh9h51oiAEs,2769
182
+ src/linters/stringly_typed/storage.py,sha256=UoaY3Ejpb4k-7it15d_wzZFcEJNjWTClMJhkY6wkc9A,22021
183
+ src/linters/stringly_typed/storage_initializer.py,sha256=3-4St1ieN8325Xkb0HTS27dVyjjluM_X-bkwOfJW1JM,1548
184
+ src/linters/stringly_typed/typescript/__init__.py,sha256=lOgclS9wxLNyszfwVGbVxKfCkbTLX1pvskHzcADi5Xg,1121
185
+ src/linters/stringly_typed/typescript/analyzer.py,sha256=iNEk6wQJJfmJoRTXx29GEeqTpKzQ5TcNIimSuQPb6UU,6376
186
+ src/linters/stringly_typed/typescript/call_tracker.py,sha256=NPRpjqTe-Owi3_qJk_baojAazqaL6EsH4E2SIOsUAjU,11299
187
+ src/linters/stringly_typed/typescript/comparison_tracker.py,sha256=TiEldIqppu6i2XYd9a040HK0U4cy7IFf6Qjjlb93wAA,12573
188
+ src/linters/stringly_typed/violation_generator.py,sha256=aye60bShNnt8f6BPQwduTOLX97jAuy7Z7DLq9wzrFB4,14769
189
+ src/orchestrator/__init__.py,sha256=XXLDJq2oaB-TpP2Y97GRnde9EkITGuFCmuLrDfxI9nY,245
190
+ src/orchestrator/core.py,sha256=rt3h-YFgF1aAFeKvTa0PP7k_8zfwpeGIqrIxKuyckxY,17683
191
+ src/orchestrator/language_detector.py,sha256=rHyVMApit80NTTNyDH1ObD1usKD8LjGmH3DwqNAWYGc,2736
192
+ src/templates/thailint_config_template.yaml,sha256=57ZtLxnIoOHtR5Ejq3clb4nhY9J4n6h36XFb79ZZPlc,12020
193
+ src/utils/__init__.py,sha256=NiBtKeQ09Y3kuUzeN4O1JNfUIYPQDS2AP1l5ODq-Dec,125
194
+ src/utils/project_root.py,sha256=aaxUM-LQ1okrPClmZWPFd_D09W3V1ArgJiidEEp_eU8,6262
195
+ thailint-0.14.0.dist-info/METADATA,sha256=KPPgrXKUO3T8G03L2L-KgKTzztoB7VORKZuYx0DkYCo,7009
196
+ thailint-0.14.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
197
+ thailint-0.14.0.dist-info/entry_points.txt,sha256=DNoGUlxpaMFqxQDgHp1yeGqohOjdFR-kH19uHYi3OUY,72
198
+ thailint-0.14.0.dist-info/licenses/LICENSE,sha256=kxh1J0Sb62XvhNJ6MZsVNe8PqNVJ7LHRn_EWa-T3djw,1070
199
+ thailint-0.14.0.dist-info/RECORD,,