thailint 0.3.1__tar.gz → 0.4.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 (83) hide show
  1. {thailint-0.3.1 → thailint-0.4.0}/PKG-INFO +79 -21
  2. {thailint-0.3.1 → thailint-0.4.0}/README.md +78 -20
  3. {thailint-0.3.1 → thailint-0.4.0}/pyproject.toml +1 -1
  4. {thailint-0.3.1 → thailint-0.4.0}/src/cli.py +135 -0
  5. {thailint-0.3.1 → thailint-0.4.0}/src/linters/magic_numbers/config.py +9 -4
  6. thailint-0.4.0/src/templates/thailint_config_template.yaml +132 -0
  7. {thailint-0.3.1 → thailint-0.4.0}/CHANGELOG.md +0 -0
  8. {thailint-0.3.1 → thailint-0.4.0}/LICENSE +0 -0
  9. {thailint-0.3.1 → thailint-0.4.0}/src/__init__.py +0 -0
  10. {thailint-0.3.1 → thailint-0.4.0}/src/analyzers/__init__.py +0 -0
  11. {thailint-0.3.1 → thailint-0.4.0}/src/analyzers/typescript_base.py +0 -0
  12. {thailint-0.3.1 → thailint-0.4.0}/src/api.py +0 -0
  13. {thailint-0.3.1 → thailint-0.4.0}/src/config.py +0 -0
  14. {thailint-0.3.1 → thailint-0.4.0}/src/core/__init__.py +0 -0
  15. {thailint-0.3.1 → thailint-0.4.0}/src/core/base.py +0 -0
  16. {thailint-0.3.1 → thailint-0.4.0}/src/core/cli_utils.py +0 -0
  17. {thailint-0.3.1 → thailint-0.4.0}/src/core/config_parser.py +0 -0
  18. {thailint-0.3.1 → thailint-0.4.0}/src/core/linter_utils.py +0 -0
  19. {thailint-0.3.1 → thailint-0.4.0}/src/core/registry.py +0 -0
  20. {thailint-0.3.1 → thailint-0.4.0}/src/core/rule_discovery.py +0 -0
  21. {thailint-0.3.1 → thailint-0.4.0}/src/core/types.py +0 -0
  22. {thailint-0.3.1 → thailint-0.4.0}/src/core/violation_builder.py +0 -0
  23. {thailint-0.3.1 → thailint-0.4.0}/src/linter_config/__init__.py +0 -0
  24. {thailint-0.3.1 → thailint-0.4.0}/src/linter_config/ignore.py +0 -0
  25. {thailint-0.3.1 → thailint-0.4.0}/src/linter_config/loader.py +0 -0
  26. {thailint-0.3.1 → thailint-0.4.0}/src/linters/__init__.py +0 -0
  27. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/__init__.py +0 -0
  28. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/base_token_analyzer.py +0 -0
  29. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/block_filter.py +0 -0
  30. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/block_grouper.py +0 -0
  31. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/cache.py +0 -0
  32. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/cache_query.py +0 -0
  33. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/config.py +0 -0
  34. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/config_loader.py +0 -0
  35. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/deduplicator.py +0 -0
  36. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/duplicate_storage.py +0 -0
  37. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/file_analyzer.py +0 -0
  38. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/inline_ignore.py +0 -0
  39. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/linter.py +0 -0
  40. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/python_analyzer.py +0 -0
  41. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/storage_initializer.py +0 -0
  42. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/token_hasher.py +0 -0
  43. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/typescript_analyzer.py +0 -0
  44. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/violation_builder.py +0 -0
  45. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/violation_filter.py +0 -0
  46. {thailint-0.3.1 → thailint-0.4.0}/src/linters/dry/violation_generator.py +0 -0
  47. {thailint-0.3.1 → thailint-0.4.0}/src/linters/file_placement/__init__.py +0 -0
  48. {thailint-0.3.1 → thailint-0.4.0}/src/linters/file_placement/config_loader.py +0 -0
  49. {thailint-0.3.1 → thailint-0.4.0}/src/linters/file_placement/directory_matcher.py +0 -0
  50. {thailint-0.3.1 → thailint-0.4.0}/src/linters/file_placement/linter.py +0 -0
  51. {thailint-0.3.1 → thailint-0.4.0}/src/linters/file_placement/path_resolver.py +0 -0
  52. {thailint-0.3.1 → thailint-0.4.0}/src/linters/file_placement/pattern_matcher.py +0 -0
  53. {thailint-0.3.1 → thailint-0.4.0}/src/linters/file_placement/pattern_validator.py +0 -0
  54. {thailint-0.3.1 → thailint-0.4.0}/src/linters/file_placement/rule_checker.py +0 -0
  55. {thailint-0.3.1 → thailint-0.4.0}/src/linters/file_placement/violation_factory.py +0 -0
  56. {thailint-0.3.1 → thailint-0.4.0}/src/linters/magic_numbers/__init__.py +0 -0
  57. {thailint-0.3.1 → thailint-0.4.0}/src/linters/magic_numbers/context_analyzer.py +0 -0
  58. {thailint-0.3.1 → thailint-0.4.0}/src/linters/magic_numbers/linter.py +0 -0
  59. {thailint-0.3.1 → thailint-0.4.0}/src/linters/magic_numbers/python_analyzer.py +0 -0
  60. {thailint-0.3.1 → thailint-0.4.0}/src/linters/magic_numbers/typescript_analyzer.py +0 -0
  61. {thailint-0.3.1 → thailint-0.4.0}/src/linters/magic_numbers/violation_builder.py +0 -0
  62. {thailint-0.3.1 → thailint-0.4.0}/src/linters/nesting/__init__.py +0 -0
  63. {thailint-0.3.1 → thailint-0.4.0}/src/linters/nesting/config.py +0 -0
  64. {thailint-0.3.1 → thailint-0.4.0}/src/linters/nesting/linter.py +0 -0
  65. {thailint-0.3.1 → thailint-0.4.0}/src/linters/nesting/python_analyzer.py +0 -0
  66. {thailint-0.3.1 → thailint-0.4.0}/src/linters/nesting/typescript_analyzer.py +0 -0
  67. {thailint-0.3.1 → thailint-0.4.0}/src/linters/nesting/typescript_function_extractor.py +0 -0
  68. {thailint-0.3.1 → thailint-0.4.0}/src/linters/nesting/violation_builder.py +0 -0
  69. {thailint-0.3.1 → thailint-0.4.0}/src/linters/srp/__init__.py +0 -0
  70. {thailint-0.3.1 → thailint-0.4.0}/src/linters/srp/class_analyzer.py +0 -0
  71. {thailint-0.3.1 → thailint-0.4.0}/src/linters/srp/config.py +0 -0
  72. {thailint-0.3.1 → thailint-0.4.0}/src/linters/srp/heuristics.py +0 -0
  73. {thailint-0.3.1 → thailint-0.4.0}/src/linters/srp/linter.py +0 -0
  74. {thailint-0.3.1 → thailint-0.4.0}/src/linters/srp/metrics_evaluator.py +0 -0
  75. {thailint-0.3.1 → thailint-0.4.0}/src/linters/srp/python_analyzer.py +0 -0
  76. {thailint-0.3.1 → thailint-0.4.0}/src/linters/srp/typescript_analyzer.py +0 -0
  77. {thailint-0.3.1 → thailint-0.4.0}/src/linters/srp/typescript_metrics_calculator.py +0 -0
  78. {thailint-0.3.1 → thailint-0.4.0}/src/linters/srp/violation_builder.py +0 -0
  79. {thailint-0.3.1 → thailint-0.4.0}/src/orchestrator/__init__.py +0 -0
  80. {thailint-0.3.1 → thailint-0.4.0}/src/orchestrator/core.py +0 -0
  81. {thailint-0.3.1 → thailint-0.4.0}/src/orchestrator/language_detector.py +0 -0
  82. {thailint-0.3.1 → thailint-0.4.0}/src/utils/__init__.py +0 -0
  83. {thailint-0.3.1 → thailint-0.4.0}/src/utils/project_root.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: thailint
3
- Version: 0.3.1
3
+ Version: 0.4.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
  Keywords: linter,ai,code-quality,static-analysis,file-placement,governance,multi-language,cli,docker,python
@@ -35,14 +35,25 @@ Description-Content-Type: text/markdown
35
35
 
36
36
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
37
37
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
38
- [![Tests](https://img.shields.io/badge/tests-317%2F317%20passing-brightgreen.svg)](tests/)
39
- [![Coverage](https://img.shields.io/badge/coverage-90%25-brightgreen.svg)](htmlcov/)
38
+ [![Tests](https://img.shields.io/badge/tests-253%2F253%20passing-brightgreen.svg)](tests/)
39
+ [![Coverage](https://img.shields.io/badge/coverage-87%25-brightgreen.svg)](htmlcov/)
40
40
 
41
41
  The AI Linter - Enterprise-ready linting and governance for AI-generated code across multiple languages.
42
42
 
43
43
  ## Overview
44
44
 
45
- thailint is a modern, enterprise-ready multi-language linter designed specifically for AI-generated code. It enforces project structure, file placement rules, and coding standards across Python, TypeScript, and other languages.
45
+ thailint is a modern, enterprise-ready multi-language linter designed specifically for AI-generated code. It focuses on common mistakes and anti-patterns that AI coding assistants frequently introduce—issues that existing linters don't catch or don't handle consistently across languages.
46
+
47
+ **Why thailint?**
48
+
49
+ We're not trying to replace the wonderful existing linters like Pylint, ESLint, or Ruff. Instead, thailint fills critical gaps:
50
+
51
+ - **AI-Specific Patterns**: AI assistants have predictable blind spots (excessive nesting, magic numbers, SRP violations) that traditional linters miss
52
+ - **Cross-Language Consistency**: Detects the same anti-patterns across Python, TypeScript, and JavaScript with unified rules
53
+ - **No Existing Solutions**: Issues like excessive nesting depth, file placement violations, and cross-project code duplication lack comprehensive multi-language detection
54
+ - **Governance Layer**: Enforces project-wide structure and organization patterns that AI can't infer from local context
55
+
56
+ thailint complements your existing linting stack by catching the patterns AI tools repeatedly miss.
46
57
 
47
58
  ## Features
48
59
 
@@ -917,49 +928,96 @@ def test_no_violations():
917
928
  ### Setup Development Environment
918
929
 
919
930
  ```bash
920
- # Install development dependencies
921
- pip install -e ".[dev]"
931
+ # Install dependencies and activate virtualenv
932
+ just init
922
933
 
923
- # Install pre-commit hooks (if using)
924
- pre-commit install
934
+ # Or manually:
935
+ poetry install
936
+ source $(poetry env info --path)/bin/activate
925
937
  ```
926
938
 
927
939
  ### Running Tests
928
940
 
929
941
  ```bash
930
- # Run all tests
931
- pytest
942
+ # Run all tests (parallel mode - fast)
943
+ just test
932
944
 
933
- # Run with coverage
934
- pytest --cov=src --cov-report=html
945
+ # Run with coverage (serial mode)
946
+ just test-coverage
935
947
 
936
948
  # Run specific test
937
- pytest tests/test_cli.py::test_hello_command
949
+ poetry run pytest tests/test_cli.py::test_hello_command -v
938
950
  ```
939
951
 
940
952
  ### Code Quality
941
953
 
942
954
  ```bash
943
- # Lint code
944
- ruff check src tests
955
+ # Fast linting (Ruff only - use during development)
956
+ just lint
957
+
958
+ # Comprehensive linting (Ruff + Pylint + Flake8 + MyPy)
959
+ just lint-all
960
+
961
+ # Security scanning
962
+ just lint-security
963
+
964
+ # Complexity analysis (Radon + Xenon + Nesting)
965
+ just lint-complexity
966
+
967
+ # SOLID principles (SRP)
968
+ just lint-solid
969
+
970
+ # DRY principles (duplicate code detection)
971
+ just lint-dry
972
+
973
+ # ALL quality checks (runs everything)
974
+ just lint-full
975
+
976
+ # Auto-fix formatting issues
977
+ just format
978
+ ```
979
+
980
+ ### Dogfooding (Lint Our Own Code)
981
+
982
+ ```bash
983
+ # Lint file placement
984
+ just lint-placement
945
985
 
946
- # Format code
947
- ruff format src tests
986
+ # Check nesting depth
987
+ just lint-nesting
948
988
 
949
- # Type checking
950
- mypy src/
989
+ # Check for magic numbers
990
+ poetry run thai-lint magic-numbers src/
951
991
  ```
952
992
 
953
- ### Building
993
+ ### Building and Publishing
954
994
 
955
995
  ```bash
956
996
  # Build Python package
957
997
  poetry build
958
998
 
959
- # Build Docker image locally (optional)
999
+ # Build Docker image locally
960
1000
  docker build -t washad/thailint:latest .
1001
+
1002
+ # Publish to PyPI and Docker Hub (runs tests + linting + version bump)
1003
+ just publish
961
1004
  ```
962
1005
 
1006
+ ### Quick Development Workflows
1007
+
1008
+ ```bash
1009
+ # Make changes, then run quality checks
1010
+ just lint-full
1011
+
1012
+ # Share changes for collaboration (skips hooks)
1013
+ just share "WIP: feature description"
1014
+
1015
+ # Clean up cache and artifacts
1016
+ just clean
1017
+ ```
1018
+
1019
+ See `just --list` or `just help` for all available commands.
1020
+
963
1021
  ## Docker Usage
964
1022
 
965
1023
  ```bash
@@ -2,14 +2,25 @@
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-317%2F317%20passing-brightgreen.svg)](tests/)
6
- [![Coverage](https://img.shields.io/badge/coverage-90%25-brightgreen.svg)](htmlcov/)
5
+ [![Tests](https://img.shields.io/badge/tests-253%2F253%20passing-brightgreen.svg)](tests/)
6
+ [![Coverage](https://img.shields.io/badge/coverage-87%25-brightgreen.svg)](htmlcov/)
7
7
 
8
8
  The AI Linter - Enterprise-ready linting and governance for AI-generated code across multiple languages.
9
9
 
10
10
  ## Overview
11
11
 
12
- thailint is a modern, enterprise-ready multi-language linter designed specifically for AI-generated code. It enforces project structure, file placement rules, and coding standards across Python, TypeScript, and other languages.
12
+ thailint is a modern, enterprise-ready multi-language linter designed specifically for AI-generated code. It focuses on common mistakes and anti-patterns that AI coding assistants frequently introduce—issues that existing linters don't catch or don't handle consistently across languages.
13
+
14
+ **Why thailint?**
15
+
16
+ We're not trying to replace the wonderful existing linters like Pylint, ESLint, or Ruff. Instead, thailint fills critical gaps:
17
+
18
+ - **AI-Specific Patterns**: AI assistants have predictable blind spots (excessive nesting, magic numbers, SRP violations) that traditional linters miss
19
+ - **Cross-Language Consistency**: Detects the same anti-patterns across Python, TypeScript, and JavaScript with unified rules
20
+ - **No Existing Solutions**: Issues like excessive nesting depth, file placement violations, and cross-project code duplication lack comprehensive multi-language detection
21
+ - **Governance Layer**: Enforces project-wide structure and organization patterns that AI can't infer from local context
22
+
23
+ thailint complements your existing linting stack by catching the patterns AI tools repeatedly miss.
13
24
 
14
25
  ## Features
15
26
 
@@ -884,49 +895,96 @@ def test_no_violations():
884
895
  ### Setup Development Environment
885
896
 
886
897
  ```bash
887
- # Install development dependencies
888
- pip install -e ".[dev]"
898
+ # Install dependencies and activate virtualenv
899
+ just init
889
900
 
890
- # Install pre-commit hooks (if using)
891
- pre-commit install
901
+ # Or manually:
902
+ poetry install
903
+ source $(poetry env info --path)/bin/activate
892
904
  ```
893
905
 
894
906
  ### Running Tests
895
907
 
896
908
  ```bash
897
- # Run all tests
898
- pytest
909
+ # Run all tests (parallel mode - fast)
910
+ just test
899
911
 
900
- # Run with coverage
901
- pytest --cov=src --cov-report=html
912
+ # Run with coverage (serial mode)
913
+ just test-coverage
902
914
 
903
915
  # Run specific test
904
- pytest tests/test_cli.py::test_hello_command
916
+ poetry run pytest tests/test_cli.py::test_hello_command -v
905
917
  ```
906
918
 
907
919
  ### Code Quality
908
920
 
909
921
  ```bash
910
- # Lint code
911
- ruff check src tests
922
+ # Fast linting (Ruff only - use during development)
923
+ just lint
924
+
925
+ # Comprehensive linting (Ruff + Pylint + Flake8 + MyPy)
926
+ just lint-all
927
+
928
+ # Security scanning
929
+ just lint-security
930
+
931
+ # Complexity analysis (Radon + Xenon + Nesting)
932
+ just lint-complexity
933
+
934
+ # SOLID principles (SRP)
935
+ just lint-solid
936
+
937
+ # DRY principles (duplicate code detection)
938
+ just lint-dry
939
+
940
+ # ALL quality checks (runs everything)
941
+ just lint-full
942
+
943
+ # Auto-fix formatting issues
944
+ just format
945
+ ```
946
+
947
+ ### Dogfooding (Lint Our Own Code)
948
+
949
+ ```bash
950
+ # Lint file placement
951
+ just lint-placement
912
952
 
913
- # Format code
914
- ruff format src tests
953
+ # Check nesting depth
954
+ just lint-nesting
915
955
 
916
- # Type checking
917
- mypy src/
956
+ # Check for magic numbers
957
+ poetry run thai-lint magic-numbers src/
918
958
  ```
919
959
 
920
- ### Building
960
+ ### Building and Publishing
921
961
 
922
962
  ```bash
923
963
  # Build Python package
924
964
  poetry build
925
965
 
926
- # Build Docker image locally (optional)
966
+ # Build Docker image locally
927
967
  docker build -t washad/thailint:latest .
968
+
969
+ # Publish to PyPI and Docker Hub (runs tests + linting + version bump)
970
+ just publish
928
971
  ```
929
972
 
973
+ ### Quick Development Workflows
974
+
975
+ ```bash
976
+ # Make changes, then run quality checks
977
+ just lint-full
978
+
979
+ # Share changes for collaboration (skips hooks)
980
+ just share "WIP: feature description"
981
+
982
+ # Clean up cache and artifacts
983
+ just clean
984
+ ```
985
+
986
+ See `just --list` or `just help` for all available commands.
987
+
930
988
  ## Docker Usage
931
989
 
932
990
  ```bash
@@ -17,7 +17,7 @@ build-backend = "poetry.core.masonry.api"
17
17
 
18
18
  [tool.poetry]
19
19
  name = "thailint"
20
- version = "0.3.1"
20
+ version = "0.4.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"
@@ -362,6 +362,141 @@ def config_reset(ctx, yes: bool):
362
362
  sys.exit(1)
363
363
 
364
364
 
365
+ @cli.command("init-config")
366
+ @click.option(
367
+ "--preset",
368
+ "-p",
369
+ type=click.Choice(["strict", "standard", "lenient"]),
370
+ default="standard",
371
+ help="Configuration preset",
372
+ )
373
+ @click.option("--non-interactive", is_flag=True, help="Skip interactive prompts (for AI agents)")
374
+ @click.option("--force", is_flag=True, help="Overwrite existing .thailint.yaml file")
375
+ @click.option(
376
+ "--output", "-o", type=click.Path(), default=".thailint.yaml", help="Output file path"
377
+ )
378
+ def init_config(preset: str, non_interactive: bool, force: bool, output: str):
379
+ """
380
+ Generate a .thailint.yaml configuration file with preset values.
381
+
382
+ Creates a richly-commented configuration file with sensible defaults
383
+ and optional customizations for different strictness levels.
384
+
385
+ For AI agents, use --non-interactive mode:
386
+ thailint init-config --non-interactive --preset lenient
387
+
388
+ Presets:
389
+ strict: Minimal allowed numbers (only -1, 0, 1)
390
+ standard: Balanced defaults (includes 2, 3, 4, 5, 10, 100, 1000)
391
+ lenient: Includes time conversions (adds 60, 3600)
392
+
393
+ Examples:
394
+
395
+ \\b
396
+ # Interactive mode (default, for humans)
397
+ thailint init-config
398
+
399
+ \\b
400
+ # Non-interactive mode (for AI agents)
401
+ thailint init-config --non-interactive
402
+
403
+ \\b
404
+ # Generate with lenient preset
405
+ thailint init-config --preset lenient
406
+
407
+ \\b
408
+ # Overwrite existing config
409
+ thailint init-config --force
410
+
411
+ \\b
412
+ # Custom output path
413
+ thailint init-config --output my-config.yaml
414
+ """
415
+ output_path = Path(output)
416
+
417
+ # Check if file exists (unless --force)
418
+ if output_path.exists() and not force:
419
+ click.echo(f"Error: {output} already exists", err=True)
420
+ click.echo("", err=True)
421
+ click.echo("Use --force to overwrite:", err=True)
422
+ click.echo(" thailint init-config --force", err=True)
423
+ sys.exit(1)
424
+
425
+ # Interactive mode: Ask user for preferences
426
+ if not non_interactive:
427
+ click.echo("thai-lint Configuration Generator")
428
+ click.echo("=" * 50)
429
+ click.echo("")
430
+ click.echo("This will create a .thailint.yaml configuration file.")
431
+ click.echo("For non-interactive mode (AI agents), use:")
432
+ click.echo(" thailint init-config --non-interactive")
433
+ click.echo("")
434
+
435
+ # Ask for preset
436
+ click.echo("Available presets:")
437
+ click.echo(" strict: Only -1, 0, 1 allowed (strictest)")
438
+ click.echo(" standard: -1, 0, 1, 2, 3, 4, 5, 10, 100, 1000 (balanced)")
439
+ click.echo(" lenient: Includes time conversions 60, 3600 (most permissive)")
440
+ click.echo("")
441
+
442
+ preset = click.prompt(
443
+ "Choose preset", type=click.Choice(["strict", "standard", "lenient"]), default=preset
444
+ )
445
+
446
+ # Generate config based on preset
447
+ config_content = _generate_config_content(preset)
448
+
449
+ # Write config file
450
+ try:
451
+ output_path.write_text(config_content, encoding="utf-8")
452
+ click.echo("")
453
+ click.echo(f"✓ Created {output}")
454
+ click.echo(f"✓ Preset: {preset}")
455
+ click.echo("")
456
+ click.echo("Next steps:")
457
+ click.echo(f" 1. Review and customize {output}")
458
+ click.echo(" 2. Run: thailint magic-numbers .")
459
+ click.echo(" 3. See docs: https://github.com/your-org/thai-lint")
460
+ except OSError as e:
461
+ click.echo(f"Error writing config file: {e}", err=True)
462
+ sys.exit(1)
463
+
464
+
465
+ def _generate_config_content(preset: str) -> str:
466
+ """Generate config file content based on preset."""
467
+ # Preset configurations
468
+ presets = {
469
+ "strict": {
470
+ "allowed_numbers": "[-1, 0, 1]",
471
+ "max_small_integer": "3",
472
+ "description": "Strict (only universal values)",
473
+ },
474
+ "standard": {
475
+ "allowed_numbers": "[-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000]",
476
+ "max_small_integer": "10",
477
+ "description": "Standard (balanced defaults)",
478
+ },
479
+ "lenient": {
480
+ "allowed_numbers": "[-1, 0, 1, 2, 3, 4, 5, 10, 60, 100, 1000, 3600]",
481
+ "max_small_integer": "10",
482
+ "description": "Lenient (includes time conversions)",
483
+ },
484
+ }
485
+
486
+ config = presets[preset]
487
+
488
+ # Read template
489
+ template_path = Path(__file__).parent / "templates" / "thailint_config_template.yaml"
490
+ template = template_path.read_text(encoding="utf-8")
491
+
492
+ # Replace placeholders
493
+ content = template.replace("{{PRESET}}", config["description"])
494
+ content = content.replace("{{ALLOWED_NUMBERS}}", config["allowed_numbers"])
495
+ content = content.replace("{{MAX_SMALL_INTEGER}}", config["max_small_integer"])
496
+
497
+ return content
498
+
499
+
365
500
  @cli.command("file-placement")
366
501
  @click.argument("paths", nargs=-1, type=click.Path())
367
502
  @click.option("--config", "-c", "config_file", type=click.Path(), help="Path to config file")
@@ -4,7 +4,7 @@ Purpose: Configuration schema for magic numbers linter
4
4
  Scope: MagicNumberConfig dataclass with allowed_numbers and max_small_integer settings
5
5
 
6
6
  Overview: Defines configuration schema for magic numbers linter. Provides MagicNumberConfig dataclass
7
- with allowed_numbers set (default includes common acceptable numbers like 0, 1, 2, -1, 10, 100, 1000)
7
+ with allowed_numbers set (default includes common acceptable numbers like -1, 0, 1, 2, 3, 4, 5, 10, 100, 1000)
8
8
  and max_small_integer threshold (default 10) for range() contexts. Supports per-file and per-directory
9
9
  config overrides through from_dict class method. Validates that configuration values are appropriate
10
10
  types. Integrates with orchestrator's configuration system to allow users to customize allowed numbers
@@ -29,7 +29,9 @@ class MagicNumberConfig:
29
29
  """Configuration for magic numbers linter."""
30
30
 
31
31
  enabled: bool = True
32
- allowed_numbers: set[int | float] = field(default_factory=lambda: {-1, 0, 1, 2, 10, 100, 1000})
32
+ allowed_numbers: set[int | float] = field(
33
+ default_factory=lambda: {-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000}
34
+ )
33
35
  max_small_integer: int = 10
34
36
 
35
37
  def __post_init__(self) -> None:
@@ -54,14 +56,17 @@ class MagicNumberConfig:
54
56
  lang_config = config[language]
55
57
  allowed_numbers = set(
56
58
  lang_config.get(
57
- "allowed_numbers", config.get("allowed_numbers", {-1, 0, 1, 2, 10, 100, 1000})
59
+ "allowed_numbers",
60
+ config.get("allowed_numbers", {-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000}),
58
61
  )
59
62
  )
60
63
  max_small_integer = lang_config.get(
61
64
  "max_small_integer", config.get("max_small_integer", 10)
62
65
  )
63
66
  else:
64
- allowed_numbers = set(config.get("allowed_numbers", {-1, 0, 1, 2, 10, 100, 1000}))
67
+ allowed_numbers = set(
68
+ config.get("allowed_numbers", {-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000})
69
+ )
65
70
  max_small_integer = config.get("max_small_integer", 10)
66
71
 
67
72
  return cls(
@@ -0,0 +1,132 @@
1
+ # thai-lint Configuration File
2
+ # Generated by: thailint init-config
3
+ #
4
+ # For non-interactive mode (AI agents): thailint init-config --non-interactive
5
+ #
6
+ # Full documentation: https://github.com/your-org/thai-lint
7
+
8
+ # ============================================================================
9
+ # MAGIC NUMBERS LINTER
10
+ # ============================================================================
11
+ # Detects unnamed numeric literals that should be extracted as constants
12
+ #
13
+ # Preset: {{PRESET}}
14
+ #
15
+ magic-numbers:
16
+ enabled: true
17
+
18
+ # Numbers that are acceptable without being named constants
19
+ # Default: [-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000]
20
+ allowed_numbers: {{ALLOWED_NUMBERS}}
21
+
22
+ # Maximum integer allowed in range() or enumerate() without flagging
23
+ # Default: 10
24
+ max_small_integer: {{MAX_SMALL_INTEGER}}
25
+
26
+ # -------------------------------------------------------------------------
27
+ # OPTIONAL: Uncomment to add time conversions (lenient mode)
28
+ # -------------------------------------------------------------------------
29
+ # allowed_numbers: [-1, 0, 1, 2, 3, 4, 5, 10, 60, 100, 1000, 3600]
30
+
31
+ # -------------------------------------------------------------------------
32
+ # OPTIONAL: Uncomment to add common HTTP status codes
33
+ # -------------------------------------------------------------------------
34
+ # allowed_numbers: [-1, 0, 1, 2, 3, 4, 5, 10, 100, 200, 201, 204, 400, 401, 403, 404, 500, 502, 503, 1000]
35
+
36
+ # -------------------------------------------------------------------------
37
+ # OPTIONAL: Uncomment to add decimal proportions (0.0-1.0)
38
+ # -------------------------------------------------------------------------
39
+ # allowed_numbers: [-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000, 0.0, 0.1, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 0.7, 0.75, 0.8, 0.9, 1.0]
40
+
41
+ # ============================================================================
42
+ # NESTING LINTER
43
+ # ============================================================================
44
+ # Checks for excessive nesting depth (if/for/while/try statements)
45
+ #
46
+ nesting:
47
+ enabled: true
48
+
49
+ # Maximum nesting depth allowed
50
+ # Default: 4
51
+ max_nesting_depth: 4
52
+
53
+ # ============================================================================
54
+ # SINGLE RESPONSIBILITY PRINCIPLE (SRP) LINTER
55
+ # ============================================================================
56
+ # Detects classes that may have too many responsibilities
57
+ #
58
+ srp:
59
+ enabled: true
60
+
61
+ # Maximum methods per class
62
+ # Default: 7
63
+ max_methods: 7
64
+
65
+ # Maximum lines of code per class
66
+ # Default: 200
67
+ max_loc: 200
68
+
69
+ # ============================================================================
70
+ # DRY (DON'T REPEAT YOURSELF) LINTER
71
+ # ============================================================================
72
+ # Detects duplicate code blocks
73
+ #
74
+ dry:
75
+ enabled: true
76
+
77
+ # Minimum lines for a block to be considered duplicate
78
+ # Default: 6
79
+ min_duplicate_lines: 6
80
+
81
+ # Enable SQLite caching for faster incremental scans
82
+ # Default: true
83
+ cache_enabled: true
84
+
85
+ # Cache file location (relative to project root)
86
+ # Default: .thailint-cache/dry.db
87
+ cache_path: .thailint-cache/dry.db
88
+
89
+ # ============================================================================
90
+ # FILE PLACEMENT LINTER
91
+ # ============================================================================
92
+ # Ensures files are in appropriate directories
93
+ #
94
+ file-placement:
95
+ enabled: true
96
+
97
+ # Rules for file placement
98
+ rules:
99
+ # Test files should be in tests/ directory
100
+ - pattern: "test_*.py"
101
+ required_dir: "tests/"
102
+ message: "Test files must be in tests/ directory"
103
+
104
+ # Config files should be in config/ or root
105
+ - pattern: "*config*.py"
106
+ required_dir: ["config/", "./"]
107
+ message: "Config files should be in config/ or project root"
108
+
109
+ # ============================================================================
110
+ # GLOBAL SETTINGS
111
+ # ============================================================================
112
+ #
113
+ # Exclude patterns (files/directories to ignore)
114
+ exclude:
115
+ - ".git/"
116
+ - ".venv/"
117
+ - "venv/"
118
+ - "node_modules/"
119
+ - "__pycache__/"
120
+ - "*.pyc"
121
+ - ".pytest_cache/"
122
+ - "dist/"
123
+ - "build/"
124
+ - ".eggs/"
125
+
126
+ # Output format (text or json)
127
+ # Default: text
128
+ output_format: text
129
+
130
+ # Exit with error code if violations found
131
+ # Default: true
132
+ fail_on_violations: true
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes