Blockwatch — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Blockwatch (Plugin) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
Aggregate score unchanged between these scans.
The primary manifest — the file an agent reads to learn what this artifact does.
[//]: # (<block name="pitch">) Some parts of a codebase have to change together: a function and its docs, or a value duplicated across configs. blockwatch lets you write those rules in comments, right next to the code. If they fall out of sync, your pre-commit hook or CI run fails.
Supports 37 languages and their common file names, such as Gemfile or .mjs.
[//]: # (</block>)
Install blockwatch:
brew install mennanov/blockwatch/blockwatchFor Windows and other ways to install, see Installation.
Then paste this prompt into your AI coding agent:
<!-- <block name="agent-prompt"> -->
Run `blockwatch skill` and save its output as `blockwatch/SKILL.md` in this project's skills directory. Then use the
skill to annotate the project and add `blockwatch.toml` if needed. List each block you added and the mistake it
catches.<!-- </block> -->
Review the suggested blocks and commit them. The skill stays in the project, so agents working on it later know the rules too. When you upgrade blockwatch, the skill tells the agent to save it again. To check every future change, add blockwatch to your pre-commit hook or CI.
A block is a pair of tags inside comments around some lines of code. The rules go in the start tag. In this example, the enum variants must match the list in README.md. Each block uses same-as-pattern to pick the values to compare. Here, \w+ matches each word, so commas and dashes don't count:
src/lib.rs:
pub enum Language {
// <block name="languages" same-as="README.md:supported-languages" same-as-pattern="\w+">
Rust,
Python,
// </block>
}README.md:
<!-- <block name="supported-languages" same-as-pattern="\w+"> -->
- Rust
- Python
<!-- </block> -->If you add a Go variant to the enum without updating README.md, blockwatch fails with exit code 1 and this message:
Block src/lib.rs:languages at line 2 disagrees with README.md:supported-languages: ["Rust", "Python", "Go"] != ["Rust", "Python"]The output shows the values on each side. Add Go to README.md too, and the run passes.
Because same-as compares block contents directly, it doesn't need a diff. Running blockwatch on its own finds mismatches anywhere in the repository. The other validators make code and docs change together, keep lists sorted, and more.
Two validators work across files:
affects fails when a diff touches a block without also changing what it points to.same-as fails when a block and its target hold different values. It needs no diff.Both can point to a block, a whole file, a specific value in a file like package.json#/version, or a section of a Markdown file (see Symbols). The other validators check a single block on their own: the things you'd otherwise have to nitpick in code review.
<!-- <block name="available-validators" same-as="src/validators/mod.rs:validator-registry, docs/validators/README.md:validators-index" same-as-format="identifier" same-as-pattern='^\|\s*\[(?P<value>[a-z-]+)\]'> -->
| Validator | Description | Attributes |
|---|---|---|
affects | Forces linked blocks to be edited together (e.g. code and its docs); needs a diff | affects |
same-as | Asserts two or more blocks hold the same value, across languages and formats; no diff needed | same-as, same-as-paths, same-as-pattern, same-as-mode, same-as-format |
keep-sorted | Enforces alphabetical or numerical ordering on list items | keep-sorted, keep-sorted-pattern, keep-sorted-format |
keep-unique | Prevents duplicate lines within a block | keep-unique |
line-pattern | Enforces that every line matches a regex, or that no line matches a forbidden one | line-pattern, line-pattern-forbidden |
line-count | Enforces lower or upper bounds on the number of lines in a block | line-count, line-count-pattern |
check-ai | Validates content against natural language rules using an LLM | check-ai, check-ai-pattern |
check-lua | Runs custom validation logic written in Lua | check-lua, check-lua-pattern, check-lua-timeout |
<!-- </block> -->
Every block can also take an optional name so other blocks can point to it, and a severity. Only error, the default, fails the run.
See the Validators Reference for full details.
# macOS and Linux, with Homebrew
brew install mennanov/blockwatch/blockwatch
# macOS and Linux, without Homebrew
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/mennanov/blockwatch/releases/latest/download/blockwatch-installer.sh | sh
# Windows
powershell -ExecutionPolicy Bypass -c "irm https://github.com/mennanov/blockwatch/releases/latest/download/blockwatch-installer.ps1 | iex"
# With Python. On PyPI, `blockwatch` is a different project.
pipx install blockwatch-cli # or: uv tool install blockwatch-cli
# From source, with Rust
cargo install blockwatchPrebuilt binaries are on the Releases page.
The skill tells an agent where blocks make sense, how to format them, and how to check its own work. blockwatch skill prints it. The Quick Start prompt sets it up in a single project. To install it globally for every project in Claude Code instead:
/plugin marketplace add mennanov/blockwatch
/plugin install blockwatch@blockwatchSee docs/agents.md for Cursor, Copilot, Codex, and other agent setups.
Running blockwatch on its own checks every block in the repository. With --diff, blockwatch reads a unified diff from stdin to enforce rules that need one, like affects. Adding --only-changed limits the check to only the blocks touched by that diff:
# Check every block in the repository
blockwatch
# Check specific files, directories or globs
blockwatch src/main.rs docs "**/*.md"
# Check every block, and enforce the rules that need a diff, such as `affects`
git diff --patch | blockwatch --diff
# Check only the blocks the diff changed
git diff --patch | blockwatch --diff --only-changed
# The same, for staged and unstaged changes
git diff --patch HEAD | blockwatch --diff --only-changed
# Dump all discovered blocks as JSON
blockwatch list
# Show what each validator of one named block read
blockwatch explain src/lib.rs:languagesRules live in comments, next to the code they describe. Settings that apply to the whole project, such as ignored paths, extension mappings, disabled validators, and skipped blocks, go into blockwatch.toml at the repository root:
ignore = ['**/generated/**']
disable = ['check-ai']
[extensions]
ino = 'cpp'The config file can also declare blocks instead of comment tags: around a key in a JSON, TOML, or YAML file, around the text a regex matches, or around a whole file. That is how a file that can't hold a tag gets rules, such as package.json, a prompt, or a module that a build copies as it is.
To keep a known violation from failing the run without editing the source, pass its address from the output to --suppress. The violation is still reported. For the failure in How it works:
blockwatch --suppress src/lib.rs:languages:same-as:270d21b4A shorter address like src/lib.rs covers more violations. See Suppressing a Violation.
To check only some blocks, or to skip some, pass FILE:BLOCK_NAME to --only-block or --skip-block. The only-blocks and skip-blocks keys of blockwatch.toml do the same for every run. See Selecting Blocks.
See docs/cli.md for all CLI flags, execution modes, the config file, path exclusions, and custom extension mappings.
pre-commit (.pre-commit-config.yaml):
<!-- <block name="pre-commit-rev" same-as-pattern='rev: v(?P<value>\d+\.\d+\.\d+)'> -->
- repo: https://github.com/mennanov/blockwatch
rev: v0.16.0 # Use latest release
hooks:
- id: blockwatch<!-- </block> -->
GitHub Actions:
- uses: mennanov/blockwatch-action@v1blockwatch exits with 1 when it finds at least one error violation, and with 2 when it can't finish the run, such as for a malformed tag. Otherwise it exits with 0. Warnings, info, and hints are printed, but won't fail the run. See Exit Codes.
For GitHub code scanning, blockwatch --format sarif writes violations as a SARIF log instead of JSON. See SARIF Output.
For plain git hooks, local pre-commit setups, and sandboxing untrusted Lua scripts in fork pull requests, see docs/ci.md.
<!-- <block name="supported-grammar" keep-sorted="asc" keep-sorted-pattern="^- (?P<value>.+)" affects=":pitch" check-lua="scripts/check_language_count.lua" check-lua-pattern="(?m)^- (?P<value>.+)"> -->
.zsh, .bashrc, .bash_profile, .zshrc and .envrc support).c, .cc, .cpp, .cxx, .h, .hh, .hpp, .hxx)CMakeLists.txt, .cmake)Containerfile and .dockerfile support).ex, .exs)go.mod, go.sum and go.work support).graphql, .graphqls, .gql).gradle and Jenkinsfile support).tf, .tfvars, .tofu, .hcl).xhtml support).json, .jsonc, .webmanifest).mjs and .cjs support).rockspec support)GNUmakefile and .mak support).ml, .mli).proto).pyi and .pyw support)Gemfile, Rakefile, Podfile, Brewfile, Fastfile, Vagrantfile, .gemspec, .podspec, .rake,.ru, .builder and .jbuilder support)
.sbt support)BUILD, WORKSPACE, MODULE.bazel, .bzl, .bzlmod, .star; Buck: BUCK; Tilt: Tiltfile)Pipfile support).mts and .cts support).csproj, .fsproj, .vbproj, .vcxproj, .props, .targets, .xaml, .xsd, .xsl and .xsltsupport)
.cff support)<!-- </block> -->
blockwatch only inspects files with the extensions listed above and skips everything else, such as .ino or .cu. To check those too, map them to a supported extension with -E or in the extensions table of blockwatch.toml:
blockwatch -E ino=cpp -E cu=cpppasses without warning you that a rule disappeared. Blocks still pointing at the deleted one do fail, as a missing reference.
it has blocks, the run fails and shows the file. A UTF-16 file is skipped even when it has tags, because blockwatch can't find them.
problems. Run blockwatch --verbosity summary to see how many files were actually checked.
PRs and issues are welcome!
To run tests locally:
cargo test~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.