Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
git clone --depth 1 https://github.com/romilly/claude-code-helpersWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/commands/romilly/claude-code-helpers/matchers)<a href="https://agentmods.dev/commands/romilly/claude-code-helpers/matchers"><img src="https://agentmods.dev/badge/commands/romilly/claude-code-helpers/matchers/github.svg" alt="Measured on agentmods" height="20"></a>Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.
<a href="https://agentmods.dev/commands/romilly/claude-code-helpers/matchers"><img src="https://agentmods.dev/badge/commands/romilly/claude-code-helpers/matchers.svg" alt="Reviewed on agentmods" width="80" height="20"></a>What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5.1 | $0.00000 | $0.01585 |
| Opus 5.5 | $0.00000 | $0.00634 |
| Sonnet 5.5 | $0.00000 | $0.00317 |
| Haiku 4.5 | $0.00000 | $0.00159 |
Grade A, and why
matchers scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 1mo ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 216 lines — stays where its author put it; the contents beside it link to each section on GitHub.
PyHamcrest Custom Matchers Guide
When to Create a Custom Matcher
Create a custom matcher when:
- Multiple tests check the same object's properties
- You want clearer failure messages than plain assertions provide
Don't create a matcher for a one-off assertion - plain assert is fine.
Spotting Opportunities
Test smell: Length assertion followed by individual item checks:
# Before - verbose and redundant length check
assert len(tasks_node.children) == 2
assert tasks_node.children[0].id == "ID_1"
assert tasks_node.children[0].title == "Task A"
assert tasks_node.children[1].id == "ID_2"
assert tasks_node.children[1].title == "Task B"
# After - expressive, no length check needed
assert_that(tasks_node.children, contains_exactly(
node(id="ID_1", title="Task A"),
node(id="ID_2", title="Task B"),
))
The contains_exactly matcher handles "exactly these items, no more, no less" - eliminating the redundant length assertion.
Naming Convention
Name matcher functions after the type they match, without an is_ prefix:
node(...)notis_node(...)link(...)notis_link(...)
This reads naturally with contains_exactly: "contains exactly node with id X, node with id Y".
The Pattern
"""Custom PyHamcrest matchers for the test suite."""
from hamcrest.core.base_matcher import BaseMatcher
from hamcrest.core.description import Description
from myproject.domain import MyObject
class IsMyObjectMatcher(BaseMatcher):
"""Matcher for MyObject with optional property checks."""
def __init__(self, name: str | None = None, value: int | None = None):
self.expected_name = name
self.expected_value = value
def _matches(self, item) -> bool:
if not isinstance(item, MyObject):
return False
if self.expected_name is not None and item.name != self.expected_name:
return False
if self.expected_value is not None and item.value != self.expected_value:
return False
return True
def describe_to(self, description: Description) -> None:
parts = ["a MyObject"]
if self.expected_name is not None:
parts.append(f"with name={self.expected_name!r}")
if self.expected_value is not None:
parts.append(f"with value={self.expected_value}")
description.append_text(" ".join(parts))
def describe_mismatch(self, item, mismatch_description: Description) -> None:
if not isinstance(item, MyObject):
mismatch_description.append_text(f"was {type(item).__name__}")
elif self.expected_name is not None and item.name != self.expected_name:
mismatch_description.append_text(f"had name={item.name!r}")
elif self.expected_value is not None and item.value != self.expected_value:
mismatch_description.append_text(f"had value={item.value}")
def my_object(name: str | None = None, value: int | None = None):
"""Match a MyObject with optional property checks."""
return IsMyObjectMatcher(name=name, value=value)
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 1mo ago First seen · 216 lines · 0 tokens per session scan A 057a15e29dca
matchers is a command published in the GitHub repository romilly/claude-code-helpers (2 stars, last pushed 8mo ago), licensed MIT. It costs nothing until one of its globs matches a file; then it loads 1,585 tokens. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-31.
Other commands, from other repositories
session
Complete guide to managing pair programming sessions.
test-tdd
Run when user calls /test-tdd. Scans modified files, locates their corresponding unit/integration test suites, and runs them.
kill-mutants
Analyze surviving mutants from a mutation testing run and write targeted unit tests to kill them. Re-runs mutations to confirm kills.
lg:node
Create, modify, and manage nodes in LangGraph graphs including LLM nodes, tool nodes, human-in-the-loop, subgraphs, and conditional nodes.
test-audit
Writes a versioned TESTAUDIT.md; with --fix, quarantines the rot in gated, revertible commits. TRIGGER WHEN: auditing a test suite, measuring test health, finding dead or flaky or redundant tests, or diagnosing failure causes and proposing temporary quarantine. DO NOT TRIGGER WHEN: one module's tests are consolidated…
story-and-test
For the component(s) in scope.