smellcheck 0.2.1__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 (42) hide show
  1. smellcheck-0.2.1/.claude-plugin/marketplace.json +32 -0
  2. smellcheck-0.2.1/.github/CODEOWNERS +5 -0
  3. smellcheck-0.2.1/.github/FUNDING.yml +1 -0
  4. smellcheck-0.2.1/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
  5. smellcheck-0.2.1/.github/ISSUE_TEMPLATE/feature_request.md +40 -0
  6. smellcheck-0.2.1/.github/PULL_REQUEST_TEMPLATE.md +41 -0
  7. smellcheck-0.2.1/.github/workflows/ci.yml +37 -0
  8. smellcheck-0.2.1/.github/workflows/lint-commits.yml +14 -0
  9. smellcheck-0.2.1/.github/workflows/release-please.yml +80 -0
  10. smellcheck-0.2.1/.gitignore +16 -0
  11. smellcheck-0.2.1/.pre-commit-config.yaml +17 -0
  12. smellcheck-0.2.1/.pre-commit-hooks.yaml +7 -0
  13. smellcheck-0.2.1/.release-please-manifest.json +3 -0
  14. smellcheck-0.2.1/CHANGELOG.md +61 -0
  15. smellcheck-0.2.1/CODE_OF_CONDUCT.md +50 -0
  16. smellcheck-0.2.1/CONTRIBUTING.md +114 -0
  17. smellcheck-0.2.1/LICENSE +21 -0
  18. smellcheck-0.2.1/PKG-INFO +299 -0
  19. smellcheck-0.2.1/README.md +274 -0
  20. smellcheck-0.2.1/SECURITY.md +41 -0
  21. smellcheck-0.2.1/action.yml +45 -0
  22. smellcheck-0.2.1/plugins/python-refactoring/.claude-plugin/plugin.json +5 -0
  23. smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/SKILL.md +149 -0
  24. smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/architecture.md +172 -0
  25. smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/control.md +266 -0
  26. smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/functions.md +294 -0
  27. smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/hygiene.md +203 -0
  28. smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/idioms.md +203 -0
  29. smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/metrics.md +432 -0
  30. smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/state.md +152 -0
  31. smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/types.md +280 -0
  32. smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/scripts/detect_smells.py +32 -0
  33. smellcheck-0.2.1/pyproject.toml +43 -0
  34. smellcheck-0.2.1/release-please-config.json +31 -0
  35. smellcheck-0.2.1/src/smellcheck/__init__.py +14 -0
  36. smellcheck-0.2.1/src/smellcheck/__main__.py +6 -0
  37. smellcheck-0.2.1/src/smellcheck/detector.py +2246 -0
  38. smellcheck-0.2.1/src/smellcheck/py.typed +0 -0
  39. smellcheck-0.2.1/tests/__init__.py +0 -0
  40. smellcheck-0.2.1/tests/test_detector.py +235 -0
  41. smellcheck-0.2.1/tests/test_regressions.py +175 -0
  42. smellcheck-0.2.1/tests/test_version_parity.py +112 -0
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "smellcheck",
3
+ "owner": {
4
+ "name": "Cheick Berthe",
5
+ "email": "cheick@example.com"
6
+ },
7
+ "metadata": {
8
+ "description": "Python code smell detector with 82+ patterns mapped to a refactoring catalog. AST-based, zero dependencies, stdlib only.",
9
+ "version": "0.2.1"
10
+ },
11
+ "plugins": [
12
+ {
13
+ "name": "python-refactoring",
14
+ "source": "./plugins/python-refactoring",
15
+ "description": "Python refactoring catalog with 82+ patterns covering immutability, class design, control flow, architecture, OO metrics, and Python idioms. Analyze code for smells and apply numbered refactoring patterns.",
16
+ "version": "0.2.1",
17
+ "author": {
18
+ "name": "Cheick Berthe"
19
+ },
20
+ "license": "MIT",
21
+ "keywords": [
22
+ "python",
23
+ "refactoring",
24
+ "code-smells",
25
+ "ast",
26
+ "static-analysis",
27
+ "code-quality"
28
+ ],
29
+ "category": "development"
30
+ }
31
+ ]
32
+ }
@@ -0,0 +1,5 @@
1
+ # Default owner for everything in the repo
2
+ * @cheickmec
3
+
4
+ # Core detector -- changes here need careful review
5
+ src/smellcheck/detector.py @cheickmec
@@ -0,0 +1 @@
1
+ github: cheickmec
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: Bug report
3
+ about: Report a false positive, false negative, crash, or incorrect output
4
+ title: ''
5
+ labels: bug
6
+ assignees: ''
7
+ ---
8
+
9
+ **Describe the bug**
10
+ A clear description of what went wrong.
11
+
12
+ **Python code that triggers the issue**
13
+ ```python
14
+ # Paste the minimal code that reproduces the problem
15
+ ```
16
+
17
+ **Expected behavior**
18
+ What should smellcheck report (or not report)?
19
+
20
+ **Actual behavior**
21
+ What does smellcheck actually output? Paste the relevant output:
22
+ ```
23
+ # smellcheck output here
24
+ ```
25
+
26
+ **Environment**
27
+ - smellcheck version: (`smellcheck --version`)
28
+ - Python version: (`python --version`)
29
+ - OS:
30
+ - Install method: pip / GitHub Action / pre-commit / Agent Skills
31
+
32
+ **Additional context**
33
+ - Is this a false positive (reported something that isn't a smell)?
34
+ - Is this a false negative (missed a smell that should be detected)?
35
+ - Is this a crash or unexpected error?
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest a new smell check, CLI feature, or improvement
4
+ title: ''
5
+ labels: enhancement
6
+ assignees: ''
7
+ ---
8
+
9
+ **What kind of feature?**
10
+ - [ ] New smell check
11
+ - [ ] New CLI option
12
+ - [ ] New output format
13
+ - [ ] Improvement to existing check
14
+ - [ ] Other
15
+
16
+ **For new smell checks: describe the code pattern**
17
+
18
+ What code should be detected?
19
+
20
+ ```python
21
+ # Before (smelly code)
22
+ ```
23
+
24
+ What does the refactored version look like?
25
+
26
+ ```python
27
+ # After (clean code)
28
+ ```
29
+
30
+ **Why is this a smell?**
31
+ Explain why this pattern is problematic. References to Fowler, Contieri, or other
32
+ refactoring literature are a plus.
33
+
34
+ **Suggested severity**
35
+ - [ ] error (always wrong, like mutable default args)
36
+ - [ ] warning (usually wrong, should be reviewed)
37
+ - [ ] info (style suggestion, may be intentional)
38
+
39
+ **Additional context**
40
+ Any other context, related patterns, or edge cases to consider.
@@ -0,0 +1,41 @@
1
+ ## What does this PR do?
2
+
3
+ Brief description of the change.
4
+
5
+ ## Type of change
6
+
7
+ - [ ] New smell check (add pattern number, e.g. `#071`)
8
+ - [ ] Bug fix (false positive, false negative, crash)
9
+ - [ ] CLI / output improvement
10
+ - [ ] Documentation
11
+ - [ ] CI / tooling
12
+ - [ ] Other
13
+
14
+ ## Checklist
15
+
16
+ - [ ] `pytest tests/ -v` passes (all tests, including version parity)
17
+ - [ ] New check has a test in `tests/test_detector.py`
18
+ - [ ] `smellcheck src/smellcheck/` self-check runs clean (or new findings are intentional)
19
+ - [ ] No dependencies added (stdlib only)
20
+ - [ ] Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/)
21
+
22
+ ## For new smell checks
23
+
24
+ - **Pattern number**: `#___`
25
+ - **Severity**: error / warning / info
26
+ - **Category**: functions / classes / complexity / imports / ...
27
+ - **Before** (smelly code):
28
+
29
+ ```python
30
+ # paste example
31
+ ```
32
+
33
+ - **After** (clean code):
34
+
35
+ ```python
36
+ # paste example
37
+ ```
38
+
39
+ ## Additional context
40
+
41
+ Any other context, edge cases, or related issues.
@@ -0,0 +1,37 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ['3.10', '3.11', '3.12', '3.13']
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - name: Install package and test deps
21
+ run: pip install -e . && pip install pytest
22
+ - name: Run tests
23
+ run: pytest tests/ -v
24
+ - name: Self-check
25
+ run: smellcheck src/smellcheck/ --min-severity warning
26
+
27
+ version-parity:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: actions/setup-python@v5
32
+ with:
33
+ python-version: '3.12'
34
+ - name: Install package and test deps
35
+ run: pip install -e . && pip install pytest
36
+ - name: Verify version parity
37
+ run: pytest tests/test_version_parity.py -v
@@ -0,0 +1,14 @@
1
+ name: Lint Commits
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ lint-commits:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: webiny/action-conventional-commits@v1.3.0
13
+ with:
14
+ allowed-commit-types: "feat,fix,refactor,test,docs,chore,ci,style,perf,build,revert"
@@ -0,0 +1,80 @@
1
+ name: Release Please
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ permissions:
9
+ contents: write
10
+ pull-requests: write
11
+
12
+ jobs:
13
+ release-please:
14
+ runs-on: ubuntu-latest
15
+ outputs:
16
+ release_created: ${{ steps.release.outputs.release_created }}
17
+ tag_name: ${{ steps.release.outputs.tag_name }}
18
+ steps:
19
+ - id: release
20
+ uses: googleapis/release-please-action@v4
21
+ with:
22
+ token: ${{ secrets.GITHUB_TOKEN }}
23
+
24
+ publish-pypi:
25
+ needs: release-please
26
+ if: needs.release-please.outputs.release_created == 'true'
27
+ runs-on: ubuntu-latest
28
+ permissions:
29
+ id-token: write
30
+ environment:
31
+ name: pypi
32
+ url: https://pypi.org/p/smellcheck
33
+ env:
34
+ TAG_NAME: ${{ needs.release-please.outputs.tag_name }}
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+ - uses: actions/setup-python@v5
38
+ with:
39
+ python-version: '3.12'
40
+
41
+ # Guard 1: tag must match pyproject.toml version
42
+ - name: Verify tag matches package version
43
+ run: |
44
+ TAG_VERSION="${TAG_NAME#v}"
45
+ PKG_VERSION=$(python3 -c "
46
+ import re, pathlib
47
+ text = pathlib.Path('pyproject.toml').read_text()
48
+ m = re.search(r'^version\s*=\s*\"([^\"]+)\"', text, re.MULTILINE)
49
+ print(m.group(1))
50
+ ")
51
+ echo "Tag version: $TAG_VERSION"
52
+ echo "Package version: $PKG_VERSION"
53
+ if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
54
+ echo "::error::Version mismatch: tag=$TAG_VERSION != pyproject.toml=$PKG_VERSION"
55
+ exit 1
56
+ fi
57
+
58
+ - name: Install build tools
59
+ run: pip install build
60
+ - name: Build package
61
+ run: python -m build
62
+
63
+ # Guard 2: smoke-test the built wheel before publishing
64
+ - name: Smoke-test wheel
65
+ run: |
66
+ TAG_VERSION="${TAG_NAME#v}"
67
+ pip install dist/smellcheck-*.whl
68
+ INSTALLED=$(smellcheck --version | awk '{print $2}')
69
+ echo "Installed version: $INSTALLED"
70
+ echo "Expected version: $TAG_VERSION"
71
+ if [ "$INSTALLED" != "$TAG_VERSION" ]; then
72
+ echo "::error::Installed version $INSTALLED does not match tag $TAG_VERSION"
73
+ exit 1
74
+ fi
75
+ echo "Smoke test: smellcheck --version OK"
76
+ smellcheck --help > /dev/null
77
+ echo "Smoke test: smellcheck --help OK"
78
+
79
+ - name: Publish to PyPI
80
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,16 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+ .venv/
10
+ venv/
11
+ .tox/
12
+ .mypy_cache/
13
+ .ruff_cache/
14
+ .pytest_cache/
15
+ *.so
16
+ .DS_Store
@@ -0,0 +1,17 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: no-commit-to-main
5
+ name: no-commit-to-main
6
+ entry: bash -c 'branch=$(git rev-parse --abbrev-ref HEAD); if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then echo "Direct commits to $branch are not allowed. Use a feature branch." >&2; exit 1; fi'
7
+ language: system
8
+ pass_filenames: false
9
+ always_run: true
10
+ stages: [pre-commit]
11
+
12
+ - id: version-parity
13
+ name: version-parity
14
+ entry: python -m pytest tests/test_version_parity.py -q --no-header --tb=short
15
+ language: system
16
+ pass_filenames: false
17
+ files: '(pyproject\.toml|\.release-please-manifest\.json|marketplace\.json)$'
@@ -0,0 +1,7 @@
1
+ - id: smellcheck
2
+ name: smellcheck
3
+ description: 'Python code smell detector (55 AST checks, 82 refactoring patterns)'
4
+ entry: smellcheck
5
+ language: python
6
+ types: [python]
7
+ require_serial: false
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.2.1"
3
+ }
@@ -0,0 +1,61 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.1](https://github.com/cheickmec/smellcheck/compare/v0.2.0...v0.2.1) (2026-02-11)
9
+
10
+
11
+ ### Features
12
+
13
+ * initial release -- 82-pattern Python refactoring skill with AST detector ([e96f750](https://github.com/cheickmec/smellcheck/commit/e96f7507e6739bb3463dca93e38ed04bc6cf1597))
14
+ * restructure for PyPI, GitHub Action, pre-commit, and Agent Skills distribution ([e1bf50b](https://github.com/cheickmec/smellcheck/commit/e1bf50b2f4f1b8191903a84c7d262a0c312f163d))
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * address 9 code review findings (P1-P3) ([39a62c0](https://github.com/cheickmec/smellcheck/commit/39a62c0b2ce9fb0ce00f54e8dc4e4a97fddfc792))
20
+
21
+
22
+ ### Documentation
23
+
24
+ * add community files for OSS maturity ([8b7fc5f](https://github.com/cheickmec/smellcheck/commit/8b7fc5fcc81f27c58469f220fd33483a116ab07c))
25
+ * update README for cross-platform compatibility and SEO ([aecccba](https://github.com/cheickmec/smellcheck/commit/aecccba107b831b7206c543e1a1b34641a0c6903))
26
+
27
+
28
+ ### Miscellaneous
29
+
30
+ * add CHANGELOG.md, fix version to 0.1.1 ([82aae13](https://github.com/cheickmec/smellcheck/commit/82aae130e0830ce38e4f0f1d46c26c095619ee33))
31
+ * add pre-commit hook to prevent direct commits to main ([b23e812](https://github.com/cheickmec/smellcheck/commit/b23e812f123f551ad181d1262959e47ff503d1c0))
32
+ * rename project from pysmells to smellcheck ([71589cc](https://github.com/cheickmec/smellcheck/commit/71589cc6efa36085d7c2bcc0e4d39c11edad1155))
33
+
34
+ ## [0.1.1] - 2026-02-11
35
+
36
+ ### Fixed
37
+ - Import analysis now captures full dotted paths, fixing under-reporting of #CYC, #UDE, #FIO on package-based repos
38
+ - RFC metric counts distinct external method calls (not class names), correctly measuring response set size
39
+ - elif chains no longer produce duplicate #014 and #068 findings
40
+ - #058 no longer false-positives on `ExitStack.enter_context(open(...))`
41
+ - Single-file scans now run per-class OO metrics (LCOM, CBO, RFC, MID)
42
+ - CLI docs updated with correct script path from repo root
43
+ - Corrected detection counts: 40 per-file + 10 cross-file + 5 OO metrics = 55
44
+ - Added missing #007 (extract class) to per-file detection table
45
+ - Fixed SKILL.md smell-to-file mappings (003, 005, 006, 008, MID pointed to wrong reference files)
46
+ - `--min-severity` now validates input and rejects invalid values with clear error message
47
+
48
+ ## [0.1.0] - 2026-02-10
49
+
50
+ ### Added
51
+ - AST-based smell detector with 55 automated checks (stdlib-only, zero dependencies)
52
+ - 40 per-file patterns (#001-#070, #CC)
53
+ - 10 cross-file patterns (#013, #CYC, #GOD, #FE, #SHO, #DIT, #WHI, #INT, #SPG, #UDE)
54
+ - 5 OO metrics (#LCOM, #CBO, #FIO, #RFC, #MID)
55
+ - 82 refactoring patterns with before/after examples across 8 reference files
56
+ - Agent Skills plugin structure for Claude Code, Codex CLI, Cursor, Copilot, Gemini CLI, Roo Code
57
+ - JSON output mode for CI/CD integration
58
+ - Severity filtering (`--min-severity info|warning|error`)
59
+
60
+ [0.1.1]: https://github.com/cheickmec/smellcheck/compare/v0.1.0...v0.1.1
61
+ [0.1.0]: https://github.com/cheickmec/smellcheck/releases/tag/v0.1.0
@@ -0,0 +1,50 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and maintainers pledge to make participation in our
6
+ project and our community a harassment-free experience for everyone, regardless of
7
+ age, body size, disability, ethnicity, sex characteristics, gender identity and
8
+ expression, level of experience, education, socio-economic status, nationality,
9
+ personal appearance, race, religion, or sexual identity and orientation.
10
+
11
+ ## Our Standards
12
+
13
+ Examples of behavior that contributes to a positive environment:
14
+
15
+ - Using welcoming and inclusive language
16
+ - Being respectful of differing viewpoints and experiences
17
+ - Gracefully accepting constructive criticism
18
+ - Focusing on what is best for the community
19
+ - Showing empathy towards other community members
20
+
21
+ Examples of unacceptable behavior:
22
+
23
+ - The use of sexualized language or imagery and unwelcome sexual attention or advances
24
+ - Trolling, insulting/derogatory comments, and personal or political attacks
25
+ - Public or private harassment
26
+ - Publishing others' private information without explicit permission
27
+ - Other conduct which could reasonably be considered inappropriate in a professional setting
28
+
29
+ ## Enforcement Responsibilities
30
+
31
+ Project maintainers are responsible for clarifying and enforcing our standards of
32
+ acceptable behavior and will take appropriate and fair corrective action in response
33
+ to any behavior that they deem inappropriate, threatening, offensive, or harmful.
34
+
35
+ ## Scope
36
+
37
+ This Code of Conduct applies within all project spaces, including issues, pull
38
+ requests, discussions, and any other communication channel associated with the project.
39
+
40
+ ## Enforcement
41
+
42
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported
43
+ to the project maintainer at **cheick@example.com**. All complaints will be reviewed
44
+ and investigated promptly and fairly.
45
+
46
+ ## Attribution
47
+
48
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
49
+ version 2.1, available at
50
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
@@ -0,0 +1,114 @@
1
+ # Contributing to smellcheck
2
+
3
+ Thanks for your interest in contributing. smellcheck is a focused tool -- a zero-dependency Python code smell detector built entirely on stdlib. Contributions that preserve that simplicity are welcome.
4
+
5
+ ## Development setup
6
+
7
+ ```bash
8
+ git clone https://github.com/cheickmec/smellcheck.git
9
+ cd smellcheck
10
+ python -m venv .venv && source .venv/bin/activate
11
+ pip install -e .
12
+ pip install pytest pre-commit
13
+ pre-commit install
14
+ ```
15
+
16
+ ## Running tests
17
+
18
+ ```bash
19
+ pytest tests/ -v
20
+ ```
21
+
22
+ Self-check (smellcheck analyzing itself):
23
+
24
+ ```bash
25
+ smellcheck src/smellcheck/ --min-severity warning
26
+ ```
27
+
28
+ ## Project structure
29
+
30
+ ```
31
+ src/smellcheck/
32
+ detector.py # The entire detector: AST visitor, cross-file analysis, CLI
33
+ __init__.py # Public API + version
34
+ __main__.py # python -m smellcheck
35
+ ```
36
+
37
+ The detector is intentionally a single file. The code is tightly coupled (AST visitor + cross-file passes + output formatting) and works well as one unit. Don't split it into modules without a strong reason.
38
+
39
+ ## Adding a new smell check
40
+
41
+ ### Per-file check (AST-based)
42
+
43
+ 1. Add a `_check_<name>` method to the `SmellDetector` class
44
+ 2. Call it from the appropriate `visit_*` method (e.g., `visit_FunctionDef`, `visit_If`)
45
+ 3. Use `self._add(line, pattern, name, severity, message, category)` to report findings
46
+ 4. Pattern format: `#NNN` for numbered patterns (e.g., `#071`), `#ABC` for named patterns
47
+ 5. Add a test in `tests/test_detector.py`
48
+
49
+ Example:
50
+
51
+ ```python
52
+ def _check_too_many_returns(self, node: ast.FunctionDef | ast.AsyncFunctionDef):
53
+ """#071 -- Function with too many return statements."""
54
+ returns = [n for n in ast.walk(node) if isinstance(n, ast.Return)]
55
+ if len(returns) > 5:
56
+ self._add(node.lineno, "#071", "Too Many Returns", "info",
57
+ f"`{node.name}` has {len(returns)} return statements",
58
+ "functions")
59
+ ```
60
+
61
+ ### Cross-file check
62
+
63
+ 1. Add a `_detect_<name>(all_data: list[FileData]) -> list[Finding]` function
64
+ 2. Call it from `cross_file_analysis()`
65
+ 3. If you need new per-file metadata, add fields to `FileData` and populate them in `SmellDetector`
66
+
67
+ ### Thresholds
68
+
69
+ Configurable thresholds live at the top of `detector.py` as `Final` constants. If your check has a numeric threshold, add it there.
70
+
71
+ ## Code style
72
+
73
+ - **Zero dependencies.** Do not add imports beyond Python stdlib. This is a hard rule.
74
+ - **Python 3.10+.** Use `|` unions, pattern matching, modern features.
75
+ - **Type hints** on all public functions.
76
+ - No linter is configured in the repo yet, but keep style consistent with the existing code.
77
+
78
+ ## Commit messages
79
+
80
+ We use [Conventional Commits](https://www.conventionalcommits.org/):
81
+
82
+ ```
83
+ feat: add #071 too-many-returns check
84
+ fix: false positive in #058 with ExitStack
85
+ test: add regression test for elif duplicate findings
86
+ docs: update README with new check
87
+ ```
88
+
89
+ Scopes are optional but helpful: `feat(detector):`, `fix(cli):`, `test(regression):`.
90
+
91
+ ## Pull requests
92
+
93
+ 1. Create a feature branch from `main`
94
+ 2. Make your changes with tests
95
+ 3. Ensure `pytest tests/ -v` passes (all tests, including version parity)
96
+ 4. Open a PR against `main`
97
+
98
+ The PR template will guide you through the checklist.
99
+
100
+ ## Reporting bugs
101
+
102
+ Use the [bug report template](https://github.com/cheickmec/smellcheck/issues/new?template=bug_report.md). Include:
103
+
104
+ - The Python code that triggered the issue
105
+ - Expected vs. actual output
106
+ - Your Python version
107
+
108
+ ## Suggesting new checks
109
+
110
+ Open a [feature request](https://github.com/cheickmec/smellcheck/issues/new?template=feature_request.md). Describe:
111
+
112
+ - What code pattern the check would detect
113
+ - Why it's a smell (reference to Fowler, Contieri, or other literature is a plus)
114
+ - Before/after examples
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cheick Berthe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.