Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Optional `filter` front-matter field: a second-stage applicability command run
after a rule's globs match, with the matched paths appended as arguments.
grep-style exit codes (`0` applies, `1` skips, anything else / timeout / missing
command fails open and applies). Disable with `--no-filters` / `runFilters:
false`; bound with `--filter-timeout` / `filterTimeoutMs` (default 10000 ms).
Filter errors surface in the new `ReviewResult.warnings`. New exports:
`makeFilterExecutor`, `FilterResult`, `FilterExecutor`, `DiscoverOptions`.

## [0.1.0] - 2026-06-26

### Added
Expand Down
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ COPY tsconfig.json tsconfig.build.json ./
COPY src ./src
RUN yarn build

# Scripts, sample rules, and entrypoint.
# Scripts, sample rules, unit tests, and entrypoint. The `test/` dir is needed
# for the `test` command (vitest); vitest is a devDependency already installed
# above by `yarn install --frozen-lockfile`.
COPY scripts ./scripts
COPY examples ./examples
COPY test ./test
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh scripts/*.sh

Expand Down
52 changes: 47 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,47 @@ globs:
Use the project logger instead of `console.log` in non-test source files.
```

| Field | Description |
| ------------- | ----------------------------------------------------------------------------- |
| `description` | Display name (falls back to the filename) |
| `globs` | Inline list or YAML list; `!` negates. A rule with no globs is never applied. |
| `reviewSkip` | If `true`, the rule is parsed but excluded from review |
| Field | Description |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `description` | Display name (falls back to the filename) |
| `globs` | Inline list or YAML list; `!` negates. A rule with no globs is never applied. |
| `reviewSkip` | If `true`, the rule is parsed but excluded from review |
| `filter` | Optional command run after a glob match to decide if the rule applies. Matched paths are appended as args. See below. Absent ⇒ no extra check. |

### Filtering beyond globs

Globs match file _paths_. A `filter` command lets a rule also depend on file
_content_ or relationships between changes. After a rule's globs select the
changed files, its `filter` runs once with those paths appended as arguments,
and decides applicability by exit code:

| Exit code | Meaning |
| ---------------------------------------------- | --------------------------------------------------- |
| `0` | Filter passed — the rule applies |
| `1` | Clean rejection — the rule is skipped |
| anything else, a missing command, or a timeout | Error — fail-open: the rule applies (and is warned) |

```markdown
---
description: No raw SQL in repositories
globs:
- 'src/repositories/**/*.ts'
filter: "grep -ilq 'select \\|insert \\|update '"
---

Use the query builder, not raw SQL strings, in repository classes.
```

Here `grep` exits `0` if any matched file contains a SQL keyword (rule applies),
`1` if none do (skipped). The command can be inline (with flags) or a script;
see [`examples/filters/`](./examples/filters/) and [`examples/rules/no-raw-sql.md`](./examples/rules/no-raw-sql.md).

> **Filter commands execute with your privileges.** Treat the rules directory as
> trusted code, like a git hook. When reviewing an untrusted diff (e.g. a fork PR
> that could edit a `filter`), pass `--no-filters`. Note also that a filter reads
> files from the working tree — for `--diff <range>` reviews those may differ from
> the diffed revision, so content filters should query git (`git grep <range>`)
> rather than read the tree.

## CLI

Expand All @@ -78,12 +114,18 @@ agent-rules --working-tree --transport codex

# Force an explicit transport command (any stdin->stdout program)
agent-rules --working-tree --exec "claude -p --output-format json"

# Review an untrusted diff without executing any rule `filter` commands
agent-rules --diff origin/main...HEAD --no-filters
```

Diff sources (exactly one): `--working-tree`, `--staged`, `--diff <range>`.
Run `agent-rules --help` for all options. Exit codes: `0` clean, `1` blocking
findings, `2` error.

Rule `filter` commands run by default (including under `--list`); `--no-filters`
disables them and `--filter-timeout <ms>` bounds each one (default `10000`).

## Library

```ts
Expand Down
20 changes: 14 additions & 6 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ docker build -t agent-rules .
# or: yarn docker:build
```

## Hermetic smoke test (no credentials)
## Hermetic tests (no credentials)

The default command runs `scripts/smoke.sh` — a fake `--exec` transport, so it
needs no agent login and is safe anywhere (also what CI runs).
These need no agent login and are safe to run anywhere (also what CI runs).

```sh
# End-to-end smoke test (fake --exec transport) — the default command
docker run --rm agent-rules
# or: yarn docker:smoke

# Unit test suite (vitest)
docker run --rm agent-rules test

# Both: unit tests + smoke
docker run --rm agent-rules check
```

## Live review against a real agent
Expand Down Expand Up @@ -65,6 +71,8 @@ docker run --rm \

## Commands

The entrypoint accepts: `smoke` (default), `verify [args]`, `demo [args]`,
`cli [args]`, or any other command to exec directly. `verify`/`demo` forward
extra args to the CLI, e.g. `--transport codex` or `--output json`.
The entrypoint accepts: `smoke` (default), `test`, `check`, `verify [args]`,
`demo [args]`, `cli [args]`, or any other command to exec directly. `smoke`,
`test`, and `check` are hermetic (no credentials); `verify`/`demo` need a real
agent and forward extra args to the CLI, e.g. `--transport codex` or
`--output json`.
9 changes: 9 additions & 0 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# Container entrypoint. Dispatches to the project scripts / CLI.
#
# smoke hermetic end-to-end test (fake transport, no creds) [default]
# test unit test suite (vitest), no creds
# check unit tests + smoke (full hermetic test pass), no creds
# verify [args] live review against a real agent (needs creds);
# extra args pass through, e.g. `verify --transport codex`
# demo [args] review the bundled examples/ sample against a real agent
Expand All @@ -15,6 +17,13 @@ case "$cmd" in
smoke)
exec bash scripts/smoke.sh
;;
test)
exec yarn test
;;
check)
yarn test
exec bash scripts/smoke.sh
;;
verify)
shift
exec bash scripts/verify-transport.sh "$@"
Expand Down
Loading
Loading