Skip to content
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea/
codexw-generic-bootstrap.yaml
59 changes: 54 additions & 5 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,36 @@
files: &sync_ai_rules_files (^\.cursor/rules/.*\.mdc$|^\.code_review/.*\.md$)
pass_filenames: false

# Nobody should ever use these hooks in production. They're just for testing PRs in
# the duolingo/pre-commit-hooks repo more easily without having to tag and push
# temporary images to Docker Hub. Usage: edit a consumer repo's hook config to
# instead declare `id: duolingo-dev` or `id: sync-ai-rules-dev` and `rev: <PR branch SHA>`,
# then run `pre-commit run <hook-id> --all-files`
- id: codex-review
name: Codex AI Code Review
description: On-demand AI code review using OpenAI Codex CLI. Requires codex CLI installed and authenticated.
entry: codex review
language: system
pass_filenames: false
stages: [manual]
verbose: true

- id: codex-review-pr-grade
name: Codex AI Code Review (PR-grade)
description: Profile-aware multi-pass Codex review via ./codexw/__main__.py (auto-generates and auto-syncs local-review-profile.yaml).
entry: ./codexw/__main__.py review
language: script
pass_filenames: false
stages: [manual]
verbose: true

- id: codexw
name: Codexw (alias)
description: Alias for codex-review-pr-grade.
entry: ./codexw/__main__.py review
language: script
pass_filenames: false
stages: [manual]
verbose: true

# Dev hooks for testing PRs in the duolingo/pre-commit-hooks repo.
# Usage: edit a consumer repo's hook config to declare `id: duolingo-dev`
# and `rev: <PR branch SHA>`, then run `pre-commit run <hook-id> --all-files`
- id: duolingo-dev
name: Duolingo (dev)
entry: /entry
Expand All @@ -34,3 +59,27 @@
language: docker
files: *sync_ai_rules_files
pass_filenames: false

- id: codex-review-dev
name: Codex AI Code Review (dev)
entry: codex review
language: system
pass_filenames: false
stages: [manual]
verbose: true

- id: codex-review-pr-grade-dev
name: Codex AI Code Review (PR-grade, dev)
entry: ./codexw/__main__.py review
language: script
pass_filenames: false
stages: [manual]
verbose: true

- id: codexw-dev
name: Codexw (alias, dev)
entry: ./codexw/__main__.py review
language: script
pass_filenames: false
stages: [manual]
verbose: true
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ shell:
# Runs tests
.PHONY: test
test:
echo "Running codexw tests..."
python3 test/codexw_test.py
docker run --rm -v "$${PWD}/test:/test" "$$(docker build --network=host -q .)" sh -c \
'cd /tmp \
&& cp -r /test/before actual \
Expand Down
240 changes: 240 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,240 @@ This hook synchronizes AI coding rules from `.cursor/rules/` and `.code_review/`

This ensures all AI coding assistants stay aware of the same rules and coding conventions.

Execution model note:

- `sync-ai-rules` is deterministic file generation, so it uses `language: docker_image` and a `files:` filter to run automatically when rule files change.
- Codex hooks are AI-review workflows (`codex review` / `codexw`) that may take longer and require local auth, so they run in `manual` stage by default and are triggered on demand.

## Codex AI Code Review Hook (`codex-review`)

On-demand AI code review using the OpenAI Codex CLI. This hook runs in `manual` stage by default, meaning it won't block normal commits.

**Prerequisites:**

- Install Codex CLI: `brew install codex` or `npm install -g @openai/codex`
- Authenticate: `codex auth login` (uses Duolingo ChatGPT org credentials)

**Usage:**

```bash
# Run Codex review on staged changes
pre-commit run codex-review

# Run on all files
pre-commit run codex-review --all-files
```

For direct CLI usage without pre-commit:

```bash
codex review --uncommitted
codex review --base master
```

## Codex PR-grade Hook (`codex-review-pr-grade`, alias: `codexw`)

Profile-aware multi-pass local review using `codexw`. This hook is also `manual` by default and does not block normal commits.

It runs detailed PR-grade review from `local-review-profile.yaml`.
`codexw` also includes compatibility fallback for Codex CLI versions that reject prompt+target combinations.
`codexw` also includes recency-biased model fallback (latest 5 candidates, for example `gpt-5.3-codex` → `gpt-5.2-codex` → `gpt-5.1-codex` → `gpt-5-codex` → `gpt-4.2-codex`) and reasoning-effort fallback (`xhigh` → `high` → `medium` → `low`).
Canonical command is `./codexw/__main__.py review`; `./codexw/__main__.py review-pr` is kept as a compatibility alias.
If profile is missing, `codexw` auto-generates `local-review-profile.yaml` on first run.
On each run, `codexw` auto-syncs profile entries derived from repository signals (rules/domains/domain prompts) while preserving manual overrides. Stale auto-managed entries are pruned when source-of-truth changes.

PR-grade outputs include:

- pass-level markdown reports
- combined markdown report (`combined-report.md`)
- machine-readable findings (`findings.json`)

**Prerequisites:**

- Install Codex CLI: `brew install codex` or `npm install -g @openai/codex`
- Authenticate: `codex auth login`
- Optional: pre-seed `local-review-profile.yaml` in target repo root (see example below)

**Usage:**

```bash
# Run PR-grade review for current diff vs profile default base branch
pre-commit run codex-review-pr-grade
pre-commit run codexw

# Run PR-grade review for all files (still uses profile + pass orchestration)
pre-commit run codex-review-pr-grade --all-files
pre-commit run codexw --all-files
```

Direct execution (without pre-commit):

```bash
./codexw/__main__.py review
./codexw/__main__.py review --base main
./codexw/__main__.py review --domains core,testing --no-fail-on-findings
# Create missing profile and exit
./codexw/__main__.py review --bootstrap-only
# Sync profile from repository signals and exit
./codexw/__main__.py review --sync-profile-only
# Validate profile loading only (no Codex run)
./codexw/__main__.py review --print-effective-profile
# Disable profile sync for one run
./codexw/__main__.py review --no-sync-profile
# Keep stale auto-managed profile entries for this run
./codexw/__main__.py review --no-prune-autogen
```

`review-pr` is an alias for `review` (kept for backward compatibility):

```bash
./codexw/__main__.py review-pr --base master
```

### codexw CLI Flags Reference

| Flag | Purpose | Default / Notes |
| --- | --- | --- |
| `--profile <path>` | Profile file to load/write. | Defaults to `local-review-profile.yaml` at repo root. |
| `--base <branch>` | Review `branch...HEAD` diff. | Mutually exclusive with `--uncommitted` and `--commit`. Defaults to profile `review.default_base` when no target flag is passed. |
| `--uncommitted` | Review working tree changes. | Mutually exclusive target mode. Includes tracked and untracked files. |
| `--commit <sha>` | Review a specific commit. | Mutually exclusive target mode. |
| `--domains <a,b,c>` | Restrict domain passes to selected domains. | Must be subset of profile `domains.allowed`. Defaults to profile `domains.default`. |
| `--depth-hotspots <n>` | Override hotspot depth pass count for this run. | Overrides profile `review.depth_hotspots`. |
| `--title <text>` | Pass custom title to `codex review`. | Optional metadata for review runs. |
| `--output-dir <path>` | Write artifacts to explicit output directory. | Defaults to `<profile.review.output_root>/<timestamp>`. |
| `--model <name>` | Requested model override for this run. | Used as preferred model; fallback chain may apply if unavailable. |
| `--print-effective-profile` | Print normalized effective profile and exit. | No review passes executed. |
| `--bootstrap-only` | Create missing profile (if needed) and exit. | No review passes executed. |
| `--sync-profile-only` | Sync profile from repo signals and exit. | No review passes executed. Cannot be combined with `--no-sync-profile`. |
| `--no-bootstrap-profile` | Disable automatic profile generation when missing. | Fails if profile file is absent. |
| `--no-sync-profile` | Disable sync from repository signals for this run. | Uses profile file as-is. |
| `--no-prune-autogen` | Keep stale auto-managed entries during sync for this run. | Sync still runs unless `--no-sync-profile` is set. |
| `--fail-on-findings` | Force strict gate (exit 2 when findings exist). | Mutually exclusive with `--no-fail-on-findings`. |
| `--no-fail-on-findings` | Advisory mode (do not fail on findings). | Mutually exclusive with `--fail-on-findings`. |

### Common Flag Combos

```bash
# Validate profile and inspect resolved settings (no Codex call)
./codexw/__main__.py review --profile local-review-profile.yaml --print-effective-profile

# Advisory targeted review for local iteration
./codexw/__main__.py review --uncommitted --domains core,testing --no-fail-on-findings

# Strict PR-grade run with explicit artifacts path
./codexw/__main__.py review --base master --fail-on-findings --output-dir .codex/review-runs/manual
```

### Sample Output for codexw-only Flags

`--bootstrap-only` (missing profile):

```text
$ ./codexw/__main__.py review --bootstrap-only
Generated local-review-profile.yaml from repository signals. Review and commit it.
Synchronized local-review-profile.yaml from repository signals.
warning: rule file 'AGENTS.md' not found
warning: rule pattern '.cursor/rules/**/*.mdc' matched no files
Profile ready: <repo>/local-review-profile.yaml
```

`--sync-profile-only`:

```text
$ ./codexw/__main__.py review --sync-profile-only
warning: rule file 'AGENTS.md' not found
warning: rule pattern '.cursor/rules/**/*.mdc' matched no files
Profile ready: <repo>/local-review-profile.yaml
```

`--print-effective-profile`:

```text
$ ./codexw/__main__.py review --print-effective-profile
warning: rule file 'AGENTS.md' not found
warning: rule pattern '.cursor/rules/**/*.mdc' matched no files
{
"profile_path": "<repo>/local-review-profile.yaml",
"repo_root": "<repo>",
"effective_profile": { ...normalized profile JSON... }
}
```

`--no-bootstrap-profile` (profile missing):

```text
$ ./codexw/__main__.py review --no-bootstrap-profile --print-effective-profile
error: profile not found: <repo>/local-review-profile.yaml. Add local-review-profile.yaml or pass --profile.
```

`--sync-profile-only` with `--no-sync-profile` (invalid combination):

```text
$ ./codexw/__main__.py review --sync-profile-only --no-sync-profile
error: --sync-profile-only cannot be combined with --no-sync-profile
```

`local-review-profile.yaml` schema (minimum practical shape):

```yaml
version: 1

repo:
name: Repo Name

review:
default_base: main
strict_gate: true
depth_hotspots: 3
output_root: .codex/review-runs

rules:
include:
- AGENTS.md
- .cursor/rules/**/*.mdc

domains:
default: [core]
allowed: [core, testing]

prompts:
global: |
Additional repo-wide review context.
by_domain:
testing: |
Additional testing-specific context.

pipeline:
include_policy_pass: true
include_core_passes: true
include_domain_passes: true
include_depth_passes: true
policy_instructions: |
Custom policy pass instructions.
core_passes:
- id: core-breadth
name: Core breadth
instructions: |
Custom breadth pass instructions.
depth_instructions: |
Task:
- Perform depth-first review of hotspot file: {hotspot}
```

Reference profiles:

- `codexw/local-review-profile.repo.example.yaml` (generic template)
- `codexw/local-review-profile.duolingo-android.example.yaml` (concrete Duolingo Android example)

Feature/use-case guide:

- `codexw/features-and-usecases.md`
- `codexw/architecture.md` (internal architecture)

Hook id for pre-commit:
`codex-review-pr-grade` (canonical), `codexw` (alias)

## Usage

Repo maintainers can declare these hooks in `.pre-commit-config.yaml`:
Expand All @@ -68,6 +302,12 @@ Repo maintainers can declare these hooks in `.pre-commit-config.yaml`:
- --scala-version=3 # Defaults to Scala 2.12
# Sync AI rules hook (for repos with Cursor AI rules)
- id: sync-ai-rules
# On-demand Codex AI code review (manual stage, requires codex CLI)
- id: codex-review
# On-demand PR-grade Codex review (manual stage, profile-aware)
- id: codex-review-pr-grade
# Equivalent alias for PR-grade Codex review (manual stage, profile-aware)
- id: codexw
```

Directories named `build` and `node_modules` are excluded by default - no need to declare them in the hook's `exclude` key.
Expand Down
31 changes: 31 additions & 0 deletions codexw/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Codexw: Profile-aware Codex PR-grade review wrapper.

Public API:
- CodexwError: Base exception type
- PassSpec: Data class for review pass specification
- build_bootstrap_profile: Build initial profile from repo signals
- default_domain_prompt_template: Generic per-domain prompt template
- normalize_profile: Normalize raw profile dict
- load_profile: Load profile from file
- write_profile: Write profile to file
"""

from .passes import PassSpec
from .profile import (
build_bootstrap_profile,
default_domain_prompt_template,
load_profile,
normalize_profile,
write_profile,
)
from .utils import CodexwError

__all__ = [
"CodexwError",
"PassSpec",
"build_bootstrap_profile",
"default_domain_prompt_template",
"load_profile",
"normalize_profile",
"write_profile",
]
Loading