Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
42 changes: 37 additions & 5 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,27 @@
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

# 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 +50,19 @@
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
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
236 changes: 236 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,238 @@ 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`)

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

# Run PR-grade review for all files (still uses profile + pass orchestration)
pre-commit run codex-review-pr-grade --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`

## Usage

Repo maintainers can declare these hooks in `.pre-commit-config.yaml`:
Expand All @@ -68,6 +300,10 @@ 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
```

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