thailint 0.4.6__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.
Files changed (106) hide show
  1. {thailint-0.4.6 → thailint-0.7.0}/PKG-INFO +149 -3
  2. {thailint-0.4.6 → thailint-0.7.0}/README.md +148 -2
  3. {thailint-0.4.6 → thailint-0.7.0}/pyproject.toml +1 -1
  4. {thailint-0.4.6 → thailint-0.7.0}/src/cli.py +228 -1
  5. {thailint-0.4.6 → thailint-0.7.0}/src/core/cli_utils.py +16 -1
  6. {thailint-0.4.6 → thailint-0.7.0}/src/core/registry.py +1 -1
  7. thailint-0.7.0/src/formatters/__init__.py +22 -0
  8. thailint-0.7.0/src/formatters/sarif.py +202 -0
  9. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_header/atemporal_detector.py +11 -11
  10. thailint-0.7.0/src/linters/file_header/base_parser.py +89 -0
  11. thailint-0.7.0/src/linters/file_header/bash_parser.py +58 -0
  12. thailint-0.7.0/src/linters/file_header/config.py +126 -0
  13. thailint-0.7.0/src/linters/file_header/css_parser.py +70 -0
  14. thailint-0.7.0/src/linters/file_header/field_validator.py +75 -0
  15. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_header/linter.py +113 -121
  16. thailint-0.7.0/src/linters/file_header/markdown_parser.py +124 -0
  17. thailint-0.7.0/src/linters/file_header/python_parser.py +42 -0
  18. thailint-0.7.0/src/linters/file_header/typescript_parser.py +73 -0
  19. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_header/violation_builder.py +13 -12
  20. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_placement/linter.py +9 -11
  21. {thailint-0.4.6 → thailint-0.7.0}/src/linters/magic_numbers/typescript_analyzer.py +1 -0
  22. {thailint-0.4.6 → thailint-0.7.0}/src/linters/nesting/typescript_analyzer.py +1 -0
  23. thailint-0.7.0/src/linters/print_statements/__init__.py +53 -0
  24. thailint-0.7.0/src/linters/print_statements/config.py +78 -0
  25. thailint-0.7.0/src/linters/print_statements/linter.py +428 -0
  26. thailint-0.7.0/src/linters/print_statements/python_analyzer.py +149 -0
  27. thailint-0.7.0/src/linters/print_statements/typescript_analyzer.py +130 -0
  28. thailint-0.7.0/src/linters/print_statements/violation_builder.py +96 -0
  29. {thailint-0.4.6 → thailint-0.7.0}/src/templates/thailint_config_template.yaml +26 -0
  30. thailint-0.4.6/src/linters/file_header/config.py +0 -66
  31. thailint-0.4.6/src/linters/file_header/field_validator.py +0 -69
  32. thailint-0.4.6/src/linters/file_header/python_parser.py +0 -86
  33. {thailint-0.4.6 → thailint-0.7.0}/CHANGELOG.md +0 -0
  34. {thailint-0.4.6 → thailint-0.7.0}/LICENSE +0 -0
  35. {thailint-0.4.6 → thailint-0.7.0}/src/__init__.py +0 -0
  36. {thailint-0.4.6 → thailint-0.7.0}/src/analyzers/__init__.py +0 -0
  37. {thailint-0.4.6 → thailint-0.7.0}/src/analyzers/typescript_base.py +0 -0
  38. {thailint-0.4.6 → thailint-0.7.0}/src/api.py +0 -0
  39. {thailint-0.4.6 → thailint-0.7.0}/src/config.py +0 -0
  40. {thailint-0.4.6 → thailint-0.7.0}/src/core/__init__.py +0 -0
  41. {thailint-0.4.6 → thailint-0.7.0}/src/core/base.py +0 -0
  42. {thailint-0.4.6 → thailint-0.7.0}/src/core/config_parser.py +0 -0
  43. {thailint-0.4.6 → thailint-0.7.0}/src/core/linter_utils.py +0 -0
  44. {thailint-0.4.6 → thailint-0.7.0}/src/core/rule_discovery.py +0 -0
  45. {thailint-0.4.6 → thailint-0.7.0}/src/core/types.py +0 -0
  46. {thailint-0.4.6 → thailint-0.7.0}/src/core/violation_builder.py +0 -0
  47. {thailint-0.4.6 → thailint-0.7.0}/src/linter_config/__init__.py +0 -0
  48. {thailint-0.4.6 → thailint-0.7.0}/src/linter_config/ignore.py +0 -0
  49. {thailint-0.4.6 → thailint-0.7.0}/src/linter_config/loader.py +0 -0
  50. {thailint-0.4.6 → thailint-0.7.0}/src/linters/__init__.py +0 -0
  51. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/__init__.py +0 -0
  52. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/base_token_analyzer.py +0 -0
  53. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/block_filter.py +0 -0
  54. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/block_grouper.py +0 -0
  55. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/cache.py +0 -0
  56. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/cache_query.py +0 -0
  57. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/config.py +0 -0
  58. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/config_loader.py +0 -0
  59. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/deduplicator.py +0 -0
  60. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/duplicate_storage.py +0 -0
  61. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/file_analyzer.py +0 -0
  62. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/inline_ignore.py +0 -0
  63. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/linter.py +0 -0
  64. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/python_analyzer.py +0 -0
  65. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/storage_initializer.py +0 -0
  66. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/token_hasher.py +0 -0
  67. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/typescript_analyzer.py +0 -0
  68. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/violation_builder.py +0 -0
  69. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/violation_filter.py +0 -0
  70. {thailint-0.4.6 → thailint-0.7.0}/src/linters/dry/violation_generator.py +0 -0
  71. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_header/__init__.py +0 -0
  72. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_placement/__init__.py +0 -0
  73. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_placement/config_loader.py +0 -0
  74. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_placement/directory_matcher.py +0 -0
  75. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_placement/path_resolver.py +0 -0
  76. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_placement/pattern_matcher.py +0 -0
  77. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_placement/pattern_validator.py +0 -0
  78. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_placement/rule_checker.py +0 -0
  79. {thailint-0.4.6 → thailint-0.7.0}/src/linters/file_placement/violation_factory.py +0 -0
  80. {thailint-0.4.6 → thailint-0.7.0}/src/linters/magic_numbers/__init__.py +0 -0
  81. {thailint-0.4.6 → thailint-0.7.0}/src/linters/magic_numbers/config.py +0 -0
  82. {thailint-0.4.6 → thailint-0.7.0}/src/linters/magic_numbers/context_analyzer.py +0 -0
  83. {thailint-0.4.6 → thailint-0.7.0}/src/linters/magic_numbers/linter.py +0 -0
  84. {thailint-0.4.6 → thailint-0.7.0}/src/linters/magic_numbers/python_analyzer.py +0 -0
  85. {thailint-0.4.6 → thailint-0.7.0}/src/linters/magic_numbers/violation_builder.py +0 -0
  86. {thailint-0.4.6 → thailint-0.7.0}/src/linters/nesting/__init__.py +0 -0
  87. {thailint-0.4.6 → thailint-0.7.0}/src/linters/nesting/config.py +0 -0
  88. {thailint-0.4.6 → thailint-0.7.0}/src/linters/nesting/linter.py +0 -0
  89. {thailint-0.4.6 → thailint-0.7.0}/src/linters/nesting/python_analyzer.py +0 -0
  90. {thailint-0.4.6 → thailint-0.7.0}/src/linters/nesting/typescript_function_extractor.py +0 -0
  91. {thailint-0.4.6 → thailint-0.7.0}/src/linters/nesting/violation_builder.py +0 -0
  92. {thailint-0.4.6 → thailint-0.7.0}/src/linters/srp/__init__.py +0 -0
  93. {thailint-0.4.6 → thailint-0.7.0}/src/linters/srp/class_analyzer.py +0 -0
  94. {thailint-0.4.6 → thailint-0.7.0}/src/linters/srp/config.py +0 -0
  95. {thailint-0.4.6 → thailint-0.7.0}/src/linters/srp/heuristics.py +0 -0
  96. {thailint-0.4.6 → thailint-0.7.0}/src/linters/srp/linter.py +0 -0
  97. {thailint-0.4.6 → thailint-0.7.0}/src/linters/srp/metrics_evaluator.py +0 -0
  98. {thailint-0.4.6 → thailint-0.7.0}/src/linters/srp/python_analyzer.py +0 -0
  99. {thailint-0.4.6 → thailint-0.7.0}/src/linters/srp/typescript_analyzer.py +0 -0
  100. {thailint-0.4.6 → thailint-0.7.0}/src/linters/srp/typescript_metrics_calculator.py +0 -0
  101. {thailint-0.4.6 → thailint-0.7.0}/src/linters/srp/violation_builder.py +0 -0
  102. {thailint-0.4.6 → thailint-0.7.0}/src/orchestrator/__init__.py +0 -0
  103. {thailint-0.4.6 → thailint-0.7.0}/src/orchestrator/core.py +0 -0
  104. {thailint-0.4.6 → thailint-0.7.0}/src/orchestrator/language_detector.py +0 -0
  105. {thailint-0.4.6 → thailint-0.7.0}/src/utils/__init__.py +0 -0
  106. {thailint-0.4.6 → 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.4.6
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
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
39
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-356%2F356%20passing-brightgreen.svg)](tests/)
40
+ [![Tests](https://img.shields.io/badge/tests-571%2F571%20passing-brightgreen.svg)](tests/)
41
41
  [![Coverage](https://img.shields.io/badge/coverage-87%25-brightgreen.svg)](htmlcov/)
42
42
  [![Documentation Status](https://readthedocs.org/projects/thai-lint/badge/?version=latest)](https://thai-lint.readthedocs.io/en/latest/?badge=latest)
43
+ [![SARIF 2.1.0](https://img.shields.io/badge/SARIF-2.1.0-orange.svg)](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)
@@ -122,7 +128,7 @@ cd thai-lint
122
128
  pip install -e ".[dev]"
123
129
  ```
124
130
 
125
- ### From PyPI (once published)
131
+ ### From PyPI
126
132
 
127
133
  ```bash
128
134
  pip install thai-lint
@@ -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
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
4
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
5
- [![Tests](https://img.shields.io/badge/tests-356%2F356%20passing-brightgreen.svg)](tests/)
5
+ [![Tests](https://img.shields.io/badge/tests-571%2F571%20passing-brightgreen.svg)](tests/)
6
6
  [![Coverage](https://img.shields.io/badge/coverage-87%25-brightgreen.svg)](htmlcov/)
7
7
  [![Documentation Status](https://readthedocs.org/projects/thai-lint/badge/?version=latest)](https://thai-lint.readthedocs.io/en/latest/?badge=latest)
8
+ [![SARIF 2.1.0](https://img.shields.io/badge/SARIF-2.1.0-orange.svg)](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)
@@ -87,7 +93,7 @@ cd thai-lint
87
93
  pip install -e ".[dev]"
88
94
  ```
89
95
 
90
- ### From PyPI (once published)
96
+ ### From PyPI
91
97
 
92
98
  ```bash
93
99
  pip install thai-lint
@@ -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.4.6"
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", "-f", type=click.Choice(["text", "json"]), default="text", help="Output 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
 
@@ -1551,5 +1555,228 @@ def _execute_magic_numbers_lint( # pylint: disable=too-many-arguments,too-many-
1551
1555
  sys.exit(1 if magic_numbers_violations else 0)
1552
1556
 
1553
1557
 
1558
+ # =============================================================================
1559
+ # Print Statements Linter Command
1560
+ # =============================================================================
1561
+
1562
+
1563
+ def _setup_print_statements_orchestrator(
1564
+ path_objs: list[Path], config_file: str | None, verbose: bool, project_root: Path | None = None
1565
+ ):
1566
+ """Set up orchestrator for print-statements command."""
1567
+ from src.orchestrator.core import Orchestrator
1568
+ from src.utils.project_root import get_project_root
1569
+
1570
+ if project_root is None:
1571
+ first_path = path_objs[0] if path_objs else Path.cwd()
1572
+ search_start = first_path if first_path.is_dir() else first_path.parent
1573
+ project_root = get_project_root(search_start)
1574
+
1575
+ orchestrator = Orchestrator(project_root=project_root)
1576
+
1577
+ if config_file:
1578
+ _load_config_file(orchestrator, config_file, verbose)
1579
+
1580
+ return orchestrator
1581
+
1582
+
1583
+ def _run_print_statements_lint(orchestrator, path_objs: list[Path], recursive: bool):
1584
+ """Execute print-statements lint on files or directories."""
1585
+ all_violations = _execute_linting_on_paths(orchestrator, path_objs, recursive)
1586
+ return [v for v in all_violations if "print-statement" in v.rule_id]
1587
+
1588
+
1589
+ @cli.command("print-statements")
1590
+ @click.argument("paths", nargs=-1, type=click.Path())
1591
+ @click.option("--config", "-c", "config_file", type=click.Path(), help="Path to config file")
1592
+ @format_option
1593
+ @click.option("--recursive/--no-recursive", default=True, help="Scan directories recursively")
1594
+ @click.pass_context
1595
+ def print_statements( # pylint: disable=too-many-arguments,too-many-positional-arguments
1596
+ ctx,
1597
+ paths: tuple[str, ...],
1598
+ config_file: str | None,
1599
+ format: str,
1600
+ recursive: bool,
1601
+ ):
1602
+ """Check for print/console statements in code.
1603
+
1604
+ Detects print() calls in Python and console.log/warn/error/debug/info calls
1605
+ in TypeScript/JavaScript that should be replaced with proper logging.
1606
+
1607
+ PATHS: Files or directories to lint (defaults to current directory if none provided)
1608
+
1609
+ Examples:
1610
+
1611
+ \b
1612
+ # Check current directory (all files recursively)
1613
+ thai-lint print-statements
1614
+
1615
+ \b
1616
+ # Check specific directory
1617
+ thai-lint print-statements src/
1618
+
1619
+ \b
1620
+ # Check single file
1621
+ thai-lint print-statements src/app.py
1622
+
1623
+ \b
1624
+ # Check multiple files
1625
+ thai-lint print-statements src/app.py src/utils.ts tests/test_app.py
1626
+
1627
+ \b
1628
+ # Get JSON output
1629
+ thai-lint print-statements --format json .
1630
+
1631
+ \b
1632
+ # Use custom config file
1633
+ thai-lint print-statements --config .thailint.yaml src/
1634
+ """
1635
+ verbose = ctx.obj.get("verbose", False)
1636
+ project_root = _get_project_root_from_context(ctx)
1637
+
1638
+ if not paths:
1639
+ paths = (".",)
1640
+
1641
+ path_objs = [Path(p) for p in paths]
1642
+
1643
+ try:
1644
+ _execute_print_statements_lint(
1645
+ path_objs, config_file, format, recursive, verbose, project_root
1646
+ )
1647
+ except Exception as e:
1648
+ _handle_linting_error(e, verbose)
1649
+
1650
+
1651
+ def _execute_print_statements_lint( # pylint: disable=too-many-arguments,too-many-positional-arguments
1652
+ path_objs, config_file, format, recursive, verbose, project_root=None
1653
+ ):
1654
+ """Execute print-statements lint."""
1655
+ _validate_paths_exist(path_objs)
1656
+ orchestrator = _setup_print_statements_orchestrator(
1657
+ path_objs, config_file, verbose, project_root
1658
+ )
1659
+ print_statements_violations = _run_print_statements_lint(orchestrator, path_objs, recursive)
1660
+
1661
+ if verbose:
1662
+ logger.info(f"Found {len(print_statements_violations)} print statement violation(s)")
1663
+
1664
+ format_violations(print_statements_violations, format)
1665
+ sys.exit(1 if print_statements_violations else 0)
1666
+
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
+
1554
1781
  if __name__ == "__main__":
1555
1782
  cli()