blockwatch-cli 0.13.0__tar.gz → 0.14.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.
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/AGENTS.md +4 -3
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/CHANGELOG.md +21 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/CLAUDE.md +4 -3
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/Cargo.lock +1 -1
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/Cargo.toml +1 -1
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/PKG-INFO +1 -1
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/README.md +1 -1
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/blockwatch.toml +4 -31
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/ci.md +2 -2
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/cli.md +3 -3
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/validators/README.md +2 -1
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/validators/same-as.md +79 -16
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/setup.py +1 -1
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/json.rs +1 -1
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/markdown.rs +1 -1
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/mdx.rs +1 -1
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/toml.rs +1 -1
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/yaml.rs +1 -1
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/skill.md +12 -28
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/validators/same_as.rs +623 -102
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.agents/.claude-plugin/plugin.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.agents/skills/blockwatch/SKILL.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.claude-plugin/marketplace.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.github/dependabot.yml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.github/workflows/blockwatch.yml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.github/workflows/codeql.yml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.github/workflows/markdownlint.yml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.github/workflows/pypi.yml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.github/workflows/release.yml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.github/workflows/rust.yml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.github/workflows/trusted-checks.yml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.gitignore +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.markdownlint-cli2.yaml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.pre-commit-config.yaml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/.pre-commit-hooks.yaml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/LICENSE +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/SECURITY.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/clippy.toml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/codecov.yml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/demo.gif +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/dist-workspace.toml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/agents.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/symbols.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/validators/affects.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/validators/check-ai.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/validators/check-lua.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/validators/keep-sorted.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/validators/keep-unique.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/validators/line-count.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/docs/validators/line-pattern.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/pypi/pyproject.toml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/pyproject.toml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/release.toml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/scripts/check_language_count.lua +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/scripts/check_latest_gpt_nano_model.lua +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/block_parser.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/blocks.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/cli.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/config.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/diff_parser.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/flags.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/fs.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/bash.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/c.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/c_sharp.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/cmake.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/cpp.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/css.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/dart.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/dockerfile.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/elixir.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/go.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/graphql.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/groovy.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/hcl.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/html.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/java.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/javascript.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/json.scm +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/kotlin.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/lean.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/lua.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/makefile.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/mod.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/nix.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/ocaml.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/php.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/proto.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/python.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/ruby.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/rust.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/scala.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/sql.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/starlark.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/swift.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/tsx.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/typescript.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/language_parsers/xml.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/lib.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/main.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/path_arguments.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/paths_glob.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/repo_path.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/report.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/sarif.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/settings.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/symbol_path.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/symbols.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/tag_parser.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/validators/affects.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/validators/check_ai.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/validators/check_lua.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/validators/keep_sorted.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/validators/keep_unique.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/validators/line_count.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/validators/line_pattern.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/validators/mod.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/violation_address.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/src/virtual_blocks.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/affects.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/check_ai.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/check_lua.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/common/empty.toml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/common/mod.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/config.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/encoding.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/general.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/keep_sorted.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/keep_unique.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/line_count.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/line_pattern.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/list.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/path_arguments.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/repository_root.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/same_as.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/sarif.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/skill.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/suppressions.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/symbol_references.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/cross_file_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/cross_file_target.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/custom_ext_source.javascript +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/custom_ext_target.javascript +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/dangling.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/missing_symbol.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/more_added_than_deleted.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/multiline_tag_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/multiline_tag_target.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/nested_mapping_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/nested_mapping_target.yaml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/sibling.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/symbol_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/symbol_target.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/table_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/table_target.toml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/tag_and_content_source.ts +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/tag_and_content_target.ts +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/whole_file_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/affects/whole_file_target.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_ai/two_blocks.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/affects.lua +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/affects.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/affects_drift.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/blocking_io.lua +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/blocking_io.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/echo.lua +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/fail.lua +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/mode.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/os.lua +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/pathfmt/report_path.lua +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/pathfmt/source.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/pattern.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/pattern_many_matches.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/pattern_no_match.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/success.lua +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/check_lua/success_and_failure.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/config/glob_target/config.toml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/config/glob_target/package.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/config/glob_target/plugins/blue/package.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/config/glob_target/plugins/green/package.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/config/glob_target/plugins/red/package.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/general/block_selection.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/general/custom_file_extension.javascript +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/general/custom_file_extension.python +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/general/disable_enable.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/general/paths/invalid.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/general/paths/subdir/nested_invalid.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/general/paths/valid.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/general/severity.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/hidden/.github/workflows/ci.yml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/keep_sorted/orderings.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/keep_unique/basic_and_pattern.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/line_count/equality_and_greater_than.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/line_pattern/forbidden_text.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/line_pattern/hex_and_word_patterns.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/list/.hg/.gitkeep +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/list/a.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/list/b.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/positions/unicode_columns.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/custom_ext_source.javascript +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/custom_ext_target.javascript +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/package.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/paths/alpha/item.txt +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/paths/beta/item.txt +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/paths_and_block_source.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/paths_source.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/sibling.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/split_table_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/split_table_target.toml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/symbol_mismatch_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/symbol_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/target.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/whole_file_mismatch_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/whole_file_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/same_as/whole_file_target.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/sarif/advisory.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/sarif/clean.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/sarif/sorted.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/sarif/unnamed.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/commit_msg.txt +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/commit_msg_gone_a.txt +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/commit_msg_gone_b.txt +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/commit_msg_lowercase_trailer.txt +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/commit_msg_malformed.txt +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/commit_msg_with_noise.txt +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/dangling.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/sorted.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/symbol.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/symbol_target.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/two_rules.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/suppressions/unnamed.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/escapes_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/escapes_target.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/filter_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/filter_target.yaml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/json_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/json_target.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/jsonc_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/jsonc_target.jsonc +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/markdown_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/markdown_target.markdown +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/md_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/md_target.md +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/mdx_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/mdx_target.mdx +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/mixed_list_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/mixed_list_target.json +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/toml_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/toml_target.toml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/yaml_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/yaml_target.yaml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/yml_source.rs +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/symbol_references/yml_target.yml +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/verbosity/affects/pair.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/verbosity/clean/counted.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/verbosity/clean/noop.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/verbosity/clean/notes.txt +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/verbosity/clean/sorted.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/verbosity/diff/two_blocks.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/verbosity/failing/unsorted.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/testdata/verbosity/mixed/both_kinds.py +0 -0
- {blockwatch_cli-0.13.0 → blockwatch_cli-0.14.0}/tests/verbosity.rs +0 -0
|
@@ -276,9 +276,10 @@ They are not instructions for working on blockwatch, and changing them changes w
|
|
|
276
276
|
guidance for working on blockwatch.
|
|
277
277
|
|
|
278
278
|
`src/skill.md` must not hold block tags. A project that saves the skill parses it too, so each tag would become a block
|
|
279
|
-
in that project. To point a block at a section of the skill,
|
|
280
|
-
|
|
281
|
-
|
|
279
|
+
in that project. To point a block at a section of the skill, use the section's headings, such as
|
|
280
|
+
`src/skill.md#/blockwatch/Symbols/JSON`. To point at only a part of a section, give that part a heading of its own.
|
|
281
|
+
When the section needs a rule of its own, such as a `same-as-pattern`, add a `[[block]]` entry for it to
|
|
282
|
+
`blockwatch.toml`, next to the others for `src/skill.md`.
|
|
282
283
|
|
|
283
284
|
Each agent's local config directory is left out of git on purpose: `.claude/`, `.cursor/`, `.gemini/`, `.windsurf/`,
|
|
284
285
|
`.aider*`, and the others listed in `.gitignore`. Don't commit agent-specific settings. Anything a future contributor
|
|
@@ -12,6 +12,27 @@ 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.0] - 2026-10-16
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- `same-as-pattern` can compare a key with its value. Every named group, such as `(?P<code>\d+)` and
|
|
20
|
+
`(?P<message>.+)`, takes part, and each match is one record. So a message that moves to another code fails, even
|
|
21
|
+
though the messages alone still agree. Both patterns need the same groups. Each group can have its own
|
|
22
|
+
`same-as-format`, such as `name:identifier,value:numeric`. See
|
|
23
|
+
[Compare several groups](docs/validators/same-as.md#compare-several-groups) and
|
|
24
|
+
[A format for each group](docs/validators/same-as.md#a-format-for-each-group)
|
|
25
|
+
([#99](https://github.com/mennanov/blockwatch/issues/99)).
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- A `same-as-pattern` with named groups other than `value` now compares them too. Before, only `value` counted, or the
|
|
30
|
+
whole match when the pattern had no `value` group. To keep comparing only `value`, change the other groups to
|
|
31
|
+
`(?:…)`.
|
|
32
|
+
- An error about the `same-as-mode`, `same-as-format` or `same-as-pattern` of a block with `same-as` or
|
|
33
|
+
`same-as-paths` now shows the block, such as `invalid same-as attributes on block src/config.rs:limits at line 3`.
|
|
34
|
+
Before, it showed only the bad value.
|
|
35
|
+
|
|
15
36
|
## [0.13.0] - 2026-10-15
|
|
16
37
|
|
|
17
38
|
### Added
|
|
@@ -276,9 +276,10 @@ They are not instructions for working on blockwatch, and changing them changes w
|
|
|
276
276
|
guidance for working on blockwatch.
|
|
277
277
|
|
|
278
278
|
`src/skill.md` must not hold block tags. A project that saves the skill parses it too, so each tag would become a block
|
|
279
|
-
in that project. To point a block at a section of the skill,
|
|
280
|
-
|
|
281
|
-
|
|
279
|
+
in that project. To point a block at a section of the skill, use the section's headings, such as
|
|
280
|
+
`src/skill.md#/blockwatch/Symbols/JSON`. To point at only a part of a section, give that part a heading of its own.
|
|
281
|
+
When the section needs a rule of its own, such as a `same-as-pattern`, add a `[[block]]` entry for it to
|
|
282
|
+
`blockwatch.toml`, next to the others for `src/skill.md`.
|
|
282
283
|
|
|
283
284
|
Each agent's local config directory is left out of git on purpose: `.claude/`, `.cursor/`, `.gemini/`, `.windsurf/`,
|
|
284
285
|
`.aider*`, and the others listed in `.gitignore`. Don't commit agent-specific settings. Anything a future contributor
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blockwatch-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
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
|
|
@@ -40,41 +40,14 @@ same-as = '''.github/workflows/codeql.yml,
|
|
|
40
40
|
same-as-pattern = '(?:actions/checkout@|^)(?P<value>v\d+)$'
|
|
41
41
|
|
|
42
42
|
# `blockwatch skill` prints src/skill.md, and projects save that output. A tag in that file would become a block in
|
|
43
|
-
# every one of those projects. So
|
|
44
|
-
#
|
|
43
|
+
# every one of those projects. So blocks point at its sections by their headings, and these entries hold the pattern
|
|
44
|
+
# that reads a section, which the section can't hold itself.
|
|
45
45
|
[[block]]
|
|
46
|
-
target = 'src/skill.md'
|
|
47
|
-
target-pattern = '(?s)<!-- validator-catalog -->.*<!-- /validator-catalog -->'
|
|
48
|
-
name = 'validator-catalog'
|
|
49
|
-
|
|
50
|
-
[[block]]
|
|
51
|
-
target = 'src/skill.md'
|
|
52
|
-
target-pattern = '(?s)<!-- validator-reference -->.*<!-- /validator-reference -->'
|
|
46
|
+
target = 'src/skill.md#/blockwatch/Validator reference'
|
|
53
47
|
name = 'validator-reference'
|
|
54
48
|
same-as-pattern = '^\|\s*`(?P<value>[a-z-]+)`'
|
|
55
49
|
|
|
56
50
|
[[block]]
|
|
57
|
-
target = 'src/skill.md'
|
|
58
|
-
target-pattern = '(?s)<!-- extensions-with-symbols -->.*<!-- /extensions-with-symbols -->'
|
|
51
|
+
target = 'src/skill.md#/blockwatch/Symbols/Files with symbols'
|
|
59
52
|
name = 'extensions-with-symbols'
|
|
60
53
|
same-as-pattern = '`\.?(?P<value>[A-Za-z]+)`'
|
|
61
|
-
|
|
62
|
-
[[block]]
|
|
63
|
-
target = 'src/skill.md'
|
|
64
|
-
target-pattern = '(?s)<!-- json-paths -->.*<!-- /json-paths -->'
|
|
65
|
-
name = 'json-paths'
|
|
66
|
-
|
|
67
|
-
[[block]]
|
|
68
|
-
target = 'src/skill.md'
|
|
69
|
-
target-pattern = '(?s)<!-- toml-paths -->.*<!-- /toml-paths -->'
|
|
70
|
-
name = 'toml-paths'
|
|
71
|
-
|
|
72
|
-
[[block]]
|
|
73
|
-
target = 'src/skill.md'
|
|
74
|
-
target-pattern = '(?s)<!-- yaml-paths -->.*<!-- /yaml-paths -->'
|
|
75
|
-
name = 'yaml-paths'
|
|
76
|
-
|
|
77
|
-
[[block]]
|
|
78
|
-
target = 'src/skill.md'
|
|
79
|
-
target-pattern = '(?s)<!-- markdown-paths -->.*<!-- /markdown-paths -->'
|
|
80
|
-
name = 'markdown-paths'
|
|
@@ -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.
|
|
22
|
+
rev: v0.14.0 # 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.
|
|
62
|
+
rev: v0.14.0 # Use latest release
|
|
63
63
|
hooks:
|
|
64
64
|
- id: blockwatch-commit-msg
|
|
65
65
|
```
|
|
@@ -555,15 +555,15 @@ Nothing else about the run changes: the same violations are found, and the exit
|
|
|
555
555
|
"tool": {
|
|
556
556
|
"driver": {
|
|
557
557
|
"name": "blockwatch",
|
|
558
|
-
"version": "0.
|
|
559
|
-
"semanticVersion": "0.
|
|
558
|
+
"version": "0.14.0",
|
|
559
|
+
"semanticVersion": "0.14.0",
|
|
560
560
|
"informationUri": "https://github.com/mennanov/blockwatch",
|
|
561
561
|
"rules": [
|
|
562
562
|
{
|
|
563
563
|
"id": "keep-sorted",
|
|
564
564
|
"name": "keep-sorted",
|
|
565
565
|
"shortDescription": { "text": "Requires the lines of a block to stay in order." },
|
|
566
|
-
"helpUri": "https://github.com/mennanov/blockwatch/blob/v0.
|
|
566
|
+
"helpUri": "https://github.com/mennanov/blockwatch/blob/v0.14.0/docs/validators/keep-sorted.md"
|
|
567
567
|
}
|
|
568
568
|
]
|
|
569
569
|
}
|
|
@@ -23,7 +23,8 @@ between two references is fine.
|
|
|
23
23
|
|
|
24
24
|
## Which validator do I want?
|
|
25
25
|
|
|
26
|
-
<!-- <block name="validators-index"
|
|
26
|
+
<!-- <block name="validators-index"
|
|
27
|
+
affects="src/skill.md#/blockwatch/Annotating a new project/Where blocks add value (catalog)"
|
|
27
28
|
same-as="src/validators/mod.rs:validator-registry, README.md:available-validators"
|
|
28
29
|
same-as-pattern='\[`(?P<value>[a-z-]+)`\]\('> -->
|
|
29
30
|
|
|
@@ -8,13 +8,13 @@ It runs on every scan, with or without a diff.
|
|
|
8
8
|
|
|
9
9
|
## Syntax
|
|
10
10
|
|
|
11
|
-
| Attribute | Value
|
|
12
|
-
|
|
13
|
-
| `same-as` | One or more [targets](#targets), comma-separated
|
|
14
|
-
| `same-as-paths` | A [glob](#paths-as-targets) of paths to compare
|
|
15
|
-
| `same-as-pattern` | A regex that picks the values to compare
|
|
16
|
-
| `same-as-mode` | `set`, `sequence`, `single` or `subset`
|
|
17
|
-
| `same-as-format` | `numeric`, `identifier` or `flat
|
|
11
|
+
| Attribute | Value | Default |
|
|
12
|
+
|-------------------|--------------------------------------------------------------------------------------|-------------------|
|
|
13
|
+
| `same-as` | One or more [targets](#targets), comma-separated | — |
|
|
14
|
+
| `same-as-paths` | A [glob](#paths-as-targets) of paths to compare | — |
|
|
15
|
+
| `same-as-pattern` | A regex that picks the values to compare | the whole content |
|
|
16
|
+
| `same-as-mode` | `set`, `sequence`, `single` or `subset` | `set` |
|
|
17
|
+
| `same-as-format` | `numeric`, `identifier` or `flat`, or [one for each group](#a-format-for-each-group) | text |
|
|
18
18
|
|
|
19
19
|
## Targets
|
|
20
20
|
|
|
@@ -55,10 +55,10 @@ Usually, though, the two places don't hold the same text. Then each block says *
|
|
|
55
55
|
|
|
56
56
|
## Pick values with `same-as-pattern`
|
|
57
57
|
|
|
58
|
-
Each block has its own regex. Every match is a value: the `(?P<value>…)` group if
|
|
59
|
-
match. A
|
|
60
|
-
|
|
61
|
-
written in different formats:
|
|
58
|
+
Each block has its own regex. Every match is a value: the `(?P<value>…)` group if the pattern has one, otherwise the
|
|
59
|
+
whole match. A pattern with [other named groups](#compare-several-groups) compares those too. A line can hold several
|
|
60
|
+
values. Lines without a match are skipped, and so are empty matches. So is a match of a branch without the `value`
|
|
61
|
+
group: `^(?:none|(?P<value>\w+))$` skips the line `none`. This lets you compare blocks written in different formats:
|
|
62
62
|
|
|
63
63
|
```rust
|
|
64
64
|
// <block same-as="README.md:supported-env-vars" same-as-pattern="BLOCKWATCH_AI_[A-Z_]+">
|
|
@@ -76,6 +76,41 @@ const API_URL: &str = "BLOCKWATCH_AI_API_URL";
|
|
|
76
76
|
[//]: # (</block>)
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
### Compare several groups
|
|
80
|
+
|
|
81
|
+
A pattern can have several named groups, such as a code and its message. Then each match is one record, and the
|
|
82
|
+
records are compared whole. So a message that moves to another code fails, even though the messages alone still agree:
|
|
83
|
+
|
|
84
|
+
```rust
|
|
85
|
+
// <block same-as="docs/errors.md:errors" same-as-pattern='(?P<code>\d+) => "(?P<message>[^"]+)"'>
|
|
86
|
+
404 => "not found",
|
|
87
|
+
409 => "already exists",
|
|
88
|
+
// </block>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```markdown
|
|
92
|
+
<!-- <block name="errors" same-as-pattern="^- `(?P<code>\d+)`: (?P<message>.+)$"> -->
|
|
93
|
+
- `404`: already exists
|
|
94
|
+
- `409`: not found
|
|
95
|
+
<!-- </block> -->
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The run reports:
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
[{code: "404", message: "not found"}, {code: "409", message: "already exists"}] != [{code: "404", message: "already exists"}, {code: "409", message: "not found"}]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- **Groups match by name**, not by their place in the pattern. So one side can write the message before the code.
|
|
105
|
+
- **Both patterns need the same groups.** Otherwise the run reports
|
|
106
|
+
`same-as-pattern groups differ: code, message != message`.
|
|
107
|
+
- **A pattern without named groups has one group, `value`.** So does a block without a pattern. Either can be compared
|
|
108
|
+
with a pattern whose only group is `value`.
|
|
109
|
+
- **A match is skipped when one of its groups is not in the match, or is empty.**
|
|
110
|
+
- **A record must fit on one line,** because the pattern reads one line at a time.
|
|
111
|
+
- **`same-as-format` applies to every group,** unless you give [each group its own](#a-format-for-each-group).
|
|
112
|
+
- **To group part of a pattern without comparing it, use `(?:…)`.** It captures nothing.
|
|
113
|
+
|
|
79
114
|
### Lines are not part of the comparison
|
|
80
115
|
|
|
81
116
|
With a pattern, only the values count, not how they are split into lines. These two blocks agree, even in `sequence`
|
|
@@ -290,17 +325,45 @@ plain `s`, so `policies.yaml` gives `policie`, which doesn't equal `Policy`.
|
|
|
290
325
|
- **Digits and accents count.** `Version2` equals `version-2`, but not `version`. `CAFÉ` equals `café`, but not `cafe`.
|
|
291
326
|
- **Messages show each value as it is compared,** such as `crontab`.
|
|
292
327
|
|
|
328
|
+
## A format for each group
|
|
329
|
+
|
|
330
|
+
With [several groups](#compare-several-groups), each group can have its own format. Write `group:format` entries,
|
|
331
|
+
separated by commas. Here the names differ in style and the numbers differ in spelling:
|
|
332
|
+
|
|
333
|
+
```rust
|
|
334
|
+
// <block same-as="docs/config.md:defaults" same-as-format="name:identifier,value:numeric" same-as-pattern="const (?P<name>\w+): \w+ = (?P<value>[\d.]+);">
|
|
335
|
+
const MAX_RETRIES: u32 = 3;
|
|
336
|
+
const TIMEOUT_SECS: f64 = 2.0;
|
|
337
|
+
// </block>
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
```markdown
|
|
341
|
+
<!-- <block name="defaults" same-as-pattern="^- `(?P<name>[a-z-]+)`: (?P<value>[\d.]+)$"> -->
|
|
342
|
+
- `max-retries`: 3
|
|
343
|
+
- `timeout-secs`: 2
|
|
344
|
+
<!-- </block> -->
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
- **A group without an entry is compared as written.** With `same-as-format="value:numeric"`, `MAX_RETRIES` and
|
|
348
|
+
`max-retries` differ.
|
|
349
|
+
- **A format without a group applies to every group.** With `same-as-format="numeric"`, the run reports that
|
|
350
|
+
`MAX_RETRIES` is not a number.
|
|
351
|
+
- **The two forms don't mix.** `identifier,value:numeric` is an error.
|
|
352
|
+
- **Each group must be a group of this block's pattern.** Otherwise the run stops, so a typo can't go unnoticed. A
|
|
353
|
+
block without a pattern, or a pattern without named groups, has the one group `value`. So `value:numeric` works
|
|
354
|
+
there too.
|
|
355
|
+
|
|
293
356
|
## Notes
|
|
294
357
|
|
|
295
358
|
- **Which block's attributes count.** The referencing block's `same-as-mode` and `same-as-format` decide how to compare.
|
|
296
359
|
Each block's `same-as-pattern` reads only that block. A whole file, a symbol or a list of paths has no block, so the
|
|
297
360
|
referencing block's pattern reads it too.
|
|
298
361
|
- **Violations:** a missing block, a missing or repeated key, a value that is not a number under `numeric`, a `single`
|
|
299
|
-
side without exactly one value, a pattern that matches nothing on either side,
|
|
300
|
-
matches no path. Two empty sides do not count as equal.
|
|
301
|
-
- **Errors that stop the run:** an unknown `same-as-mode` or `same-as-format`,
|
|
302
|
-
`same-as-paths` without `same-as-pattern`, a missing target
|
|
303
|
-
symbols, or with several YAML documents.
|
|
362
|
+
side without exactly one value, a pattern that matches nothing on either side, two patterns with different groups,
|
|
363
|
+
and a `same-as-paths` glob that matches no path. Two empty sides do not count as equal.
|
|
364
|
+
- **Errors that stop the run:** an unknown `same-as-mode` or `same-as-format`, a `same-as-format` entry for a group
|
|
365
|
+
that the pattern doesn't have, an invalid regex or glob, `same-as-paths` without `same-as-pattern`, a missing target
|
|
366
|
+
file, and a symbol in a file with a syntax error, without symbols, or with several YAML documents.
|
|
304
367
|
- **Run it on the whole tree now and then.** `same-as` needs no diff, so a plain `blockwatch` run catches drift that
|
|
305
368
|
`--only-changed` misses. See [CI integration](../ci.md).
|
|
306
369
|
- **A rename alone goes unnoticed in a diff.** Renaming a target file without changing it (a plain `git mv`) gives
|
|
@@ -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.
|
|
9
|
+
install_requires=['blockwatch-cli==0.14.0'],
|
|
10
10
|
# </block>
|
|
11
11
|
# Without it, setuptools takes the directories under src/ for Python packages.
|
|
12
12
|
packages=[],
|
|
@@ -93,7 +93,7 @@ mod tests {
|
|
|
93
93
|
"containers": [{"key": "value"}, ["item"]]
|
|
94
94
|
}"#;
|
|
95
95
|
|
|
96
|
-
// <block affects="docs/symbols.md:json-paths, src/skill.md
|
|
96
|
+
// <block affects="docs/symbols.md:json-paths, src/skill.md#/blockwatch/Symbols/JSON">
|
|
97
97
|
assert_eq!(
|
|
98
98
|
derived_symbols(source)?,
|
|
99
99
|
expected_symbols([
|
|
@@ -390,7 +390,7 @@ mod tests {
|
|
|
390
390
|
.collect()
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
-
// <block affects="docs/symbols.md:markdown-paths, src/skill.md
|
|
393
|
+
// <block affects="docs/symbols.md:markdown-paths, src/skill.md#/blockwatch/Symbols/Markdown">
|
|
394
394
|
#[test]
|
|
395
395
|
fn nested_sections_parse_symbols_returns_each_section_with_its_body() -> anyhow::Result<()> {
|
|
396
396
|
let source = r"Text before the first heading.
|
|
@@ -431,7 +431,7 @@ title: <3 # not a comment
|
|
|
431
431
|
Ok(())
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
-
// <block affects="docs/symbols.md:markdown-paths, src/skill.md
|
|
434
|
+
// <block affects="docs/symbols.md:markdown-paths, src/skill.md#/blockwatch/Symbols/Markdown">
|
|
435
435
|
#[test]
|
|
436
436
|
fn document_with_esm_and_jsx_parse_symbols_returns_its_sections() -> anyhow::Result<()> {
|
|
437
437
|
let source = r"import {Badge} from './badge.js'
|
|
@@ -465,7 +465,7 @@ name = "plantain"
|
|
|
465
465
|
let banana = "[[fruits]]\nname = \"banana\"\n";
|
|
466
466
|
let plantain = "[[fruits.varieties]]\nname = \"plantain\"\n";
|
|
467
467
|
|
|
468
|
-
// <block affects="docs/symbols.md:toml-paths, src/skill.md
|
|
468
|
+
// <block affects="docs/symbols.md:toml-paths, src/skill.md#/blockwatch/Symbols/TOML">
|
|
469
469
|
assert_eq!(
|
|
470
470
|
derived_symbols(source)?,
|
|
471
471
|
expected_symbols(&[
|
|
@@ -779,7 +779,7 @@ multi: this is
|
|
|
779
779
|
last: end
|
|
780
780
|
"#;
|
|
781
781
|
|
|
782
|
-
// <block affects="docs/symbols.md:yaml-paths, src/skill.md
|
|
782
|
+
// <block affects="docs/symbols.md:yaml-paths, src/skill.md#/blockwatch/Symbols/YAML">
|
|
783
783
|
assert_eq!(
|
|
784
784
|
derived_symbols(source)?,
|
|
785
785
|
expected_symbols(&[
|
|
@@ -49,8 +49,6 @@ Then run `git diff --patch | blockwatch --diff` to confirm the new tags pass (se
|
|
|
49
49
|
|
|
50
50
|
### Where blocks add value (catalog)
|
|
51
51
|
|
|
52
|
-
<!-- validator-catalog -->
|
|
53
|
-
|
|
54
52
|
| You see... | Add | Why |
|
|
55
53
|
|----------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|----------------------------------------------------------------------------|
|
|
56
54
|
| A hand-maintained list/enum/match that should stay ordered (dependencies, CLI flags, feature lists, route tables) | `keep-sorted` | Eliminates "please sort this" review nits |
|
|
@@ -63,8 +61,6 @@ Then run `git diff --patch | blockwatch --diff` to confirm the new tags pass (se
|
|
|
63
61
|
| Prose or config with a natural-language rule ("must mention X", "no TODOs left") | `check-ai="..."` | Rules regex can't express |
|
|
64
62
|
| Domain logic too complex for regex | `check-lua="script.lua"` | Custom programmable checks |
|
|
65
63
|
|
|
66
|
-
<!-- /validator-catalog -->
|
|
67
|
-
|
|
68
64
|
Prefer the deterministic validators (`keep-sorted`, `keep-unique`, `affects`, `same-as`, `line-pattern`, `line-count`)
|
|
69
65
|
first — they are free, fast, and need no API keys. Reserve `check-ai` for rules the cheaper validators genuinely can't
|
|
70
66
|
express.
|
|
@@ -121,17 +117,15 @@ pub enum Language { Rust, Python }
|
|
|
121
117
|
|
|
122
118
|
## Validator reference
|
|
123
119
|
|
|
124
|
-
<!-- validator-reference -->
|
|
125
|
-
|
|
126
120
|
| Attribute | Syntax | Notes |
|
|
127
121
|
|--------------------------|---------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
128
122
|
| `name` | `name="foo"` | Gives the block a name, so `affects` and `same-as` can point at it; shown by `blockwatch list`. |
|
|
129
123
|
| `affects` | `affects="file:foo"`, `":foo"` (same file), `"file"` (whole file) or `"file#/path"` (one [symbol](#symbols)); comma-separate multiple | If this block's content changes in a diff, each target must change too, else a violation. Only fires under `--diff`. One-way: put `affects` on **both** blocks, each with a `name`, to catch both directions. A whole-file target is satisfied by *any* change to the file; use it for files that cannot hold a comment (`.env`, lockfiles). If the file has symbols, point at one instead (`package.json#/version`): only a change to that key or its value counts. A missing target file fails the run; a missing block or key is a violation. |
|
|
130
124
|
| `same-as` | `same-as="file:foo"`, `":foo"` (same file), `"file"` (whole file) or `"file#/path"` (one [symbol](#symbols)); comma-separate multiple | This block and each target must hold the same **value**. Unlike `affects`, it also runs on a full-tree scan. Under `--only-changed`, only a changed block that *carries* `same-as` starts the check, so put `same-as` on both blocks if a change to either side must be caught. Compares the whole trimmed content by default. A whole-file or `#/path` target has no block of its own, so **this** block's `same-as-pattern` reads it. A `#/path` target is one value: a string compares as its unquoted text, any other value as written, and a value that holds other values (an object, a table, a mapping or a list) as its text. |
|
|
131
125
|
| `same-as-paths` | `same-as-paths="services/*/"` | This block must hold the same values as the repository paths that match the glob: sorted, each once. A glob that ends with `/` lists directories (`services/auth/`), otherwise files. `*` stops at `/`, `**` crosses it. One glob; for several directories use braces: `{services,workers}/*/`. Needs `same-as-pattern`, which reads both this block and the paths; `.+` compares whole lines. Files that `.gitignore` leaves out and symlinks are not listed; the `ignore` setting doesn't apply. A glob that matches nothing is a violation. |
|
|
132
|
-
| `same-as-pattern` | `same-as-pattern="id: (?P<value>\d+)"` | Per line, compare the `value` capture group (or the whole match); **every** match on a line counts, and all lines flatten into one list — values are compared, not their layout. Each side reads *itself*, so put a pattern on both blocks when the two are in different formats.
|
|
126
|
+
| `same-as-pattern` | `same-as-pattern="id: (?P<value>\d+)"` | Per line, compare the `value` capture group (or the whole match); **every** match on a line counts, and all lines flatten into one list — values are compared, not their layout. With several named groups, such as `(?P<key>\w+)=(?P<value>\d+)`, each match is one record, compared whole; both patterns need the same group names. Each side reads *itself*, so put a pattern on both blocks when the two are in different formats. |
|
|
133
127
|
| `same-as-mode` | `same-as-mode="set"` (default) `/ sequence / single / subset` | `set` order/duplicate-insensitive; `sequence` ordered; `single` exactly one token per side; `subset` this block's tokens must all appear in the target (directional). Governed by the source block. |
|
|
134
|
-
| `same-as-format` | `same-as-format="numeric"` / `"identifier"` / `"flat"` | `numeric`: parse tokens as numbers before comparing, so `8080` == `8080.0`. `identifier`: compare the words of a name, so `PreExistingFailure` == `pre-existing-failure` == `PRE_EXISTING_FAILURE`; a run of capitals is one word (`HTTPServer` == `http-server`). `flat`: compare only the lower-cased letters and digits, so `CronTab` == `crontab` == `cron-tab`; use it when one side has no word breaks, since `identifier` keeps `Signin` != `sign-in`. Governed by the source block.
|
|
128
|
+
| `same-as-format` | `same-as-format="numeric"` / `"identifier"` / `"flat"` | `numeric`: parse tokens as numbers before comparing, so `8080` == `8080.0`. `identifier`: compare the words of a name, so `PreExistingFailure` == `pre-existing-failure` == `PRE_EXISTING_FAILURE`; a run of capitals is one word (`HTTPServer` == `http-server`). `flat`: compare only the lower-cased letters and digits, so `CronTab` == `crontab` == `cron-tab`; use it when one side has no word breaks, since `identifier` keeps `Signin` != `sign-in`. With several groups in `same-as-pattern`, each can have its own: `name:identifier,value:numeric`; a group left out compares as written. Governed by the source block. |
|
|
135
129
|
| `keep-sorted` | `keep-sorted` / `keep-sorted="asc"` / `keep-sorted="desc"` | Default `asc`, compared lexicographically. |
|
|
136
130
|
| `keep-sorted-pattern` | `keep-sorted-pattern="id: (?P<value>\d+)"` | Sort by the regex capture group named `value` instead of the whole line. |
|
|
137
131
|
| `keep-sorted-format` | `keep-sorted-format="numeric"` | Compare the value numerically rather than as text (`"10"` after `"2"`). Every line must then *be* a number, and a trailing comma is part of the trimmed line — on a real list literal pair this with `keep-sorted-pattern` to lift the number out, or the run is a hard error. |
|
|
@@ -146,20 +140,18 @@ pub enum Language { Rust, Python }
|
|
|
146
140
|
| `check-lua-timeout` | `check-lua-timeout="60"` | Wall-clock budget for the script, in whole seconds; default `30`. A script that runs past it fails the run with an error, not a violation. A value below `1` is a hard error. |
|
|
147
141
|
| `severity` | `severity="error"` (default) `/ warning / info / hint` | Only `error` fails the run (exit 1); the others are reported but exit 0. |
|
|
148
142
|
|
|
149
|
-
<!-- /validator-reference -->
|
|
150
|
-
|
|
151
143
|
## Symbols
|
|
152
144
|
|
|
153
145
|
A symbol is one value inside a file, such as the `version` key in `package.json`. `affects` and `same-as` point at one
|
|
154
146
|
with `file#/path`, or with `#/path` for the block's own file. Use a symbol when only one key in a file matters, or when
|
|
155
147
|
the file cannot hold a comment. To put rules on the value itself, wrap it in a `[[block]]` entry in `blockwatch.toml`.
|
|
156
148
|
|
|
157
|
-
|
|
149
|
+
### Files with symbols
|
|
158
150
|
|
|
159
151
|
Only these files have symbols: JSON (`.json`, `.jsonc`, `.webmanifest`), TOML (`.toml`, `Pipfile`), YAML (`.yaml`,
|
|
160
152
|
`.yml`, `.cff`) and Markdown (`.md`, `.markdown`, `.mdx`). A path into any other file fails the run.
|
|
161
153
|
|
|
162
|
-
|
|
154
|
+
### Writing a path
|
|
163
155
|
|
|
164
156
|
- **Paths follow RFC 6901.** `/` separates keys. An array item is its position, counted from 0: `#/files/0`.
|
|
165
157
|
- **Pick a list item by a key's value when items can be added above it:** `#/services/~[name=web]/port` is the `port`
|
|
@@ -170,19 +162,17 @@ Only these files have symbols: JSON (`.json`, `.jsonc`, `.webmanifest`), TOML (`
|
|
|
170
162
|
- **A missing key is a violation.** The message suggests similar paths.
|
|
171
163
|
- **A missing or broken file fails the run.** blockwatch does not guess what a broken file meant.
|
|
172
164
|
|
|
173
|
-
|
|
165
|
+
### JSON
|
|
174
166
|
|
|
175
|
-
|
|
167
|
+
Every key and every array item is a symbol.
|
|
176
168
|
|
|
177
169
|
- A comment is not an array item, so it does not shift the positions after it.
|
|
178
170
|
- A trailing comma breaks the file, even in `.jsonc`.
|
|
179
171
|
- A key written twice is a violation, because blockwatch can't tell which copy you mean.
|
|
180
172
|
|
|
181
|
-
|
|
173
|
+
### TOML
|
|
182
174
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
**TOML.** Every key, every table and every array item is a symbol.
|
|
175
|
+
Every key, every table and every array item is a symbol.
|
|
186
176
|
|
|
187
177
|
- A path follows the keys, however the file writes them. `[tool.ruff]` with `line-length = 88` below it is
|
|
188
178
|
`#/tool/ruff/line-length`, and so is `tool.ruff.line-length = 88`.
|
|
@@ -192,11 +182,9 @@ Only these files have symbols: JSON (`.json`, `.jsonc`, `.webmanifest`), TOML (`
|
|
|
192
182
|
each place, in order.
|
|
193
183
|
- A key written twice breaks the file.
|
|
194
184
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
<!-- yaml-paths -->
|
|
185
|
+
### YAML
|
|
198
186
|
|
|
199
|
-
|
|
187
|
+
Every key and every list item is a symbol. Block style and flow style give the same paths.
|
|
200
188
|
|
|
201
189
|
- A string counts as the text YAML reads, `|` and `>` blocks included. Tags and anchors are not part of a value.
|
|
202
190
|
An empty value (`key:`) is an empty string.
|
|
@@ -206,11 +194,9 @@ Only these files have symbols: JSON (`.json`, `.jsonc`, `.webmanifest`), TOML (`
|
|
|
206
194
|
- A key written twice is a violation.
|
|
207
195
|
- A file with several documents fails the run. Use a named block there instead.
|
|
208
196
|
|
|
209
|
-
|
|
197
|
+
### Markdown
|
|
210
198
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
**Markdown.** Every section is a symbol: a heading and everything up to the next heading of the same or a higher
|
|
199
|
+
Every section is a symbol: a heading and everything up to the next heading of the same or a higher
|
|
214
200
|
level. Its path is the text of each heading it is under, then its own, the top `#` title included:
|
|
215
201
|
`README.md#/My project/Install/From source`.
|
|
216
202
|
|
|
@@ -223,8 +209,6 @@ level. Its path is the text of each heading it is under, then its own, the top `
|
|
|
223
209
|
- An `.mdx` file gets the same paths. JSX text and the code in braces count as heading text. A heading inside a JSX
|
|
224
210
|
element doesn't count.
|
|
225
211
|
|
|
226
|
-
<!-- /markdown-paths -->
|
|
227
|
-
|
|
228
212
|
## The config file (`blockwatch.toml`)
|
|
229
213
|
|
|
230
214
|
Every run reads `blockwatch.toml` from the repository root, from any directory: the hook, CI and your own runs.
|