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
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,14 @@ python3 scripts/run_review.py --context ./workspace/review_context.json --gemini
# Use Codex's internal sandbox instead of the default bypass mode
python3 scripts/run_review.py --context ./workspace/review_context.json --codex-use-sandbox -o ./review-output

# Specify consolidation model (default: codex-spark)
# Specify a non-default consolidation provider (default: codex-spark)
python3 scripts/run_review.py --context ./workspace/review_context.json --gemini --consolidation-model gemini -o ./review-output

# Explicitly pin the default Codex Spark consolidation settings
python3 scripts/run_review.py --context ./workspace/review_context.json --gemini --consolidation-model codex-spark --codex-reasoning-effort xhigh -o ./review-output
# Explicitly pin the default GPT-5.3-Codex-Spark consolidation settings
python3 scripts/run_review.py --context ./workspace/review_context.json --consolidation-model codex-spark --codex-reasoning-effort xhigh -o ./review-output

# Use GPT-5.6-Sol for consolidation instead
python3 scripts/run_review.py --context ./workspace/review_context.json --gemini --consolidation-model codex --codex-reasoning-effort xhigh -o ./review-output
```

### GitCode One-Click Review
Expand Down Expand Up @@ -125,7 +128,7 @@ Default layout for `openHiTLS/hitls4j` PR `35`:
| `--codex-reasoning-effort` | Override Codex reasoning effort: low, medium, high, or xhigh (default: xhigh) |
| `--init`, `-i` | Initialize AI tools before review |
| `--no-consolidate` | Skip consolidation phase |
| `--consolidation-model` | AI model for consolidation phase: claude, gemini, codex, codex-spark, or opencode (default: codex-spark) |
| `--consolidation-model` | AI provider/model for consolidation phase: claude, gemini, codex, codex-spark, or opencode (default: codex-spark using `gpt-5.3-codex-spark`; codex uses `gpt-5.6-sol`) |
| `--base-ref` | Base ref for diff (default: origin/main) |
| `--head-ref` | Head ref for diff (default: HEAD) |
| `--custom-rules` | Custom review rules text to inject into the review prompt |
Expand Down Expand Up @@ -175,10 +178,9 @@ PR URL --> fetch_pr.py --clone --> cloned repo + context.json
+----------------+----------------+----------------+----------------+
|
Consolidation Phase
(Codex Spark validates by default
with xhigh reasoning effort,
use --consolidation-model to change,
including codex-spark and opencode)
(Codex GPT-5.6-Sol validates by default
with xhigh reasoning effort; use
--consolidation-model to change provider)
|
final_report.md/html/json
```
Expand Down Expand Up @@ -309,10 +311,10 @@ FIX:
|------|---------------|----------------|
| Claude | `claude -p --output-format text --dangerously-skip-permissions "<prompt>"` | `--dangerously-skip-permissions` |
| Gemini | `gemini -p "<prompt>" -y` | `-y` (YOLO mode) |
| Codex | `codex exec --dangerously-bypass-approvals-and-sandbox -` | `--dangerously-bypass-approvals-and-sandbox` |
| Codex | `codex exec --model gpt-5.6-sol --dangerously-bypass-approvals-and-sandbox -` | `--dangerously-bypass-approvals-and-sandbox` |
| OpenCode | `opencode run --dangerously-skip-permissions "<prompt>"` | `--dangerously-skip-permissions` |

**Note**: Codex reads the prompt from stdin via `-`. Gemini, Claude, and OpenCode run headlessly by receiving the prompt as a CLI argument. Codex now bypasses its internal sandbox by default; pass `--codex-use-sandbox` to restore the older `--full-auto` mode. `run_review.py` now defaults `--codex-reasoning-effort` to `xhigh`, and consolidation defaults to `--consolidation-model codex-spark`, which maps to `gpt-5.3-codex-spark`. The review flow is constrained to the local checkout and should not need remote PR pages or web search.
**Note**: Codex reads the prompt from stdin via `-`. Gemini, Claude, and OpenCode run headlessly by receiving the prompt as a CLI argument. Regular Codex phases use `gpt-5.6-sol` with `--codex-reasoning-effort xhigh` by default, while consolidation defaults to `gpt-5.3-codex-spark`; pass `--consolidation-model codex` to use `gpt-5.6-sol` for consolidation. Codex bypasses its internal sandbox by default; pass `--codex-use-sandbox` to restore the older `--full-auto` mode. The review flow is constrained to the local checkout and should not need remote PR pages or web search.

## Timeouts

Expand Down
2 changes: 1 addition & 1 deletion SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ npm install -g @openai/codex
|------|---------|-----------|
| Claude | `claude -p --output-format text --dangerously-skip-permissions` | `--dangerously-skip-permissions` |
| Gemini | `gemini -y` | `-y` |
| Codex | `codex exec --dangerously-bypass-approvals-and-sandbox -` | `--dangerously-bypass-approvals-and-sandbox` |
| Codex | `codex exec --model gpt-5.6-sol --dangerously-bypass-approvals-and-sandbox -` | `--dangerously-bypass-approvals-and-sandbox` |

- Initialization runs in **parallel** for all enabled tools
- Init timeout: 10 min, Review timeout: 30 min
Expand Down
39 changes: 29 additions & 10 deletions scripts/run_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@
import pr_comments as pr_comments_module


DEFAULT_CODEX_MODEL = "gpt-5.6-sol"
CODEX_SPARK_MODEL = "gpt-5.3-codex-spark"
CODEX_REASONING_EFFORT_CHOICES = ["low", "medium", "high", "xhigh"]
DEFAULT_CONSOLIDATION_MODEL = "codex-spark"
DEFAULT_CODEX_REASONING_EFFORT = "xhigh"
CODEX_INIT_REASONING_EFFORT = "low"
CODEX_INIT_TIMEOUT_SECONDS = 180


@dataclass
Expand Down Expand Up @@ -225,12 +228,15 @@ def build_codex_command(
repo_dir: Path,
*,
use_sandbox: bool = False,
model: Optional[str] = None,
model: Optional[str] = DEFAULT_CODEX_MODEL,
reasoning_effort: Optional[str] = DEFAULT_CODEX_REASONING_EFFORT,
ignore_user_config: bool = False,
) -> list[str]:
"""Build a Codex CLI command for this repository."""
command = ['codex', 'exec']

if ignore_user_config:
command.append('--ignore-user-config')
if model:
command.extend(['--model', model])
if reasoning_effort:
Expand Down Expand Up @@ -528,8 +534,8 @@ def init_claude(repo_dir: Path) -> bool:
def init_codex(
repo_dir: Path,
use_sandbox: bool = False,
model: Optional[str] = None,
reasoning_effort: Optional[str] = DEFAULT_CODEX_REASONING_EFFORT,
model: Optional[str] = DEFAULT_CODEX_MODEL,
reasoning_effort: Optional[str] = CODEX_INIT_REASONING_EFFORT,
) -> bool:
"""Initialize Codex context by generating AGENTS.md if not exists.

Expand All @@ -555,6 +561,7 @@ def init_codex(
use_sandbox=use_sandbox,
model=model,
reasoning_effort=reasoning_effort,
ignore_user_config=True,
)
command[-1:-1] = ['--output-last-message', str(agents_md)]
proc = subprocess.run(
Expand All @@ -563,7 +570,7 @@ def init_codex(
capture_output=True,
text=True,
cwd=repo_dir,
timeout=600
timeout=CODEX_INIT_TIMEOUT_SECONDS,
)
if agents_md.exists() and agents_md.stat().st_size > 0:
print_success("AGENTS.md generated")
Expand All @@ -575,6 +582,14 @@ def init_codex(
if proc.stderr:
print_warning(f"stderr: {proc.stderr[:500]}")
return False
except subprocess.TimeoutExpired:
if agents_md.exists() and agents_md.stat().st_size > 0:
print_success("AGENTS.md generated before Codex init timed out")
return True
print_warning(
f"Codex init timed out after {CODEX_INIT_TIMEOUT_SECONDS} seconds"
)
return False
except Exception as e:
print_warning(f"Codex init failed: {e}")
return False
Expand Down Expand Up @@ -717,7 +732,7 @@ def init_agents_md(
if use_codex and init_codex(
repo_dir,
use_sandbox=codex_use_sandbox,
reasoning_effort=codex_reasoning_effort,
reasoning_effort=CODEX_INIT_REASONING_EFFORT,
):
return True

Expand Down Expand Up @@ -974,7 +989,7 @@ def run_codex_agent(
prompt: str,
output_file: Path,
use_sandbox: bool = False,
model: Optional[str] = None,
model: Optional[str] = DEFAULT_CODEX_MODEL,
reasoning_effort: Optional[str] = DEFAULT_CODEX_REASONING_EFFORT,
) -> tuple[Path, list[str]]:
"""Run generic Codex CLI agent with real-time output streaming."""
Expand All @@ -1001,7 +1016,7 @@ def run_codex_review_agent(
prompt: str,
output_file: Path,
use_sandbox: bool = False,
model: Optional[str] = None,
model: Optional[str] = DEFAULT_CODEX_MODEL,
reasoning_effort: Optional[str] = DEFAULT_CODEX_REASONING_EFFORT,
) -> tuple[Path, list[str]]:
"""Run Codex CLI for review using the project-specific stdin prompt."""
Expand Down Expand Up @@ -1399,11 +1414,12 @@ def run_consolidation(
agent_map = {
'claude': ('Claude Code', lambda repo_dir, prompt, output_file: run_claude_agent(repo_dir, prompt, output_file)),
'gemini': ('Gemini CLI', lambda repo_dir, prompt, output_file: run_gemini_agent(repo_dir, prompt, output_file)),
'codex': ('Codex CLI', lambda repo_dir, prompt, output_file: run_codex_agent(
'codex': ('Codex CLI (GPT-5.6-Sol)', lambda repo_dir, prompt, output_file: run_codex_agent(
repo_dir,
prompt,
output_file,
use_sandbox=codex_use_sandbox,
model=DEFAULT_CODEX_MODEL,
reasoning_effort=codex_reasoning_effort,
)),
'codex-spark': ('Codex CLI (GPT-5.3-Codex-Spark)', lambda repo_dir, prompt, output_file: run_codex_agent(
Expand Down Expand Up @@ -1879,7 +1895,10 @@ def main():
%(prog)s --context ./workspace/review_context.json --gemini --output ./review-output

# Explicitly pin the default Codex Spark consolidation settings
%(prog)s --context ./workspace/review_context.json --gemini --consolidation-model codex-spark --codex-reasoning-effort xhigh --output ./review-output
%(prog)s --context ./workspace/review_context.json --consolidation-model codex-spark --codex-reasoning-effort xhigh --output ./review-output

# Use GPT-5.6-Sol for consolidation instead
%(prog)s --context ./workspace/review_context.json --gemini --consolidation-model codex --codex-reasoning-effort xhigh --output ./review-output

AI Tool Context Files:
- Claude Code: CLAUDE.md (project instructions, coding style)
Expand Down Expand Up @@ -1920,7 +1939,7 @@ def main():
help="Head ref for diff (default: HEAD)")
parser.add_argument("--consolidation-model", type=str, default=DEFAULT_CONSOLIDATION_MODEL,
choices=['claude', 'gemini', 'codex', 'codex-spark', 'opencode'],
help=f"AI model for consolidation phase (default: {DEFAULT_CONSOLIDATION_MODEL})")
help=f"AI provider for consolidation phase (default: {DEFAULT_CONSOLIDATION_MODEL})")
parser.add_argument("--custom-rules", type=str, default=None,
help="Custom review rules text to inject into the review prompt")
parser.add_argument("--custom-rules-file", type=Path, default=None,
Expand Down
50 changes: 48 additions & 2 deletions tests/test_init_codex.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ def test_init_codex_writes_last_message_to_agents_md(self):

def fake_run(cmd, **kwargs):
self.assertIn("--output-last-message", cmd)
self.assertIn("--ignore-user-config", cmd)
self.assertIn(
f'model_reasoning_effort="{run_review.CODEX_INIT_REASONING_EFFORT}"',
cmd,
)
self.assertEqual(
kwargs["timeout"], run_review.CODEX_INIT_TIMEOUT_SECONDS
)
output_path = Path(cmd[cmd.index("--output-last-message") + 1])
output_path.write_text("# Project Overview\nGenerated by test.\n")
return subprocess.CompletedProcess(cmd, 0, "", "")
Expand Down Expand Up @@ -92,6 +100,8 @@ def test_build_codex_command_bypasses_sandbox_by_default(self):
)

self.assertIn("-c", cmd)
self.assertIn("--model", cmd)
self.assertIn(run_review.DEFAULT_CODEX_MODEL, cmd)
self.assertIn(
f'model_reasoning_effort="{run_review.DEFAULT_CODEX_REASONING_EFFORT}"',
cmd,
Expand All @@ -110,15 +120,23 @@ def test_build_codex_command_can_use_sandbox(self):
self.assertNotIn("--dangerously-bypass-approvals-and-sandbox", cmd)
self.assertEqual(cmd[-1], "-")

def test_build_codex_command_can_ignore_user_config(self):
cmd = run_review.build_codex_command(
Path("/tmp/repo"),
ignore_user_config=True,
)

self.assertIn("--ignore-user-config", cmd)

def test_build_codex_command_can_set_model_and_reasoning_effort(self):
cmd = run_review.build_codex_command(
Path("/tmp/repo"),
model=run_review.CODEX_SPARK_MODEL,
model=run_review.DEFAULT_CODEX_MODEL,
reasoning_effort="xhigh",
)

self.assertIn("--model", cmd)
self.assertIn(run_review.CODEX_SPARK_MODEL, cmd)
self.assertIn(run_review.DEFAULT_CODEX_MODEL, cmd)
self.assertIn("-c", cmd)
self.assertIn('model_reasoning_effort="xhigh"', cmd)
self.assertEqual(cmd[-1], "-")
Expand Down Expand Up @@ -360,6 +378,34 @@ def test_run_consolidation_defaults_to_codex_spark_with_xhigh_reasoning_effort(s
run_review.DEFAULT_CODEX_REASONING_EFFORT,
)

def test_run_consolidation_supports_explicit_codex_sol(self):
with tempfile.TemporaryDirectory() as tmpdir:
repo_dir = Path(tmpdir) / "repo"
repo_dir.mkdir()
output_dir = Path(tmpdir) / "out"
output_dir.mkdir()
review_report = output_dir / "gemini_review.md"
review_report.write_text("===ISSUE===\nFILE: src/main.py\nLINE: 1\nTITLE: t\n===END===\n")

with patch(
"scripts.run_review.run_codex_agent",
return_value=(output_dir / "consolidation_output.txt", []),
) as mock_codex:
run_review.run_consolidation(
repo_dir=repo_dir,
review_reports={"gemini": review_report},
context={"owner": "owner", "repo": "repo", "pr_id": "123"},
output_dir=output_dir,
consolidation_model="codex",
)

kwargs = mock_codex.call_args.kwargs
self.assertEqual(kwargs["model"], run_review.DEFAULT_CODEX_MODEL)
self.assertEqual(
kwargs["reasoning_effort"],
run_review.DEFAULT_CODEX_REASONING_EFFORT,
)

def test_run_consolidation_supports_opencode(self):
with tempfile.TemporaryDirectory() as tmpdir:
repo_dir = Path(tmpdir) / "repo"
Expand Down