pyossmtool 0.0.1__tar.gz → 0.0.3__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 (142) hide show
  1. pyossmtool-0.0.3/PKG-INFO +259 -0
  2. pyossmtool-0.0.3/README.md +230 -0
  3. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/pyproject.toml +1 -1
  4. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/__init__.py +1 -1
  5. pyossmtool-0.0.3/src/pyossmtool/bundle/suites/all.yaml +51 -0
  6. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/cli.py +49 -17
  7. pyossmtool-0.0.3/src/pyossmtool/error_formatters.py +218 -0
  8. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/models.py +26 -0
  9. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/reporter.py +1 -1
  10. pyossmtool-0.0.3/src/pyossmtool.egg-info/PKG-INFO +259 -0
  11. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool.egg-info/SOURCES.txt +3 -0
  12. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_check_modes.py +7 -0
  13. pyossmtool-0.0.3/tests/test_cli.py +49 -0
  14. pyossmtool-0.0.3/tests/test_error_formatters.py +117 -0
  15. pyossmtool-0.0.1/PKG-INFO +0 -94
  16. pyossmtool-0.0.1/README.md +0 -65
  17. pyossmtool-0.0.1/src/pyossmtool.egg-info/PKG-INFO +0 -94
  18. pyossmtool-0.0.1/tests/test_cli.py +0 -17
  19. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/LICENSE +0 -0
  20. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/setup.cfg +0 -0
  21. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/bandit.scan.yaml +0 -0
  22. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/codespell.spelling.yaml +0 -0
  23. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/deadcode.scan.yaml +0 -0
  24. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/gate.acronym-allowlist.yaml +0 -0
  25. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/gate.folder-breadth.yaml +0 -0
  26. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/gate.module-private-vars.yaml +0 -0
  27. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/gate.module-size.yaml +0 -0
  28. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/gitleaks.secrets.yaml +0 -0
  29. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/hadolint.dockerfile.yaml +0 -0
  30. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/jscpd.duplication.yaml +0 -0
  31. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/markdownlint.check.yaml +0 -0
  32. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/mdformat.apply.yaml +0 -0
  33. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/mdformat.format.yaml +0 -0
  34. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/mutmut.run.yaml +0 -0
  35. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/pydeps.cycles.yaml +0 -0
  36. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/pytest.coverage.yaml +0 -0
  37. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/pytest.test.yaml +0 -0
  38. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/radon.cc.yaml +0 -0
  39. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/radon.mi.yaml +0 -0
  40. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/ruff.format.apply.yaml +0 -0
  41. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/ruff.format.yaml +0 -0
  42. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/ruff.lint.yaml +0 -0
  43. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/ruff.unused.yaml +0 -0
  44. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/semgrep.scan.yaml +0 -0
  45. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/shellcheck.yaml +0 -0
  46. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/shfmt.apply.yaml +0 -0
  47. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/shfmt.format.yaml +0 -0
  48. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/sourcery.review.yaml +0 -0
  49. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/ty.check.yaml +0 -0
  50. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/vulture.scan.yaml +0 -0
  51. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/yamlfmt.apply.yaml +0 -0
  52. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/yamlfmt.format.yaml +0 -0
  53. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/yamllint.check.yaml +0 -0
  54. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/bandit.yaml +0 -0
  55. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/codespell.yaml +0 -0
  56. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/deadcode.yaml +0 -0
  57. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/gitleaks.yaml +0 -0
  58. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/hadolint.yaml +0 -0
  59. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/jscpd.yaml +0 -0
  60. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/markdownlint.yaml +0 -0
  61. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/mdformat.yaml +0 -0
  62. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/mutmut.yaml +0 -0
  63. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/pydeps.yaml +0 -0
  64. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/pytest.yaml +0 -0
  65. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/radon.yaml +0 -0
  66. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/ruff.yaml +0 -0
  67. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/script.yaml +0 -0
  68. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/semgrep.yaml +0 -0
  69. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/shellcheck.yaml +0 -0
  70. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/shfmt.yaml +0 -0
  71. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/sourcery.yaml +0 -0
  72. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/ty.yaml +0 -0
  73. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/vulture.yaml +0 -0
  74. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/yamlfmt.yaml +0 -0
  75. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/yamllint.yaml +0 -0
  76. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/allowlists/acronyms.yaml +0 -0
  77. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/allowlists/folder-breadth.txt +0 -0
  78. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/allowlists/module-private-vars.txt +0 -0
  79. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/allowlists/module-size.txt +0 -0
  80. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/bandit.yaml +0 -0
  81. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/gates/acronym-allowlist.yaml +0 -0
  82. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/gates/folder-breadth.yaml +0 -0
  83. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/gates/module-private-vars.yaml +0 -0
  84. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/gates/module-size.yaml +0 -0
  85. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/markdownlint.json +0 -0
  86. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/ruff.toml +0 -0
  87. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/ty.toml +0 -0
  88. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/yamlfmt.yaml +0 -0
  89. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/yamllint.yaml +0 -0
  90. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/README.md +0 -0
  91. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/acronym-allowlist.sh +0 -0
  92. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/folder-breadth.sh +0 -0
  93. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/lib.sh +0 -0
  94. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/module-private-vars.sh +0 -0
  95. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/module-size.sh +0 -0
  96. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/semgrep/python-quality.yml +0 -0
  97. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/demo.yaml +0 -0
  98. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/extended.yaml +0 -0
  99. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/format.yaml +0 -0
  100. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/formatting.yaml +0 -0
  101. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/policy.yaml +0 -0
  102. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/python-quality.yaml +0 -0
  103. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/reslab-parity.yaml +0 -0
  104. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/standard.yaml +0 -0
  105. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/config_resolver.py +0 -0
  106. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/discovery.py +0 -0
  107. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/gate_config.py +0 -0
  108. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/gates.py +0 -0
  109. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/ignore.py +0 -0
  110. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/ignore_materialize.py +0 -0
  111. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/installer.py +0 -0
  112. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/__init__.py +0 -0
  113. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/analysis.py +0 -0
  114. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/base.py +0 -0
  115. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/common.py +0 -0
  116. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/format_text.py +0 -0
  117. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/gates.py +0 -0
  118. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/patterns.py +0 -0
  119. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/prose.py +0 -0
  120. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/ruff.py +0 -0
  121. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/shell.py +0 -0
  122. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/policy/__init__.py +0 -0
  123. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/policy/acronym_allowlist.py +0 -0
  124. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/policy/folder_breadth.py +0 -0
  125. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/registry.py +0 -0
  126. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/resolver.py +0 -0
  127. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/runner.py +0 -0
  128. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/runner_script.py +0 -0
  129. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/target_expand.py +0 -0
  130. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool.egg-info/dependency_links.txt +0 -0
  131. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool.egg-info/entry_points.txt +0 -0
  132. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool.egg-info/requires.txt +0 -0
  133. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool.egg-info/top_level.txt +0 -0
  134. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_config_and_targets.py +0 -0
  135. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_discovery.py +0 -0
  136. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_gate_config.py +0 -0
  137. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_ignore.py +0 -0
  138. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_npm_install.py +0 -0
  139. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_packaging.py +0 -0
  140. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_parser_registry.py +0 -0
  141. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_script_gates.py +0 -0
  142. {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_wheel_install.py +0 -0
@@ -0,0 +1,259 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyossmtool
3
+ Version: 0.0.3
4
+ Summary: Quality-gate orchestrator for AI-assisted development
5
+ Author-email: Vishal Kumar Mishra <vishal.k.mishra2@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Changelog, https://github.com/inquilabee/pyossmtool/blob/main/CHANGELOG.md
8
+ Project-URL: Homepage, https://github.com/inquilabee/pyossmtool
9
+ Project-URL: Issues, https://github.com/inquilabee/pyossmtool/issues
10
+ Project-URL: Repository, https://github.com/inquilabee/pyossmtool
11
+ Keywords: quality,lint,format,pre-commit,python,static-analysis
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Quality Assurance
21
+ Requires-Python: <3.14,>=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: pydantic>=2.0
25
+ Requires-Dist: pathspec>=0.12
26
+ Requires-Dist: pyyaml>=6.0
27
+ Requires-Dist: typer>=0.12
28
+ Dynamic: license-file
29
+
30
+ # pyossmtool
31
+
32
+ Quality gates for Python repos that developers can run locally, in CI, or hand to an AI agent without inventing a new workflow each time.
33
+
34
+ `pyossmtool` gives you one project config, a bundled catalog of checks, quiet success, and structured failure reports. It is inspired by Trunk and pre-commit, but keeps the surface area small:
35
+
36
+ ```bash
37
+ pyossmtool install
38
+ pyossmtool format
39
+ pyossmtool check
40
+ ```
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ pip install pyossmtool
46
+ # or
47
+ uv add --dev pyossmtool
48
+ ```
49
+
50
+ Requires Python 3.11-3.13.
51
+
52
+ ## 60-Second Setup
53
+
54
+ Create `pyossmtool.yaml`:
55
+
56
+ ```yaml
57
+ suite: all
58
+ env: auto
59
+ target: .
60
+ error-format: compact
61
+ configs:
62
+ mode: auto
63
+ ```
64
+
65
+ Then run:
66
+
67
+ ```bash
68
+ pyossmtool install # install tools needed by suite:
69
+ pyossmtool format # apply formatter/autofix checks
70
+ pyossmtool check # report-only quality checks
71
+ ```
72
+
73
+ The `suite:` value is the project default. You do not need to repeat `--suite all`; the CLI reads it from `pyossmtool.yaml`.
74
+
75
+ Success is silent and exits `0`. Failures exit `1`, write a JSON report under `reports/failures/`, and print the same report through your configured `error-format`.
76
+
77
+ ## Mental Model
78
+
79
+ - **Suite**: a named checklist, such as `python-quality`, `standard`, or `all`.
80
+ - **Check**: one rule runner inside a suite, such as `ruff.lint` or `ty.check`.
81
+ - **`check`**: report-only. It should not rewrite your files.
82
+ - **`format`**: applies write/fix checks, such as formatters.
83
+ - **`install`**: installs the tools needed by the selected suite.
84
+
85
+ Most teams pick a suite once in `pyossmtool.yaml` and run the same three commands everywhere.
86
+
87
+ ## Config That Matters
88
+
89
+ ```yaml
90
+ suite: python-quality # default checklist for install/check/format
91
+ env: auto # auto | managed | project
92
+ target: . # scan root; defaults to .
93
+ error-format: compact # json | log | text | compact | github
94
+
95
+ configs:
96
+ mode: auto # repo configs first, bundled fallback
97
+ ```
98
+
99
+ Use `--suite` only when you want a one-off override:
100
+
101
+ ```bash
102
+ pyossmtool check --suite extended
103
+ pyossmtool install --suite standard
104
+ ```
105
+
106
+ Copy [`pyossmtool.yaml.example`](pyossmtool.yaml.example) for an annotated config with ignores, custom checks, gates, and custom error formatters.
107
+
108
+ ## Suites
109
+
110
+ Suites are bundled starting points. They choose which checks run; tools still discover their own files and `.gitignore` is always respected.
111
+
112
+ | Suite | Use it for |
113
+ | --- | --- |
114
+ | `all` | Run every bundled check |
115
+ | `python-quality` | Core Python lint, format-check, and type checks |
116
+ | `formatting` | Report-only formatting drift |
117
+ | `format` | Formatter/autofix checks that write files |
118
+ | `standard` | Broader portable quality baseline |
119
+ | `extended` | Slower optional checks on top of standard |
120
+ | `policy` | Bundled script gates |
121
+ | `demo` | Internal/sample fixture suite |
122
+
123
+ Examples:
124
+
125
+ ```yaml
126
+ # Local Python repo
127
+ suite: python-quality
128
+ error-format: text
129
+ ```
130
+
131
+ ```yaml
132
+ # CI baseline with GitHub annotations
133
+ suite: standard
134
+ error-format: github
135
+ ```
136
+
137
+ ```yaml
138
+ # Max coverage while developing pyossmtool itself
139
+ suite: all
140
+ error-format: compact
141
+ ```
142
+
143
+ ## Error Output
144
+
145
+ On failure, pyossmtool always writes the canonical JSON report to disk:
146
+
147
+ ```text
148
+ reports/failures/ruff.lint-20260716T163000Z/report.json
149
+ ```
150
+
151
+ `error-format` only controls what is printed to stderr.
152
+
153
+ | Format | Example output |
154
+ | --- | --- |
155
+ | `json` | Pretty JSON report, including `report_path` |
156
+ | `log` | `2026-07-16T12:00:00+00:00 [error] ruff.lint/E501 src/app.py:42: Line too long` |
157
+ | `text` | `- [error] E501: Line too long (src/app.py:42)` |
158
+ | `compact` | `src/app.py:42: error: E501 Line too long` |
159
+ | `github` | `::error file=src/app.py,title=ruff.lint/E501,line=42::Line too long` |
160
+
161
+ Default is `json` when `error-format` is omitted.
162
+
163
+ Custom formatters live in `pyossmtool.yaml`:
164
+
165
+ ```yaml
166
+ suite: all
167
+ error-format: short
168
+ error-formatters:
169
+ short:
170
+ kind: finding_line
171
+ template: "{severity}\t{rule_id}\t{file}:{line}\t{message}"
172
+ jq-summary:
173
+ kind: jq
174
+ program: >
175
+ .findings[] | "[\(.severity)] \(.rule_id) \(.message)"
176
+ ```
177
+
178
+ `finding_line` placeholders: `severity`, `rule_id`, `message`, `file`, `line`, `check_id`, `report_path`.
179
+
180
+ `jq` formatters require `jq` on `PATH`.
181
+
182
+ ## Daily Commands
183
+
184
+ ```bash
185
+ pyossmtool install
186
+ pyossmtool format
187
+ pyossmtool check
188
+ ```
189
+
190
+ Inspect what is available:
191
+
192
+ ```bash
193
+ pyossmtool list suites
194
+ pyossmtool list tools
195
+ pyossmtool list checks
196
+ ```
197
+
198
+ Run a single check:
199
+
200
+ ```bash
201
+ pyossmtool check --check ruff.lint --target .
202
+ ```
203
+
204
+ Export the failure report schema:
205
+
206
+ ```bash
207
+ pyossmtool schema > failure-report.schema.json
208
+ ```
209
+
210
+ ## Project-Local Gates
211
+
212
+ Use gates when a repo needs a policy that is not covered by the bundled catalog.
213
+
214
+ ```bash
215
+ pyossmtool gate init module-size --description "Cap module line counts"
216
+ ```
217
+
218
+ That creates a shell gate under `.pyossmtool/gates/` and a catalog entry under `.pyossmtool/catalog/checks/`. Enable it from `pyossmtool.yaml`:
219
+
220
+ ```yaml
221
+ checks:
222
+ - id: gate.module-size
223
+ ```
224
+
225
+ ## CI
226
+
227
+ Minimal GitHub Actions job:
228
+
229
+ ```yaml
230
+ name: quality
231
+
232
+ on:
233
+ pull_request:
234
+ push:
235
+ branches: [main]
236
+
237
+ jobs:
238
+ pyossmtool:
239
+ runs-on: ubuntu-latest
240
+ steps:
241
+ - uses: actions/checkout@v4
242
+ - uses: astral-sh/setup-uv@v6
243
+ - run: uvx pyossmtool install
244
+ - run: uvx pyossmtool check
245
+ ```
246
+
247
+ For GitHub PR annotations, set:
248
+
249
+ ```yaml
250
+ error-format: github
251
+ ```
252
+
253
+ ## Contributing
254
+
255
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for local development, adding tools/checks, and release notes.
256
+
257
+ ## License
258
+
259
+ MIT
@@ -0,0 +1,230 @@
1
+ # pyossmtool
2
+
3
+ Quality gates for Python repos that developers can run locally, in CI, or hand to an AI agent without inventing a new workflow each time.
4
+
5
+ `pyossmtool` gives you one project config, a bundled catalog of checks, quiet success, and structured failure reports. It is inspired by Trunk and pre-commit, but keeps the surface area small:
6
+
7
+ ```bash
8
+ pyossmtool install
9
+ pyossmtool format
10
+ pyossmtool check
11
+ ```
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pip install pyossmtool
17
+ # or
18
+ uv add --dev pyossmtool
19
+ ```
20
+
21
+ Requires Python 3.11-3.13.
22
+
23
+ ## 60-Second Setup
24
+
25
+ Create `pyossmtool.yaml`:
26
+
27
+ ```yaml
28
+ suite: all
29
+ env: auto
30
+ target: .
31
+ error-format: compact
32
+ configs:
33
+ mode: auto
34
+ ```
35
+
36
+ Then run:
37
+
38
+ ```bash
39
+ pyossmtool install # install tools needed by suite:
40
+ pyossmtool format # apply formatter/autofix checks
41
+ pyossmtool check # report-only quality checks
42
+ ```
43
+
44
+ The `suite:` value is the project default. You do not need to repeat `--suite all`; the CLI reads it from `pyossmtool.yaml`.
45
+
46
+ Success is silent and exits `0`. Failures exit `1`, write a JSON report under `reports/failures/`, and print the same report through your configured `error-format`.
47
+
48
+ ## Mental Model
49
+
50
+ - **Suite**: a named checklist, such as `python-quality`, `standard`, or `all`.
51
+ - **Check**: one rule runner inside a suite, such as `ruff.lint` or `ty.check`.
52
+ - **`check`**: report-only. It should not rewrite your files.
53
+ - **`format`**: applies write/fix checks, such as formatters.
54
+ - **`install`**: installs the tools needed by the selected suite.
55
+
56
+ Most teams pick a suite once in `pyossmtool.yaml` and run the same three commands everywhere.
57
+
58
+ ## Config That Matters
59
+
60
+ ```yaml
61
+ suite: python-quality # default checklist for install/check/format
62
+ env: auto # auto | managed | project
63
+ target: . # scan root; defaults to .
64
+ error-format: compact # json | log | text | compact | github
65
+
66
+ configs:
67
+ mode: auto # repo configs first, bundled fallback
68
+ ```
69
+
70
+ Use `--suite` only when you want a one-off override:
71
+
72
+ ```bash
73
+ pyossmtool check --suite extended
74
+ pyossmtool install --suite standard
75
+ ```
76
+
77
+ Copy [`pyossmtool.yaml.example`](pyossmtool.yaml.example) for an annotated config with ignores, custom checks, gates, and custom error formatters.
78
+
79
+ ## Suites
80
+
81
+ Suites are bundled starting points. They choose which checks run; tools still discover their own files and `.gitignore` is always respected.
82
+
83
+ | Suite | Use it for |
84
+ | --- | --- |
85
+ | `all` | Run every bundled check |
86
+ | `python-quality` | Core Python lint, format-check, and type checks |
87
+ | `formatting` | Report-only formatting drift |
88
+ | `format` | Formatter/autofix checks that write files |
89
+ | `standard` | Broader portable quality baseline |
90
+ | `extended` | Slower optional checks on top of standard |
91
+ | `policy` | Bundled script gates |
92
+ | `demo` | Internal/sample fixture suite |
93
+
94
+ Examples:
95
+
96
+ ```yaml
97
+ # Local Python repo
98
+ suite: python-quality
99
+ error-format: text
100
+ ```
101
+
102
+ ```yaml
103
+ # CI baseline with GitHub annotations
104
+ suite: standard
105
+ error-format: github
106
+ ```
107
+
108
+ ```yaml
109
+ # Max coverage while developing pyossmtool itself
110
+ suite: all
111
+ error-format: compact
112
+ ```
113
+
114
+ ## Error Output
115
+
116
+ On failure, pyossmtool always writes the canonical JSON report to disk:
117
+
118
+ ```text
119
+ reports/failures/ruff.lint-20260716T163000Z/report.json
120
+ ```
121
+
122
+ `error-format` only controls what is printed to stderr.
123
+
124
+ | Format | Example output |
125
+ | --- | --- |
126
+ | `json` | Pretty JSON report, including `report_path` |
127
+ | `log` | `2026-07-16T12:00:00+00:00 [error] ruff.lint/E501 src/app.py:42: Line too long` |
128
+ | `text` | `- [error] E501: Line too long (src/app.py:42)` |
129
+ | `compact` | `src/app.py:42: error: E501 Line too long` |
130
+ | `github` | `::error file=src/app.py,title=ruff.lint/E501,line=42::Line too long` |
131
+
132
+ Default is `json` when `error-format` is omitted.
133
+
134
+ Custom formatters live in `pyossmtool.yaml`:
135
+
136
+ ```yaml
137
+ suite: all
138
+ error-format: short
139
+ error-formatters:
140
+ short:
141
+ kind: finding_line
142
+ template: "{severity}\t{rule_id}\t{file}:{line}\t{message}"
143
+ jq-summary:
144
+ kind: jq
145
+ program: >
146
+ .findings[] | "[\(.severity)] \(.rule_id) \(.message)"
147
+ ```
148
+
149
+ `finding_line` placeholders: `severity`, `rule_id`, `message`, `file`, `line`, `check_id`, `report_path`.
150
+
151
+ `jq` formatters require `jq` on `PATH`.
152
+
153
+ ## Daily Commands
154
+
155
+ ```bash
156
+ pyossmtool install
157
+ pyossmtool format
158
+ pyossmtool check
159
+ ```
160
+
161
+ Inspect what is available:
162
+
163
+ ```bash
164
+ pyossmtool list suites
165
+ pyossmtool list tools
166
+ pyossmtool list checks
167
+ ```
168
+
169
+ Run a single check:
170
+
171
+ ```bash
172
+ pyossmtool check --check ruff.lint --target .
173
+ ```
174
+
175
+ Export the failure report schema:
176
+
177
+ ```bash
178
+ pyossmtool schema > failure-report.schema.json
179
+ ```
180
+
181
+ ## Project-Local Gates
182
+
183
+ Use gates when a repo needs a policy that is not covered by the bundled catalog.
184
+
185
+ ```bash
186
+ pyossmtool gate init module-size --description "Cap module line counts"
187
+ ```
188
+
189
+ That creates a shell gate under `.pyossmtool/gates/` and a catalog entry under `.pyossmtool/catalog/checks/`. Enable it from `pyossmtool.yaml`:
190
+
191
+ ```yaml
192
+ checks:
193
+ - id: gate.module-size
194
+ ```
195
+
196
+ ## CI
197
+
198
+ Minimal GitHub Actions job:
199
+
200
+ ```yaml
201
+ name: quality
202
+
203
+ on:
204
+ pull_request:
205
+ push:
206
+ branches: [main]
207
+
208
+ jobs:
209
+ pyossmtool:
210
+ runs-on: ubuntu-latest
211
+ steps:
212
+ - uses: actions/checkout@v4
213
+ - uses: astral-sh/setup-uv@v6
214
+ - run: uvx pyossmtool install
215
+ - run: uvx pyossmtool check
216
+ ```
217
+
218
+ For GitHub PR annotations, set:
219
+
220
+ ```yaml
221
+ error-format: github
222
+ ```
223
+
224
+ ## Contributing
225
+
226
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for local development, adding tools/checks, and release notes.
227
+
228
+ ## License
229
+
230
+ MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyossmtool"
3
- version = "0.0.1"
3
+ version = "0.0.3"
4
4
  description = "Quality-gate orchestrator for AI-assisted development"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11,<3.14"
@@ -1,3 +1,3 @@
1
1
  """pyossmtool — quality-gate orchestrator for AI-assisted development."""
2
2
 
3
- __version__ = "0.0.1"
3
+ __version__ = "0.0.3"
@@ -0,0 +1,51 @@
1
+ id: all
2
+ name: All Checks
3
+ description: Full catalog — every bundled check (check-mode and format-apply). Use
4
+ pyossmtool check for report-only checks and pyossmtool format for apply checks.
5
+ env: auto
6
+ target: .
7
+ ignore-profile:
8
+ - .gitignore
9
+ - .cursorignore
10
+ ignore-paths:
11
+ - .pyossmtool/
12
+ - venv/
13
+ - .venv/
14
+ - reports/
15
+ - sample_files/
16
+ checks:
17
+ - id: ruff.format
18
+ - id: ruff.lint
19
+ - id: ruff.unused
20
+ - id: ty.check
21
+ - id: bandit.scan
22
+ - id: gitleaks.secrets
23
+ - id: semgrep.scan
24
+ - id: deadcode.scan
25
+ - id: vulture.scan
26
+ - id: pydeps.cycles
27
+ - id: radon.cc
28
+ - id: radon.mi
29
+ - id: jscpd.duplication
30
+ - id: shfmt.format
31
+ - id: shellcheck
32
+ - id: mdformat.format
33
+ - id: markdownlint.check
34
+ - id: yamlfmt.format
35
+ - id: yamllint.check
36
+ - id: codespell.spelling
37
+ - id: pytest.test
38
+ target: tests/
39
+ - id: pytest.coverage
40
+ target: tests/
41
+ - id: hadolint.dockerfile
42
+ - id: mutmut.run
43
+ - id: sourcery.review
44
+ - id: gate.module-size
45
+ - id: gate.module-private-vars
46
+ - id: gate.folder-breadth
47
+ - id: gate.acronym-allowlist
48
+ - id: ruff.format.apply
49
+ - id: shfmt.apply
50
+ - id: mdformat.apply
51
+ - id: yamlfmt.apply
@@ -7,9 +7,10 @@ from pathlib import Path
7
7
 
8
8
  import typer
9
9
 
10
+ from pyossmtool.error_formatters import ErrorFormatter, format_report_file, resolve_error_formatter
10
11
  from pyossmtool.gates import lib_path, scaffold_gate
11
12
  from pyossmtool.installer import Installer
12
- from pyossmtool.models import CheckMode, FailureReport
13
+ from pyossmtool.models import CheckMode, FailureReport, ProjectConfig
13
14
  from pyossmtool.registry import Registry
14
15
  from pyossmtool.runner import Runner
15
16
 
@@ -88,12 +89,22 @@ def export_schema() -> None:
88
89
 
89
90
  @app.command("install")
90
91
  def install(
91
- suite: str = typer.Option("demo", "--suite", help="Suite whose tool dependencies to install"),
92
+ suite: str | None = typer.Option(
93
+ None,
94
+ "--suite",
95
+ help="Suite whose tools to install (default: suite from pyossmtool.yaml)",
96
+ ),
92
97
  ) -> None:
93
98
  registry = _registry()
94
- installer = Installer(registry, _project_root())
99
+ project_root = _project_root()
100
+ project_config = registry.load_project_config(project_root)
101
+ suite_id = suite or (project_config.suite if project_config else None)
102
+ if not suite_id:
103
+ typer.echo("SETUP provide --suite or set suite: in pyossmtool.yaml", err=True)
104
+ raise typer.Exit(code=2)
105
+ installer = Installer(registry, project_root)
95
106
  try:
96
- installer.install_suite(suite)
107
+ installer.install_suite(suite_id)
97
108
  except Exception as exc:
98
109
  typer.echo(f"SETUP {exc}", err=True)
99
110
  raise typer.Exit(code=2) from exc
@@ -101,7 +112,11 @@ def install(
101
112
 
102
113
  @app.command("check")
103
114
  def check_cmd(
104
- suite: str | None = typer.Option(None, "--suite", help="Suite id to run"),
115
+ suite: str | None = typer.Option(
116
+ None,
117
+ "--suite",
118
+ help="Suite id to run (default: suite from pyossmtool.yaml)",
119
+ ),
105
120
  check: str | None = typer.Option(None, "--check", help="Single check id to run"),
106
121
  target: str | None = typer.Option(None, "--target", help="Target path for single check"),
107
122
  fail_fast: bool = typer.Option(False, "--fail-fast", help="Stop at first failure"),
@@ -113,7 +128,11 @@ def check_cmd(
113
128
 
114
129
  @app.command("format")
115
130
  def format_cmd(
116
- suite: str | None = typer.Option("format", "--suite", help="Suite id to run"),
131
+ suite: str | None = typer.Option(
132
+ None,
133
+ "--suite",
134
+ help="Suite id to run (default: suite from pyossmtool.yaml)",
135
+ ),
117
136
  check: str | None = typer.Option(None, "--check", help="Single check id to run"),
118
137
  target: str | None = typer.Option(None, "--target", help="Target path for single check"),
119
138
  fail_fast: bool = typer.Option(False, "--fail-fast", help="Stop at first failure"),
@@ -134,14 +153,19 @@ def _execute(
134
153
  registry = _registry()
135
154
  project_root = _project_root()
136
155
  project_config = registry.load_project_config(project_root)
156
+ try:
157
+ formatter = resolve_error_formatter(project_config)
158
+ except (KeyError, ValueError, RuntimeError) as exc:
159
+ typer.echo(f"SETUP {exc}", err=True)
160
+ return 2
137
161
  runner = Runner(registry, project_root, verbose=verbose)
138
162
 
139
163
  if check:
140
- return _run_single_check(runner, check, target, suite, project_config, mode)
164
+ return _run_single_check(runner, check, target, suite, project_config, mode, formatter)
141
165
 
142
166
  suite_id = suite or (project_config.suite if project_config else None)
143
167
  if not suite_id:
144
- typer.echo("SETUP provide --suite or create pyossmtool.yaml", err=True)
168
+ typer.echo("SETUP provide --suite or set suite: in pyossmtool.yaml", err=True)
145
169
  return 2
146
170
 
147
171
  try:
@@ -154,7 +178,7 @@ def _execute(
154
178
  except KeyError as exc:
155
179
  typer.echo(f"SETUP {exc}", err=True)
156
180
  return 2
157
- return _emit_suite_result(suite_result)
181
+ return _emit_suite_result(suite_result, formatter)
158
182
 
159
183
 
160
184
  def _run_single_check(
@@ -162,8 +186,9 @@ def _run_single_check(
162
186
  check: str,
163
187
  target: str | None,
164
188
  suite: str | None,
165
- project_config,
189
+ project_config: ProjectConfig | None,
166
190
  mode: CheckMode,
191
+ formatter: ErrorFormatter,
167
192
  ) -> int:
168
193
  if not target:
169
194
  typer.echo("SETUP --target is required with --check", err=True)
@@ -181,35 +206,42 @@ def _run_single_check(
181
206
  suite_id=suite or (project_config.suite if project_config else None),
182
207
  project_config=project_config,
183
208
  )
184
- return _exit_for_check_result(result)
209
+ return _exit_for_check_result(result, formatter)
185
210
 
186
211
 
187
- def _exit_for_check_result(result) -> int:
212
+ def _exit_for_check_result(result, formatter: ErrorFormatter) -> int:
188
213
  if result.error:
189
214
  typer.echo(f"SETUP {result.error}", err=True)
190
215
  return 2
191
216
  if result.passed:
192
217
  return 0
193
- typer.echo(f"FAIL {result.check_id} -> {result.report_path}", err=True)
218
+ _emit_failed_check(result, formatter)
194
219
  return 1
195
220
 
196
221
 
197
- def _emit_suite_result(suite_result) -> int:
222
+ def _emit_suite_result(suite_result, formatter: ErrorFormatter) -> int:
198
223
  if suite_result.passed:
199
224
  return 0
200
225
  for result in suite_result.results:
201
- _emit_failed_check(result)
226
+ _emit_failed_check(result, formatter)
202
227
  return 1
203
228
 
204
229
 
205
- def _emit_failed_check(result) -> None:
230
+ def _emit_failed_check(result, formatter: ErrorFormatter) -> None:
206
231
  if result.passed:
207
232
  return
208
233
  if result.error:
209
234
  typer.echo(f"SETUP {result.check_id}: {result.error}", err=True)
210
235
  return
211
- if result.report_path:
236
+ if not result.report_path:
237
+ return
238
+ try:
239
+ text = format_report_file(result.report_path, formatter)
240
+ except (OSError, ValueError, RuntimeError) as exc:
241
+ typer.echo(f"SETUP failed to format report for {result.check_id}: {exc}", err=True)
212
242
  typer.echo(f"FAIL {result.check_id} -> {result.report_path}", err=True)
243
+ return
244
+ typer.echo(text, err=True)
213
245
 
214
246
 
215
247
  def main() -> None: