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
35 changes: 0 additions & 35 deletions .codereview/agents/macos/swift-appkit/prompt.md

This file was deleted.

36 changes: 0 additions & 36 deletions .codereview/agents/rust/implementation-tests/prompt.md

This file was deleted.

35 changes: 0 additions & 35 deletions .codereview/agents/tauri/config-ipc/prompt.md

This file was deleted.

6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ Secrets are never written to `config.yml`. Non-secret config lives in the
`codereview` config directory resolved by the operating system, and durable
review data lives under the OS data directory for the `cr` binary.

`cr` ships no reviewer agents: a review needs at least one agent source, either
a profile `agent_sources` directory, a repository's `.codereview/agents/`, or
`--agents-dir`. [`starter-agents/`](starter-agents/README.md) is a
general-purpose catalog to start from, with instructions for using it as a
profile source and for adding repository-specific reviewers on top of it.

For repo review guidance, reviewer-facing dossier context, and dossier/workbench
retention conventions, see [docs/review-guidance.md](docs/review-guidance.md).
For first runs, reruns, fresh sessions, thread checkpoints, and interrupted-run
Expand Down
6 changes: 6 additions & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ release, change `PI_RUNTIME_VERSION` and run the target locally first.
- Intended binary: `cr`
- Main branch: `main`
- Support scripts are documented in `scripts/README.md`.
- `starter-agents/` is the general reviewer catalog offered to users as a
profile agent source; it is data, not loaded by the binary.
`internal/agents/starter_catalog_test.go` loads it with the strict
profile-source parser and checks that `starter-agents/README.md` lists every
agent. `.codereview/agents/` holds only the reviewers specific to this
repository, some of which replace starter agents by ID.
- Local workflow files: `.github/workflows/ci.yml`,
`.github/workflows/auto-release.yml`, and `.github/workflows/release.yml`
- Packaging identity: `packaging/identity.yml`
Expand Down
4 changes: 4 additions & 0 deletions docs/review-guidance.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ under `.codereview/agents/`. Shared profile agent sources provide organization-
or developer-managed reviewers without requiring repositories to duplicate
them.

`cr` itself ships no reviewers. The starter catalog in
[`starter-agents/`](../starter-agents/README.md) is a general set to use as a
profile source and extend with organization or repository agents.

Those agent definitions can shape review behavior by:

- adding repository-specific reviewers
Expand Down
96 changes: 96 additions & 0 deletions internal/agents/starter_catalog_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package agents

import (
"bufio"
"context"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"testing"
)

const starterCatalogDir = "../../starter-agents"

// starterTableRow matches a row of the agent table in starter-agents/README.md:
// | `category:name` | Runs | What it reviews |
var starterTableRow = regexp.MustCompile("^\\| `([a-z0-9-]+:[a-z0-9-]+)` \\| ([^|]+) \\|")

// The starter catalog ships as data, so nothing else compiles or loads it in
// CI. Loading it here keeps every agent valid under the strict profile-source
// parser and keeps the README's agent table in step with what loads.
func TestStarterCatalogLoads(t *testing.T) {
catalog, err := Load(context.Background(), LoadOptions{ProfileDirs: []string{starterCatalogDir}})
if err != nil {
t.Fatalf("Load(%s): %v", starterCatalogDir, err)
}
if len(catalog.Agents) == 0 {
t.Fatal("starter catalog loaded no agents")
}
for _, agent := range catalog.Agents {
if strings.TrimSpace(agent.Prompt) == "" {
t.Errorf("%s has an empty prompt", agent.ID)
Comment thread
piekstra marked this conversation as resolved.
}
}

for _, id := range []string{"general:correctness", "general:repo-conventions", "documentation:docs"} {
agent, ok := catalog.Find(id)
if !ok {
t.Fatalf("starter catalog is missing baseline agent %s", id)
}
if !agent.RequiredOnMatch {
t.Errorf("%s must set required_on_match so every matching PR gets it", id)
}
}

rows := readStarterTable(t)
var loaded []string
for _, agent := range catalog.Agents {
loaded = append(loaded, agent.ID)
runs, listed := rows[agent.ID]
if !listed {
t.Errorf("starter-agents/README.md agent table has no row for %s", agent.ID)
continue
}
if documentedRequired := strings.HasPrefix(runs, "Every PR"); documentedRequired != agent.RequiredOnMatch {
t.Errorf("README says %s runs %q, but required_on_match is %v", agent.ID, runs, agent.RequiredOnMatch)
}
}
sort.Strings(loaded)
for id := range rows {
if _, ok := catalog.Find(id); !ok {
t.Errorf("starter-agents/README.md lists %s, which the catalog does not load (loaded: %s)", id, strings.Join(loaded, ", "))
}
}
}

// readStarterTable returns the agent table rows of starter-agents/README.md,
// keyed by agent ID, with the "Runs" column as the value.
func readStarterTable(t *testing.T) map[string]string {
t.Helper()
f, err := os.Open(filepath.Join(starterCatalogDir, "README.md"))
if err != nil {
t.Fatalf("open starter catalog README: %v", err)
}
defer f.Close()
rows := map[string]string{}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
match := starterTableRow.FindStringSubmatch(scanner.Text())
if match == nil {
continue
}
if _, dup := rows[match[1]]; dup {
t.Errorf("starter-agents/README.md lists %s twice", match[1])
}
rows[match[1]] = strings.TrimSpace(match[2])
}
if err := scanner.Err(); err != nil {
t.Fatalf("read starter catalog README: %v", err)
}
if len(rows) == 0 {
t.Fatal("starter-agents/README.md has no agent table rows")
}
return rows
}
146 changes: 146 additions & 0 deletions starter-agents/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# Starter Agent Catalog

`cr` ships no reviewers of its own: every review criterion comes from agent
definitions you point it at. This directory is a ready-to-use set of general
reviewers, so a new profile can review pull requests on day one and a team has
a base to extend instead of a blank page.

Each agent is a directory with an `index.yaml` (routing, model tier, effort)
and a `prompt.md` (what to review). The format is described in
[docs/review-guidance.md](../docs/review-guidance.md).

## What is included

| Agent | Runs | What it reviews |
| --- | --- | --- |
| `general:correctness` | Every PR that changes a non-Markdown file | Bugs the change introduces, each shown with a concrete input or sequence |
| `general:repo-conventions` | Every PR that changes a non-Markdown file | Rules the repository writes down in `AGENTS.md`, `CLAUDE.md`, `CONTRIBUTING`, `ARCHITECTURE`, and `docs/`, cited by document and section |
| `documentation:docs` | Every PR that changes a Markdown file | Accuracy of edited docs, including statements the same PR made false |
| `tests:behavior` | Selected for behavior changes | Whether tests would fail if the implementation were wrong |
| `security:trust-boundaries` | Selected for security-relevant changes | Exploitable vulnerabilities, classified by input trust and traced to a sink |
| `architecture:design` | Selected for structural changes | SOLID and dependency hygiene in the language's own idiom, one cited rule per finding |
| `structure:repo-health` | Selected for cross-cutting changes | Drift that compounds: undocumented decisions, crossed boundaries, unenforced rules |
| `automation:ci-release` | Selected for CI, build, and release changes | Gates, permissions, pinning, version identity, and failure visibility |
| `go:implementation-tests` | Selected for Go changes | Testable seams, context, error wrapping, typed boundaries, and tests that prove behavior |
| `rust:implementation-tests` | Selected for Rust changes | Soundness, reachable panics, async and locking, and tests that prove behavior |
| `tauri:config-ipc` | Selected for Tauri changes | Capabilities, IPC input validation, CSP, and updater configuration |
| `macos:swift-appkit` | Selected for Swift and AppKit changes | Main-thread use, leaks, crashes, entitlements, and Info.plist |
| `frontend:correctness` | Selected for browser frontend changes | Races, cleanup, browser security, loading and error states, and feature-flag fallbacks |
| `frontend:accessibility` | Selected for rendered UI changes | Accessibility barriers mapped to WCAG 2.2 success criteria |

The first three set `required_on_match: true`, so `cr` adds them whenever
their globs match a changed file, even if the selector leaves them out. The
rest are optional: the selector picks them by file globs and `applies_when`,
up to five optional reviewers per run by default. `architecture:design` uses
the `large` tier; everything else uses `medium`.

## Use it as a profile agent source

`cr` reads agent sources from local directories, live, at review time. Keep a
clone of this repository on `main` and point a profile at this directory:

```bash
git clone https://github.com/open-cli-collective/codereview-cli.git ~/src/codereview-cli
cr --profile work config agent-source add ~/src/codereview-cli/starter-agents
cr --profile work agents list
```

Pull the clone to pick up catalog changes. Do not point a profile at a working
copy you edit for other branches: a profile source applies to every review that
profile runs.

To try the catalog without changing a profile, pass it for one dry run:

```bash
cr review --dry-run --agents-dir ~/src/codereview-cli/starter-agents <pr-url>
```

`--agents-dir` adds to the profile's sources rather than replacing them, so
for a clean comparison use a profile that has no other agent sources.

## Make it your own

The catalog is a starting point. Two ways to extend it, which combine:

**A shared catalog for your organization.** Copy this directory into a
repository your team owns, edit the prompts, and add agents for your stack and
your organization-wide rules. Point every profile at that copy instead of this
one. An agent ID is `<category>:<name>`, from the directory names.

**Repository-local agents.** A repository can carry its own agents under
`.codereview/agents/`, in the same layout. `cr` reads them from the pull
request's base branch, so a PR cannot change the reviewers that review it.
Every repository-local agent that applies runs, without counting against the
optional limit. A repository-local agent with the same ID as a profile agent
replaces it for that repository: this repository, for example, replaces
`documentation:docs`, `structure:repo-health`, `automation:ci-release`, and
`go:implementation-tests` with versions that know its layout, and adds
`policies:conventions` for the shared standards it follows. See
[`.codereview/agents/`](../.codereview/agents/).

## Turn your AGENTS.md into a reviewer

`general:repo-conventions` reads a repository's guidance on every PR, but it
has to find and interpret the rules each time. For rules that matter, a
repository-local conventions agent that names them works better: reviewers
cite the rule, and coding agents that skipped the docs get caught at review.

1. Write the rule where people and agents read it first: `AGENTS.md`, a nested
`AGENTS.md` for one subtree, or a topic guide under `docs/` that the entry
file links to.
2. In the same PR, add the rule to a repository-local agent prompt, in a
section whose heading names the document it comes from. When the rule
changes later, change both in one PR.
3. Say what to flag and what not to flag, name the exceptions (or the
exemption file that lists them), and say what the fix looks like.

A minimal agent at `.codereview/agents/repo/conventions/`:

```yaml
# index.yaml
name: conventions
description: Checks changes against the conventions in AGENTS.md and docs/.
model_tier: medium
effort: medium
file_globs:
- "src/**"
- "package.json"
applies_when:
- A change touches application code or root tooling configuration.
```

```markdown
<!-- prompt.md -->
You review diffs in this repository against the conventions below. Flag each
deviation with the file and line, cite the rule's document and section, and
name the fix. Do not restate rules a change already follows.

## Module boundaries (AGENTS.md)

- `src/api` never imports from `src/ui`. Flag any new import across that line.
- Logic both apps use belongs in `src/shared`, not in a copy per app. Flag a
second copy and name the module it belongs in. Do not flag a pair listed in
`scripts/allowed-duplicates.txt` with its reason.

## Testing (docs/testing.md)

- Component tests render through `test/render.tsx`. Flag direct imports of
the testing library's `render` in component tests.
```

Keep a deterministic check (a lint rule, a script, a test) for anything a
machine can verify, and use the prompt for the judgment calls around it; tell
the reviewer to treat the check's output as a floor, not a ceiling. A docs
index that marks each document as current or in progress helps reviewers know
which documents to trust.

## Writing prompts that hold up

Every prompt in this catalog shares a review bar: report only what the PR
introduces or worsens, with a concrete consequence and the input or sequence
that shows it, stated as confirmed or inferred, actionable from the changed
code, and not already caught by a tool the repository runs. Zero findings is a
normal result. Keeping that bar in each prompt, plus a "What not to report"
list and a severity scale written for the agent's lane, is what keeps reviews
short and believable. Agents stay in their own lane without naming other
agents; when two lanes report the same problem, the rollup merges them.
Loading
Loading