Conditions compose

AND, veto, and the guard that ignores reads.

Commands are one attack surface. Files are the other: an agent that writes TOKEN=sk-live-... into .env has just committed your secret to its transcript. This guard watches for exactly that — and nothing else. Three conditions, one veto: pick a tool, a path, and file content, and see which combination fires:

The composition rules, in the order the widget just taught them:

only_if conditions AND together. Tool must match, path must match, content must match — miss any one and the hook stays quiet. Writing a secret to src/config.py passes (wrong path); writing prose to .env passes (no secret pattern).

skip_if is a veto. .env.example matches every only_if condition, and the skip wins anyway. Vetoes are how you carve the sanctioned exception out of a broad rule without weakening the rule.

A Read never trips a write guard. Tool("Edit|Write") scopes the hook to mutations. The agent can read .env all day — this hook has no opinion about that.

Next: when conditions can’t say it, walk the command.