code-standards 8.56.1__tar.gz → 8.58.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 (179) hide show
  1. {code_standards-8.56.1 → code_standards-8.58.0}/PKG-INFO +6 -2
  2. {code_standards-8.56.1 → code_standards-8.58.0}/README.md +4 -0
  3. {code_standards-8.56.1 → code_standards-8.58.0}/pyproject.toml +2 -2
  4. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/api.py +48 -14
  5. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/cli/main.py +109 -35
  6. code_standards-8.58.0/src/sarj_standards/configs/cli-reference.v1.json +1 -0
  7. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/eslint-selected-rules.mjs +4 -2
  8. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/rule-calibration-campaign.v1.json +8 -0
  9. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/rule-inventory.v1.json +1 -1
  10. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/rule-ledger.json +3 -1
  11. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/doctor.py +2 -4
  12. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/hooks.py +133 -37
  13. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/lifecycle.py +2 -2
  14. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/scaffold.py +3 -3
  15. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/analysis.py +4 -0
  16. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/external.py +2 -0
  17. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/runner.py +8 -2
  18. code_standards-8.58.0/src/sarj_standards/libs/linting/test_value.py +37 -0
  19. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/docs.py +11 -0
  20. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/hooks.py +56 -2
  21. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/schemas/rule-catalog.v1.json +1 -1
  22. code_standards-8.56.1/src/sarj_standards/configs/cli-reference.v1.json +0 -1
  23. {code_standards-8.56.1 → code_standards-8.58.0}/.gitignore +0 -0
  24. {code_standards-8.56.1 → code_standards-8.58.0}/LICENSE +0 -0
  25. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/__init__.py +0 -0
  26. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/__main__.py +0 -0
  27. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/_meta.py +0 -0
  28. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/cli/__init__.py +0 -0
  29. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/Mintfile.mobile.strict +0 -0
  30. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/actionlint.strict.yaml +0 -0
  31. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/basedpyright.python315-watch.json +0 -0
  32. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/basedpyright.strict.json +0 -0
  33. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/checkov.strict.yml +0 -0
  34. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/detekt.strict.yml +0 -0
  35. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/devops-verification.v1.json +0 -0
  36. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/doctor.config.json +0 -0
  37. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/eslint-compact-formatter.mjs +0 -0
  38. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/eslint.application.mjs +0 -0
  39. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/eslint.peers.json +0 -0
  40. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/eslint.strict.mjs +0 -0
  41. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/hadolint.strict.yaml +0 -0
  42. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/ktlint.strict.editorconfig +0 -0
  43. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/markdownlint.strict.yaml +0 -0
  44. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/mobile-tools.versions.json +0 -0
  45. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/mobsf.strict.yml +0 -0
  46. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/pyright.strict.json +0 -0
  47. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/ruff.application.toml +0 -0
  48. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/ruff.python315-watch.toml +0 -0
  49. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/ruff.strict.toml +0 -0
  50. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/rule-level-source.mjs +0 -0
  51. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/schemas/SKAFFOLD-LICENSE +0 -0
  52. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/schemas/skaffold-v4beta7.json +0 -0
  53. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/schemas/upstream-schemas.json +0 -0
  54. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/semgrep-overrides.txt +0 -0
  55. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/shellcheck.strict.rc +0 -0
  56. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/swiftformat.strict +0 -0
  57. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/swiftlint.strict.yml +0 -0
  58. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/taplo.strict.toml +0 -0
  59. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/tflint.strict.hcl +0 -0
  60. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/tflint.version.hcl +0 -0
  61. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/yamllint.strict.yaml +0 -0
  62. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/configs/zizmor.strict.yml +0 -0
  63. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/__init__.py +0 -0
  64. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/__init__.py +0 -0
  65. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/age_transition.py +0 -0
  66. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/configs.py +0 -0
  67. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/devops.py +0 -0
  68. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/exclusions.py +0 -0
  69. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/launcher.py +0 -0
  70. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/manifest.py +0 -0
  71. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/native_bootstrap.py +0 -0
  72. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/packagemanager.py +0 -0
  73. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/repository_baseline.py +0 -0
  74. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/retired_suppressions.py +0 -0
  75. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/service.py +0 -0
  76. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/transaction.py +0 -0
  77. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/upgrade.py +0 -0
  78. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/adoption/uvtool.py +0 -0
  79. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/catalogs/__init__.py +0 -0
  80. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/catalogs/slack_automations.py +0 -0
  81. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/corpus/__init__.py +0 -0
  82. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/corpus/manifest.py +0 -0
  83. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/corpus/snapshot.py +0 -0
  84. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/diagnostics/__init__.py +0 -0
  85. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/diagnostics/analysis.schema.json +0 -0
  86. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/diagnostics/baseline.py +0 -0
  87. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/diagnostics/models.py +0 -0
  88. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/diagnostics/serialize.py +0 -0
  89. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/diagnostics/source.py +0 -0
  90. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/filesystem.py +0 -0
  91. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/json_boundary.py +0 -0
  92. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/__init__.py +0 -0
  93. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/chart_schemas.py +0 -0
  94. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/cloudbuild.py +0 -0
  95. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/devops_programs.py +0 -0
  96. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/devops_schema.py +0 -0
  97. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/devops_source.py +0 -0
  98. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/devops_tools.py +0 -0
  99. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/duplicate_code.py +0 -0
  100. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/embedded_shell.py +0 -0
  101. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/kubernetes_context.py +0 -0
  102. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/library_policy.py +0 -0
  103. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/mobile_tools.py +0 -0
  104. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/policy.py +0 -0
  105. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/prepared_devops.py +0 -0
  106. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/prepared_kubernetes.py +0 -0
  107. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/repo_standards.py +0 -0
  108. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/scheduling.py +0 -0
  109. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/security_tools.py +0 -0
  110. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/shell_ast.py +0 -0
  111. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/shell_format.py +0 -0
  112. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/shell_policy.py +0 -0
  113. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/text_rule_base.py +0 -0
  114. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/text_rules/__init__.py +0 -0
  115. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/text_rules/_registry.py +0 -0
  116. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/text_rules/cloudbuild_contract.py +0 -0
  117. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/textlint.py +0 -0
  118. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/toml_literals.py +0 -0
  119. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/linting/unused_modules.py +0 -0
  120. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/__init__.py +0 -0
  121. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/_values.py +0 -0
  122. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/artifacts.py +0 -0
  123. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/causality.py +0 -0
  124. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/changes.py +0 -0
  125. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/ci_artifacts.py +0 -0
  126. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/make_checks.py +0 -0
  127. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/metadata_verification.py +0 -0
  128. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/native_test_setup.py +0 -0
  129. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/process.py +0 -0
  130. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/publish.py +0 -0
  131. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/registry.py +0 -0
  132. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/release_age.py +0 -0
  133. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/release_detect.py +0 -0
  134. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/retirement.py +0 -0
  135. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/reviewed_analysis.py +0 -0
  136. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/rollout.py +0 -0
  137. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/status.py +0 -0
  138. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/tags.py +0 -0
  139. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/test_selection.py +0 -0
  140. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/typescript.py +0 -0
  141. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/verification_process.py +0 -0
  142. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/release/wheel_tests.py +0 -0
  143. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/__init__.py +0 -0
  144. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/cli_reference_artifact.py +0 -0
  145. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/comment_corpus.py +0 -0
  146. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/config_generation.py +0 -0
  147. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/immutable_git.py +0 -0
  148. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/ledger.py +0 -0
  149. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/repository.py +0 -0
  150. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/ruff_freshness.py +0 -0
  151. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_authoring.py +0 -0
  152. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_catalog_artifact.py +0 -0
  153. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_changes.py +0 -0
  154. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_checkout.py +0 -0
  155. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_dependencies.py +0 -0
  156. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_examples.py +0 -0
  157. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_imports.cjs +0 -0
  158. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_inventory_artifact.py +0 -0
  159. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_level_source.py +0 -0
  160. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_lifecycle.py +0 -0
  161. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_maintenance.py +0 -0
  162. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/rule_registration.py +0 -0
  163. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/shell_checks.py +0 -0
  164. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/repository/third_party_catalog_artifact.py +0 -0
  165. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/rules/__init__.py +0 -0
  166. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/rules/catalog.py +0 -0
  167. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/rules/contracts.py +0 -0
  168. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/rules/corpus_runner.py +0 -0
  169. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/rules/evaluation.py +0 -0
  170. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/schema_boundary.py +0 -0
  171. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/setup/__init__.py +0 -0
  172. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/setup/repository.py +0 -0
  173. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/typed_containers.py +0 -0
  174. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/libs/yaml_boundary.py +0 -0
  175. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/py.typed +0 -0
  176. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/schemas/__init__.py +0 -0
  177. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/schemas/_paths.py +0 -0
  178. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/schemas/rule-catalog.v1.schema.json +0 -0
  179. {code_standards-8.56.1 → code_standards-8.58.0}/src/sarj_standards/schemas/slack-automations.v1.schema.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: code-standards
3
- Version: 8.56.1
3
+ Version: 8.58.0
4
4
  Summary: One deterministic quality gate for Sarj Python, TypeScript, Swift, Kotlin, shell, SQL, IaC, and repository policy.
5
5
  Project-URL: Homepage, https://code-standards.sarj.ai/
6
6
  Project-URL: Documentation, https://code-standards.sarj.ai/cli/
@@ -31,7 +31,7 @@ Requires-Dist: referencing==0.37.0
31
31
  Requires-Dist: repo-standards==6.6.0
32
32
  Requires-Dist: ruff==0.17.0
33
33
  Requires-Dist: sarj-iac-lint==0.21.0
34
- Requires-Dist: sarj-python-lint==1.36.1
34
+ Requires-Dist: sarj-python-lint==1.37.0
35
35
  Requires-Dist: sarj-rule-contracts==0.2.4
36
36
  Requires-Dist: sarj-sql-lint==0.20.0
37
37
  Requires-Dist: shellcheck-py==0.11.0.1; (sys_platform == 'darwin' and platform_machine == 'arm64') or (sys_platform == 'darwin' and platform_machine == 'x86_64') or (sys_platform == 'linux' and platform_machine == 'x86_64') or (sys_platform == 'win32' and platform_machine == 'AMD64')
@@ -50,6 +50,10 @@ uv tool install --python 3.15 code-standards
50
50
 
51
51
  Adopt the repository once with `code-standards setup`. It installs one canonical staged gate that includes semantic lint and repository policy, plus commit-message and pull-request history enforcement. Do not install Repo Standards separately.
52
52
 
53
+ The pre-commit gate runs once on every commit and discovers paths directly from the Git index, including mixed file types and deletion-only commits. The shared dispatcher selects applicable rules and retains type analysis, repository policy, exclusions and suppression accountability. Errors and incomplete analysis block the commit; warning-stage findings stay visible even when the hook passes. Agents should address the diagnostics, run `code-standards fix --staged` for safe mechanical fixes, review and stage intended edits, then retry. Staged fixes never fall back to the whole repository and refuse files with unstaged changes.
54
+
55
+ Setup installs the selected hook manager. In a fresh checkout, run `code-standards doctor --repair` before committing; doctor checks installation and the exact unconditional command. Conditional Lefthook jobs must be moved to an unconditional job without changing unrelated hooks. Git can bypass local hooks with `--no-verify`; the canonical CI gate independently enforces policy.
56
+
53
57
  ```bash
54
58
  code-standards setup
55
59
  code-standards check
@@ -10,6 +10,10 @@ uv tool install --python 3.15 code-standards
10
10
 
11
11
  Adopt the repository once with `code-standards setup`. It installs one canonical staged gate that includes semantic lint and repository policy, plus commit-message and pull-request history enforcement. Do not install Repo Standards separately.
12
12
 
13
+ The pre-commit gate runs once on every commit and discovers paths directly from the Git index, including mixed file types and deletion-only commits. The shared dispatcher selects applicable rules and retains type analysis, repository policy, exclusions and suppression accountability. Errors and incomplete analysis block the commit; warning-stage findings stay visible even when the hook passes. Agents should address the diagnostics, run `code-standards fix --staged` for safe mechanical fixes, review and stage intended edits, then retry. Staged fixes never fall back to the whole repository and refuse files with unstaged changes.
14
+
15
+ Setup installs the selected hook manager. In a fresh checkout, run `code-standards doctor --repair` before committing; doctor checks installation and the exact unconditional command. Conditional Lefthook jobs must be moved to an unconditional job without changing unrelated hooks. Git can bypass local hooks with `--no-verify`; the canonical CI gate independently enforces policy.
16
+
13
17
  ```bash
14
18
  code-standards setup
15
19
  code-standards check
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "code-standards"
3
- version = "8.56.1"
3
+ version = "8.58.0"
4
4
  description = "One deterministic quality gate for Sarj Python, TypeScript, Swift, Kotlin, shell, SQL, IaC, and repository policy."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -32,7 +32,7 @@ dependencies = [
32
32
  "ruff==0.17.0",
33
33
  "shellcheck-py==0.11.0.1; (sys_platform == 'darwin' and platform_machine == 'arm64') or (sys_platform == 'darwin' and platform_machine == 'x86_64') or (sys_platform == 'linux' and platform_machine == 'x86_64') or (sys_platform == 'win32' and platform_machine == 'AMD64')",
34
34
  "sarj-iac-lint==0.21.0",
35
- "sarj-python-lint==1.36.1",
35
+ "sarj-python-lint==1.37.0",
36
36
  "sarj-sql-lint==0.20.0",
37
37
  "typer==0.27.3",
38
38
  ]
@@ -54,7 +54,7 @@ from .libs.diagnostics import (
54
54
  )
55
55
  from .libs.filesystem import is_link_like
56
56
  from .libs.linting.analysis import analyze as analyze_paths, report_from_tools
57
- from .libs.linting.external import analyze_external
57
+ from .libs.linting.external import UpstreamESLintRules, analyze_external
58
58
  from .libs.linting.library_policy import (
59
59
  ManifestPolicyError,
60
60
  accepts_path as library_policy_accepts_path,
@@ -78,6 +78,7 @@ if TYPE_CHECKING:
78
78
 
79
79
  _INVALID_EXIT = 2
80
80
  _INTERRUPTED_EXIT = 130
81
+ _NO_UPSTREAM_ESLINT_RULES = UpstreamESLintRules()
81
82
  _INVALID_DOCTOR_FINDING_IDS = frozenset(
82
83
  {"doctor.manifest.destination", "doctor.config.unknown", "doctor.package-json.invalid"}
83
84
  )
@@ -250,6 +251,7 @@ class Standards:
250
251
  trust: TrustMode | str = TrustMode.SAFE,
251
252
  mode: AnalysisMode | str = AnalysisMode.POLICY,
252
253
  rules: Sequence[str | RuleSelector] | None = None,
254
+ upstream_rules: UpstreamESLintRules = _NO_UPSTREAM_ESLINT_RULES,
253
255
  staged: bool = False,
254
256
  react_doctor_triggered: bool = False,
255
257
  include_react_doctor: bool = True,
@@ -262,6 +264,7 @@ class Standards:
262
264
  ) -> AnalysisReport:
263
265
  if jobs not in {1, 2}:
264
266
  return _failed_analysis(self.root, "invalid-input", "analysis jobs must be 1 or 2")
267
+ external = external or bool(upstream_rules.ids)
265
268
  try:
266
269
  normalized_trust = TrustMode(trust)
267
270
  normalized_mode = AnalysisMode(mode)
@@ -291,10 +294,7 @@ class Standards:
291
294
  selected_groups = group_paths(active_selected, policy=selection_policy)
292
295
  except (OSError, TypeError, ValueError) as exc:
293
296
  return _failed_analysis(self.root, "invalid-input", str(exc))
294
- requested_paths = adopted.verify_paths if paths is None and adopted is not None else paths
295
- react_doctor_full_scan = not staged and (
296
- requested_paths is None or any(Path(path) == Path() for path in requested_paths)
297
- )
297
+ react_doctor_full_scan = _react_doctor_full_scan(adopted, paths, staged=staged)
298
298
 
299
299
  def native_analysis() -> tuple[ToolReport, ...]:
300
300
  native = analyze_paths(
@@ -311,7 +311,9 @@ class Standards:
311
311
  prepared = _prepared_reports(self.root, adopted, prepared_devops, prepared_targets)
312
312
  return (*native.tools, *prepared)
313
313
 
314
- coverage = _selection_coverage(self.root, selected, active_selected, selected_groups, rule_selection)
314
+ coverage = _selection_coverage(
315
+ self.root, selected, active_selected, selected_groups, rule_selection, upstream_rules=upstream_rules
316
+ )
315
317
  coverage.extend(_prepared_coverage(adopted, prepared_devops))
316
318
  if not external:
317
319
  native = report_from_tools(self.root, native_analysis())
@@ -355,14 +357,18 @@ class Standards:
355
357
  react_doctor_full_scan=react_doctor_full_scan,
356
358
  pass_on_unpruned_eslint_suppressions=pass_on_unpruned_eslint_suppressions,
357
359
  python_type_check=python_type_check,
360
+ upstream_rules=upstream_rules,
358
361
  )
359
362
 
360
363
  combined = report_from_tools(
361
364
  self.root, analyze_groups(self.root, native_analysis, external_analysis, jobs=jobs)
362
365
  )
363
366
  combined = _with_shell_format_severity(combined, paths=paths, staged=staged, changed_scope=changed_scope)
364
- if normalized_mode in {AnalysisMode.POLICY, AnalysisMode.OBSERVE}:
365
- combined = _with_warning_severity(combined, _warning_rule_keys())
367
+ combined = _with_warning_severity(
368
+ combined,
369
+ _warning_rule_keys() if normalized_mode in {AnalysisMode.POLICY, AnalysisMode.OBSERVE} else frozenset(),
370
+ upstream_rules=upstream_rules,
371
+ )
366
372
  return _with_coverage(
367
373
  _without_baselined_diagnostics(combined, baseline_counts, changed_scope=changed_scope), coverage
368
374
  )
@@ -717,6 +723,7 @@ def _selected_external_analysis(
717
723
  react_doctor_full_scan: bool,
718
724
  pass_on_unpruned_eslint_suppressions: bool,
719
725
  python_type_check: bool,
726
+ upstream_rules: UpstreamESLintRules,
720
727
  ) -> tuple[ToolReport, ...]:
721
728
  rule_ids = (
722
729
  frozenset(str(value) for value in rule_selection.ids_for(RuleEngine.ESLINT))
@@ -724,12 +731,13 @@ def _selected_external_analysis(
724
731
  else None
725
732
  )
726
733
  external_engines = frozenset({RuleEngine.ESLINT, RuleEngine.CHECKOV, RuleEngine.ZIZMOR})
734
+ upstream_capabilities = frozenset({"eslint"}) if upstream_rules.ids else frozenset[str]()
727
735
  selected_capabilities = (
728
- frozenset(engine.value for engine in rule_selection.engines & external_engines)
736
+ frozenset(engine.value for engine in rule_selection.engines & external_engines) | upstream_capabilities
729
737
  if rule_selection is not None
730
738
  else None
731
739
  )
732
- run_external = rule_selection is None or not rule_selection.engines.isdisjoint(external_engines)
740
+ run_external = selected_capabilities is None or bool(selected_capabilities)
733
741
  external_reports = (
734
742
  (
735
743
  analyze_external(
@@ -742,6 +750,7 @@ def _selected_external_analysis(
742
750
  ),
743
751
  grouped=selected_groups,
744
752
  rule_ids=rule_ids,
753
+ upstream_rules=upstream_rules,
745
754
  security_selection=rule_selection,
746
755
  include_react_doctor=include_react_doctor and rule_selection is None,
747
756
  force_react_doctor=react_doctor_triggered,
@@ -758,6 +767,7 @@ def _selected_external_analysis(
758
767
  capabilities=selected_capabilities,
759
768
  grouped=selected_groups,
760
769
  rule_ids=rule_ids,
770
+ upstream_rules=upstream_rules,
761
771
  security_selection=rule_selection,
762
772
  include_react_doctor=include_react_doctor and rule_selection is None,
763
773
  force_react_doctor=react_doctor_triggered,
@@ -771,7 +781,10 @@ def _selected_external_analysis(
771
781
  else ()
772
782
  )
773
783
  if rule_selection is not None:
774
- external_reports = tuple(_filter_report_selectors(report, rule_selection) for report in external_reports)
784
+ external_reports = tuple(
785
+ _filter_report_selectors(report, rule_selection, upstream_rules=upstream_rules)
786
+ for report in external_reports
787
+ )
775
788
  return external_reports
776
789
 
777
790
 
@@ -805,12 +818,19 @@ def _with_repository_analysis(root: Path, native: AnalysisReport, *, staged: boo
805
818
  return native
806
819
 
807
820
 
821
+ def _react_doctor_full_scan(adopted: Manifest | None, paths: Sequence[str] | None, *, staged: bool) -> bool:
822
+ requested_paths = adopted.verify_paths if paths is None and adopted is not None else paths
823
+ return not staged and (requested_paths is None or any(Path(path) == Path() for path in requested_paths))
824
+
825
+
808
826
  def _selection_coverage(
809
827
  root: Path,
810
828
  selected: list[str],
811
829
  active_selected: list[str],
812
830
  selected_groups: GroupedPaths,
813
831
  rule_selection: RuleSelection | None,
832
+ *,
833
+ upstream_rules: UpstreamESLintRules,
814
834
  ) -> list[CoverageNotice]:
815
835
  coverage: list[CoverageNotice] = []
816
836
  excluded = sum(Path(item).is_file() and item not in active_selected for item in selected)
@@ -824,6 +844,8 @@ def _selection_coverage(
824
844
  )
825
845
  )
826
846
  routed = _routed_for_selection(selected_groups, rule_selection)
847
+ if upstream_rules.ids:
848
+ routed.update(selected_groups.typescript)
827
849
  if rule_selection is None:
828
850
  routed.update(
829
851
  item for item in active_selected if Path(item).is_file() and library_policy_accepts_path(Path(item), root)
@@ -941,9 +963,13 @@ def _routed_for_selection(grouped: object, selected: RuleSelection | None) -> se
941
963
  def _filter_report_selectors(
942
964
  report: ToolReport,
943
965
  selected: RuleSelection,
966
+ *,
967
+ upstream_rules: UpstreamESLintRules = _NO_UPSTREAM_ESLINT_RULES,
944
968
  ) -> ToolReport:
945
969
  engine = RuleEngine(report.name) if report.name in {"eslint", "zizmor", "checkov"} else None
946
970
  allowed: frozenset[str] = frozenset() if engine is None else selected.native_ids_for(engine)
971
+ if engine is RuleEngine.ESLINT:
972
+ allowed |= upstream_rules.ids
947
973
  return ToolReport(
948
974
  report.name,
949
975
  report.completion,
@@ -966,15 +992,23 @@ def _warning_rule_keys() -> frozenset[RuleSelector]:
966
992
  return warning_selectors()
967
993
 
968
994
 
969
- def _with_warning_severity(report: AnalysisReport, selectors: frozenset[RuleSelector]) -> AnalysisReport:
970
- if not selectors:
995
+ def _with_warning_severity(
996
+ report: AnalysisReport,
997
+ selectors: frozenset[RuleSelector],
998
+ *,
999
+ upstream_rules: UpstreamESLintRules = _NO_UPSTREAM_ESLINT_RULES,
1000
+ ) -> AnalysisReport:
1001
+ if not selectors and not upstream_rules.warning_only:
971
1002
  return report
972
1003
  tools = tuple(
973
1004
  ToolReport(
974
1005
  tool.name,
975
1006
  tool.completion,
976
1007
  diagnostics=tuple(
977
- replace(item, severity=Severity.WARNING) if _selector_for_diagnostic(item) in selectors else item
1008
+ replace(item, severity=Severity.WARNING)
1009
+ if _selector_for_diagnostic(item) in selectors
1010
+ or (tool.name == "eslint" and upstream_rules.warning_only and item.rule_id in upstream_rules.ids)
1011
+ else item
978
1012
  for item in tool.diagnostics
979
1013
  ),
980
1014
  issues=tool.issues,
@@ -180,6 +180,8 @@ class _Args:
180
180
  ratchet_cmd: str = ""
181
181
  baseline_cmd: str = ""
182
182
  selected_rules: list[RuleSelector] = field(default_factory=list)
183
+ test_value: bool = False
184
+ observation_base: str | None = None
183
185
  corpus_manifest: Path | None = None
184
186
  private_overlay: Path | None = None
185
187
  selector: RuleSelector | None = None
@@ -1128,15 +1130,6 @@ def cmd_check(args: _Args) -> int:
1128
1130
  if health_status:
1129
1131
  return health_status
1130
1132
  args.files = [path for path in args.files if runner.accepts_hook_path(Path(path), root=root)]
1131
- if not args.files and not scope.react_doctor_triggered:
1132
- return _run_canonical_check(
1133
- root,
1134
- (),
1135
- trusted=args.trust_repository_code,
1136
- staged=True,
1137
- jobs=args.jobs,
1138
- python_type_check=args.python_type_check,
1139
- )
1140
1133
  if args.output_format != "text":
1141
1134
  return _check_machine_output(args, root, scope)
1142
1135
  return _check_text_output(args, root, scope)
@@ -1160,6 +1153,11 @@ def _check_selected_rules(args: _Args, root: Path, scope: _CheckScope) -> int:
1160
1153
 
1161
1154
 
1162
1155
  def _check_text_output(args: _Args, root: Path, scope: _CheckScope) -> int:
1156
+ if args.staged and not args.files:
1157
+ check_options: dict[str, bool] = {"trusted": args.trust_repository_code, "staged": True}
1158
+ if scope.react_doctor_triggered:
1159
+ check_options["react_doctor_triggered"] = True
1160
+ return _run_canonical_check(root, (), jobs=args.jobs, python_type_check=args.python_type_check, **check_options)
1163
1161
  if scope.pull_request_scoped:
1164
1162
  adoption_report = _machine_adoption_gate(root)
1165
1163
  if adoption_report is not None:
@@ -1183,7 +1181,7 @@ def _check_text_output(args: _Args, root: Path, scope: _CheckScope) -> int:
1183
1181
  )
1184
1182
  if not args.files:
1185
1183
  return cmd_verify(args)
1186
- check_options: dict[str, bool] = {
1184
+ check_options = {
1187
1185
  "trusted": args.trust_repository_code,
1188
1186
  "staged": args.staged,
1189
1187
  }
@@ -1286,11 +1284,11 @@ def _check_machine_output(args: _Args, root: Path, scope: _CheckScope) -> int:
1286
1284
  args.external = True
1287
1285
  args.trust = "trusted" if args.trust_repository_code else "safe"
1288
1286
  args.analysis_mode = "policy"
1289
- if scope.repository_wide:
1287
+ if scope.repository_wide and not args.staged:
1290
1288
  adoption_report = _machine_adoption_gate(root)
1291
1289
  if adoption_report is not None:
1292
1290
  return _emit_analysis_report(args, root, adoption_report)
1293
- if scope.pull_request_scoped and not args.files:
1291
+ if (scope.pull_request_scoped or args.staged) and not args.files:
1294
1292
  from sarj_standards.api import ( # ruff: ignore[import-outside-top-level]
1295
1293
  AnalysisMode,
1296
1294
  Standards,
@@ -1303,6 +1301,7 @@ def _check_machine_output(args: _Args, root: Path, scope: _CheckScope) -> int:
1303
1301
  jobs=args.jobs,
1304
1302
  trust=TrustMode.TRUSTED if args.trust_repository_code else TrustMode.SAFE,
1305
1303
  mode=AnalysisMode.POLICY,
1304
+ staged=args.staged,
1306
1305
  react_doctor_triggered=scope.react_doctor_triggered,
1307
1306
  python_type_check=args.python_type_check,
1308
1307
  )
@@ -1400,6 +1399,11 @@ def _run_canonical_check(
1400
1399
  rendered = to_text(report)
1401
1400
  if rendered:
1402
1401
  print(rendered, end="" if rendered.endswith("\n") else "\n")
1402
+ if staged and report.diagnostics:
1403
+ print(
1404
+ "Agent: address the findings above. Run `code-standards fix --staged` for safe mechanical fixes, "
1405
+ "review the changes, stage the intended edits, and retry the commit."
1406
+ )
1403
1407
  return report.exit_code
1404
1408
 
1405
1409
 
@@ -1613,32 +1617,75 @@ def _object_table(value: object) -> dict[str, object] | None:
1613
1617
 
1614
1618
  def cmd_observe(args: _Args) -> int:
1615
1619
  from sarj_standards.api import AnalysisMode, Standards, TrustMode # ruff: ignore[import-outside-top-level]
1616
- from sarj_standards.libs.linting.policy import warning_selectors # ruff: ignore[import-outside-top-level]
1620
+ from sarj_standards.libs.linting.external import UpstreamESLintRules # ruff: ignore[import-outside-top-level]
1621
+ from sarj_standards.libs.linting.test_value import UPSTREAM_RULES # ruff: ignore[import-outside-top-level]
1617
1622
 
1618
1623
  root = _resolve_dest(args.dest)
1619
1624
  if _validate_analysis_output(args, root):
1620
1625
  return 2
1621
- warning_rules = warning_selectors()
1622
- invalid = sorted(set(args.selected_rules) - warning_rules)
1623
- if invalid:
1624
- rendered = ", ".join(map(str, invalid))
1625
- print(
1626
- f"error: observe accepts warning-stage rules only: {rendered}\n"
1627
- f"next: code-standards maintain rules stage-warning {invalid[0]}",
1628
- file=sys.stderr,
1626
+ try:
1627
+ selected_rules = _observation_rules(args)
1628
+ files = _observation_paths(args, root)
1629
+ except (OSError, ValueError, subprocess.SubprocessError) as exc:
1630
+ print(f"error: {exc}", file=sys.stderr)
1631
+ return _emit_analysis_report(args, root, _machine_input_error(root, str(exc)))
1632
+ if files == []:
1633
+ from sarj_standards.libs.diagnostics import ( # ruff: ignore[import-outside-top-level]
1634
+ AnalysisReport,
1635
+ Completion,
1636
+ Conclusion,
1629
1637
  )
1630
- return 2
1638
+
1639
+ return _emit_analysis_report(args, root, AnalysisReport(root, Completion.COMPLETE, Conclusion.PASSED, ()))
1631
1640
  report = Standards(root).analyze(
1632
- args.files or None,
1641
+ files,
1633
1642
  jobs=args.jobs,
1634
- external=any(selector.engine.value == "eslint" for selector in args.selected_rules),
1643
+ external=args.test_value or any(selector.engine.value == "eslint" for selector in selected_rules),
1635
1644
  trust=TrustMode.TRUSTED if args.trust_repository_code else TrustMode.SAFE,
1636
1645
  mode=AnalysisMode.OBSERVE,
1637
- rules=args.selected_rules,
1646
+ rules=selected_rules,
1647
+ upstream_rules=UpstreamESLintRules(UPSTREAM_RULES, warning_only=True)
1648
+ if args.test_value
1649
+ else UpstreamESLintRules(),
1638
1650
  )
1639
1651
  return _emit_analysis_report(args, root, report)
1640
1652
 
1641
1653
 
1654
+ def _observation_rules(args: _Args) -> list[RuleSelector]:
1655
+ from sarj_standards.libs.linting.policy import warning_selectors # ruff: ignore[import-outside-top-level]
1656
+ from sarj_standards.libs.linting.test_value import SELECTORS # ruff: ignore[import-outside-top-level]
1657
+
1658
+ warning_rules = warning_selectors()
1659
+ selected_rules = sorted(set(args.selected_rules) | (SELECTORS if args.test_value else frozenset()), key=str)
1660
+ if not selected_rules:
1661
+ msg = "observe requires --rule or --test-value"
1662
+ raise ValueError(msg)
1663
+ invalid = sorted(set(selected_rules) - warning_rules)
1664
+ if invalid:
1665
+ rendered = ", ".join(map(str, invalid))
1666
+ msg = (
1667
+ f"observe accepts warning-stage rules only: {rendered}; "
1668
+ f"next: code-standards maintain rules stage-warning {invalid[0]}"
1669
+ )
1670
+ raise ValueError(msg)
1671
+ return selected_rules
1672
+
1673
+
1674
+ def _observation_paths(args: _Args, root: Path) -> list[str] | None:
1675
+ from sarj_standards.libs.linting.test_value import select_test_paths # ruff: ignore[import-outside-top-level]
1676
+
1677
+ files = args.files or None
1678
+ if args.observation_base is not None:
1679
+ if args.files:
1680
+ msg = "observe --base cannot be combined with explicit paths"
1681
+ raise ValueError(msg)
1682
+ files = _changed_file_paths(root, args.observation_base)
1683
+ if args.test_value:
1684
+ candidates = _selected_paths(root, files) if files is not None else _selected_paths(root, ["."])
1685
+ return select_test_paths(root, candidates)
1686
+ return files
1687
+
1688
+
1642
1689
  def _rule_evaluation_summary(report: object, selectors: Sequence[RuleSelector]) -> str:
1643
1690
  from sarj_standards.libs.diagnostics import AnalysisReport # ruff: ignore[import-outside-top-level]
1644
1691
  from sarj_standards.libs.rules import RuleEngine # ruff: ignore[import-outside-top-level]
@@ -2000,7 +2047,7 @@ def _staged_file_names(root: Path) -> list[str]:
2000
2047
  msg = "git is required for --staged"
2001
2048
  raise OSError(msg)
2002
2049
  completed = subprocess.run( # ruff: ignore[subprocess-without-shell-equals-true] -- fixed argv, no shell.
2003
- [git, "diff", "--cached", "--name-only", "--diff-filter=ACDMR", "-z"],
2050
+ [git, "diff", "--cached", "--name-only", "-z"],
2004
2051
  cwd=root,
2005
2052
  check=True,
2006
2053
  capture_output=True,
@@ -2150,11 +2197,9 @@ def cmd_format(args: _Args) -> int:
2150
2197
  print("fix: run `code-standards doctor --repair`, then retry", file=sys.stderr)
2151
2198
  return 2 if any(finding.id in _INVALID_DOCTOR_IDS for finding in drifted) else 1
2152
2199
  if args.staged:
2153
- try:
2154
- args.files = _staged_files(root)
2155
- except (OSError, subprocess.SubprocessError) as exc:
2156
- print(f"error: cannot read staged files: {exc}", file=sys.stderr)
2157
- return 2
2200
+ selection_status = _select_staged_fix(args, root)
2201
+ if selection_status is not None:
2202
+ return selection_status
2158
2203
  elif args.files:
2159
2204
  try:
2160
2205
  args.files = _selected_paths(root, args.files)
@@ -2171,6 +2216,27 @@ def cmd_format(args: _Args) -> int:
2171
2216
  return lifecycle.execute(commands)
2172
2217
 
2173
2218
 
2219
+ def _select_staged_fix(args: _Args, root: Path) -> int | None:
2220
+ try:
2221
+ staged_names = _staged_file_names(root)
2222
+ drifted = _unstaged_versions(root, staged_names)
2223
+ if drifted:
2224
+ print(
2225
+ "error: --staged found files with unstaged content; stage the intended versions before fixing: "
2226
+ + ", ".join(drifted),
2227
+ file=sys.stderr,
2228
+ )
2229
+ return 2
2230
+ args.files = _safe_staged_paths(root, staged_names)
2231
+ except (OSError, subprocess.SubprocessError) as exc:
2232
+ print(f"error: cannot read staged files: {exc}", file=sys.stderr)
2233
+ return 2
2234
+ if not args.files:
2235
+ print("No staged files to fix.")
2236
+ return 0
2237
+ return None
2238
+
2239
+
2174
2240
  def _staged_files(root: Path) -> list[str]:
2175
2241
  return _safe_staged_paths(root, _staged_file_names(root))
2176
2242
 
@@ -3043,7 +3109,7 @@ def build_app(handler: Callable[[_Args], int] = _dispatch) -> typer.Typer:
3043
3109
  bool,
3044
3110
  typer.Option(
3045
3111
  "--staged",
3046
- help="run custom rules on hook-supplied paths, or discover staged files when none are supplied",
3112
+ help="run the complete gate on staged files; optional paths narrow the staged selection",
3047
3113
  ),
3048
3114
  ] = False,
3049
3115
  selected_rules: Annotated[
@@ -3094,9 +3160,15 @@ def build_app(handler: Callable[[_Args], int] = _dispatch) -> typer.Typer:
3094
3160
  ctx: typer.Context,
3095
3161
  *,
3096
3162
  selected_rules: Annotated[
3097
- list[RuleSelector],
3163
+ list[RuleSelector] | None,
3098
3164
  typer.Option("--rule", parser=_parse_rule_selector, help="canonical ENGINE:ID selector (repeatable)"),
3099
- ],
3165
+ ] = None,
3166
+ test_value: Annotated[
3167
+ bool, typer.Option("--test-value", help="report the curated opt-in test-oracle advisories")
3168
+ ] = False,
3169
+ base: Annotated[
3170
+ str | None, typer.Option("--base", help="observe existing paths changed from this Git base")
3171
+ ] = None,
3100
3172
  output_format: Annotated[_DiagnosticFormat, typer.Option("--format")] = _DiagnosticFormat.TEXT,
3101
3173
  output: Annotated[Path | None, typer.Option("--output")] = None,
3102
3174
  trust_repository_code: Annotated[
@@ -3112,7 +3184,9 @@ def build_app(handler: Callable[[_Args], int] = _dispatch) -> typer.Typer:
3112
3184
  _Args(
3113
3185
  dest=_command_root(ctx),
3114
3186
  cmd="observe",
3115
- selected_rules=selected_rules,
3187
+ selected_rules=selected_rules or [],
3188
+ test_value=test_value,
3189
+ observation_base=base,
3116
3190
  output_format=output_format.value,
3117
3191
  output=output if output is not None else None,
3118
3192
  trust_repository_code=trust_repository_code,