python-code-quality 0.1.8__tar.gz → 0.1.10__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 (59) hide show
  1. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/PKG-INFO +81 -51
  2. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/README.md +75 -45
  3. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/pyproject.toml +5 -5
  4. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/cli.py +54 -5
  5. python_code_quality-0.1.8/src/py_cq/config/tools.yaml → python_code_quality-0.1.10/src/py_cq/config/config.yaml +28 -37
  6. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/execution_engine.py +2 -2
  7. python_code_quality-0.1.10/src/py_cq/language_detector.py +29 -0
  8. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/llm_formatter.py +2 -1
  9. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/localtypes.py +5 -1
  10. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/banditparser.py +2 -2
  11. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/common.py +31 -0
  12. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/compileparser.py +2 -2
  13. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/coverageparser.py +1 -1
  14. python_code_quality-0.1.10/src/py_cq/parsers/exitcodeparser.py +16 -0
  15. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/halsteadparser.py +1 -1
  16. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/interrogateparser.py +1 -1
  17. python_code_quality-0.1.10/src/py_cq/parsers/linecountparser.py +26 -0
  18. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/pytestparser.py +53 -10
  19. python_code_quality-0.1.10/src/py_cq/parsers/regexcountparser.py +35 -0
  20. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/ruffparser.py +2 -2
  21. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/typarser.py +2 -2
  22. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/vultureparser.py +2 -2
  23. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/tool_registry.py +5 -4
  24. python_code_quality-0.1.8/.github/workflows/python-publish.yml +0 -70
  25. python_code_quality-0.1.8/.gitignore +0 -17
  26. python_code_quality-0.1.8/.python-version +0 -1
  27. python_code_quality-0.1.8/CLAUDE.md +0 -54
  28. python_code_quality-0.1.8/LICENSE +0 -21
  29. python_code_quality-0.1.8/data/problems/travelling_salesman/ts_bad.py +0 -65
  30. python_code_quality-0.1.8/data/problems/travelling_salesman/ts_good.py +0 -61
  31. python_code_quality-0.1.8/tests/conftest.py +0 -7
  32. python_code_quality-0.1.8/tests/test_cli.py +0 -228
  33. python_code_quality-0.1.8/tests/test_common.py +0 -73
  34. python_code_quality-0.1.8/tests/test_config.py +0 -84
  35. python_code_quality-0.1.8/tests/test_context_hash.py +0 -81
  36. python_code_quality-0.1.8/tests/test_execution_engine.py +0 -291
  37. python_code_quality-0.1.8/tests/test_llm_formatter.py +0 -237
  38. python_code_quality-0.1.8/tests/test_localtypes.py +0 -74
  39. python_code_quality-0.1.8/tests/test_parser_bandit.py +0 -66
  40. python_code_quality-0.1.8/tests/test_parser_compile.py +0 -61
  41. python_code_quality-0.1.8/tests/test_parser_complexity.py +0 -76
  42. python_code_quality-0.1.8/tests/test_parser_coverage.py +0 -59
  43. python_code_quality-0.1.8/tests/test_parser_halstead.py +0 -116
  44. python_code_quality-0.1.8/tests/test_parser_interrogate.py +0 -72
  45. python_code_quality-0.1.8/tests/test_parser_maintainability.py +0 -74
  46. python_code_quality-0.1.8/tests/test_parser_pytest.py +0 -49
  47. python_code_quality-0.1.8/tests/test_parser_ruff.py +0 -33
  48. python_code_quality-0.1.8/tests/test_parser_ty.py +0 -33
  49. python_code_quality-0.1.8/tests/test_parser_vulture.py +0 -51
  50. python_code_quality-0.1.8/uv.lock +0 -519
  51. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/__init__.py +0 -0
  52. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/config/__init__.py +0 -0
  53. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/context_hash.py +0 -0
  54. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/main.py +0 -0
  55. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/metric_aggregator.py +0 -0
  56. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/__init__.py +0 -0
  57. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/complexityparser.py +0 -0
  58. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/parsers/maintainabilityparser.py +0 -0
  59. {python_code_quality-0.1.8 → python_code_quality-0.1.10}/src/py_cq/py.typed +0 -0
@@ -1,20 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-code-quality
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: Python Code Quality Analysis Tool - feed the results from 11 CQ tools straight into an LLM. Minimal tokens.
5
- Project-URL: Homepage, https://github.com/rhiza-fr/py-cq
6
- Project-URL: Repository, https://github.com/rhiza-fr/py-cq
5
+ Author: Chris Kilner
7
6
  Author-email: Chris Kilner <chris@rhiza.fr>
8
7
  License-Expression: MIT
9
- License-File: LICENSE
10
- Requires-Python: >=3.12
11
8
  Requires-Dist: bandit>=1.8.0
12
9
  Requires-Dist: coverage>=7.8.2
13
10
  Requires-Dist: diskcache>=5.6.3
14
11
  Requires-Dist: interrogate>=1.7.0
12
+ Requires-Dist: pytest>=8.4.0
15
13
  Requires-Dist: pytest-cov>=6.1.1
16
14
  Requires-Dist: pytest-json-report>=1.5.0
17
- Requires-Dist: pytest>=8.4.0
18
15
  Requires-Dist: pyyaml>=6.0.2
19
16
  Requires-Dist: radon>=6.0.1
20
17
  Requires-Dist: rich>=14.0.0
@@ -22,26 +19,25 @@ Requires-Dist: ruff>=0.14.1
22
19
  Requires-Dist: ty>=0.0.17
23
20
  Requires-Dist: typer>=0.16.0
24
21
  Requires-Dist: vulture>=2.14
22
+ Requires-Python: >=3.12
23
+ Project-URL: Homepage, https://github.com/rhiza-fr/py-cq
24
+ Project-URL: Repository, https://github.com/rhiza-fr/py-cq
25
25
  Description-Content-Type: text/markdown
26
26
 
27
27
  # CQ - Python Code Quality Analysis Tool
28
28
 
29
29
  Feed the results from 11+ code quality tools to an LLM. Minimal tokens.
30
30
 
31
+ Why? It removes the mental burden of understanding all these tools and parsing their results.
32
+
31
33
  The primary workflow is:
32
34
 
33
35
  ```bash
34
36
  # get the single most critical defect as markdown
35
37
  cq check . -o llm
36
38
  ```
37
- Selects the single most critical defect using this priority order:
38
-
39
- 1. **Severity** — tools with score below `error_threshold` come before those only below `warning_threshold`
40
- 2. **Order** — among tools at the same severity, lower-order tools win (compile before lint before style)
41
- 3. **Score** — among ties, the lower score wins
42
39
 
43
- The code context is expanded if available.
44
- ```md
40
+ ```python
45
41
  `data/problems/travelling_salesman/ts_bad.py:21` — **F841**: Local variable `unused_variable` is assigned to but never used
46
42
 
47
43
  18: min_dist = float("inf")
@@ -60,9 +56,10 @@ Feed to an LLM with edit tools and repeat until there are no issues, e.g.
60
56
  ```python
61
57
  cq check . -o llm | claude -p "fix this"
62
58
  # or
63
- cq check . -o llm | ollama gpt-oss:20b "Explain how to fix this"
59
+ cq check . -o llm | ollama run gpt-oss:20b "Explain how to fix this"
64
60
  ```
65
61
 
62
+
66
63
  ## Install
67
64
 
68
65
  ```bash
@@ -70,21 +67,22 @@ cq check . -o llm | ollama gpt-oss:20b "Explain how to fix this"
70
67
  uv tool install python-code-quality
71
68
 
72
69
  # or, clone it then install
73
- git pull https://github.com/rhiza-fr/py-cq.git
70
+ git clone https://github.com/rhiza-fr/py-cq.git
74
71
  cd py-cq
75
72
  uv tool install .
76
73
  ```
77
74
 
78
75
  ## Tools
79
76
 
80
- These tools are run in **parallel** except when looking for the first error in -o llm mode:
77
+ These tools are run in **parallel** except:
78
+ When running '-o llm', we run sequentially and exit early at the first error.
81
79
 
82
80
  | Order | Tool | Measures |
83
81
  |----------|------|----------|
84
82
  | 1 | compileall | Syntax errors |
85
- | 2 | bandit | Security vulnerabilities |
86
- | 3 | ruff | Lint / style |
87
- | 4 | ty | Type errors |
83
+ | 2 | ruff | Lint / style |
84
+ | 3 | ty | Type errors |
85
+ | 4 | bandit | Security vulnerabilities |
88
86
  | 5 | pytest | Test pass rate |
89
87
  | 6 | coverage | Test coverage |
90
88
  | 7 | radon cc | Cyclomatic complexity |
@@ -93,14 +91,14 @@ These tools are run in **parallel** except when looking for the first error in -
93
91
  | 10 | vulture | Dead code |
94
92
  | 11 | interrogate | Docstring coverage |
95
93
 
96
- Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults: <100 Mb, <5 days, No pickle
94
+ Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults: <100 Mb, <5 days, No pickle risk.
97
95
 
98
96
 
99
97
  ## Usage
100
98
 
101
99
  ```bash
102
100
  cq check . # Table overview of scores for humans
103
- cq check -o llm # Top defect as markdown for LLMs
101
+ cq check . -o llm # Top defect as markdown for LLMs
104
102
  cq check . -o score # Numeric score only for CI
105
103
  cq check . -o json # Detailed parsed JSON output for jq
106
104
  cq check . -o raw # Raw tool output for debug
@@ -110,6 +108,46 @@ cq check . --clear-cache # Clear cached results before running (rarely needed)
110
108
  cq config path/to/project/ # Show effective tool configuration
111
109
  ```
112
110
 
111
+ **Exit codes:** `cq check` exits with code `1` if any tool metric falls below its `error_threshold`, making it suitable as a CI gate:
112
+
113
+ ```bash
114
+ cq check . && deploy # block deploy on errors
115
+ cq check . -o score # print score, exit 1 on errors
116
+ ```
117
+
118
+ ## Claude Code Integration
119
+
120
+ Add a stop hook to your project's `.claude/settings.json` so Claude automatically checks quality after each session and loops until clean:
121
+
122
+ ```json
123
+ {
124
+ "hooks": {
125
+ "Stop": [{
126
+ "matcher": "",
127
+ "hooks": [{"type": "command", "command": "cq check . -o score && echo 'CQ: all clear' || cq check . -o llm"}]
128
+ }]
129
+ }
130
+ }
131
+ ```
132
+
133
+ When the score passes, Claude sees `CQ: all clear` (~5 tokens). When it fails, Claude receives the targeted fix prompt and continues working. This automates the `cq check . -o llm | claude -p "fix this"` loop.
134
+
135
+ > **Note:** Use project-level `.claude/settings.json`, not global settings — this hook only makes sense in Python projects.
136
+
137
+ ### As a slash command (skill)
138
+
139
+ For manual invocation, create `.claude/commands/cq-fix.md`:
140
+
141
+ ```markdown
142
+ $(cq check . -o llm)
143
+ ```
144
+
145
+ Then invoke it with `/cq-fix` in Claude Code. The `$(...)` embeds the live `cq` output directly into the prompt before Claude starts, so it sees the issue immediately without an extra tool call.
146
+
147
+ **Hook vs skill:**
148
+ - **Stop hook** — automatic, runs after every session, best for unattended loops
149
+ - **Skill** — manual `/cq-fix`, gives you explicit control over when to check
150
+
113
151
  ## Table output
114
152
 
115
153
  ```bash
@@ -121,9 +159,9 @@ cq config path/to/project/ # Show effective tool configuration
121
159
  ┃ Tool ┃ Time ┃ Metric ┃ Score ┃ Status ┃
122
160
  ┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━┩
123
161
  │ compile │ 0.42s │ compile │ 1.000 │ OK │
124
- │ bandit │ 0.56s │ security │ 1.000 │ OK │
125
162
  │ ruff │ 0.17s │ lint │ 1.000 │ OK │
126
163
  │ ty │ 0.33s │ type_check │ 1.000 │ OK │
164
+ │ bandit │ 0.56s │ security │ 1.000 │ OK │
127
165
  │ pytest │ 0.91s │ tests │ 1.000 │ OK │
128
166
  │ coverage │ 1.26s │ coverage │ 0.910 │ OK │
129
167
  │ radon cc │ 0.32s │ simplicity │ 0.982 │ OK │
@@ -167,7 +205,7 @@ cq config path/to/project/ # Show effective tool configuration
167
205
 
168
206
  ## Raw output
169
207
  ```bash
170
- > cq check -o raw
208
+ > cq check . -o raw
171
209
  ```
172
210
 
173
211
  ```json
@@ -193,66 +231,63 @@ Add a `[tool.cq]` section to your project's `pyproject.toml`:
193
231
  # Skip tools that are slow or not relevant to your project
194
232
  disable = ["coverage", "interrogate"]
195
233
 
234
+ # Lines of source context shown around each defect in LLM output (default: 15)
235
+ context_lines = 15
236
+
196
237
  # Override warning/error thresholds per tool
197
238
  [tool.cq.thresholds.coverage]
198
239
  warning = 0.9
199
240
  error = 0.7
200
241
  ```
201
242
 
202
- Tool IDs match the keys in `config/tools.yaml`: `compilation`, `bandit`, `ruff`, `ty`, `pytest`, `coverage`, `complexity`, `maintainability`, `halstead`, `vulture`, `interrogate`.
243
+ Tool IDs match the keys in `config/config.yaml`: `compile`, `ruff`, `ty`, `bandit`, `pytest`, `coverage`, `radon-cc`, `radon-mi`, `radon-hal`, `vulture`, `interrogate`.
203
244
 
204
245
 
205
246
  ### Default config
206
247
 
207
248
  ```yaml
208
- tools:
249
+ python:
209
250
 
210
- compilation:
211
- name: "compile"
251
+ compile:
212
252
  command: "{python} -m compileall -r 10 -j 8 {context_path} -x .*venv"
213
253
  parser: "CompileParser"
214
254
  order: 1
215
255
  warning_threshold: 0.9999
216
256
  error_threshold: 0.9999
217
257
 
218
- bandit:
219
- name: "bandit"
220
- command: "{python} -m bandit -r {context_path} -f json -q -s B101 --severity-level medium --exclude {input_path_posix}/.venv,{input_path_posix}/tests"
221
- parser: "BanditParser"
222
- order: 2
223
- warning_threshold: 0.9999
224
- error_threshold: 0.8
225
-
226
258
  ruff:
227
- name: "ruff"
228
259
  command: "{python} -m ruff check --output-format concise --no-cache {context_path}"
229
260
  parser: "RuffParser"
230
- order: 3
261
+ order: 2
231
262
  warning_threshold: 0.9999
232
263
  error_threshold: 0.9
233
264
 
234
265
  ty:
235
- name: "ty"
236
266
  command: "{python} -m ty check --output-format concise --color never {context_path}"
237
267
  parser: "TyParser"
238
- order: 4
268
+ order: 3
239
269
  warning_threshold: 0.9999
240
270
  error_threshold: 0.8
241
271
  run_in_target_env: true
242
272
  extra_deps:
243
273
  - ty
244
274
 
275
+ bandit:
276
+ command: "{python} -m bandit -r {context_path} -f json -q -s B101 --severity-level medium --exclude {input_path_posix}/.venv,{input_path_posix}/tests"
277
+ parser: "BanditParser"
278
+ order: 4
279
+ warning_threshold: 0.9999
280
+ error_threshold: 0.8
281
+
245
282
  pytest:
246
- name: "pytest"
247
283
  command: "{python} -m pytest -v {context_path}"
248
284
  parser: "PytestParser"
249
285
  order: 5
250
- warning_threshold: 0.7
251
- error_threshold: 0.5
286
+ warning_threshold: 1.0
287
+ error_threshold: 1.0
252
288
  run_in_target_env: true
253
289
 
254
290
  coverage:
255
- name: "coverage"
256
291
  command: "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest {context_path} && {python} -m coverage report --omit=*/tests/*,*/test_*.py"
257
292
  parser: "CoverageParser"
258
293
  order: 6
@@ -263,24 +298,21 @@ tools:
263
298
  - coverage
264
299
  - pytest
265
300
 
266
- complexity:
267
- name: "radon cc"
301
+ radon-cc:
268
302
  command: "{python} -m radon cc --json {context_path}"
269
303
  parser: "ComplexityParser"
270
304
  order: 7
271
305
  warning_threshold: 0.6
272
306
  error_threshold: 0.4
273
307
 
274
- maintainability:
275
- name: "radon mi"
308
+ radon-mi:
276
309
  command: "{python} -m radon mi -s --json {context_path}"
277
310
  parser: "MaintainabilityParser"
278
311
  order: 8
279
312
  warning_threshold: 0.6
280
313
  error_threshold: 0.4
281
314
 
282
- halstead:
283
- name: "radon hal"
315
+ radon-hal:
284
316
  command: "{python} -m radon hal -f --json {context_path}"
285
317
  parser: "HalsteadParser"
286
318
  order: 9
@@ -288,7 +320,6 @@ tools:
288
320
  error_threshold: 0.3
289
321
 
290
322
  vulture:
291
- name: "vulture"
292
323
  command: "{python} -m vulture {context_path} --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git"
293
324
  parser: "VultureParser"
294
325
  order: 10
@@ -296,7 +327,6 @@ tools:
296
327
  error_threshold: 0.8
297
328
 
298
329
  interrogate:
299
- name: "interrogate"
300
330
  command: "{python} -m interrogate {context_path} -v --fail-under 0"
301
331
  parser: "InterrogateParser"
302
332
  order: 11
@@ -2,20 +2,16 @@
2
2
 
3
3
  Feed the results from 11+ code quality tools to an LLM. Minimal tokens.
4
4
 
5
+ Why? It removes the mental burden of understanding all these tools and parsing their results.
6
+
5
7
  The primary workflow is:
6
8
 
7
9
  ```bash
8
10
  # get the single most critical defect as markdown
9
11
  cq check . -o llm
10
12
  ```
11
- Selects the single most critical defect using this priority order:
12
-
13
- 1. **Severity** — tools with score below `error_threshold` come before those only below `warning_threshold`
14
- 2. **Order** — among tools at the same severity, lower-order tools win (compile before lint before style)
15
- 3. **Score** — among ties, the lower score wins
16
13
 
17
- The code context is expanded if available.
18
- ```md
14
+ ```python
19
15
  `data/problems/travelling_salesman/ts_bad.py:21` — **F841**: Local variable `unused_variable` is assigned to but never used
20
16
 
21
17
  18: min_dist = float("inf")
@@ -34,9 +30,10 @@ Feed to an LLM with edit tools and repeat until there are no issues, e.g.
34
30
  ```python
35
31
  cq check . -o llm | claude -p "fix this"
36
32
  # or
37
- cq check . -o llm | ollama gpt-oss:20b "Explain how to fix this"
33
+ cq check . -o llm | ollama run gpt-oss:20b "Explain how to fix this"
38
34
  ```
39
35
 
36
+
40
37
  ## Install
41
38
 
42
39
  ```bash
@@ -44,21 +41,22 @@ cq check . -o llm | ollama gpt-oss:20b "Explain how to fix this"
44
41
  uv tool install python-code-quality
45
42
 
46
43
  # or, clone it then install
47
- git pull https://github.com/rhiza-fr/py-cq.git
44
+ git clone https://github.com/rhiza-fr/py-cq.git
48
45
  cd py-cq
49
46
  uv tool install .
50
47
  ```
51
48
 
52
49
  ## Tools
53
50
 
54
- These tools are run in **parallel** except when looking for the first error in -o llm mode:
51
+ These tools are run in **parallel** except:
52
+ When running '-o llm', we run sequentially and exit early at the first error.
55
53
 
56
54
  | Order | Tool | Measures |
57
55
  |----------|------|----------|
58
56
  | 1 | compileall | Syntax errors |
59
- | 2 | bandit | Security vulnerabilities |
60
- | 3 | ruff | Lint / style |
61
- | 4 | ty | Type errors |
57
+ | 2 | ruff | Lint / style |
58
+ | 3 | ty | Type errors |
59
+ | 4 | bandit | Security vulnerabilities |
62
60
  | 5 | pytest | Test pass rate |
63
61
  | 6 | coverage | Test coverage |
64
62
  | 7 | radon cc | Cyclomatic complexity |
@@ -67,14 +65,14 @@ These tools are run in **parallel** except when looking for the first error in -
67
65
  | 10 | vulture | Dead code |
68
66
  | 11 | interrogate | Docstring coverage |
69
67
 
70
- Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults: <100 Mb, <5 days, No pickle
68
+ Diskcache is used to cache tool output for lightning fast re-runs. Sane defaults: <100 Mb, <5 days, No pickle risk.
71
69
 
72
70
 
73
71
  ## Usage
74
72
 
75
73
  ```bash
76
74
  cq check . # Table overview of scores for humans
77
- cq check -o llm # Top defect as markdown for LLMs
75
+ cq check . -o llm # Top defect as markdown for LLMs
78
76
  cq check . -o score # Numeric score only for CI
79
77
  cq check . -o json # Detailed parsed JSON output for jq
80
78
  cq check . -o raw # Raw tool output for debug
@@ -84,6 +82,46 @@ cq check . --clear-cache # Clear cached results before running (rarely needed)
84
82
  cq config path/to/project/ # Show effective tool configuration
85
83
  ```
86
84
 
85
+ **Exit codes:** `cq check` exits with code `1` if any tool metric falls below its `error_threshold`, making it suitable as a CI gate:
86
+
87
+ ```bash
88
+ cq check . && deploy # block deploy on errors
89
+ cq check . -o score # print score, exit 1 on errors
90
+ ```
91
+
92
+ ## Claude Code Integration
93
+
94
+ Add a stop hook to your project's `.claude/settings.json` so Claude automatically checks quality after each session and loops until clean:
95
+
96
+ ```json
97
+ {
98
+ "hooks": {
99
+ "Stop": [{
100
+ "matcher": "",
101
+ "hooks": [{"type": "command", "command": "cq check . -o score && echo 'CQ: all clear' || cq check . -o llm"}]
102
+ }]
103
+ }
104
+ }
105
+ ```
106
+
107
+ When the score passes, Claude sees `CQ: all clear` (~5 tokens). When it fails, Claude receives the targeted fix prompt and continues working. This automates the `cq check . -o llm | claude -p "fix this"` loop.
108
+
109
+ > **Note:** Use project-level `.claude/settings.json`, not global settings — this hook only makes sense in Python projects.
110
+
111
+ ### As a slash command (skill)
112
+
113
+ For manual invocation, create `.claude/commands/cq-fix.md`:
114
+
115
+ ```markdown
116
+ $(cq check . -o llm)
117
+ ```
118
+
119
+ Then invoke it with `/cq-fix` in Claude Code. The `$(...)` embeds the live `cq` output directly into the prompt before Claude starts, so it sees the issue immediately without an extra tool call.
120
+
121
+ **Hook vs skill:**
122
+ - **Stop hook** — automatic, runs after every session, best for unattended loops
123
+ - **Skill** — manual `/cq-fix`, gives you explicit control over when to check
124
+
87
125
  ## Table output
88
126
 
89
127
  ```bash
@@ -95,9 +133,9 @@ cq config path/to/project/ # Show effective tool configuration
95
133
  ┃ Tool ┃ Time ┃ Metric ┃ Score ┃ Status ┃
96
134
  ┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━┩
97
135
  │ compile │ 0.42s │ compile │ 1.000 │ OK │
98
- │ bandit │ 0.56s │ security │ 1.000 │ OK │
99
136
  │ ruff │ 0.17s │ lint │ 1.000 │ OK │
100
137
  │ ty │ 0.33s │ type_check │ 1.000 │ OK │
138
+ │ bandit │ 0.56s │ security │ 1.000 │ OK │
101
139
  │ pytest │ 0.91s │ tests │ 1.000 │ OK │
102
140
  │ coverage │ 1.26s │ coverage │ 0.910 │ OK │
103
141
  │ radon cc │ 0.32s │ simplicity │ 0.982 │ OK │
@@ -141,7 +179,7 @@ cq config path/to/project/ # Show effective tool configuration
141
179
 
142
180
  ## Raw output
143
181
  ```bash
144
- > cq check -o raw
182
+ > cq check . -o raw
145
183
  ```
146
184
 
147
185
  ```json
@@ -167,66 +205,63 @@ Add a `[tool.cq]` section to your project's `pyproject.toml`:
167
205
  # Skip tools that are slow or not relevant to your project
168
206
  disable = ["coverage", "interrogate"]
169
207
 
208
+ # Lines of source context shown around each defect in LLM output (default: 15)
209
+ context_lines = 15
210
+
170
211
  # Override warning/error thresholds per tool
171
212
  [tool.cq.thresholds.coverage]
172
213
  warning = 0.9
173
214
  error = 0.7
174
215
  ```
175
216
 
176
- Tool IDs match the keys in `config/tools.yaml`: `compilation`, `bandit`, `ruff`, `ty`, `pytest`, `coverage`, `complexity`, `maintainability`, `halstead`, `vulture`, `interrogate`.
217
+ Tool IDs match the keys in `config/config.yaml`: `compile`, `ruff`, `ty`, `bandit`, `pytest`, `coverage`, `radon-cc`, `radon-mi`, `radon-hal`, `vulture`, `interrogate`.
177
218
 
178
219
 
179
220
  ### Default config
180
221
 
181
222
  ```yaml
182
- tools:
223
+ python:
183
224
 
184
- compilation:
185
- name: "compile"
225
+ compile:
186
226
  command: "{python} -m compileall -r 10 -j 8 {context_path} -x .*venv"
187
227
  parser: "CompileParser"
188
228
  order: 1
189
229
  warning_threshold: 0.9999
190
230
  error_threshold: 0.9999
191
231
 
192
- bandit:
193
- name: "bandit"
194
- command: "{python} -m bandit -r {context_path} -f json -q -s B101 --severity-level medium --exclude {input_path_posix}/.venv,{input_path_posix}/tests"
195
- parser: "BanditParser"
196
- order: 2
197
- warning_threshold: 0.9999
198
- error_threshold: 0.8
199
-
200
232
  ruff:
201
- name: "ruff"
202
233
  command: "{python} -m ruff check --output-format concise --no-cache {context_path}"
203
234
  parser: "RuffParser"
204
- order: 3
235
+ order: 2
205
236
  warning_threshold: 0.9999
206
237
  error_threshold: 0.9
207
238
 
208
239
  ty:
209
- name: "ty"
210
240
  command: "{python} -m ty check --output-format concise --color never {context_path}"
211
241
  parser: "TyParser"
212
- order: 4
242
+ order: 3
213
243
  warning_threshold: 0.9999
214
244
  error_threshold: 0.8
215
245
  run_in_target_env: true
216
246
  extra_deps:
217
247
  - ty
218
248
 
249
+ bandit:
250
+ command: "{python} -m bandit -r {context_path} -f json -q -s B101 --severity-level medium --exclude {input_path_posix}/.venv,{input_path_posix}/tests"
251
+ parser: "BanditParser"
252
+ order: 4
253
+ warning_threshold: 0.9999
254
+ error_threshold: 0.8
255
+
219
256
  pytest:
220
- name: "pytest"
221
257
  command: "{python} -m pytest -v {context_path}"
222
258
  parser: "PytestParser"
223
259
  order: 5
224
- warning_threshold: 0.7
225
- error_threshold: 0.5
260
+ warning_threshold: 1.0
261
+ error_threshold: 1.0
226
262
  run_in_target_env: true
227
263
 
228
264
  coverage:
229
- name: "coverage"
230
265
  command: "{python} -m coverage run --omit=*/tests/*,*/test_*.py -m pytest {context_path} && {python} -m coverage report --omit=*/tests/*,*/test_*.py"
231
266
  parser: "CoverageParser"
232
267
  order: 6
@@ -237,24 +272,21 @@ tools:
237
272
  - coverage
238
273
  - pytest
239
274
 
240
- complexity:
241
- name: "radon cc"
275
+ radon-cc:
242
276
  command: "{python} -m radon cc --json {context_path}"
243
277
  parser: "ComplexityParser"
244
278
  order: 7
245
279
  warning_threshold: 0.6
246
280
  error_threshold: 0.4
247
281
 
248
- maintainability:
249
- name: "radon mi"
282
+ radon-mi:
250
283
  command: "{python} -m radon mi -s --json {context_path}"
251
284
  parser: "MaintainabilityParser"
252
285
  order: 8
253
286
  warning_threshold: 0.6
254
287
  error_threshold: 0.4
255
288
 
256
- halstead:
257
- name: "radon hal"
289
+ radon-hal:
258
290
  command: "{python} -m radon hal -f --json {context_path}"
259
291
  parser: "HalsteadParser"
260
292
  order: 9
@@ -262,7 +294,6 @@ tools:
262
294
  error_threshold: 0.3
263
295
 
264
296
  vulture:
265
- name: "vulture"
266
297
  command: "{python} -m vulture {context_path} --min-confidence 80 --exclude .venv,dist,.*_cache,docs,.git"
267
298
  parser: "VultureParser"
268
299
  order: 10
@@ -270,7 +301,6 @@ tools:
270
301
  error_threshold: 0.8
271
302
 
272
303
  interrogate:
273
- name: "interrogate"
274
304
  command: "{python} -m interrogate {context_path} -v --fail-under 0"
275
305
  parser: "InterrogateParser"
276
306
  order: 11
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-code-quality"
3
- version = "0.1.8"
3
+ version = "0.1.10"
4
4
  description = "Python Code Quality Analysis Tool - feed the results from 11 CQ tools straight into an LLM. Minimal tokens."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -29,11 +29,11 @@ Homepage = "https://github.com/rhiza-fr/py-cq"
29
29
  Repository = "https://github.com/rhiza-fr/py-cq"
30
30
 
31
31
  [build-system]
32
- requires = ["hatchling"]
33
- build-backend = "hatchling.build"
32
+ requires = ["uv_build"]
33
+ build-backend = "uv_build"
34
34
 
35
- [tool.hatch.build.targets.wheel]
36
- packages = ["src/py_cq"]
35
+ [tool.uv.build-backend]
36
+ module-name = "py_cq"
37
37
 
38
38
  [project.scripts]
39
39
  cq = "py_cq.main:main"