# CommitBrief v1.15.0

> Four new surfaces. commitbrief leaks audits the working tree and git history for committed credentials with no provider call; commitbrief map draws the commit graph and shows exactly what a filter selected; commit-level filters (--author, --committer, --start-date, --end-date, --text) review a set of commits instead of one diff; and commitbrief upgrade updates the binary across every install method.

Released July 26, 2026

Canonical URL: https://commitbrief.com/changelog/v1.15.0/

---

## Added

- **`commitbrief leaks` — audit the working tree and git history for committed
  credentials (ADR-0036).** The pre-send secret scanner is a *gate*: it only ever
  sees the one diff about to be sent, so it cannot answer "is there a key in my
  tree right now?" or "did anyone ever commit one?" — and a key that was
  committed and later removed is still in the history, still reachable in every
  clone and fork. `leaks` answers both with the same eight built-in patterns plus
  your `guard.secret_patterns`, deterministically: **no provider call, no cache,
  no cost.**

  ```sh
  commitbrief leaks                            # tracked files + last 200 commits
  commitbrief leaks --no-history               # working tree only, fast
  commitbrief leaks main..HEAD --no-worktree   # exactly that range
  commitbrief leaks --json | commitbrief guard --from-json -
  ```

  Both halves run by default (`--no-worktree` / `--no-history` switch either
  off). The working-tree half reads every **tracked** file whole — untracked,
  gitignored files like `.env` are deliberately out of scope, since that is
  where a secret is supposed to live and it cannot leak through git. The history
  half scans the **added lines** of the commits ADR-0035's filters select,
  attributing each hit to its commit, author and date, because that is what
  decides whether a key still needs rotating. Bounded by `--max-commits`
  (default 200), and truncation is always reported.

  It exits 1 on any hit, so it gates CI out of the box; `--fail-on none` reports
  without failing. `--json` emits the existing schema v1 with
  `meta.provider: "builtin"`, so the policy gate consumes it with no new
  plumbing. Findings carry a file, a line and the pattern names — **never the
  matched text**. Two limits are documented rather than hidden: it honors the
  ignore layers, so a key inside `vendor/**` is not reported, and it is
  regex-only, so a high-entropy blob with no recognizable prefix is invisible.

- **`commitbrief map` — the commit graph, and what your filter actually selected
  (ADR-0037).** The commit filters can pick a non-contiguous set from anywhere in
  the history, and the only feedback was a count ("12 commits matched") — so a
  wrong filter silently reviewed the wrong code. `map` draws the DAG with
  matching commits highlighted and the rest dimmed as context, which makes a
  filter checkable **before** you pay for a review. `--branches` switches to a
  branch topology summary: where each branch sits relative to the base and how
  far ahead / behind. Deterministic, and always exits 0 — a viewer, not a gate.
  Rows clip to the terminal rather than wrapping, and colour plus box-drawing
  fall back together, so a pipe or `--color=never` yields plain ASCII. No new
  dependencies: lane assignment is a pure function in a new `internal/graph`
  package.

- **Commit-level filters: `--author`, `--committer`, `--start-date`,
  `--end-date`, `--text` (ADR-0035).** Review a *set of commits* rather than a
  single diff. `git diff` has no author or date options — those are `git log`
  options — so setting any of these switches diff acquisition to a commit walk:
  pick the matching commits, then concatenate their patches. Different filter
  kinds are AND'd and multiple values of one kind are OR'd, so
  `--author alice --author bob --start-date 2026-06-01` means "(Alice or Bob)
  and since June". Identity matching is a case-insensitive substring over both
  the name and the email. `--end-date` is **inclusive** of the day named (git's
  bare `--until` stops at that day's midnight and silently drops it). `--text`
  matches the commit message *and* the name of a branch, in which case the
  commits unique to that branch are pulled in — best-effort by nature, since a
  squash- or rebase-merged branch no longer owns its commits.

  With no explicit range the walk covers `HEAD`; a subcommand's range bounds it
  (`commitbrief diff main..develop --author alice`). Two modifiers shape a walk
  but never start one, and are rejected if used alone: `--max-commits N`
  (default 200) caps the selection and always reports truncation rather than
  silently reviewing a subset, and `--merges` keeps merge commits, which are
  excluded by default.

  Available on the default review, `diff`, `summary`, `dry-run`, the MCP
  `review` tool and `guard`. Rejected by `commit` (it describes the staged
  index, which has no commits) and by `remote pr` (its diff comes from
  `gh pr diff`, not local git).

- **`--exclude-file` / `--exclude-dir` path denylists.** The inverse of `--file`
  / `--dir`, sharing their exact matching rules (literal path or gitignore-style
  glob) and applied after them, so an exclusion always wins:
  `--dir internal --exclude-dir internal/cli`. An invalid glob errors before any
  provider call, as it does for the allowlist.

- **Path and commit filters are now reachable over MCP.** The `review` tool
  gained `file`, `dir`, `exclude_file`, `exclude_dir`, `author`, `committer`,
  `start_date`, `end_date`, `text`, `max_commits` and `merges` arguments.
  `--file` / `--dir` were previously CLI-only in practice: the MCP seam resets
  the global flag state, so a host had no way to narrow a review by path.
  `guard` forwards the same set from its inherited persistent flags.

- **`meta.filtered_commits` in the JSON output.** Optional and `omitempty`, so
  the schema stays `1` — the count of commits whose patches make up the reviewed
  diff. `dry-run` gains matching `Commits (walked)` / `Commits (matched)` lines
  and an `--exclude-file` / `--exclude-dir` row in its per-layer file accounting.

- **`commitbrief upgrade` — in-tool updates across every install method
  (ADR-0034).** Detects whether the running binary came from Homebrew, Scoop,
  `go install` or a GitHub Releases tarball. Package-managed installs are
  delegated to their own manager (`brew upgrade` / `scoop update` /
  `go install …@latest`), because overwriting a manager-owned binary
  desynchronizes its metadata; only a manual install is replaced in place, after
  its SHA-256 is verified against the release `checksums.txt`. An unwritable
  target aborts **before** anything is downloaded and prints the exact command
  to run — CommitBrief never invokes `sudo` itself. `--check` reports without
  installing and always exits 0; `--json` implies `--check`. The version check
  runs **only** when you invoke the command: there is no automatic update check
  and no telemetry.

## Changed

- `commitbrief list`'s built-in command reference was several releases stale — it
  never listed `commit`, `guard`, `mcp`, `remote pr`, `doctor`, `providers`,
  `config`, `install-hook` or `upgrade`. Rewritten to cover the whole surface,
  including the path and commit filters.
- `--max-commits` and `--merges` are usage errors on a review when no commit
  filter is set (nothing to modify), but ordinary bounds on `leaks` and `map`,
  which always walk history.

## Fixed

- `commitbrief remote pr` now applies `--file` / `--dir` on the **posting** path
  too. Only the `--no-post` path honored them, so a narrowed run that commented
  on GitHub reviewed a different file set than the same command with
  `--no-post`.