blockwatch-cli 0.14.1__tar.gz → 0.14.2__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.
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.github/workflows/pypi.yml +2 -2
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/AGENTS.md +2 -1
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/CHANGELOG.md +32 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/CLAUDE.md +2 -1
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/Cargo.lock +1 -1
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/Cargo.toml +1 -1
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/PKG-INFO +1 -1
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/README.md +5 -2
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/dist-workspace.toml +1 -1
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/ci.md +2 -2
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/cli.md +104 -4
- blockwatch_cli-0.14.2/docs/validators/line-count.md +48 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/setup.py +1 -1
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/blocks.rs +23 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/cli.rs +49 -5
- blockwatch_cli-0.14.2/src/explain.rs +69 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/flags.rs +86 -3
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/lib.rs +1 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/skill.md +8 -1
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/symbols.rs +41 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/target_reference.rs +3 -3
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/validators/affects.rs +217 -108
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/validators/check_ai.rs +222 -95
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/validators/check_lua.rs +266 -139
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/validators/keep_sorted.rs +131 -4
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/validators/keep_unique.rs +141 -2
- blockwatch_cli-0.14.2/src/validators/line_count.rs +707 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/validators/line_pattern.rs +129 -4
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/validators/mod.rs +62 -2
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/validators/same_as.rs +476 -79
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/virtual_blocks.rs +1 -10
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/affects.rs +2 -2
- blockwatch_cli-0.14.2/tests/explain.rs +221 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/same_as.rs +1 -1
- blockwatch_cli-0.14.2/tests/testdata/explain/a.py +16 -0
- blockwatch_cli-0.14.2/tests/testdata/explain/check.lua +3 -0
- blockwatch_cli-0.14.1/docs/validators/line-count.md +0 -29
- blockwatch_cli-0.14.1/src/validators/line_count.rs +0 -403
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.agents/.claude-plugin/plugin.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.agents/skills/blockwatch/SKILL.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.claude-plugin/marketplace.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.github/dependabot.yml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.github/workflows/blockwatch.yml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.github/workflows/codeql.yml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.github/workflows/markdownlint.yml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.github/workflows/release.yml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.github/workflows/rust.yml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.github/workflows/trusted-checks.yml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.gitignore +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.markdownlint-cli2.yaml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.pre-commit-config.yaml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/.pre-commit-hooks.yaml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/LICENSE +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/SECURITY.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/blockwatch.toml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/clippy.toml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/codecov.yml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/demo.gif +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/agents.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/symbols.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/validators/README.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/validators/affects.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/validators/check-ai.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/validators/check-lua.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/validators/keep-sorted.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/validators/keep-unique.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/validators/line-pattern.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/docs/validators/same-as.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/pypi/pyproject.toml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/pyproject.toml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/release.toml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/scripts/check_language_count.lua +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/scripts/check_latest_gpt_nano_model.lua +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/block_parser.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/config.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/diff_parser.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/fs.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/bash.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/c.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/c_sharp.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/cmake.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/cpp.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/css.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/dart.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/dockerfile.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/elixir.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/go.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/graphql.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/groovy.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/hcl.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/html.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/java.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/javascript.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/json.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/json.scm +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/kotlin.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/lean.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/lua.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/makefile.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/markdown.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/mdx.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/mod.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/nix.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/ocaml.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/php.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/proto.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/python.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/ruby.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/rust.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/scala.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/sql.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/starlark.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/swift.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/toml.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/tsx.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/typescript.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/xml.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/language_parsers/yaml.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/main.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/path_arguments.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/paths_glob.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/repo_path.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/report.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/sarif.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/scan.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/settings.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/symbol_path.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/tag_parser.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/src/violation_address.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/check_ai.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/check_lua.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/common/empty.toml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/common/mod.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/config.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/encoding.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/general.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/keep_sorted.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/keep_unique.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/line_count.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/line_pattern.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/list.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/path_arguments.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/repository_root.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/sarif.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/skill.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/suppressions.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/symbol_references.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/cross_file_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/cross_file_target.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/custom_ext_source.javascript +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/custom_ext_target.javascript +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/dangling.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/missing_symbol.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/more_added_than_deleted.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/multiline_tag_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/multiline_tag_target.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/nested_mapping_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/nested_mapping_target.yaml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/sibling.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/symbol_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/symbol_target.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/table_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/table_target.toml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/tag_and_content_source.ts +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/tag_and_content_target.ts +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/whole_file_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/affects/whole_file_target.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_ai/two_blocks.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/affects.lua +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/affects.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/affects_drift.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/blocking_io.lua +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/blocking_io.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/echo.lua +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/fail.lua +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/mode.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/os.lua +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/pathfmt/report_path.lua +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/pathfmt/source.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/pattern.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/pattern_many_matches.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/pattern_no_match.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/success.lua +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/check_lua/success_and_failure.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/config/glob_target/config.toml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/config/glob_target/package.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/config/glob_target/plugins/blue/package.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/config/glob_target/plugins/green/package.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/config/glob_target/plugins/red/package.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/general/block_selection.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/general/custom_file_extension.javascript +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/general/custom_file_extension.python +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/general/disable_enable.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/general/paths/invalid.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/general/paths/subdir/nested_invalid.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/general/paths/valid.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/general/severity.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/hidden/.github/workflows/ci.yml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/keep_sorted/orderings.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/keep_unique/basic_and_pattern.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/line_count/equality_and_greater_than.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/line_pattern/forbidden_text.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/line_pattern/hex_and_word_patterns.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/list/.hg/.gitkeep +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/list/a.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/list/b.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/positions/unicode_columns.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/custom_ext_source.javascript +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/custom_ext_target.javascript +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/package.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/paths/alpha/item.txt +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/paths/beta/item.txt +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/paths_and_block_source.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/paths_source.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/sibling.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/split_table_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/split_table_target.toml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/symbol_mismatch_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/symbol_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/target.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/whole_file_mismatch_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/whole_file_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/same_as/whole_file_target.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/sarif/advisory.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/sarif/clean.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/sarif/sorted.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/sarif/unnamed.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/commit_msg.txt +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/commit_msg_gone_a.txt +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/commit_msg_gone_b.txt +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/commit_msg_lowercase_trailer.txt +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/commit_msg_malformed.txt +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/commit_msg_with_noise.txt +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/dangling.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/sorted.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/symbol.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/symbol_target.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/two_rules.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/suppressions/unnamed.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/escapes_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/escapes_target.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/filter_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/filter_target.yaml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/json_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/json_target.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/jsonc_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/jsonc_target.jsonc +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/markdown_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/markdown_target.markdown +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/md_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/md_target.md +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/mdx_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/mdx_target.mdx +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/mixed_list_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/mixed_list_target.json +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/toml_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/toml_target.toml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/yaml_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/yaml_target.yaml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/yml_source.rs +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/symbol_references/yml_target.yml +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/verbosity/affects/pair.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/verbosity/clean/counted.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/verbosity/clean/noop.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/verbosity/clean/notes.txt +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/verbosity/clean/sorted.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/verbosity/diff/two_blocks.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/verbosity/failing/unsorted.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/testdata/verbosity/mixed/both_kinds.py +0 -0
- {blockwatch_cli-0.14.1 → blockwatch_cli-0.14.2}/tests/verbosity.rs +0 -0
|
@@ -24,9 +24,9 @@ jobs:
|
|
|
24
24
|
target: x86_64-unknown-linux-gnu
|
|
25
25
|
- runner: ubuntu-24.04-arm
|
|
26
26
|
target: aarch64-unknown-linux-gnu
|
|
27
|
-
- runner: macos-
|
|
27
|
+
- runner: macos-15
|
|
28
28
|
target: aarch64-apple-darwin
|
|
29
|
-
- runner: macos-
|
|
29
|
+
- runner: macos-15
|
|
30
30
|
target: x86_64-apple-darwin
|
|
31
31
|
- runner: windows-latest
|
|
32
32
|
target: x86_64-pc-windows-msvc
|
|
@@ -193,7 +193,8 @@ The whole pipeline is in `src/cli.rs`. `src/main.rs` only calls `blockwatch::run
|
|
|
193
193
|
report goes to stdout. The exit code depends on the error-severity violations that are not suppressed.
|
|
194
194
|
|
|
195
195
|
The `list` subcommand stops after step 6 and writes the blocks it found to stdout as JSON. The `skill` subcommand
|
|
196
|
-
prints `src/skill.md` right after step 1.
|
|
196
|
+
prints `src/skill.md` right after step 1. The `explain` subcommand runs like `--only-block` with the file of its
|
|
197
|
+
address as the only path. It then writes that block to stdout as JSON.
|
|
197
198
|
|
|
198
199
|
The main modules:
|
|
199
200
|
|
|
@@ -12,6 +12,38 @@ Releases up to and including v0.3.11 predate this file. Their notes live on the
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased] - ReleaseDate
|
|
14
14
|
|
|
15
|
+
## [0.14.2] - 2026-10-21
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- `line-count-pattern` makes `line-count` count only the lines that match the regex. For example,
|
|
20
|
+
`line-count="<=20" line-count-pattern='^"\w+",$'` skips comments and blank lines in a list. See
|
|
21
|
+
[`line-count`](docs/validators/line-count.md).
|
|
22
|
+
- `blockwatch explain FILE:BLOCK_NAME` checks one named block and prints it as JSON: its attributes, the text its
|
|
23
|
+
validators read and where that text is. For example: `blockwatch explain package.json:version`. The output also lists
|
|
24
|
+
the validators the block asks for but the settings turn off (`"disabled"`). Under `"checks"`, it shows what each
|
|
25
|
+
validator saw:
|
|
26
|
+
- `same-as`: each record the pattern matched, with its line, on both sides, and the reason of each mismatch.
|
|
27
|
+
- `keep-sorted` and `keep-unique`: the value each line gives, up to the first line out of order or repeated.
|
|
28
|
+
- `line-pattern`: what each pattern matched on each line, up to the first line that fails.
|
|
29
|
+
- `affects`: whether the block changed, and for each target, whether it changed or why it doesn't resolve.
|
|
30
|
+
- `line-count`: the value each line gives, how many lines count, and the rule they were checked against.
|
|
31
|
+
- `check-lua`: the `content` and `ctx.affects` the script got, and the message it returned.
|
|
32
|
+
- `check-ai`: the model, the prompt it was sent and its answer.
|
|
33
|
+
|
|
34
|
+
`"with_diff"` says whether `--diff` was given. Without it, no block counts as changed. Violations go to stderr, and
|
|
35
|
+
the exit code is the same as for a normal run. See [The `explain` Command](docs/cli.md#the-explain-command).
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- A message of `affects` or `same-as` about a whole-file target shows the path alone, without `file` in front. For
|
|
40
|
+
example: `Block a.py:b at line 1 is modified, but config.json is not`.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- A `check-lua` script got no `ctx.affects` entry for a block target that the run didn't check, such as with
|
|
45
|
+
`--only-block` or `--diff --only-changed`. Now `ctx.affects` holds the same targets in every run.
|
|
46
|
+
|
|
15
47
|
## [0.14.1] - 2026-10-17
|
|
16
48
|
|
|
17
49
|
### Changed
|
|
@@ -193,7 +193,8 @@ The whole pipeline is in `src/cli.rs`. `src/main.rs` only calls `blockwatch::run
|
|
|
193
193
|
report goes to stdout. The exit code depends on the error-severity violations that are not suppressed.
|
|
194
194
|
|
|
195
195
|
The `list` subcommand stops after step 6 and writes the blocks it found to stdout as JSON. The `skill` subcommand
|
|
196
|
-
prints `src/skill.md` right after step 1.
|
|
196
|
+
prints `src/skill.md` right after step 1. The `explain` subcommand runs like `--only-block` with the file of its
|
|
197
|
+
address as the only path. It then writes that block to stdout as JSON.
|
|
197
198
|
|
|
198
199
|
The main modules:
|
|
199
200
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blockwatch-cli
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.2
|
|
4
4
|
Summary: Language agnostic linter that keeps your code and documentation in sync and valid
|
|
5
5
|
Keywords: linter,sync,drift,comments,agents
|
|
6
6
|
Home-Page: https://github.com/mennanov/blockwatch
|
|
@@ -104,7 +104,7 @@ you'd otherwise have to nitpick in code review.
|
|
|
104
104
|
| [`keep-sorted`](docs/validators/keep-sorted.md) | Enforces alphabetical or numerical ordering on list items | `keep-sorted`, `keep-sorted-pattern`, `keep-sorted-format` |
|
|
105
105
|
| [`keep-unique`](docs/validators/keep-unique.md) | Prevents duplicate lines within a block | `keep-unique` |
|
|
106
106
|
| [`line-pattern`](docs/validators/line-pattern.md) | Enforces that every line matches a regex, or that no line matches a forbidden one | `line-pattern`, `line-pattern-forbidden` |
|
|
107
|
-
| [`line-count`](docs/validators/line-count.md) | Enforces lower or upper bounds on the number of lines in a block | `line-count`
|
|
107
|
+
| [`line-count`](docs/validators/line-count.md) | Enforces lower or upper bounds on the number of lines in a block | `line-count`, `line-count-pattern` |
|
|
108
108
|
| [`check-ai`](docs/validators/check-ai.md) | Validates content against natural language rules using an LLM | `check-ai`, `check-ai-pattern` |
|
|
109
109
|
| [`check-lua`](docs/validators/check-lua.md) | Runs custom validation logic written in Lua | `check-lua`, `check-lua-pattern`, `check-lua-timeout` |
|
|
110
110
|
|
|
@@ -173,6 +173,9 @@ git diff --patch HEAD | blockwatch --diff --only-changed
|
|
|
173
173
|
|
|
174
174
|
# Dump all discovered blocks as JSON
|
|
175
175
|
blockwatch list
|
|
176
|
+
|
|
177
|
+
# Show what each validator of one named block read
|
|
178
|
+
blockwatch explain src/lib.rs:languages
|
|
176
179
|
```
|
|
177
180
|
|
|
178
181
|
Rules live in comments, next to the code they describe. Settings that apply to the whole project, such as ignored
|
|
@@ -215,7 +218,7 @@ exclusions, and custom extension mappings.
|
|
|
215
218
|
|
|
216
219
|
```yaml
|
|
217
220
|
- repo: https://github.com/mennanov/blockwatch
|
|
218
|
-
rev: v0.14.
|
|
221
|
+
rev: v0.14.2 # Use latest release
|
|
219
222
|
hooks:
|
|
220
223
|
- id: blockwatch
|
|
221
224
|
```
|
|
@@ -19,7 +19,7 @@ To use [pre-commit](https://pre-commit.com), add the hook to `.pre-commit-config
|
|
|
19
19
|
|
|
20
20
|
```yaml
|
|
21
21
|
- repo: https://github.com/mennanov/blockwatch
|
|
22
|
-
rev: v0.14.
|
|
22
|
+
rev: v0.14.2 # Use latest release
|
|
23
23
|
hooks:
|
|
24
24
|
- id: blockwatch
|
|
25
25
|
```
|
|
@@ -59,7 +59,7 @@ The `blockwatch` hook runs before the commit message exists, so it cannot see a
|
|
|
59
59
|
|
|
60
60
|
```yaml
|
|
61
61
|
- repo: https://github.com/mennanov/blockwatch
|
|
62
|
-
rev: v0.14.
|
|
62
|
+
rev: v0.14.2 # Use latest release
|
|
63
63
|
hooks:
|
|
64
64
|
- id: blockwatch-commit-msg
|
|
65
65
|
```
|
|
@@ -14,6 +14,8 @@ For command-line flag documentation directly in your terminal, run `blockwatch -
|
|
|
14
14
|
every block in the repository.
|
|
15
15
|
- **List Blocks**: `blockwatch list` outputs a JSON report of all discovered blocks. It takes `--diff`,
|
|
16
16
|
`--only-changed`, `--config`, `-E` and `--ignore`.
|
|
17
|
+
- **Explain a Block**: `blockwatch explain FILE:BLOCK_NAME` checks one named block and prints what each of its
|
|
18
|
+
validators read, as JSON. See [The `explain` Command](#the-explain-command).
|
|
17
19
|
- **Print the Agent Skill**: `blockwatch skill` prints the [skill for AI agents](#the-skill-command), as a `SKILL.md`
|
|
18
20
|
- **Config File**: `blockwatch --config FILE` reads the project's settings from FILE instead of `blockwatch.toml`
|
|
19
21
|
- **Custom Extensions**: Map custom file extensions: `blockwatch -E ino=cpp` (config key: `extensions`)
|
|
@@ -555,15 +557,15 @@ Nothing else about the run changes: the same violations are found, and the exit
|
|
|
555
557
|
"tool": {
|
|
556
558
|
"driver": {
|
|
557
559
|
"name": "blockwatch",
|
|
558
|
-
"version": "0.14.
|
|
559
|
-
"semanticVersion": "0.14.
|
|
560
|
+
"version": "0.14.2",
|
|
561
|
+
"semanticVersion": "0.14.2",
|
|
560
562
|
"informationUri": "https://github.com/mennanov/blockwatch",
|
|
561
563
|
"rules": [
|
|
562
564
|
{
|
|
563
565
|
"id": "keep-sorted",
|
|
564
566
|
"name": "keep-sorted",
|
|
565
567
|
"shortDescription": { "text": "Requires the lines of a block to stay in order." },
|
|
566
|
-
"helpUri": "https://github.com/mennanov/blockwatch/blob/v0.14.
|
|
568
|
+
"helpUri": "https://github.com/mennanov/blockwatch/blob/v0.14.2/docs/validators/keep-sorted.md"
|
|
567
569
|
}
|
|
568
570
|
]
|
|
569
571
|
}
|
|
@@ -617,7 +619,7 @@ What to expect from the log:
|
|
|
617
619
|
- **Lines and columns are 1-based, with an exclusive end column.** A column counts characters, which the run declares as
|
|
618
620
|
`"columnKind": "unicodeCodePoints"` so that a consumer does not count UTF-16 code units instead.
|
|
619
621
|
|
|
620
|
-
`--format` cannot be combined with the `list`
|
|
622
|
+
`--format` cannot be combined with the `list` and `explain` subcommands, which write JSON of their own. To upload a log
|
|
621
623
|
to GitHub code scanning, see [CI Integration](ci.md#github-code-scanning).
|
|
622
624
|
|
|
623
625
|
## The `list` Command
|
|
@@ -701,6 +703,104 @@ block's attributes are sorted by name; the files themselves are not ordered, and
|
|
|
701
703
|
emit them differently. Sort by key downstream if you need to diff one run against another — or use
|
|
702
704
|
[`--verbosity full`](#full-reports), which does order its files.
|
|
703
705
|
|
|
706
|
+
## The `explain` Command
|
|
707
|
+
|
|
708
|
+
`blockwatch explain FILE:BLOCK_NAME` checks one named block and prints what each of its validators read, as JSON. Run it
|
|
709
|
+
after you write a rule, to see that the rule matches what you meant. A rule can be wrong and still pass. For example, a
|
|
710
|
+
`same-as-pattern` can take the same wrong part of each line on both sides.
|
|
711
|
+
|
|
712
|
+
```shell
|
|
713
|
+
blockwatch explain src/colors.py:colors
|
|
714
|
+
blockwatch explain package.json:version # a block from blockwatch.toml
|
|
715
|
+
git diff --patch | blockwatch explain --diff src/colors.py:colors # affects needs the diff
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
- It takes one address, as `--only-block` does. A block without a `name` can't be explained, so give it one first.
|
|
719
|
+
- It takes `--diff`, `--config`, `-E` and `--ignore`, and rejects the other flags.
|
|
720
|
+
- `only-blocks` and `skip-blocks` in `blockwatch.toml` play no part, because the address already picks the block.
|
|
721
|
+
`enable` and `disable` still apply.
|
|
722
|
+
- The explanation goes to stdout. Violations go to stderr, and the [exit code](#exit-codes) is the one a normal run
|
|
723
|
+
gives. `check-ai` calls the API, as in a normal run.
|
|
724
|
+
|
|
725
|
+
### Explain Output Example
|
|
726
|
+
|
|
727
|
+
For this block:
|
|
728
|
+
|
|
729
|
+
```python
|
|
730
|
+
COLORS = [
|
|
731
|
+
# <block name="colors" keep-sorted="asc" keep-sorted-pattern='"(?P<value>\w+)"'>
|
|
732
|
+
"blue",
|
|
733
|
+
"red",
|
|
734
|
+
# </block>
|
|
735
|
+
]
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
`blockwatch explain src/colors.py:colors` prints:
|
|
739
|
+
|
|
740
|
+
[//]: # (<block name="explain-output-example">)
|
|
741
|
+
|
|
742
|
+
```json
|
|
743
|
+
{
|
|
744
|
+
"block": {
|
|
745
|
+
"attributes": {
|
|
746
|
+
"keep-sorted": "asc",
|
|
747
|
+
"keep-sorted-pattern": "\"(?P<value>\\w+)\"",
|
|
748
|
+
"name": "colors"
|
|
749
|
+
},
|
|
750
|
+
"column": 7,
|
|
751
|
+
"is_content_modified": false,
|
|
752
|
+
"line": 2,
|
|
753
|
+
"name": "colors"
|
|
754
|
+
},
|
|
755
|
+
"checks": {
|
|
756
|
+
"keep-sorted": {
|
|
757
|
+
"format": "lexicographic",
|
|
758
|
+
"lines": [
|
|
759
|
+
{ "line": 3, "text": "\"blue\",", "value": "blue" },
|
|
760
|
+
{ "line": 4, "text": "\"red\",", "value": "red" }
|
|
761
|
+
],
|
|
762
|
+
"order": "asc"
|
|
763
|
+
}
|
|
764
|
+
},
|
|
765
|
+
"content": {
|
|
766
|
+
"start": { "line": 2, "character": 85 },
|
|
767
|
+
"end": { "line": 5, "character": 5 },
|
|
768
|
+
"text": "\n \"blue\",\n \"red\",\n "
|
|
769
|
+
},
|
|
770
|
+
"disabled": [],
|
|
771
|
+
"file": "src/colors.py",
|
|
772
|
+
"with_diff": false
|
|
773
|
+
}
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
[//]: # (</block>)
|
|
777
|
+
|
|
778
|
+
| Key | What it holds |
|
|
779
|
+
|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
780
|
+
| `block` | The block, as [`list`](#the-list-command) writes it. |
|
|
781
|
+
| `checks` | What each validator read, by validator. See [What Each Validator Shows](#what-each-validator-shows). |
|
|
782
|
+
| `content` | The text the validators read. `start` is its first character, and `end` is the first character after it. The column is `character`, as in the `range` of a violation. For a [virtual block](#blocks-in-the-config-file) around a quoted or escaped value, `text` is the decoded value. |
|
|
783
|
+
| `disabled` | The validators that the block asks for, but the settings turn off. They have no entry in `checks`. |
|
|
784
|
+
| `file` | The block's file. |
|
|
785
|
+
| `with_diff` | Whether `--diff` was given. Without it, no block counts as changed, so `affects` can't fire. |
|
|
786
|
+
|
|
787
|
+
### What Each Validator Shows
|
|
788
|
+
|
|
789
|
+
A trace stops where its validator stops, such as at the first line out of order. A `line` is a line of the block's
|
|
790
|
+
file, and `text` is that line trimmed, as the validator compares it. A key that holds what a violation reports has the
|
|
791
|
+
name the violation's `data` gives it, such as `reason`.
|
|
792
|
+
|
|
793
|
+
| Validator | What it shows |
|
|
794
|
+
|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
795
|
+
| `same-as` | `records`: each match of `same-as-pattern` in the block, with its `line`, the text the regex matched (`match`), and each named group after `same-as-format` (`groups`). `targets`: the `records` of each target, and the `reason` of its violation, or `null` when the two agree. `paths`: with `same-as-paths`, the `glob`, the paths it `matched`, their `records` and the `reason`. |
|
|
796
|
+
| `keep-sorted` | `lines`: each line read, with the `value` it compares, or `null` when `keep-sorted-pattern` skips the line. `format` and `order`. |
|
|
797
|
+
| `keep-unique` | `lines`, as for `keep-sorted`. A repeated value has `duplicate_of`: the line where it first appeared. |
|
|
798
|
+
| `line-pattern` | `lines`: each line read, with the text `line-pattern` matched (`match`). A line that fails has `fails`: the check it fails. `forbidden_match` is the text `line-pattern-forbidden` found. |
|
|
799
|
+
| `line-count` | `lines`, as for `keep-sorted`. A line counts when its `value` isn't `null`. The number of lines counted (`actual`), and the rule (`op` and `expected`). |
|
|
800
|
+
| `affects` | Whether the diff changed the block (`block_changed`). For each target: whether the diff `changed` it, or the `reason` it doesn't resolve. |
|
|
801
|
+
| `check-lua` | The `content` and the `affects` the script got, and the message it returned (`lua_error`), or `null` when it passed. `affects` is `ctx.affects` without the content of each target. |
|
|
802
|
+
| `check-ai` | The `model`, the `prompt` sent and the `response`. Both are `null` when `check-ai-pattern` matched nothing, so no request was sent. |
|
|
803
|
+
|
|
704
804
|
## Run Reports
|
|
705
805
|
|
|
706
806
|
By default `blockwatch` prints nothing when a run succeeds. That makes a check that passed look exactly like a check
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# `line-count`
|
|
2
|
+
|
|
3
|
+
Constrains how many lines a block may contain. Flags unbounded growth in a public API surface, or pins a switch to the
|
|
4
|
+
size of the enum it maps.
|
|
5
|
+
|
|
6
|
+
## Syntax
|
|
7
|
+
|
|
8
|
+
| Attribute | Value | Default |
|
|
9
|
+
|----------------------|--------------------------------------------|--------------------------|
|
|
10
|
+
| `line-count` | `<N`, `<=N`, `==N`, `>=N`, `>N` | — |
|
|
11
|
+
| `line-count-pattern` | regex; a line counts when it gives a value | every line that has text |
|
|
12
|
+
|
|
13
|
+
## Example
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
# <block line-count="<=5">
|
|
17
|
+
"a",
|
|
18
|
+
"b",
|
|
19
|
+
"c"
|
|
20
|
+
# </block>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Counting only some lines
|
|
24
|
+
|
|
25
|
+
`line-count-pattern` counts only the lines it matches:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
# <block line-count="<=2" line-count-pattern='^"\w+",$'>
|
|
29
|
+
"a",
|
|
30
|
+
# a comment
|
|
31
|
+
"b",
|
|
32
|
+
# </block>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The block above counts 2: the comment line doesn't match.
|
|
36
|
+
|
|
37
|
+
Lines that do not match are skipped, as are lines whose match is empty. So is a line that matches a branch without the
|
|
38
|
+
`value` group: `^(?:none|(?P<value>\w+))$` skips the line `none`. The pattern runs on the trimmed line, so `^` and `$`
|
|
39
|
+
match at the text, not at the indentation.
|
|
40
|
+
|
|
41
|
+
## Notes
|
|
42
|
+
|
|
43
|
+
- **Blank lines are not counted.** The block above counts 3.
|
|
44
|
+
- A malformed comparator is a hard error, not a violation.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
← [Validators](README.md) · [README](../../README.md)
|
|
@@ -6,7 +6,7 @@ setup(
|
|
|
6
6
|
name='blockwatch-pre-commit-placeholder',
|
|
7
7
|
version='0.0.0',
|
|
8
8
|
# <block name="blockwatch-cli-version" same-as-pattern="blockwatch-cli==(?P<value>\d+\.\d+\.\d+)">
|
|
9
|
-
install_requires=['blockwatch-cli==0.14.
|
|
9
|
+
install_requires=['blockwatch-cli==0.14.2'],
|
|
10
10
|
# </block>
|
|
11
11
|
# Without it, setuptools takes the directories under src/ for Python packages.
|
|
12
12
|
packages=[],
|
|
@@ -184,6 +184,15 @@ impl Content {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
/// Describes the content for JSON. `source` must be the text of the block's file.
|
|
188
|
+
pub(crate) fn listing<'a>(&'a self, source: &'a str) -> ContentListing<'a> {
|
|
189
|
+
ContentListing {
|
|
190
|
+
start: self.positions.start,
|
|
191
|
+
end: self.positions.end,
|
|
192
|
+
text: self.text(source),
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
187
196
|
/// Returns the content's text. `source` must be the text of the block's file.
|
|
188
197
|
pub(crate) fn text<'a>(&'a self, source: &'a str) -> &'a str {
|
|
189
198
|
match &self.text {
|
|
@@ -424,6 +433,19 @@ pub(crate) struct BlockListing<'a> {
|
|
|
424
433
|
}
|
|
425
434
|
// </block>
|
|
426
435
|
|
|
436
|
+
/// The content of a block, as it is written in JSON: its text, and where the block's file writes
|
|
437
|
+
/// it.
|
|
438
|
+
#[derive(Serialize, Debug)]
|
|
439
|
+
pub(crate) struct ContentListing<'a> {
|
|
440
|
+
/// The position of the content's first character.
|
|
441
|
+
start: Position,
|
|
442
|
+
/// The first position after the content.
|
|
443
|
+
end: Position,
|
|
444
|
+
/// The text the validators read. For a virtual block around a quoted or escaped value, it is
|
|
445
|
+
/// the decoded value.
|
|
446
|
+
text: &'a str,
|
|
447
|
+
}
|
|
448
|
+
|
|
427
449
|
impl<'a> From<&'a BlockWithContext> for BlockListing<'a> {
|
|
428
450
|
/// Describes the block, with no `checks`.
|
|
429
451
|
fn from(block_with_context: &'a BlockWithContext) -> Self {
|
|
@@ -461,6 +483,7 @@ const RECOGNIZED_ATTRIBUTES: &[&str] = &[
|
|
|
461
483
|
"keep-sorted-pattern",
|
|
462
484
|
"keep-unique",
|
|
463
485
|
"line-count",
|
|
486
|
+
"line-count-pattern",
|
|
464
487
|
"line-pattern",
|
|
465
488
|
"line-pattern-forbidden",
|
|
466
489
|
"name",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
//! The whole run: reads the flags and the input, runs the validators or `
|
|
2
|
-
//! output.
|
|
1
|
+
//! The whole run: reads the flags and the input, runs the validators, `list` or `explain`, and
|
|
2
|
+
//! writes the output.
|
|
3
3
|
|
|
4
|
-
use crate::blocks::BlockSeverity;
|
|
4
|
+
use crate::blocks::{BlockAddress, BlockSeverity};
|
|
5
5
|
use crate::config;
|
|
6
6
|
use crate::diff_parser::{self, DiffChanges};
|
|
7
|
+
use crate::explain::Explanation;
|
|
7
8
|
use crate::flags;
|
|
8
9
|
use crate::fs::FileSystem;
|
|
9
10
|
use crate::language_parsers;
|
|
@@ -12,7 +13,7 @@ use crate::repo_path::RepoPath;
|
|
|
12
13
|
use crate::report;
|
|
13
14
|
use crate::sarif;
|
|
14
15
|
use crate::scan;
|
|
15
|
-
use crate::settings::Settings;
|
|
16
|
+
use crate::settings::{BlockSelection, Settings};
|
|
16
17
|
use crate::validators;
|
|
17
18
|
use crate::validators::Violation;
|
|
18
19
|
use crate::violation_address::ViolationAddress;
|
|
@@ -27,7 +28,8 @@ use std::sync::Arc;
|
|
|
27
28
|
use std::{env, fs};
|
|
28
29
|
|
|
29
30
|
/// Runs the program with the command-line flags of the process: validates the blocks, lists them
|
|
30
|
-
/// with the `list` subcommand,
|
|
31
|
+
/// with the `list` subcommand, checks one block with the `explain` subcommand, or prints the agent
|
|
32
|
+
/// skill with the `skill` subcommand.
|
|
31
33
|
///
|
|
32
34
|
/// Returns the exit code of the process:
|
|
33
35
|
/// - `0` when no violation fails the run.
|
|
@@ -41,6 +43,7 @@ pub fn run() -> ExitCode {
|
|
|
41
43
|
let result = args.validate().and_then(|()| match &args.command {
|
|
42
44
|
Some(flags::Subcommand::List { .. }) => run_list(&args).map(|()| ExitCode::SUCCESS),
|
|
43
45
|
Some(flags::Subcommand::Skill) => run_skill().map(|()| ExitCode::SUCCESS),
|
|
46
|
+
Some(flags::Subcommand::Explain { address }) => run_explain(&args, address),
|
|
44
47
|
None => run_validators(&args),
|
|
45
48
|
});
|
|
46
49
|
result.unwrap_or_else(|error| {
|
|
@@ -65,6 +68,47 @@ fn run_skill() -> anyhow::Result<()> {
|
|
|
65
68
|
write_line(std::io::stdout(), skill.trim_end())
|
|
66
69
|
}
|
|
67
70
|
|
|
71
|
+
/// Runs the `explain` subcommand: checks the one block that `address` selects, writes what its
|
|
72
|
+
/// validators saw to stdout as JSON, and writes its violations to stderr.
|
|
73
|
+
///
|
|
74
|
+
/// Returns the exit code of a validation run: 1 when a violation of error severity is found, and 0
|
|
75
|
+
/// otherwise.
|
|
76
|
+
fn run_explain(args: &flags::Args, address: &BlockAddress) -> anyhow::Result<ExitCode> {
|
|
77
|
+
let Prepared {
|
|
78
|
+
file_system,
|
|
79
|
+
settings,
|
|
80
|
+
mut context,
|
|
81
|
+
..
|
|
82
|
+
} = prepare(args)?;
|
|
83
|
+
// The address picks the block, so the config's `only-blocks` and `skip-blocks` play no part.
|
|
84
|
+
context.apply_block_selection(
|
|
85
|
+
&BlockSelection::Only(vec![address.clone()]),
|
|
86
|
+
file_system.as_ref(),
|
|
87
|
+
)?;
|
|
88
|
+
let (sync_validators, async_validators) = validators::detect_validators(
|
|
89
|
+
&context,
|
|
90
|
+
validators::Validator::detect,
|
|
91
|
+
settings.validator_selection(),
|
|
92
|
+
&file_system,
|
|
93
|
+
)?;
|
|
94
|
+
context.request_traces();
|
|
95
|
+
let context = Arc::new(context);
|
|
96
|
+
let log = validators::run(Arc::clone(&context), sync_validators, async_validators)?;
|
|
97
|
+
|
|
98
|
+
// Violations are what the run is for, so they are written first, as in `run_validators`. A
|
|
99
|
+
// failure to write the explanation then can't discard them.
|
|
100
|
+
let has_error_severity = process_violations(&log.violations, flags::OutputFormat::Json)?;
|
|
101
|
+
write_json(
|
|
102
|
+
std::io::stdout(),
|
|
103
|
+
&Explanation::new(&context, &settings, &log, args.diff),
|
|
104
|
+
)?;
|
|
105
|
+
Ok(if has_error_severity {
|
|
106
|
+
ExitCode::from(1)
|
|
107
|
+
} else {
|
|
108
|
+
ExitCode::SUCCESS
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
68
112
|
/// Runs the `list` subcommand: parses every block in scope and writes a JSON report to stdout.
|
|
69
113
|
fn run_list(args: &flags::Args) -> anyhow::Result<()> {
|
|
70
114
|
let Prepared { context, .. } = prepare(args)?;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//! The output of the `explain` subcommand.
|
|
2
|
+
|
|
3
|
+
use crate::blocks::{BlockListing, ContentListing};
|
|
4
|
+
use crate::repo_path::RepoPath;
|
|
5
|
+
use crate::settings::Settings;
|
|
6
|
+
use crate::validators::{ValidationContext, ValidationLog, Validator, ValidatorTrace};
|
|
7
|
+
use serde::Serialize;
|
|
8
|
+
use std::collections::{BTreeMap, BTreeSet};
|
|
9
|
+
use strum::IntoEnumIterator;
|
|
10
|
+
|
|
11
|
+
/// What `explain` writes to stdout: one block, the content its validators read, what they saw, and
|
|
12
|
+
/// how the settings treat it.
|
|
13
|
+
///
|
|
14
|
+
/// The fields are declared in alphabetical order, so the keys come out sorted.
|
|
15
|
+
#[derive(Serialize, Debug)]
|
|
16
|
+
pub(crate) struct Explanation<'a> {
|
|
17
|
+
// <block name="explanation" affects="docs/cli.md:explain-output-example">
|
|
18
|
+
/// The block, as `list` writes it.
|
|
19
|
+
block: BlockListing<'a>,
|
|
20
|
+
/// What each validator saw in the block, by validator.
|
|
21
|
+
checks: BTreeMap<Validator, &'a ValidatorTrace>,
|
|
22
|
+
content: ContentListing<'a>,
|
|
23
|
+
/// The validators that the block's attributes ask for, but the settings turn off.
|
|
24
|
+
disabled: BTreeSet<Validator>,
|
|
25
|
+
file: &'a RepoPath,
|
|
26
|
+
/// Whether the run was given a diff. Without one, no block counts as changed.
|
|
27
|
+
with_diff: bool,
|
|
28
|
+
// </block>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
impl<'a> Explanation<'a> {
|
|
32
|
+
/// Describes the one block that `context` keeps, as `settings` treat it, with the traces that
|
|
33
|
+
/// `log` holds for it. `with_diff` is whether the run was given a diff.
|
|
34
|
+
///
|
|
35
|
+
/// # Panics
|
|
36
|
+
/// Panics if `context` doesn't keep exactly one block.
|
|
37
|
+
pub(crate) fn new(
|
|
38
|
+
context: &'a ValidationContext,
|
|
39
|
+
settings: &Settings,
|
|
40
|
+
log: &'a ValidationLog,
|
|
41
|
+
with_diff: bool,
|
|
42
|
+
) -> Self {
|
|
43
|
+
assert_eq!(context.blocks().len(), 1, "the context keeps one file");
|
|
44
|
+
let (file, file_blocks) = context.blocks().iter().next().expect("one file");
|
|
45
|
+
let [block_with_context] = file_blocks.blocks_with_context.as_slice() else {
|
|
46
|
+
panic!("the context keeps one block");
|
|
47
|
+
};
|
|
48
|
+
let block = &block_with_context.block;
|
|
49
|
+
Self {
|
|
50
|
+
block: BlockListing::from(block_with_context),
|
|
51
|
+
checks: log
|
|
52
|
+
.traces
|
|
53
|
+
.get(&(file.clone(), block.key()))
|
|
54
|
+
.into_iter()
|
|
55
|
+
.flatten()
|
|
56
|
+
.map(|(validator, trace)| (*validator, trace))
|
|
57
|
+
.collect(),
|
|
58
|
+
content: block.content.listing(&file_blocks.file_content),
|
|
59
|
+
disabled: Validator::iter()
|
|
60
|
+
.filter(|validator| {
|
|
61
|
+
block.attributes.contains_key(validator.as_str())
|
|
62
|
+
&& !settings.validator_selection().includes(*validator)
|
|
63
|
+
})
|
|
64
|
+
.collect(),
|
|
65
|
+
file,
|
|
66
|
+
with_diff,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|