thailint 0.5.0__tar.gz → 0.7.0__tar.gz
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.
- {thailint-0.5.0 → thailint-0.7.0}/PKG-INFO +148 -2
- {thailint-0.5.0 → thailint-0.7.0}/README.md +147 -1
- {thailint-0.5.0 → thailint-0.7.0}/pyproject.toml +1 -1
- {thailint-0.5.0 → thailint-0.7.0}/src/cli.py +118 -1
- {thailint-0.5.0 → thailint-0.7.0}/src/core/cli_utils.py +16 -1
- {thailint-0.5.0 → thailint-0.7.0}/src/core/registry.py +1 -1
- thailint-0.7.0/src/formatters/__init__.py +22 -0
- thailint-0.7.0/src/formatters/sarif.py +202 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_header/atemporal_detector.py +11 -11
- thailint-0.7.0/src/linters/file_header/base_parser.py +89 -0
- thailint-0.7.0/src/linters/file_header/bash_parser.py +58 -0
- thailint-0.7.0/src/linters/file_header/config.py +126 -0
- thailint-0.7.0/src/linters/file_header/css_parser.py +70 -0
- thailint-0.7.0/src/linters/file_header/field_validator.py +75 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_header/linter.py +113 -121
- thailint-0.7.0/src/linters/file_header/markdown_parser.py +124 -0
- thailint-0.7.0/src/linters/file_header/python_parser.py +42 -0
- thailint-0.7.0/src/linters/file_header/typescript_parser.py +73 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_header/violation_builder.py +13 -12
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_placement/linter.py +9 -11
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/print_statements/config.py +7 -12
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/print_statements/linter.py +13 -15
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/print_statements/python_analyzer.py +8 -14
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/print_statements/typescript_analyzer.py +9 -14
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/print_statements/violation_builder.py +12 -14
- thailint-0.5.0/src/linters/file_header/config.py +0 -66
- thailint-0.5.0/src/linters/file_header/field_validator.py +0 -69
- thailint-0.5.0/src/linters/file_header/python_parser.py +0 -86
- {thailint-0.5.0 → thailint-0.7.0}/CHANGELOG.md +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/LICENSE +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/analyzers/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/analyzers/typescript_base.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/api.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/config.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/core/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/core/base.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/core/config_parser.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/core/linter_utils.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/core/rule_discovery.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/core/types.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/core/violation_builder.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linter_config/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linter_config/ignore.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linter_config/loader.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/base_token_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/block_filter.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/block_grouper.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/cache.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/cache_query.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/config.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/config_loader.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/deduplicator.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/duplicate_storage.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/file_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/inline_ignore.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/linter.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/python_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/storage_initializer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/token_hasher.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/typescript_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/violation_builder.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/violation_filter.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/dry/violation_generator.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_header/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_placement/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_placement/config_loader.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_placement/directory_matcher.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_placement/path_resolver.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_placement/pattern_matcher.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_placement/pattern_validator.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_placement/rule_checker.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/file_placement/violation_factory.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/magic_numbers/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/magic_numbers/config.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/magic_numbers/context_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/magic_numbers/linter.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/magic_numbers/python_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/magic_numbers/typescript_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/magic_numbers/violation_builder.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/nesting/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/nesting/config.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/nesting/linter.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/nesting/python_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/nesting/typescript_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/nesting/typescript_function_extractor.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/nesting/violation_builder.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/print_statements/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/srp/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/srp/class_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/srp/config.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/srp/heuristics.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/srp/linter.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/srp/metrics_evaluator.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/srp/python_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/srp/typescript_analyzer.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/srp/typescript_metrics_calculator.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/linters/srp/violation_builder.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/orchestrator/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/orchestrator/core.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/orchestrator/language_detector.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/templates/thailint_config_template.yaml +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/utils/__init__.py +0 -0
- {thailint-0.5.0 → thailint-0.7.0}/src/utils/project_root.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thailint
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: The AI Linter - Enterprise-grade linting and governance for AI-generated code across multiple languages
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -37,9 +37,10 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
|
|
38
38
|
[](https://opensource.org/licenses/MIT)
|
|
39
39
|
[](https://www.python.org/downloads/)
|
|
40
|
-
[](tests/)
|
|
41
41
|
[](htmlcov/)
|
|
42
42
|
[](https://thai-lint.readthedocs.io/en/latest/?badge=latest)
|
|
43
|
+
[](docs/sarif-output.md)
|
|
43
44
|
|
|
44
45
|
The AI Linter - Enterprise-ready linting and governance for AI-generated code across multiple languages.
|
|
45
46
|
|
|
@@ -73,6 +74,11 @@ thailint complements your existing linting stack by catching the patterns AI too
|
|
|
73
74
|
|
|
74
75
|
### Core Capabilities
|
|
75
76
|
- **File Placement Linting** - Enforce project structure and organization
|
|
77
|
+
- **File Header Linting** - Validate documentation headers in source files
|
|
78
|
+
- Python, TypeScript, JavaScript, Bash, Markdown, CSS support
|
|
79
|
+
- Mandatory field validation (Purpose, Scope, Overview)
|
|
80
|
+
- Atemporal language detection (no dates, "currently", "now")
|
|
81
|
+
- Language-specific header format parsing
|
|
76
82
|
- **Magic Numbers Linting** - Detect unnamed numeric literals that should be constants
|
|
77
83
|
- Python and TypeScript support with AST analysis
|
|
78
84
|
- Context-aware detection (ignores constants, test files, range() usage)
|
|
@@ -158,11 +164,17 @@ thailint dry .
|
|
|
158
164
|
# Check for magic numbers
|
|
159
165
|
thailint magic-numbers src/
|
|
160
166
|
|
|
167
|
+
# Check file headers
|
|
168
|
+
thailint file-header src/
|
|
169
|
+
|
|
161
170
|
# With config file
|
|
162
171
|
thailint dry --config .thailint.yaml src/
|
|
163
172
|
|
|
164
173
|
# JSON output for CI/CD
|
|
165
174
|
thailint dry --format json src/
|
|
175
|
+
|
|
176
|
+
# SARIF output for GitHub Code Scanning
|
|
177
|
+
thailint nesting --format sarif src/ > results.sarif
|
|
166
178
|
```
|
|
167
179
|
|
|
168
180
|
**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.
|
|
@@ -869,6 +881,136 @@ def get_ports(): # thailint: ignore[magic-numbers] - Standard ports
|
|
|
869
881
|
|
|
870
882
|
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
883
|
|
|
884
|
+
## File Header Linter
|
|
885
|
+
|
|
886
|
+
### Overview
|
|
887
|
+
|
|
888
|
+
The file header linter validates that source files have proper documentation headers containing required fields (Purpose, Scope, Overview) and don't use temporal language (dates, "currently", "now"). It enforces consistent documentation patterns across entire codebases.
|
|
889
|
+
|
|
890
|
+
### Why File Headers?
|
|
891
|
+
|
|
892
|
+
File headers serve as **self-documentation** that helps developers (and AI assistants) quickly understand:
|
|
893
|
+
|
|
894
|
+
- **Purpose**: What does this file do?
|
|
895
|
+
- **Scope**: What area of the system does it cover?
|
|
896
|
+
- **Dependencies**: What does it rely on?
|
|
897
|
+
- **Exports**: What does it provide to other modules?
|
|
898
|
+
|
|
899
|
+
### Quick Start
|
|
900
|
+
|
|
901
|
+
```bash
|
|
902
|
+
# Check file headers in current directory
|
|
903
|
+
thailint file-header .
|
|
904
|
+
|
|
905
|
+
# Check specific directory
|
|
906
|
+
thailint file-header src/
|
|
907
|
+
|
|
908
|
+
# Get JSON output
|
|
909
|
+
thailint file-header --format json src/
|
|
910
|
+
|
|
911
|
+
# Get SARIF output for CI/CD
|
|
912
|
+
thailint file-header --format sarif src/ > results.sarif
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
### Configuration
|
|
916
|
+
|
|
917
|
+
Add to `.thailint.yaml`:
|
|
918
|
+
|
|
919
|
+
```yaml
|
|
920
|
+
file-header:
|
|
921
|
+
enabled: true
|
|
922
|
+
mandatory_fields:
|
|
923
|
+
- Purpose
|
|
924
|
+
- Scope
|
|
925
|
+
- Overview
|
|
926
|
+
ignore:
|
|
927
|
+
- "**/__init__.py"
|
|
928
|
+
- "**/migrations/**"
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
### Example Violation
|
|
932
|
+
|
|
933
|
+
**Code without proper header:**
|
|
934
|
+
```python
|
|
935
|
+
import os
|
|
936
|
+
|
|
937
|
+
def process_data():
|
|
938
|
+
pass
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
**Violation messages:**
|
|
942
|
+
```
|
|
943
|
+
src/utils.py:1 - Missing mandatory field: Purpose
|
|
944
|
+
src/utils.py:1 - Missing mandatory field: Scope
|
|
945
|
+
src/utils.py:1 - Missing mandatory field: Overview
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
**Refactored with header:**
|
|
949
|
+
```python
|
|
950
|
+
"""
|
|
951
|
+
Purpose: Data processing utilities for ETL pipeline
|
|
952
|
+
|
|
953
|
+
Scope: Data transformation layer, used by batch processing jobs
|
|
954
|
+
|
|
955
|
+
Overview: Provides data transformation functions for the ETL pipeline.
|
|
956
|
+
Handles parsing, validation, and normalization of incoming data.
|
|
957
|
+
|
|
958
|
+
Dependencies: os, json
|
|
959
|
+
|
|
960
|
+
Exports: process_data(), validate_input(), transform_record()
|
|
961
|
+
"""
|
|
962
|
+
import os
|
|
963
|
+
|
|
964
|
+
def process_data():
|
|
965
|
+
pass
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
### Atemporal Language Detection
|
|
969
|
+
|
|
970
|
+
The linter detects temporal language that becomes stale:
|
|
971
|
+
|
|
972
|
+
**Temporal (flagged):**
|
|
973
|
+
```python
|
|
974
|
+
"""
|
|
975
|
+
Purpose: Authentication module
|
|
976
|
+
|
|
977
|
+
Overview: Currently handles OAuth. This was recently updated.
|
|
978
|
+
Created: 2024-01-15. Will be extended in the future.
|
|
979
|
+
"""
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
**Atemporal (correct):**
|
|
983
|
+
```python
|
|
984
|
+
"""
|
|
985
|
+
Purpose: Authentication module
|
|
986
|
+
|
|
987
|
+
Overview: Handles OAuth authentication with Google and GitHub.
|
|
988
|
+
Implements authorization code flow with PKCE for security.
|
|
989
|
+
"""
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
### Language Support
|
|
993
|
+
|
|
994
|
+
- **Python**: Module docstrings (`"""..."""`)
|
|
995
|
+
- **TypeScript/JavaScript**: JSDoc comments (`/** ... */`)
|
|
996
|
+
- **Bash**: Hash comments after shebang (`# ...`)
|
|
997
|
+
- **Markdown**: YAML frontmatter (`---...---`)
|
|
998
|
+
- **CSS/SCSS**: Block comments (`/* ... */`)
|
|
999
|
+
|
|
1000
|
+
### Ignoring Violations
|
|
1001
|
+
|
|
1002
|
+
```python
|
|
1003
|
+
# File-level ignore
|
|
1004
|
+
# thailint: ignore-file[file-header]
|
|
1005
|
+
|
|
1006
|
+
# Line-level ignore for atemporal violation
|
|
1007
|
+
"""
|
|
1008
|
+
Overview: Created 2024-01-15. # thailint: ignore[file-header]
|
|
1009
|
+
"""
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
See **[How to Ignore Violations](https://thai-lint.readthedocs.io/en/latest/how-to-ignore-violations/)** and **[File Header Linter Guide](https://thai-lint.readthedocs.io/en/latest/file-header-linter/)** for complete documentation.
|
|
1013
|
+
|
|
872
1014
|
## Pre-commit Hooks
|
|
873
1015
|
|
|
874
1016
|
Automate code quality checks before every commit and push with pre-commit hooks.
|
|
@@ -1153,11 +1295,13 @@ docker run --rm -v /path/to/workspace:/workspace \
|
|
|
1153
1295
|
- **[CLI Reference](https://thai-lint.readthedocs.io/en/latest/cli-reference/)** - All CLI commands and options
|
|
1154
1296
|
- **[Deployment Modes](https://thai-lint.readthedocs.io/en/latest/deployment-modes/)** - CLI, Library, and Docker usage
|
|
1155
1297
|
- **[File Placement Linter](https://thai-lint.readthedocs.io/en/latest/file-placement-linter/)** - Detailed linter guide
|
|
1298
|
+
- **[File Header Linter](https://thai-lint.readthedocs.io/en/latest/file-header-linter/)** - File header validation guide
|
|
1156
1299
|
- **[Magic Numbers Linter](https://thai-lint.readthedocs.io/en/latest/magic-numbers-linter/)** - Magic numbers detection guide
|
|
1157
1300
|
- **[Nesting Depth Linter](https://thai-lint.readthedocs.io/en/latest/nesting-linter/)** - Nesting depth analysis guide
|
|
1158
1301
|
- **[SRP Linter](https://thai-lint.readthedocs.io/en/latest/srp-linter/)** - Single Responsibility Principle guide
|
|
1159
1302
|
- **[DRY Linter](https://thai-lint.readthedocs.io/en/latest/dry-linter/)** - Duplicate code detection guide
|
|
1160
1303
|
- **[Pre-commit Hooks](https://thai-lint.readthedocs.io/en/latest/pre-commit-hooks/)** - Automated quality checks
|
|
1304
|
+
- **[SARIF Output Guide](docs/sarif-output.md)** - SARIF format for GitHub Code Scanning and CI/CD
|
|
1161
1305
|
- **[Publishing Guide](https://thai-lint.readthedocs.io/en/latest/releasing/)** - Release and publishing workflow
|
|
1162
1306
|
- **[Publishing Checklist](https://thai-lint.readthedocs.io/en/latest/publishing-checklist/)** - Post-publication validation
|
|
1163
1307
|
|
|
@@ -1168,6 +1312,8 @@ See [`examples/`](examples/) directory for working code:
|
|
|
1168
1312
|
- **[basic_usage.py](examples/basic_usage.py)** - Simple library API usage
|
|
1169
1313
|
- **[advanced_usage.py](examples/advanced_usage.py)** - Advanced patterns and workflows
|
|
1170
1314
|
- **[ci_integration.py](examples/ci_integration.py)** - CI/CD integration example
|
|
1315
|
+
- **[sarif_usage.py](examples/sarif_usage.py)** - SARIF output format examples
|
|
1316
|
+
- **[file_header_usage.py](examples/file_header_usage.py)** - File header validation examples
|
|
1171
1317
|
|
|
1172
1318
|
## Project Structure
|
|
1173
1319
|
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
4
|
[](https://www.python.org/downloads/)
|
|
5
|
-
[](tests/)
|
|
6
6
|
[](htmlcov/)
|
|
7
7
|
[](https://thai-lint.readthedocs.io/en/latest/?badge=latest)
|
|
8
|
+
[](docs/sarif-output.md)
|
|
8
9
|
|
|
9
10
|
The AI Linter - Enterprise-ready linting and governance for AI-generated code across multiple languages.
|
|
10
11
|
|
|
@@ -38,6 +39,11 @@ thailint complements your existing linting stack by catching the patterns AI too
|
|
|
38
39
|
|
|
39
40
|
### Core Capabilities
|
|
40
41
|
- **File Placement Linting** - Enforce project structure and organization
|
|
42
|
+
- **File Header Linting** - Validate documentation headers in source files
|
|
43
|
+
- Python, TypeScript, JavaScript, Bash, Markdown, CSS support
|
|
44
|
+
- Mandatory field validation (Purpose, Scope, Overview)
|
|
45
|
+
- Atemporal language detection (no dates, "currently", "now")
|
|
46
|
+
- Language-specific header format parsing
|
|
41
47
|
- **Magic Numbers Linting** - Detect unnamed numeric literals that should be constants
|
|
42
48
|
- Python and TypeScript support with AST analysis
|
|
43
49
|
- Context-aware detection (ignores constants, test files, range() usage)
|
|
@@ -123,11 +129,17 @@ thailint dry .
|
|
|
123
129
|
# Check for magic numbers
|
|
124
130
|
thailint magic-numbers src/
|
|
125
131
|
|
|
132
|
+
# Check file headers
|
|
133
|
+
thailint file-header src/
|
|
134
|
+
|
|
126
135
|
# With config file
|
|
127
136
|
thailint dry --config .thailint.yaml src/
|
|
128
137
|
|
|
129
138
|
# JSON output for CI/CD
|
|
130
139
|
thailint dry --format json src/
|
|
140
|
+
|
|
141
|
+
# SARIF output for GitHub Code Scanning
|
|
142
|
+
thailint nesting --format sarif src/ > results.sarif
|
|
131
143
|
```
|
|
132
144
|
|
|
133
145
|
**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.
|
|
@@ -834,6 +846,136 @@ def get_ports(): # thailint: ignore[magic-numbers] - Standard ports
|
|
|
834
846
|
|
|
835
847
|
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.
|
|
836
848
|
|
|
849
|
+
## File Header Linter
|
|
850
|
+
|
|
851
|
+
### Overview
|
|
852
|
+
|
|
853
|
+
The file header linter validates that source files have proper documentation headers containing required fields (Purpose, Scope, Overview) and don't use temporal language (dates, "currently", "now"). It enforces consistent documentation patterns across entire codebases.
|
|
854
|
+
|
|
855
|
+
### Why File Headers?
|
|
856
|
+
|
|
857
|
+
File headers serve as **self-documentation** that helps developers (and AI assistants) quickly understand:
|
|
858
|
+
|
|
859
|
+
- **Purpose**: What does this file do?
|
|
860
|
+
- **Scope**: What area of the system does it cover?
|
|
861
|
+
- **Dependencies**: What does it rely on?
|
|
862
|
+
- **Exports**: What does it provide to other modules?
|
|
863
|
+
|
|
864
|
+
### Quick Start
|
|
865
|
+
|
|
866
|
+
```bash
|
|
867
|
+
# Check file headers in current directory
|
|
868
|
+
thailint file-header .
|
|
869
|
+
|
|
870
|
+
# Check specific directory
|
|
871
|
+
thailint file-header src/
|
|
872
|
+
|
|
873
|
+
# Get JSON output
|
|
874
|
+
thailint file-header --format json src/
|
|
875
|
+
|
|
876
|
+
# Get SARIF output for CI/CD
|
|
877
|
+
thailint file-header --format sarif src/ > results.sarif
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
### Configuration
|
|
881
|
+
|
|
882
|
+
Add to `.thailint.yaml`:
|
|
883
|
+
|
|
884
|
+
```yaml
|
|
885
|
+
file-header:
|
|
886
|
+
enabled: true
|
|
887
|
+
mandatory_fields:
|
|
888
|
+
- Purpose
|
|
889
|
+
- Scope
|
|
890
|
+
- Overview
|
|
891
|
+
ignore:
|
|
892
|
+
- "**/__init__.py"
|
|
893
|
+
- "**/migrations/**"
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
### Example Violation
|
|
897
|
+
|
|
898
|
+
**Code without proper header:**
|
|
899
|
+
```python
|
|
900
|
+
import os
|
|
901
|
+
|
|
902
|
+
def process_data():
|
|
903
|
+
pass
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
**Violation messages:**
|
|
907
|
+
```
|
|
908
|
+
src/utils.py:1 - Missing mandatory field: Purpose
|
|
909
|
+
src/utils.py:1 - Missing mandatory field: Scope
|
|
910
|
+
src/utils.py:1 - Missing mandatory field: Overview
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
**Refactored with header:**
|
|
914
|
+
```python
|
|
915
|
+
"""
|
|
916
|
+
Purpose: Data processing utilities for ETL pipeline
|
|
917
|
+
|
|
918
|
+
Scope: Data transformation layer, used by batch processing jobs
|
|
919
|
+
|
|
920
|
+
Overview: Provides data transformation functions for the ETL pipeline.
|
|
921
|
+
Handles parsing, validation, and normalization of incoming data.
|
|
922
|
+
|
|
923
|
+
Dependencies: os, json
|
|
924
|
+
|
|
925
|
+
Exports: process_data(), validate_input(), transform_record()
|
|
926
|
+
"""
|
|
927
|
+
import os
|
|
928
|
+
|
|
929
|
+
def process_data():
|
|
930
|
+
pass
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
### Atemporal Language Detection
|
|
934
|
+
|
|
935
|
+
The linter detects temporal language that becomes stale:
|
|
936
|
+
|
|
937
|
+
**Temporal (flagged):**
|
|
938
|
+
```python
|
|
939
|
+
"""
|
|
940
|
+
Purpose: Authentication module
|
|
941
|
+
|
|
942
|
+
Overview: Currently handles OAuth. This was recently updated.
|
|
943
|
+
Created: 2024-01-15. Will be extended in the future.
|
|
944
|
+
"""
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
**Atemporal (correct):**
|
|
948
|
+
```python
|
|
949
|
+
"""
|
|
950
|
+
Purpose: Authentication module
|
|
951
|
+
|
|
952
|
+
Overview: Handles OAuth authentication with Google and GitHub.
|
|
953
|
+
Implements authorization code flow with PKCE for security.
|
|
954
|
+
"""
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
### Language Support
|
|
958
|
+
|
|
959
|
+
- **Python**: Module docstrings (`"""..."""`)
|
|
960
|
+
- **TypeScript/JavaScript**: JSDoc comments (`/** ... */`)
|
|
961
|
+
- **Bash**: Hash comments after shebang (`# ...`)
|
|
962
|
+
- **Markdown**: YAML frontmatter (`---...---`)
|
|
963
|
+
- **CSS/SCSS**: Block comments (`/* ... */`)
|
|
964
|
+
|
|
965
|
+
### Ignoring Violations
|
|
966
|
+
|
|
967
|
+
```python
|
|
968
|
+
# File-level ignore
|
|
969
|
+
# thailint: ignore-file[file-header]
|
|
970
|
+
|
|
971
|
+
# Line-level ignore for atemporal violation
|
|
972
|
+
"""
|
|
973
|
+
Overview: Created 2024-01-15. # thailint: ignore[file-header]
|
|
974
|
+
"""
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
See **[How to Ignore Violations](https://thai-lint.readthedocs.io/en/latest/how-to-ignore-violations/)** and **[File Header Linter Guide](https://thai-lint.readthedocs.io/en/latest/file-header-linter/)** for complete documentation.
|
|
978
|
+
|
|
837
979
|
## Pre-commit Hooks
|
|
838
980
|
|
|
839
981
|
Automate code quality checks before every commit and push with pre-commit hooks.
|
|
@@ -1118,11 +1260,13 @@ docker run --rm -v /path/to/workspace:/workspace \
|
|
|
1118
1260
|
- **[CLI Reference](https://thai-lint.readthedocs.io/en/latest/cli-reference/)** - All CLI commands and options
|
|
1119
1261
|
- **[Deployment Modes](https://thai-lint.readthedocs.io/en/latest/deployment-modes/)** - CLI, Library, and Docker usage
|
|
1120
1262
|
- **[File Placement Linter](https://thai-lint.readthedocs.io/en/latest/file-placement-linter/)** - Detailed linter guide
|
|
1263
|
+
- **[File Header Linter](https://thai-lint.readthedocs.io/en/latest/file-header-linter/)** - File header validation guide
|
|
1121
1264
|
- **[Magic Numbers Linter](https://thai-lint.readthedocs.io/en/latest/magic-numbers-linter/)** - Magic numbers detection guide
|
|
1122
1265
|
- **[Nesting Depth Linter](https://thai-lint.readthedocs.io/en/latest/nesting-linter/)** - Nesting depth analysis guide
|
|
1123
1266
|
- **[SRP Linter](https://thai-lint.readthedocs.io/en/latest/srp-linter/)** - Single Responsibility Principle guide
|
|
1124
1267
|
- **[DRY Linter](https://thai-lint.readthedocs.io/en/latest/dry-linter/)** - Duplicate code detection guide
|
|
1125
1268
|
- **[Pre-commit Hooks](https://thai-lint.readthedocs.io/en/latest/pre-commit-hooks/)** - Automated quality checks
|
|
1269
|
+
- **[SARIF Output Guide](docs/sarif-output.md)** - SARIF format for GitHub Code Scanning and CI/CD
|
|
1126
1270
|
- **[Publishing Guide](https://thai-lint.readthedocs.io/en/latest/releasing/)** - Release and publishing workflow
|
|
1127
1271
|
- **[Publishing Checklist](https://thai-lint.readthedocs.io/en/latest/publishing-checklist/)** - Post-publication validation
|
|
1128
1272
|
|
|
@@ -1133,6 +1277,8 @@ See [`examples/`](examples/) directory for working code:
|
|
|
1133
1277
|
- **[basic_usage.py](examples/basic_usage.py)** - Simple library API usage
|
|
1134
1278
|
- **[advanced_usage.py](examples/advanced_usage.py)** - Advanced patterns and workflows
|
|
1135
1279
|
- **[ci_integration.py](examples/ci_integration.py)** - CI/CD integration example
|
|
1280
|
+
- **[sarif_usage.py](examples/sarif_usage.py)** - SARIF output format examples
|
|
1281
|
+
- **[file_header_usage.py](examples/file_header_usage.py)** - File header validation examples
|
|
1136
1282
|
|
|
1137
1283
|
## Project Structure
|
|
1138
1284
|
|
|
@@ -17,7 +17,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
17
17
|
|
|
18
18
|
[tool.poetry]
|
|
19
19
|
name = "thailint"
|
|
20
|
-
version = "0.
|
|
20
|
+
version = "0.7.0"
|
|
21
21
|
description = "The AI Linter - Enterprise-grade linting and governance for AI-generated code across multiple languages"
|
|
22
22
|
authors = ["Steve Jackson"]
|
|
23
23
|
license = "MIT"
|
|
@@ -38,7 +38,11 @@ logger = logging.getLogger(__name__)
|
|
|
38
38
|
def format_option(func):
|
|
39
39
|
"""Add --format option to a command for output format selection."""
|
|
40
40
|
return click.option(
|
|
41
|
-
"--format",
|
|
41
|
+
"--format",
|
|
42
|
+
"-f",
|
|
43
|
+
type=click.Choice(["text", "json", "sarif"]),
|
|
44
|
+
default="text",
|
|
45
|
+
help="Output format",
|
|
42
46
|
)(func)
|
|
43
47
|
|
|
44
48
|
|
|
@@ -1661,5 +1665,118 @@ def _execute_print_statements_lint( # pylint: disable=too-many-arguments,too-ma
|
|
|
1661
1665
|
sys.exit(1 if print_statements_violations else 0)
|
|
1662
1666
|
|
|
1663
1667
|
|
|
1668
|
+
# File Header Command Helper Functions
|
|
1669
|
+
|
|
1670
|
+
|
|
1671
|
+
def _setup_file_header_orchestrator(
|
|
1672
|
+
path_objs: list[Path], config_file: str | None, verbose: bool, project_root: Path | None = None
|
|
1673
|
+
):
|
|
1674
|
+
"""Set up orchestrator for file-header command."""
|
|
1675
|
+
from src.orchestrator.core import Orchestrator
|
|
1676
|
+
from src.utils.project_root import get_project_root
|
|
1677
|
+
|
|
1678
|
+
# Use provided project_root or fall back to auto-detection
|
|
1679
|
+
if project_root is None:
|
|
1680
|
+
first_path = path_objs[0] if path_objs else Path.cwd()
|
|
1681
|
+
search_start = first_path if first_path.is_dir() else first_path.parent
|
|
1682
|
+
project_root = get_project_root(search_start)
|
|
1683
|
+
|
|
1684
|
+
orchestrator = Orchestrator(project_root=project_root)
|
|
1685
|
+
|
|
1686
|
+
if config_file:
|
|
1687
|
+
_load_config_file(orchestrator, config_file, verbose)
|
|
1688
|
+
|
|
1689
|
+
return orchestrator
|
|
1690
|
+
|
|
1691
|
+
|
|
1692
|
+
def _run_file_header_lint(orchestrator, path_objs: list[Path], recursive: bool):
|
|
1693
|
+
"""Execute file-header lint on files or directories."""
|
|
1694
|
+
all_violations = _execute_linting_on_paths(orchestrator, path_objs, recursive)
|
|
1695
|
+
return [v for v in all_violations if "file-header" in v.rule_id]
|
|
1696
|
+
|
|
1697
|
+
|
|
1698
|
+
@cli.command("file-header")
|
|
1699
|
+
@click.argument("paths", nargs=-1, type=click.Path())
|
|
1700
|
+
@click.option("--config", "-c", "config_file", type=click.Path(), help="Path to config file")
|
|
1701
|
+
@format_option
|
|
1702
|
+
@click.option("--recursive/--no-recursive", default=True, help="Scan directories recursively")
|
|
1703
|
+
@click.pass_context
|
|
1704
|
+
def file_header(
|
|
1705
|
+
ctx,
|
|
1706
|
+
paths: tuple[str, ...],
|
|
1707
|
+
config_file: str | None,
|
|
1708
|
+
format: str,
|
|
1709
|
+
recursive: bool,
|
|
1710
|
+
):
|
|
1711
|
+
"""Check file headers for mandatory fields and atemporal language.
|
|
1712
|
+
|
|
1713
|
+
Validates that source files have proper documentation headers containing
|
|
1714
|
+
required fields (Purpose, Scope, Overview, etc.) and don't use temporal
|
|
1715
|
+
language (dates, "currently", "now", etc.).
|
|
1716
|
+
|
|
1717
|
+
Supports Python, TypeScript, JavaScript, Bash, Markdown, and CSS files.
|
|
1718
|
+
|
|
1719
|
+
PATHS: Files or directories to lint (defaults to current directory if none provided)
|
|
1720
|
+
|
|
1721
|
+
Examples:
|
|
1722
|
+
|
|
1723
|
+
\b
|
|
1724
|
+
# Check current directory (all files recursively)
|
|
1725
|
+
thai-lint file-header
|
|
1726
|
+
|
|
1727
|
+
\b
|
|
1728
|
+
# Check specific directory
|
|
1729
|
+
thai-lint file-header src/
|
|
1730
|
+
|
|
1731
|
+
\b
|
|
1732
|
+
# Check single file
|
|
1733
|
+
thai-lint file-header src/cli.py
|
|
1734
|
+
|
|
1735
|
+
\b
|
|
1736
|
+
# Check multiple files
|
|
1737
|
+
thai-lint file-header src/cli.py src/api.py tests/
|
|
1738
|
+
|
|
1739
|
+
\b
|
|
1740
|
+
# Get JSON output
|
|
1741
|
+
thai-lint file-header --format json .
|
|
1742
|
+
|
|
1743
|
+
\b
|
|
1744
|
+
# Get SARIF output for CI/CD integration
|
|
1745
|
+
thai-lint file-header --format sarif src/
|
|
1746
|
+
|
|
1747
|
+
\b
|
|
1748
|
+
# Use custom config file
|
|
1749
|
+
thai-lint file-header --config .thailint.yaml src/
|
|
1750
|
+
"""
|
|
1751
|
+
verbose = ctx.obj.get("verbose", False)
|
|
1752
|
+
project_root = _get_project_root_from_context(ctx)
|
|
1753
|
+
|
|
1754
|
+
# Default to current directory if no paths provided
|
|
1755
|
+
if not paths:
|
|
1756
|
+
paths = (".",)
|
|
1757
|
+
|
|
1758
|
+
path_objs = [Path(p) for p in paths]
|
|
1759
|
+
|
|
1760
|
+
try:
|
|
1761
|
+
_execute_file_header_lint(path_objs, config_file, format, recursive, verbose, project_root)
|
|
1762
|
+
except Exception as e:
|
|
1763
|
+
_handle_linting_error(e, verbose)
|
|
1764
|
+
|
|
1765
|
+
|
|
1766
|
+
def _execute_file_header_lint( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
1767
|
+
path_objs, config_file, format, recursive, verbose, project_root=None
|
|
1768
|
+
):
|
|
1769
|
+
"""Execute file-header lint."""
|
|
1770
|
+
_validate_paths_exist(path_objs)
|
|
1771
|
+
orchestrator = _setup_file_header_orchestrator(path_objs, config_file, verbose, project_root)
|
|
1772
|
+
file_header_violations = _run_file_header_lint(orchestrator, path_objs, recursive)
|
|
1773
|
+
|
|
1774
|
+
if verbose:
|
|
1775
|
+
logger.info(f"Found {len(file_header_violations)} file header violation(s)")
|
|
1776
|
+
|
|
1777
|
+
format_violations(file_header_violations, format)
|
|
1778
|
+
sys.exit(1 if file_header_violations else 0)
|
|
1779
|
+
|
|
1780
|
+
|
|
1664
1781
|
if __name__ == "__main__":
|
|
1665
1782
|
cli()
|
|
@@ -146,10 +146,12 @@ def format_violations(violations: list, output_format: str) -> None:
|
|
|
146
146
|
|
|
147
147
|
Args:
|
|
148
148
|
violations: List of violation objects with rule_id, file_path, line, column, message, severity
|
|
149
|
-
output_format: Output format ("text" or "
|
|
149
|
+
output_format: Output format ("text", "json", or "sarif")
|
|
150
150
|
"""
|
|
151
151
|
if output_format == "json":
|
|
152
152
|
_output_json(violations)
|
|
153
|
+
elif output_format == "sarif":
|
|
154
|
+
_output_sarif(violations)
|
|
153
155
|
else:
|
|
154
156
|
_output_text(violations)
|
|
155
157
|
|
|
@@ -177,6 +179,19 @@ def _output_json(violations: list) -> None:
|
|
|
177
179
|
click.echo(json.dumps(output, indent=2))
|
|
178
180
|
|
|
179
181
|
|
|
182
|
+
def _output_sarif(violations: list) -> None:
|
|
183
|
+
"""Output violations in SARIF v2.1.0 format.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
violations: List of violation objects
|
|
187
|
+
"""
|
|
188
|
+
from src.formatters.sarif import SarifFormatter
|
|
189
|
+
|
|
190
|
+
formatter = SarifFormatter()
|
|
191
|
+
sarif_doc = formatter.format(violations)
|
|
192
|
+
click.echo(json.dumps(sarif_doc, indent=2))
|
|
193
|
+
|
|
194
|
+
|
|
180
195
|
def _output_text(violations: list) -> None:
|
|
181
196
|
"""Output violations in human-readable text format.
|
|
182
197
|
|
|
@@ -6,7 +6,7 @@ Scope: Dynamic rule management and discovery across all linter plugin packages
|
|
|
6
6
|
Overview: Implements rule registry that maintains a collection of registered linting rules indexed
|
|
7
7
|
by rule_id. Provides methods to register individual rules, retrieve rules by identifier, list
|
|
8
8
|
all available rules, and discover rules from packages using the RuleDiscovery helper. Enables
|
|
9
|
-
the extensible plugin architecture by allowing
|
|
9
|
+
the extensible plugin architecture by allowing dynamic rule registration without framework
|
|
10
10
|
modifications. Validates rule uniqueness and handles registration errors gracefully.
|
|
11
11
|
|
|
12
12
|
Dependencies: BaseLintRule, RuleDiscovery
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: SARIF formatter package for thai-lint output
|
|
3
|
+
|
|
4
|
+
Scope: SARIF v2.1.0 formatter implementation and package exports
|
|
5
|
+
|
|
6
|
+
Overview: Formatters package providing SARIF (Static Analysis Results Interchange Format) v2.1.0
|
|
7
|
+
output generation from thai-lint Violation objects. Enables integration with GitHub Code
|
|
8
|
+
Scanning, Azure DevOps, VS Code SARIF Viewer, and other industry-standard CI/CD platforms.
|
|
9
|
+
Provides the SarifFormatter class for converting violations to SARIF JSON documents.
|
|
10
|
+
|
|
11
|
+
Dependencies: sarif module for SarifFormatter class
|
|
12
|
+
|
|
13
|
+
Exports: SarifFormatter class from sarif.py module
|
|
14
|
+
|
|
15
|
+
Interfaces: from src.formatters.sarif import SarifFormatter
|
|
16
|
+
|
|
17
|
+
Implementation: Package initialization with SarifFormatter export
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from src.formatters.sarif import SarifFormatter
|
|
21
|
+
|
|
22
|
+
__all__ = ["SarifFormatter"]
|