From 698dad7f85e8e480a795d32bf4a67dcccb0cbf04 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Tue, 4 Nov 2025 11:17:52 -0500 Subject: [PATCH 01/22] feat(ai_commit_review): adds agentic commit review hook --- .pre-commit-hooks.yaml | 12 ++ CHANGELOG.md | 7 + CONFIGURATION_GUIDE.md | 29 ++++ DEPENDENCIES.md | 30 ++++ README.md | 62 ++++++++ examples/ai-enhanced-security.yaml | 32 ++++ hooks/ai_commit_check.py | 231 +++++++++++++++++++++++++++++ pyproject.toml | 3 + 8 files changed, 406 insertions(+) create mode 100644 examples/ai-enhanced-security.yaml create mode 100755 hooks/ai_commit_check.py diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index b350331..909f142 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -109,6 +109,18 @@ files: \.(yaml|yml|json)$ require_serial: false +# ============================================================================= +# AI-POWERED VALIDATION HOOKS +# ============================================================================= + +- id: ai_commit_check + name: AI-powered commit validation + entry: ai_commit_check + language: python + pass_filenames: false + stages: [pre-commit] + require_serial: true + # ============================================================================= # SECURITY SCANNING HOOKS # ============================================================================= diff --git a/CHANGELOG.md b/CHANGELOG.md index cbd4a3c..3a712c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- AI-powered commit validation hook (`ai_commit_check`) using Opencode AI + - Reviews staged changes for code quality, security, and best practices + - Interactive feedback with suggestions for improvements + - Configurable via environment variables (model, provider, timeout) +- New dependencies: `opencode-ai>=0.1.0a36` and `rich>=13.7.0` + ## [1.0.0] - 2024-01-XX ### Added diff --git a/CONFIGURATION_GUIDE.md b/CONFIGURATION_GUIDE.md index 01e99d5..be26a76 100644 --- a/CONFIGURATION_GUIDE.md +++ b/CONFIGURATION_GUIDE.md @@ -6,6 +6,35 @@ This guide helps you configure the pre-commit hooks to minimize false positives Use the `examples/practical-security.yaml` configuration for a balanced approach that reduces false positives. +## AI-Powered Commit Validation + +### ai_commit_check Configuration + +**Problem**: AI validation may be too slow or strict for all commits + +**Solution**: Configure via environment variables + +```bash +# Set in your shell profile or CI/CD +export OPENCODE_PORT=61164 # Port for opencode server +export OPENCODE_MODEL=claude-sonnet-4.5 # Model selection +export OPENCODE_PROVIDER=github-copilot # Provider (github-copilot, anthropic, etc.) +export OPENCODE_TIMEOUT=90 # Timeout in seconds +``` + +**Usage in .pre-commit-config.yaml:** +```yaml +- id: ai_commit_check + # Only run manually or in CI, not on every commit + stages: [manual] +``` + +Or disable for quick commits: +```bash +# Skip AI validation for urgent commits +git commit --no-verify -m "Quick fix" +``` + ## Common False Positive Issues and Solutions ### 1. detect-secrets and hardcoded-credentials diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index 0e83170..1b1efd8 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -75,6 +75,36 @@ pipx install pre-commit pip install black flake8 isort mypy bandit safety detect_secrets ``` +#### AI-Powered Hooks (Optional) +**Both platforms:** +```bash +# Required for ai_commit_check hook +pip install opencode-ai rich + +# Or install opencode CLI directly +npm install -g @sst/opencode +``` + +**Setup Opencode Authentication:** +```bash +# Authenticate with Opencode +opencode auth login +# Select: github-copilot + +# Configure the AI model +# In the opencode interface, enter: /models +# Select: claude-sonnet-4.5 +``` + +**Environment Variables:** +```bash +# Configure AI hook behavior (optional) +export OPENCODE_PORT=61164 # Port for opencode server (default: 61164) +export OPENCODE_MODEL=claude-sonnet-4.5 # AI model to use (default: claude-sonnet-4.5) +export OPENCODE_PROVIDER=github-copilot # AI provider (default: github-copilot) +export OPENCODE_TIMEOUT=90 # Server startup timeout in seconds (default: 90) +``` + #### JavaScript/TypeScript/Node.js **macOS:** ```bash diff --git a/README.md b/README.md index c9258fc..47ce910 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,12 @@ pre-commit run --all-files ## šŸ“‹ Available Hooks +### šŸ¤– AI-Powered Hooks + +| Hook ID | Description | Languages | +| ------------------ | ------------------------------------------------ | --------- | +| `ai_commit_check` | AI-powered commit validation with Opencode AI | All | + ### šŸ”’ Security Hooks | Hook ID | Description | Languages | @@ -470,6 +476,62 @@ repos: ## šŸŽ›ļø Hook Configuration +### AI-Powered Commit Validation + +The `ai_commit_check` hook uses Opencode AI to review your staged changes before committing: + +```yaml +repos: + - repo: https://github.com/TriaFed/pre-commit-library + rev: v1.1.7 + hooks: + - id: ai_commit_check + stages: [manual] # Run manually to avoid slowing down every commit +``` + +**Setup:** +```bash +# Install opencode +pip install opencode-ai rich +# or +npm install -g @sst/opencode + +# Authenticate with Opencode +opencode auth login +# Select: github-copilot + +# Configure the AI model +# In the opencode interface, enter: /models +# Select: claude-sonnet-4.5 +``` + +**Environment Variables:** +```bash +export OPENCODE_PORT=61164 # Default: 61164 +export OPENCODE_MODEL=claude-sonnet-4.5 # Default: claude-sonnet-4.5 +export OPENCODE_PROVIDER=github-copilot # Default: github-copilot +export OPENCODE_TIMEOUT=90 # Default: 90 seconds +``` + +**Usage:** +```bash +# Run manually +pre-commit run --hook-stage manual ai_commit_check + +# Or configure to run automatically on every commit +# (Remove the stages: [manual] line from config) +``` + +The hook will: +- Review code quality and best practices +- Identify potential bugs or security concerns +- Suggest improvements +- Block commits with critical issues (starting with "-COMMIT REJECTED-") +- Provide commands to continue the AI session or auto-fix issues + +**Troubleshooting:** +If the hook fails with authentication errors, ensure you've completed the `opencode auth login` setup above. + ### Environment Variables You can customize hook behavior using environment variables: diff --git a/examples/ai-enhanced-security.yaml b/examples/ai-enhanced-security.yaml new file mode 100644 index 0000000..3313bb2 --- /dev/null +++ b/examples/ai-enhanced-security.yaml @@ -0,0 +1,32 @@ +# AI-Enhanced Security Configuration +# This configuration combines AI-powered validation with essential security hooks +# Suitable for teams using GenAI tools and wanting automated code review + +repos: + - repo: https://github.com/TriaFed/pre-commit-library + rev: v1.1.7 + hooks: + # AI-Powered Validation (runs on manual trigger or CI) + - id: ai_commit_check + stages: [manual] # Run with: pre-commit run --hook-stage manual ai_commit_check + + # Essential Security Hooks + - id: detect_secrets + args: ['--baseline', '.secrets.baseline'] + - id: hardcoded_credentials + - id: genai_security_check + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-added-large-files + - id: check-yaml + - id: check-json + - id: trailing-whitespace + - id: end-of-file-fixer + +# Usage: +# 1. Regular commits: Security hooks run automatically +# 2. AI validation: Run manually with: +# pre-commit run --hook-stage manual ai_commit_check +# 3. Or configure CI to always run AI validation on pull requests diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py new file mode 100755 index 0000000..f81fb7d --- /dev/null +++ b/hooks/ai_commit_check.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python3 +""" +AI-powered commit validation using Opencode AI. +Reviews staged changes for code quality, security, and best practices. +""" + +from opencode_ai import Opencode +from typing import Optional, Iterable +import subprocess +import sys +import time +import socket +import os +from datetime import datetime + +try: + from rich.console import Console + from rich.markdown import Markdown + from rich.panel import Panel +except ImportError: + Console = None + Markdown = None + Panel = None + +def find_available_port(start_port=61164, max_attempts=10): + for i in range(max_attempts): + port = start_port + i + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('localhost', port)) + sock.close() + return port + except OSError: + continue + return None + +def wait_for_port(port, timeout=30): + start = time.time() + while time.time() - start < timeout: + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(1) + sock.connect(('localhost', port)) + sock.close() + return True + except (socket.error, socket.timeout): + time.sleep(0.5) + return False + +def main(): + OPENCODE_PORT = int(os.getenv('OPENCODE_PORT', '61164')) + OPENCODE_MODEL = os.getenv('OPENCODE_MODEL', 'claude-sonnet-4.5') + OPENCODE_PROVIDER = os.getenv('OPENCODE_PROVIDER', 'github-copilot') + OPENCODE_TIMEOUT = int(os.getenv('OPENCODE_TIMEOUT', '90')) + + if not (1024 <= OPENCODE_PORT <= 65535): + print(f"Error: Invalid port {OPENCODE_PORT}. Port must be between 1024 and 65535.", file=sys.stderr) + return 3 + + if not (1 <= OPENCODE_TIMEOUT <= 300): + print(f"Error: Invalid timeout {OPENCODE_TIMEOUT}. Timeout must be between 1 and 300 seconds.", file=sys.stderr) + return 3 + + serve_process = None + try: + available_port = find_available_port(OPENCODE_PORT) + if not available_port: + print(f"Error: No available ports found starting from port {OPENCODE_PORT}. " + f"Please check if ports {OPENCODE_PORT}-{OPENCODE_PORT+9} are available.", file=sys.stderr) + return 3 + + if available_port != OPENCODE_PORT: + OPENCODE_PORT = available_port + + OPENCODE_BASE_URL = f'http://127.0.0.1:{OPENCODE_PORT}' + os.environ['OPENCODE_BASE_URL'] = OPENCODE_BASE_URL + + repo_root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip() + + serve_process = subprocess.Popen( + ['opencode', 'serve', '--port', str(OPENCODE_PORT)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=repo_root + ) + + if not wait_for_port(OPENCODE_PORT, timeout=OPENCODE_TIMEOUT): + stdout, stderr = serve_process.communicate(timeout=5) + error_msg = stderr.decode() if stderr else "Unknown error" + print(f"Error: Opencode server failed to start within {OPENCODE_TIMEOUT} seconds.", file=sys.stderr) + print(f"Server error: {error_msg}", file=sys.stderr) + print("\nTroubleshooting:", file=sys.stderr) + print(" 1. Check if opencode is installed: opencode --version", file=sys.stderr) + print(" 2. Verify authentication: opencode auth login", file=sys.stderr) + print(" 3. Increase timeout: export OPENCODE_TIMEOUT=120", file=sys.stderr) + return 3 + + client = Opencode() + + repo_name = os.path.basename(repo_root) + commit_hash = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], text=True).strip() + timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + session_name = f"Commit Review - {repo_name} {commit_hash} - {timestamp}" + + new_session = client.session.create( + extra_body={ + 'name': session_name, + } + ) + + new_session_id = new_session.id + + new_session_chat = client.session.chat(id=new_session_id, parts=[ + {'type': 'text', 'text': """ + Please review the currently staged changes for this commit. Evaluate the changes for: + + - Code quality and best practices + - Potential bugs or issues + - Security concerns + - Performance implications + - Test coverage + - Documentation needs + - Adherence to project conventions + + If there are any critical issues that should prevent this commit, start your response with "-COMMIT REJECTED-" + followed by a clear explanation of the blocking issues. + + If the changes are acceptable, provide constructive feedback and suggestions for improvement. + """} + ], model_id=OPENCODE_MODEL, provider_id=OPENCODE_PROVIDER) + + new_session_chat_messages = client.session.messages(id=new_session_id) + + def get_last_assistant_message(messages: Iterable) -> Optional[str]: + last_text: Optional[str] = None + for item in messages: + info = getattr(item, 'info', None) + role = getattr(info, 'role', None) + if role == 'assistant': + parts = getattr(item, 'parts', []) or [] + text_segments = [] + for part in parts: + if getattr(part, 'type', None) == 'text': + text_segments.append((getattr(part, 'text', '') or '').strip()) + combined = '\n'.join(s for s in text_segments if s) + if combined: + last_text = combined + return last_text + + def render_markdown_terminal(message: Optional[str]) -> None: + title = 'AI Commit Review' + if Console and Markdown and Panel: + console = Console() + if not message: + console.print(Panel('_(no assistant message found)_', title=title, border_style='red')) + return + md = Markdown(message) + console.print(Panel(md, title=title, border_style='cyan')) + else: + if not message: + print(f"{title}\n(no assistant message found)") + else: + print(f"{title}\n\n{message}") + + last_msg = get_last_assistant_message(new_session_chat_messages) + + is_rejected = last_msg and '-COMMIT REJECTED-' in last_msg + if is_rejected and last_msg: + last_msg = last_msg.replace('-COMMIT REJECTED-', '').strip() + + render_markdown_terminal(last_msg) + + continue_command = f"opencode --session {new_session_id}" + + if is_rejected: + fix_command = f"opencode run \"resolve these issues\" --session {new_session_id}" + + if Console and Panel: + console = Console() + console.print('\n') + console.print(Panel( + f"[bold yellow]To automatically resolve these issues:[/bold yellow]\n" + f"[bold green]{fix_command}[/bold green]\n\n" + f"[bold yellow]To continue chatting with this session:[/bold yellow]\n" + f"[bold cyan]{continue_command}[/bold cyan]", + border_style='bold red', + padding=(1, 2) + )) + else: + print('\n' + '='*80) + print('To automatically resolve these issues:') + print(f"{fix_command}\n") + print('To continue chatting with this session:') + print(f"{continue_command}") + print('='*80) + + return 1 + else: + improve_command = f"opencode run \"apply the suggested improvements\" --session {new_session_id}" + + if Console and Panel: + console = Console() + console.print('\n') + console.print(Panel( + f"[bold yellow]To apply suggested improvements:[/bold yellow]\n" + f"[bold green]{improve_command}[/bold green]\n\n" + f"[bold yellow]To continue chatting with this session:[/bold yellow]\n" + f"[bold cyan]{continue_command}[/bold cyan]", + border_style='green', + padding=(1, 2) + )) + else: + print('\n' + '='*80) + print('To apply suggested improvements:') + print(f"{improve_command}\n") + print('To continue chatting with this session:') + print(f"{continue_command}") + print('='*80) + + return 0 + finally: + if serve_process: + serve_process.terminate() + try: + serve_process.wait(timeout=5) + except subprocess.TimeoutExpired: + serve_process.kill() + serve_process.wait() + +if __name__ == '__main__': + sys.exit(main()) diff --git a/pyproject.toml b/pyproject.toml index 7f6d77e..3cf6976 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,8 @@ dependencies = [ "detect-secrets>=1.4.0", "bandit>=1.7.0", "safety>=2.0.0", + "opencode-ai>=0.1.0a36", + "rich>=13.7.0", ] [project.optional-dependencies] @@ -52,6 +54,7 @@ Issues = "https://github.com/TriaFed/pre-commit-library/issues" Documentation = "https://github.com/TriaFed/pre-commit-library#readme" [project.scripts] +ai_commit_check = "hooks.ai_commit_check:main" ansible_security_scan = "hooks.ansible_security_scan:main" check_license = "hooks.check_license:main" check_xml = "hooks.check_xml:main" From 1ead07290720fd7ee990549c006b3556ffde9b59 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Tue, 4 Nov 2025 12:41:38 -0500 Subject: [PATCH 02/22] docs(opencode): adding documentation on using opencode and created agents.md --- AGENTS.md | 368 +++++++++++++++++++++++++++++++++++++++ CONFIGURATION_GUIDE.md | 106 ++++++++++- DEPENDENCIES.md | 26 +++ README.md | 84 +++++++++ examples/opencode.jsonc | 31 ++++ hooks/ai_commit_check.py | 36 +++- 6 files changed, 648 insertions(+), 3 deletions(-) create mode 100644 AGENTS.md create mode 100644 examples/opencode.jsonc diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..bfd244c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,368 @@ +# AI Agent Instructions for Pre-Commit Hooks Library + +This file provides context for AI assistants working on this repository. + +## Repository Purpose + +This is a **pre-commit hooks library** for validating and securing code generated by GenAI tools (GitHub Copilot, ChatGPT, etc.). The library provides hooks that detect: +- Hardcoded credentials and secrets +- Security vulnerabilities +- Code quality issues +- Infrastructure misconfigurations + +## Code Architecture + +### Directory Structure +- `hooks/` - Hook implementations (Python scripts and shell scripts) +- `examples/` - Example pre-commit configurations for different project types +- `.pre-commit-hooks.yaml` - Hook registry defining all available hooks +- `README.md` - Main documentation +- `CONFIGURATION_GUIDE.md` - Detailed configuration guide +- `DEPENDENCIES.md` - Installation instructions + +### Hook Types + +**Python Hooks** (`hooks/*.py`): +- Security scanners: `detect_hardcoded_credentials.py`, `detect_hardcoded_urls.py`, `genai_security_check.py` +- Language-specific scanners: `dotnet_security_scan.py`, `ansible_security_scan.py` +- File validators: `check_xml.py`, `check_license.py` +- AI-powered validation: `ai_commit_check.py` + +**Shell Script Hooks** (`hooks/*.sh`): +- Language tooling wrappers: `eslint.sh`, `prettier.sh`, `go_fmt.sh` +- Security tools: `semgrep.sh`, `truffhog.sh` +- Infrastructure validation: `terraform_validate.sh`, `cfn_validate.sh` + +## Coding Standards + +### Python Hooks +```python +#!/usr/bin/env python3 +""" +Brief description of what this hook checks. +""" + +import sys +import argparse +from typing import List, Tuple + +def main(filenames: List[str]) -> int: + """Main entry point.""" + # Hook logic here + return 0 # Pass: 0, Fail: 1, Tool missing: 3 + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) +``` + +**Standards**: +- Use type hints for function signatures +- Exit codes: 0 (pass), 1 (fail), 3 (tool missing/error) +- Handle file encoding issues gracefully (UTF-8, ASCII) +- Support inline suppression comments where appropriate +- Provide clear, actionable error messages + +### Shell Script Hooks +```bash +#!/usr/bin/env bash + +# Check if required tool is available +if ! command -v tool_name &> /dev/null; then + echo "Error: tool_name is not installed" >&2 + exit 3 +fi + +# Run the tool +tool_name "$@" +exit $? +``` + +**Standards**: +- Use `#!/usr/bin/env bash` for portability +- Check tool availability before running +- Quote variables to handle paths with spaces +- Exit with appropriate codes + +### Hook Registry (`.pre-commit-hooks.yaml`) +```yaml +- id: hook_name + name: Human-readable name + entry: hook_script_or_command + language: python|script|system + types: [python] # or files: \.py$ + require_serial: false +``` + +## Security Principles + +### For Hook Implementations +1. **No false negatives**: Better to flag false positives than miss real issues +2. **Clear suppression mechanism**: Users should be able to suppress false positives with inline comments +3. **Context awareness**: Be more lenient in test files, strict in production code +4. **Performance**: Hooks should run quickly, even on large codebases + +### For This Repository +1. **No real secrets**: All examples must use obviously fake credentials +2. **Secure defaults**: Default configurations should be secure +3. **Safe examples**: Example code should demonstrate best practices + +### For AI Commit Check Hook +The `ai_commit_check` hook has special security requirements: +- Requires `opencode.jsonc` configuration file +- Denies dangerous operations: `webfetch`, cloud CLIs, `curl`, `wget`, `terraform` +- Only allows read-only git commands and safe tools +- Blocks commits with `-COMMIT REJECTED-` prefix + +## Testing Approach + +### Before Committing Hook Changes +1. **Test with multiple file types**: Ensure hook works across languages +2. **Test false positives**: Verify legitimate code isn't flagged incorrectly +3. **Test edge cases**: Empty files, binary files, large files, special characters +4. **Test performance**: Run on large repositories +5. **Cross-platform**: Test on Linux, macOS, and Windows if possible + +### Common Test Cases +```bash +# Test pass case +echo 'const x = 1;' > test.js && python hooks/your_hook.py test.js + +# Test fail case +echo 'password = "secret123"' > test.py && python hooks/your_hook.py test.py + +# Test suppression +echo 'password = "test" # pragma: allowlist secret' > test.py && python hooks/your_hook.py test.py +``` + +## Common Patterns + +### Inline Suppression Support +Hooks should support these suppression comments: +- `# pragma: allowlist secret` - For credentials/secrets (Python) +- `// pragma: allowlist secret` - For credentials/secrets (JavaScript/Java) +- `# genai:ignore` - For GenAI security checks + +### Safe Context Detection +Be more lenient in these contexts: +```python +SAFE_CONTEXTS = { + 'test', 'spec', '__tests__', '.test.', '.spec.', # Test files + 'example', 'demo', 'sample', # Examples + 'docker', 'compose', # Docker configs + 'dev', 'development', 'local', # Dev configs +} +``` + +### Pattern Matching Best Practices +```python +# Good: Specific pattern with context +r'password\s*[:=]\s*["\'][^"\']{3,}["\']' + +# Bad: Too broad, many false positives +r'password.*=.*' + +# Good: Negative lookbehind to exclude compound words +r'(? opencode.jsonc < Date: Tue, 4 Nov 2025 12:59:42 -0500 Subject: [PATCH 03/22] fix(opencode): incorporating prompt from pr review agent --- hooks/ai_commit_check.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index 665c086..ae34630 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -144,20 +144,32 @@ def main(): new_session_chat = client.session.chat(id=new_session_id, parts=[ {'type': 'text', 'text': """ - Please review the currently staged changes for this commit. Evaluate the changes for: + Review the staged changes for this commit, focusing on these critical issues: - - Code quality and best practices - - Potential bugs or issues - - Security concerns - - Performance implications - - Test coverage - - Documentation needs - - Adherence to project conventions + **CRITICAL ISSUES TO CHECK:** + 1. **Unused Variables/Imports/Code**: Variables declared but never used, unused imports, dead code + 2. **Logic Bugs**: Incorrect implementations, missing null checks, wrong conditions, off-by-one errors + 3. **Security Issues**: Input validation missing, potential injection vulnerabilities, hardcoded credentials + 4. **Performance Problems**: Inefficient patterns, potential memory leaks, unnecessary computations + 5. **Type/Syntax Issues**: Incorrect types, missing error handling, type mismatches + 6. **Configuration Issues**: Problems in config files (JSON, YAML, Jenkinsfile, package.json, etc.) + 7. **Build/CI Issues**: Problems with build scripts, pipelines, dependencies - If there are any critical issues that should prevent this commit, start your response with "-COMMIT REJECTED-" - followed by a clear explanation of the blocking issues. + **REVIEW INSTRUCTIONS:** + - Analyze BOTH the diff changes AND the complete file contents provided + - Focus ONLY on code that has actual problems + - Pay special attention to variables that are declared but never referenced + - Look for imports that aren't used anywhere in the file + - Check for functions or code blocks that serve no purpose + - Review configuration files thoroughly for syntax and logic issues + - Consider how changes in one file might affect other files + - If you find unused variables, clearly state which variables are unused and suggest removing them - If the changes are acceptable, provide constructive feedback and suggestions for improvement. + **RESPONSE FORMAT:** + - If you find critical issues that should block the commit, start your response with "-COMMIT REJECTED-" + followed by a clear explanation of the blocking issues with specific file names and line numbers + - If the code has no significant issues, respond with exactly: "āœ… **Code looks good!** No significant issues found." + - If you have suggestions but no blockers, provide constructive feedback with specific suggestions """} ], model_id=OPENCODE_MODEL, provider_id=OPENCODE_PROVIDER) From 12cbfbef161a19671bb1cd0401dc3c241485369b Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Tue, 4 Nov 2025 13:14:17 -0500 Subject: [PATCH 04/22] fix(opencode): adjusting hook location --- .pre-commit-hooks.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 909f142..170fcc8 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -115,8 +115,8 @@ - id: ai_commit_check name: AI-powered commit validation - entry: ai_commit_check - language: python + entry: hooks/ai_commit_check.py + language: script pass_filenames: false stages: [pre-commit] require_serial: true From 5be00901f2e761de24f46ce97bc8566703e9ee49 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Tue, 4 Nov 2025 13:17:15 -0500 Subject: [PATCH 05/22] fix(opencode): adjusting hook location --- .pre-commit-hooks.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 170fcc8..fcdb5b7 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -115,11 +115,12 @@ - id: ai_commit_check name: AI-powered commit validation - entry: hooks/ai_commit_check.py - language: script + entry: ai_commit_check + language: python pass_filenames: false stages: [pre-commit] require_serial: true + additional_dependencies: ['opencode-ai>=0.1.0a36', 'rich>=13.7.0'] # ============================================================================= # SECURITY SCANNING HOOKS From c9e1326c048cca0e4bc2c4de01d1866d5b53fab8 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Fri, 14 Nov 2025 16:44:59 -0500 Subject: [PATCH 06/22] adding pre-push instructions --- .pre-commit-hooks.yaml | 2 +- AGENTS.md | 25 ++++++++++-- CONFIGURATION_GUIDE.md | 85 ++++++++++++++++++++++++++++++++++++++-- README.md | 38 ++++++++++++++++-- examples/opencode.jsonc | 1 + hooks/ai_commit_check.py | 59 ++++++++++++++++++++++++++-- 6 files changed, 196 insertions(+), 14 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index fcdb5b7..533677d 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -118,7 +118,7 @@ entry: ai_commit_check language: python pass_filenames: false - stages: [pre-commit] + stages: [pre-commit, pre-push] require_serial: true additional_dependencies: ['opencode-ai>=0.1.0a36', 'rich>=13.7.0'] diff --git a/AGENTS.md b/AGENTS.md index bfd244c..23ede8d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,7 +26,7 @@ This is a **pre-commit hooks library** for validating and securing code generate - Security scanners: `detect_hardcoded_credentials.py`, `detect_hardcoded_urls.py`, `genai_security_check.py` - Language-specific scanners: `dotnet_security_scan.py`, `ansible_security_scan.py` - File validators: `check_xml.py`, `check_license.py` -- AI-powered validation: `ai_commit_check.py` +- AI-powered validation: `ai_commit_check.py` (supports both pre-commit and pre-push modes) **Shell Script Hooks** (`hooks/*.sh`): - Language tooling wrappers: `eslint.sh`, `prettier.sh`, `go_fmt.sh` @@ -107,11 +107,30 @@ exit $? 3. **Safe examples**: Example code should demonstrate best practices ### For AI Commit Check Hook -The `ai_commit_check` hook has special security requirements: +The `ai_commit_check` hook has special security requirements and dual-mode operation: +- **Dual Mode Support**: Automatically detects pre-commit (staged changes) or pre-push (branch comparison) mode +- **Pre-commit mode**: Reviews staged changes using `git diff --cached` +- **Pre-push mode**: Compares current branch to `origin/main` or `origin/master` using `git diff origin/{branch}...HEAD` - Requires `opencode.jsonc` configuration file - Denies dangerous operations: `webfetch`, cloud CLIs, `curl`, `wget`, `terraform` - Only allows read-only git commands and safe tools -- Blocks commits with `-COMMIT REJECTED-` prefix +- Blocks commits/pushes with `-COMMIT REJECTED-` prefix + +**Important Setup Note**: When using pre-push mode, users must run: +```bash +pre-commit install --hook-type pre-push +``` + +**Configuration Example**: +```yaml +repos: + - repo: https://github.com/MattDonnellySoftrams/pre-commit-library + rev: 5be00901f2e761de24f46ce97bc8566703e9ee49 + hooks: + - id: ai_commit_check + stages: + - pre-push +``` ## Testing Approach diff --git a/CONFIGURATION_GUIDE.md b/CONFIGURATION_GUIDE.md index 91e576e..ea7b9d4 100644 --- a/CONFIGURATION_GUIDE.md +++ b/CONFIGURATION_GUIDE.md @@ -10,9 +10,67 @@ Use the `examples/practical-security.yaml` configuration for a balanced approach ### ai_commit_check Configuration -**Problem**: AI validation may be too slow or strict for all commits +The `ai_commit_check` hook intelligently adapts to two modes: +- **Pre-commit mode**: Reviews staged changes before committing +- **Pre-push mode**: Reviews all branch changes compared to `origin/main` or `origin/master` -**Solution**: Configure via environment variables and `opencode.jsonc` +**Problem**: AI validation may be too slow for every commit, or you want comprehensive branch review before pushing + +**Solution**: Configure for pre-commit (manual/automatic) or pre-push stages + +#### Mode Selection + +The hook automatically determines which mode to use: +1. **Pre-commit mode**: If staged changes exist (`git diff --cached`), reviews only those changes +2. **Pre-push mode**: If no staged changes, assumes pre-push and compares current branch to `origin/main` or `origin/master` + +#### Configuration Options + +**Option 1: Manual pre-commit (recommended for frequent commits)** +```yaml +repos: + - repo: https://github.com/TriaFed/pre-commit-library + rev: v1.1.7 + hooks: + - id: ai_commit_check + stages: [manual] +``` + +Run with: `pre-commit run --hook-stage manual ai_commit_check` + +**Option 2: Automatic pre-push (recommended for comprehensive review)** +```yaml +repos: + - repo: https://github.com/MattDonnellySoftrams/pre-commit-library + rev: 5be00901f2e761de24f46ce97bc8566703e9ee49 + hooks: + - id: ai_commit_check + stages: + - pre-push +``` + +**IMPORTANT**: For pre-push mode, you must install the pre-push hooks: +```bash +pre-commit install --hook-type pre-push +``` + +This runs automatically on `git push` and reviews all changes in your branch. + +**Option 3: Both modes** +```yaml +repos: + - repo: https://github.com/TriaFed/pre-commit-library + rev: v1.1.7 + hooks: + - id: ai_commit_check + stages: [pre-commit, pre-push] +``` + +Install both hook types: +```bash +pre-commit install # For pre-commit +pre-commit install --hook-type pre-push # For pre-push +``` #### Required Setup @@ -128,15 +186,34 @@ Reference instruction files in `opencode.jsonc`: **Usage in .pre-commit-config.yaml:** ```yaml +# Manual pre-commit mode - id: ai_commit_check - # Only run manually or in CI, not on every commit stages: [manual] + +# Automatic pre-push mode +- id: ai_commit_check + stages: [pre-push] ``` -Or disable for quick commits: +**Running the hook:** +```bash +# Manual pre-commit +pre-commit run --hook-stage manual ai_commit_check + +# Pre-push (runs automatically on git push) +git push + +# Or run pre-push manually +pre-commit run --hook-stage pre-push ai_commit_check +``` + +Or skip for urgent commits: ```bash # Skip AI validation for urgent commits git commit --no-verify -m "Quick fix" + +# Skip pre-push validation +git push --no-verify ``` ## Common False Positive Issues and Solutions diff --git a/README.md b/README.md index 0417ff5..c26c927 100644 --- a/README.md +++ b/README.md @@ -478,8 +478,12 @@ repos: ### AI-Powered Commit Validation -The `ai_commit_check` hook uses Opencode AI to review your staged changes before committing: +The `ai_commit_check` hook uses Opencode AI to review your code changes. It supports two modes: +- **Pre-commit mode**: Reviews staged changes before committing +- **Pre-push mode**: Reviews all branch changes against `origin/main` or `origin/master` before pushing + +**Pre-commit Configuration:** ```yaml repos: - repo: https://github.com/TriaFed/pre-commit-library @@ -489,6 +493,17 @@ repos: stages: [manual] # Run manually to avoid slowing down every commit ``` +**Pre-push Configuration (Recommended for comprehensive review):** +```yaml +repos: + - repo: https://github.com/MattDonnellySoftrams/pre-commit-library + rev: 5be00901f2e761de24f46ce97bc8566703e9ee49 + hooks: + - id: ai_commit_check + stages: + - pre-push +``` + **Setup:** ```bash # Install opencode @@ -514,6 +529,12 @@ curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-libr # Commit the configuration files git add opencode.jsonc git commit -m "Add AI commit check configuration" + +# Install pre-commit hooks +pre-commit install + +# IMPORTANT: For pre-push mode, also run: +pre-commit install --hook-type pre-push ``` **āš ļø Security Requirement:** @@ -539,18 +560,29 @@ export OPENCODE_TIMEOUT=90 # Default: 90 seconds **Usage:** ```bash -# Run manually +# Pre-commit mode (review staged changes) pre-commit run --hook-stage manual ai_commit_check +# Pre-push mode (review branch changes) +# This runs automatically on git push if configured with stages: [pre-push] +git push + +# Or run manually +pre-commit run --hook-stage pre-push ai_commit_check + # Or configure to run automatically on every commit # (Remove the stages: [manual] line from config) ``` +The hook automatically detects its mode: +- **Pre-commit**: If there are staged changes, reviews only those changes +- **Pre-push**: If no staged changes, compares current branch against `origin/main` or `origin/master` + The hook will: - Review code quality and best practices - Identify potential bugs or security concerns - Suggest improvements -- Block commits with critical issues (starting with "-COMMIT REJECTED-") +- Block commits/pushes with critical issues (starting with "-COMMIT REJECTED-") - Provide commands to continue the AI session or auto-fix issues **Configuring Permissions (Optional):** diff --git a/examples/opencode.jsonc b/examples/opencode.jsonc index 3dc50a5..6a150b5 100644 --- a/examples/opencode.jsonc +++ b/examples/opencode.jsonc @@ -1,5 +1,6 @@ { "$schema": "https://opencode.ai/config.json", + "share": "disabled", "permission": { "edit": "allow", "bash": { diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index ae34630..737e251 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -142,10 +142,63 @@ def main(): new_session_id = new_session.id - new_session_chat = client.session.chat(id=new_session_id, parts=[ - {'type': 'text', 'text': """ - Review the staged changes for this commit, focusing on these critical issues: + # Determine review mode: pre-commit (staged changes) or pre-push (branch comparison) + try: + staged_diff = subprocess.check_output( + ['git', 'diff', '--cached', '--name-only'], + text=True, + cwd=repo_root + ).strip() + has_staged_changes = bool(staged_diff) + except subprocess.CalledProcessError: + has_staged_changes = False + # Determine which mode and construct appropriate prompt + if has_staged_changes: + # Pre-commit mode: review staged changes + review_mode = "pre-commit" + review_target = "the staged changes for this commit" + review_context = """ + **REVIEW MODE:** Pre-commit (staged changes) + + Use `git diff --cached` to see the staged changes that are about to be committed. + """ + else: + # Pre-push mode: compare current branch to origin/main or origin/master + review_mode = "pre-push" + + # Determine the default branch (main or master) + default_branch = None + for branch in ['main', 'master']: + try: + subprocess.check_output( + ['git', 'rev-parse', '--verify', f'origin/{branch}'], + stderr=subprocess.DEVNULL, + text=True, + cwd=repo_root + ) + default_branch = branch + break + except subprocess.CalledProcessError: + continue + + if not default_branch: + print("Error: Could not find origin/main or origin/master branch for comparison.", file=sys.stderr) + print("Please ensure you have a remote tracking branch set up.", file=sys.stderr) + return 3 + + review_target = f"all changes in the current branch compared to origin/{default_branch}" + review_context = f""" + **REVIEW MODE:** Pre-push (branch comparison) + + Use `git diff origin/{default_branch}...HEAD` to see all changes in the current branch + that differ from origin/{default_branch}. Review ALL commits and changes since branching. + """ + + new_session_chat = client.session.chat(id=new_session_id, parts=[ + {'type': 'text', 'text': f""" + Review {review_target}, focusing on these critical issues: + {review_context} **CRITICAL ISSUES TO CHECK:** 1. **Unused Variables/Imports/Code**: Variables declared but never used, unused imports, dead code 2. **Logic Bugs**: Incorrect implementations, missing null checks, wrong conditions, off-by-one errors From d8d9d4b7c8d147c7e85ead1fdb7196d531676888 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Fri, 14 Nov 2025 16:57:35 -0500 Subject: [PATCH 07/22] preparing for merge with origin repo --- AGENTS.md | 4 ++-- CONFIGURATION_GUIDE.md | 4 ++-- README.md | 4 ++-- hooks/ai_commit_check.py | 28 ++++++++++++++++++++-------- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 23ede8d..2f93fcf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -124,8 +124,8 @@ pre-commit install --hook-type pre-push **Configuration Example**: ```yaml repos: - - repo: https://github.com/MattDonnellySoftrams/pre-commit-library - rev: 5be00901f2e761de24f46ce97bc8566703e9ee49 + - repo: https://github.com/TriaFed/pre-commit-library + rev: # Replace with latest release version hooks: - id: ai_commit_check stages: diff --git a/CONFIGURATION_GUIDE.md b/CONFIGURATION_GUIDE.md index ea7b9d4..11cdf14 100644 --- a/CONFIGURATION_GUIDE.md +++ b/CONFIGURATION_GUIDE.md @@ -41,8 +41,8 @@ Run with: `pre-commit run --hook-stage manual ai_commit_check` **Option 2: Automatic pre-push (recommended for comprehensive review)** ```yaml repos: - - repo: https://github.com/MattDonnellySoftrams/pre-commit-library - rev: 5be00901f2e761de24f46ce97bc8566703e9ee49 + - repo: https://github.com/TriaFed/pre-commit-library + rev: # Replace with latest release version hooks: - id: ai_commit_check stages: diff --git a/README.md b/README.md index c26c927..87204d0 100644 --- a/README.md +++ b/README.md @@ -496,8 +496,8 @@ repos: **Pre-push Configuration (Recommended for comprehensive review):** ```yaml repos: - - repo: https://github.com/MattDonnellySoftrams/pre-commit-library - rev: 5be00901f2e761de24f46ce97bc8566703e9ee49 + - repo: https://github.com/TriaFed/pre-commit-library + rev: # Replace with latest release version hooks: - id: ai_commit_check stages: diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index 737e251..99c0ee6 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -109,12 +109,24 @@ def main(): OPENCODE_BASE_URL = f'http://127.0.0.1:{OPENCODE_PORT}' os.environ['OPENCODE_BASE_URL'] = OPENCODE_BASE_URL - serve_process = subprocess.Popen( - ['opencode', 'serve', '--port', str(OPENCODE_PORT)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - cwd=repo_root - ) + try: + serve_process = subprocess.Popen( + ['opencode', 'serve', '--port', str(OPENCODE_PORT)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=repo_root + ) + except FileNotFoundError: + print("Error: opencode is not installed.", file=sys.stderr) + print("", file=sys.stderr) + print("To install opencode:", file=sys.stderr) + print(" Visit: https://opencode.ai", file=sys.stderr) + print("", file=sys.stderr) + print("After installation, authenticate with:", file=sys.stderr) + print(" opencode auth login", file=sys.stderr) + print(" Select: GitHub Public", file=sys.stderr) + print("", file=sys.stderr) + return 3 if not wait_for_port(OPENCODE_PORT, timeout=OPENCODE_TIMEOUT): stdout, stderr = serve_process.communicate(timeout=5) @@ -122,8 +134,8 @@ def main(): print(f"Error: Opencode server failed to start within {OPENCODE_TIMEOUT} seconds.", file=sys.stderr) print(f"Server error: {error_msg}", file=sys.stderr) print("\nTroubleshooting:", file=sys.stderr) - print(" 1. Check if opencode is installed: opencode --version", file=sys.stderr) - print(" 2. Verify authentication: opencode auth login", file=sys.stderr) + print(" 1. Install opencode: Visit https://opencode.ai", file=sys.stderr) + print(" 2. Authenticate: opencode auth login (select GitHub Public)", file=sys.stderr) print(" 3. Increase timeout: export OPENCODE_TIMEOUT=120", file=sys.stderr) return 3 From 26dd7194e437dcc29b1de18fa05e93b199aeef47 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Fri, 14 Nov 2025 17:05:45 -0500 Subject: [PATCH 08/22] improving readme and ai commit review output --- AGENTS.md | 74 +++++++++--------- CONFIGURATION_GUIDE.md | 165 ++++++--------------------------------- hooks/ai_commit_check.py | 26 +++--- 3 files changed, 75 insertions(+), 190 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 2f93fcf..79d1c09 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -108,29 +108,21 @@ exit $? ### For AI Commit Check Hook The `ai_commit_check` hook has special security requirements and dual-mode operation: -- **Dual Mode Support**: Automatically detects pre-commit (staged changes) or pre-push (branch comparison) mode + +**Technical Implementation:** +- **Mode Detection**: Checks `git diff --cached` for staged changes to determine mode - **Pre-commit mode**: Reviews staged changes using `git diff --cached` - **Pre-push mode**: Compares current branch to `origin/main` or `origin/master` using `git diff origin/{branch}...HEAD` -- Requires `opencode.jsonc` configuration file -- Denies dangerous operations: `webfetch`, cloud CLIs, `curl`, `wget`, `terraform` -- Only allows read-only git commands and safe tools -- Blocks commits/pushes with `-COMMIT REJECTED-` prefix +- **Security**: Requires `opencode.jsonc` configuration file with denied dangerous operations +- **Exit behavior**: Blocks commits/pushes with `-COMMIT REJECTED-` prefix in response -**Important Setup Note**: When using pre-push mode, users must run: -```bash -pre-commit install --hook-type pre-push -``` +**Key Implementation Details:** +- Exit codes: 0 (pass), 1 (fail/rejected), 3 (tool missing/error) +- Denies: `webfetch`, cloud CLIs (`aws`, `az`, `gcloud`), `curl`, `wget`, `terraform` +- Starts temporary opencode server on available port (default: 61164) +- Creates AI session with commit context and security review prompt -**Configuration Example**: -```yaml -repos: - - repo: https://github.com/TriaFed/pre-commit-library - rev: # Replace with latest release version - hooks: - - id: ai_commit_check - stages: - - pre-push -``` +For user-facing configuration and setup instructions, see CONFIGURATION_GUIDE.md and README.md. ## Testing Approach @@ -207,7 +199,8 @@ r'key\s*[:=]\s*["\'].*["\']' ## Environment Variables -Hooks respect these environment variables: +Hooks respect these environment variables (see CONFIGURATION_GUIDE.md for user documentation): + ```bash # npm audit NPM_AUDIT_LEVEL=high # low, moderate, high, critical @@ -227,25 +220,28 @@ OPENCODE_TIMEOUT=90 ## Common False Positives -### Hardcoded Credentials -- Test fixtures with fake credentials -- ORM configuration keys (e.g., `foreignKey`, `sourceKey`) -- Variable names containing "key" or "token" -- Documentation examples - -### SQL Injection -- Parameterized queries (`:param`, `$1`, `?`) -- ORM query builders -- String templates for query parts (not values) - -### Path Traversal -- Relative imports (`../utils`, `../../config`) -- Framework conventions (e.g., Next.js `@/components`) - -### Information Disclosure -- `console.error()` without sensitive data -- Structured logging without secrets -- Error messages with sanitized data +When implementing hooks, be aware of these common false positive patterns. For user-facing guidance on handling false positives, see CONFIGURATION_GUIDE.md. + +### Technical Patterns to Avoid Flagging + +**Hardcoded Credentials:** +- ORM configuration keys (e.g., `foreignKey`, `sourceKey`, `primaryKey`) +- Variable names containing "key" or "token" without actual secrets +- Test fixtures with obviously fake credentials + +**SQL Injection:** +- Parameterized query placeholders (`:param`, `$1`, `?`, `@param`) +- ORM query builders (Sequelize, TypeORM, Entity Framework) +- String concatenation for table/column names (not values) + +**Path Traversal:** +- Relative imports (`../utils`, `../../config`, `@/components`) +- Framework-specific conventions (Next.js, Angular path aliases) + +**Information Disclosure:** +- `console.error()` or logging without sensitive keywords +- Structured logging frameworks +- Error messages with sanitized/generic data ## Development Workflow diff --git a/CONFIGURATION_GUIDE.md b/CONFIGURATION_GUIDE.md index 11cdf14..b718fe1 100644 --- a/CONFIGURATION_GUIDE.md +++ b/CONFIGURATION_GUIDE.md @@ -16,13 +16,7 @@ The `ai_commit_check` hook intelligently adapts to two modes: **Problem**: AI validation may be too slow for every commit, or you want comprehensive branch review before pushing -**Solution**: Configure for pre-commit (manual/automatic) or pre-push stages - -#### Mode Selection - -The hook automatically determines which mode to use: -1. **Pre-commit mode**: If staged changes exist (`git diff --cached`), reviews only those changes -2. **Pre-push mode**: If no staged changes, assumes pre-push and compares current branch to `origin/main` or `origin/master` +**Solution**: Choose the stage that fits your workflow #### Configuration Options @@ -45,59 +39,24 @@ repos: rev: # Replace with latest release version hooks: - id: ai_commit_check - stages: - - pre-push + stages: [pre-push] ``` -**IMPORTANT**: For pre-push mode, you must install the pre-push hooks: +**IMPORTANT**: For pre-push mode, install the pre-push hooks: ```bash pre-commit install --hook-type pre-push ``` This runs automatically on `git push` and reviews all changes in your branch. -**Option 3: Both modes** -```yaml -repos: - - repo: https://github.com/TriaFed/pre-commit-library - rev: v1.1.7 - hooks: - - id: ai_commit_check - stages: [pre-commit, pre-push] -``` - -Install both hook types: -```bash -pre-commit install # For pre-commit -pre-commit install --hook-type pre-push # For pre-push -``` - #### Required Setup -**āš ļø Security Requirement:** You must create an `opencode.jsonc` file in your repository root before using this hook. +**āš ļø Security Requirement:** You must create an `opencode.jsonc` file in your repository root. ```bash # Download the recommended configuration curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/examples/opencode.jsonc -# Or create minimal security config -cat > opencode.jsonc < None: console = Console() console.print('\n') console.print(Panel( - f"[bold yellow]To automatically resolve these issues:[/bold yellow]\n" - f"[bold green]{fix_command}[/bold green]\n\n" - f"[bold yellow]To continue chatting with this session:[/bold yellow]\n" - f"[bold cyan]{continue_command}[/bold cyan]", + f"[bold red]Critical issues found that block this commit/push.[/bold red]", border_style='bold red', padding=(1, 2) )) + console.print('\n[bold yellow]To automatically resolve these issues:[/bold yellow]') + console.print(f'[bold green]{fix_command}[/bold green]\n') + console.print('[bold yellow]To continue chatting with this session:[/bold yellow]') + console.print(f'[bold cyan]{continue_command}[/bold cyan]') else: print('\n' + '='*80) - print('To automatically resolve these issues:') + print('COMMIT REJECTED - Critical issues found') + print('='*80) + print('\nTo automatically resolve these issues:') print(f"{fix_command}\n") print('To continue chatting with this session:') print(f"{continue_command}") @@ -311,16 +314,19 @@ def render_markdown_terminal(message: Optional[str]) -> None: console = Console() console.print('\n') console.print(Panel( - f"[bold yellow]To apply suggested improvements:[/bold yellow]\n" - f"[bold green]{improve_command}[/bold green]\n\n" - f"[bold yellow]To continue chatting with this session:[/bold yellow]\n" - f"[bold cyan]{continue_command}[/bold cyan]", + f"[bold green]āœ“ No blocking issues found.[/bold green]", border_style='green', padding=(1, 2) )) + console.print('\n[bold yellow]To apply suggested improvements:[/bold yellow]') + console.print(f'[bold green]{improve_command}[/bold green]\n') + console.print('[bold yellow]To continue chatting with this session:[/bold yellow]') + console.print(f'[bold cyan]{continue_command}[/bold cyan]') else: print('\n' + '='*80) - print('To apply suggested improvements:') + print('No blocking issues found') + print('='*80) + print('\nTo apply suggested improvements:') print(f"{improve_command}\n") print('To continue chatting with this session:') print(f"{continue_command}") From f17ede35e7acf5314c14c56158647bd01a4e3432 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Mon, 1 Dec 2025 12:38:03 -0500 Subject: [PATCH 09/22] Adding Bedrock support and setting AI hook to opt-in --- .pre-commit-hooks.yaml | 2 +- AGENTS.md | 12 ++++++ CONFIGURATION_GUIDE.md | 73 +++++++++++++++++++++++++++++++-- README.md | 87 +++++++++++++++++++++++++++++++++++++--- examples/opencode.jsonc | 49 +++++++++++++++++++++- hooks/ai_commit_check.py | 51 ++++++++++++++++++++++- 6 files changed, 263 insertions(+), 11 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 533677d..d8defba 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -118,7 +118,7 @@ entry: ai_commit_check language: python pass_filenames: false - stages: [pre-commit, pre-push] + stages: [manual] require_serial: true additional_dependencies: ['opencode-ai>=0.1.0a36', 'rich>=13.7.0'] diff --git a/AGENTS.md b/AGENTS.md index 79d1c09..c1eecda 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -110,17 +110,29 @@ exit $? The `ai_commit_check` hook has special security requirements and dual-mode operation: **Technical Implementation:** +- **Disabled by default**: Hook uses `stages: [manual]` in `.pre-commit-hooks.yaml` to require explicit opt-in +- **User opt-in required**: Users must add `stages: [pre-commit]` or `stages: [pre-push]` to their config to enable - **Mode Detection**: Checks `git diff --cached` for staged changes to determine mode - **Pre-commit mode**: Reviews staged changes using `git diff --cached` - **Pre-push mode**: Compares current branch to `origin/main` or `origin/master` using `git diff origin/{branch}...HEAD` - **Security**: Requires `opencode.jsonc` configuration file with denied dangerous operations +- **Model Restrictions**: Only allows GitHub Copilot and Amazon Bedrock with Claude Sonnet 4.5 - **Exit behavior**: Blocks commits/pushes with `-COMMIT REJECTED-` prefix in response +**Supported Providers:** +- **GitHub Copilot** (default): `github-copilot/claude-sonnet-4-5` +- **Amazon Bedrock**: `bedrock/anthropic.claude-sonnet-4-5-v2:0` + - Required region: `us-east-1` or `us-gov-*` + - Enforced via `OPENCODE_BEDROCK_REGION` environment variable + - Hook exits with error code 3 if region is invalid or not us-east-1/us-gov-* + **Key Implementation Details:** - Exit codes: 0 (pass), 1 (fail/rejected), 3 (tool missing/error) - Denies: `webfetch`, cloud CLIs (`aws`, `az`, `gcloud`), `curl`, `wget`, `terraform` +- Disables all AI providers except: `github-copilot` and `bedrock` - Starts temporary opencode server on available port (default: 61164) - Creates AI session with commit context and security review prompt +- Sets AWS region environment variables (`AWS_DEFAULT_REGION`, `AWS_REGION`) when using Bedrock For user-facing configuration and setup instructions, see CONFIGURATION_GUIDE.md and README.md. diff --git a/CONFIGURATION_GUIDE.md b/CONFIGURATION_GUIDE.md index 41a79e5..402ec2d 100644 --- a/CONFIGURATION_GUIDE.md +++ b/CONFIGURATION_GUIDE.md @@ -27,9 +27,11 @@ repos: rev: v1.1.7 hooks: - id: ai_commit_check - stages: [manual] + stages: [manual] # Default: disabled, opt-in required ``` +**Note:** The hook is **disabled by default**. This is the default behavior - users must explicitly opt-in. + Run with: `pre-commit run --hook-stage manual ai_commit_check` **Option 2: Automatic pre-push (recommended for comprehensive review)** @@ -70,9 +72,10 @@ See `examples/opencode.jsonc` for the full configuration with security defaults. ```bash export OPENCODE_PORT=61164 # Port for opencode server (default: 61164) -export OPENCODE_MODEL=claude-sonnet-4.5 # AI model to use -export OPENCODE_PROVIDER=github-copilot # Provider: github-copilot, anthropic, etc. +export OPENCODE_MODEL=github-copilot/claude-sonnet-4-5 # AI model to use (default) +export OPENCODE_PROVIDER=github-copilot # Provider: github-copilot, bedrock (default: github-copilot) export OPENCODE_TIMEOUT=90 # Timeout in seconds (default: 90) +export OPENCODE_BEDROCK_REGION=us-east-1 # Required for bedrock provider ``` #### Permissions and Custom Instructions @@ -99,6 +102,70 @@ This creates instruction files with project-specific coding standards that the A | Pre-push (manual) | `pre-commit run --hook-stage pre-push ai_commit_check` | | Skip validation | `git commit --no-verify` or `git push --no-verify` | +#### Amazon Bedrock Provider + +**Problem**: Need to use Amazon Bedrock instead of GitHub Copilot + +**Solution**: Configure Bedrock with required region validation + +```yaml +repos: + - repo: https://github.com/TriaFed/pre-commit-library + rev: v1.2.0 + hooks: + - id: ai_commit_check + stages: [manual] +``` + +**Set environment variables:** +```bash +export OPENCODE_PROVIDER=bedrock +export OPENCODE_MODEL=bedrock/anthropic.claude-sonnet-4-5-v2:0 +export OPENCODE_BEDROCK_REGION=us-east-1 +``` + +**Required AWS Setup:** +```bash +# 1. Configure AWS region (required) +aws configure set region us-east-1 + +# 2. Verify AWS credentials +aws sts get-caller-identity + +# 3. Authenticate with OpenCode +opencode auth login # Select: Amazon Bedrock + +# 4. Test the connection +opencode run "hello world" +``` + +**Allowed Regions**: `us-east-1`, `us-gov-west-1`, `us-gov-east-1` + +**Security Note**: Region validation is enforced for compliance. The hook will exit with an error if an unauthorized region is specified. + +**Example Usage:** +```bash +# Set environment for the session +export OPENCODE_PROVIDER=bedrock +export OPENCODE_BEDROCK_REGION=us-east-1 + +# Run the AI commit check +pre-commit run --hook-stage manual ai_commit_check +``` + +**Troubleshooting:** + +If you see: +``` +Error: Amazon Bedrock region 'eu-west-1' is not allowed. +``` + +Fix with: +```bash +export OPENCODE_BEDROCK_REGION=us-east-1 +aws configure set region us-east-1 +``` + ## Common False Positive Issues and Solutions ### 1. detect-secrets and hardcoded-credentials diff --git a/README.md b/README.md index 15d9907..48050e4 100644 --- a/README.md +++ b/README.md @@ -490,9 +490,13 @@ repos: rev: v1.1.7 hooks: - id: ai_commit_check - stages: [manual] # Run manually to avoid slowing down every commit + stages: [manual] # Default: Run manually (opt-in) + # To enable automatic pre-commit: stages: [pre-commit] + # To enable automatic pre-push: stages: [pre-push] ``` +**Note:** The AI commit check is **disabled by default** (manual stage). You must explicitly opt-in by changing the `stages` configuration. + **Pre-push Configuration (Recommended for comprehensive review):** ```yaml repos: @@ -552,10 +556,18 @@ See the [OpenCode permissions documentation](https://opencode.ai/docs/permission **Environment Variables:** ```bash -export OPENCODE_PORT=61164 # Default: 61164 -export OPENCODE_MODEL=claude-sonnet-4.5 # Default: claude-sonnet-4.5 -export OPENCODE_PROVIDER=github-copilot # Default: github-copilot -export OPENCODE_TIMEOUT=90 # Default: 90 seconds +# GitHub Copilot Configuration (Default) +export OPENCODE_PROVIDER=github-copilot +export OPENCODE_MODEL=github-copilot/claude-sonnet-4-5 + +# Amazon Bedrock Configuration +export OPENCODE_PROVIDER=bedrock +export OPENCODE_MODEL=bedrock/anthropic.claude-sonnet-4-5-v2:0 +export OPENCODE_BEDROCK_REGION=us-east-1 # Required: us-east-1 or us-gov-* + +# Server Configuration +export OPENCODE_PORT=61164 # Default: 61164 +export OPENCODE_TIMEOUT=90 # Default: 90 seconds ``` **Usage:** @@ -606,6 +618,71 @@ The required `opencode.jsonc` file includes safe defaults. You can customize per Learn more: [OpenCode Permissions Documentation](https://opencode.ai/docs/permissions/) +### Using Amazon Bedrock + +The AI commit check hook supports Amazon Bedrock with Claude Sonnet 4.5. For security and compliance, only specific AWS regions are allowed. + +**Allowed Regions:** +- `us-east-1` (US East - N. Virginia) +- `us-gov-west-1` (AWS GovCloud US-West) +- `us-gov-east-1` (AWS GovCloud US-East) + +**Setup Steps:** + +1. **Configure AWS credentials and region:** + ```bash + # Set your AWS region + aws configure set region us-east-1 + + # Verify your configuration + aws sts get-caller-identity + ``` + +2. **Authenticate with OpenCode:** + ```bash + opencode auth login + # Select: Amazon Bedrock + ``` + +3. **Set environment variables:** + ```bash + export OPENCODE_PROVIDER=bedrock + export OPENCODE_MODEL=bedrock/anthropic.claude-sonnet-4-5-v2:0 + export OPENCODE_BEDROCK_REGION=us-east-1 + ``` + +4. **Verify the configuration:** + ```bash + # Test that OpenCode can access Bedrock + opencode run "hello world" + ``` + +5. **Run the AI commit check:** + ```bash + pre-commit run --hook-stage manual ai_commit_check + ``` + +**Troubleshooting:** + +If you get a region error: +``` +Error: Amazon Bedrock region 'us-west-2' is not allowed. +``` + +Fix by setting the correct region: +```bash +export OPENCODE_BEDROCK_REGION=us-east-1 +aws configure set region us-east-1 +``` + +**Why only specific regions?** + +For security and compliance reasons, this hook restricts Amazon Bedrock to: +- `us-east-1` - Standard AWS region with Claude Sonnet 4.5 availability +- `us-gov-*` - AWS GovCloud regions for government compliance + +If you need a different region, please file an issue with your compliance requirements. + **Custom Instructions (Optional):** Provide project-specific context to the AI by creating instruction files: diff --git a/examples/opencode.jsonc b/examples/opencode.jsonc index 6a150b5..8e7457f 100644 --- a/examples/opencode.jsonc +++ b/examples/opencode.jsonc @@ -1,6 +1,51 @@ { "$schema": "https://opencode.ai/config.json", + + // Security: Disable sharing and auto-updates "share": "disabled", + "autoupdate": false, + + // Model configuration - ONLY allow approved models + "model": "github-copilot/claude-sonnet-4-5", + "small_model": "github-copilot/claude-sonnet-4-5", + + // Disable all providers except approved ones (github-copilot and bedrock) + "disabled_providers": [ + "openai", + "anthropic", + "gemini", + "azure", + "openrouter", + "ollama", + "lmstudio", + "together", + "fireworks", + "groq", + "deepseek", + "cohere", + "mistral", + "perplexity" + ], + + // Provider-specific configuration + "provider": { + "github-copilot": { + "models": { + "claude-sonnet-4-5": { + "options": {} + } + } + }, + "bedrock": { + "models": { + "anthropic.claude-sonnet-4-5-v2:0": { + "options": {} + } + } + } + }, + + // Strict permissions - deny dangerous operations "permission": { "edit": "allow", "bash": { @@ -28,5 +73,7 @@ }, "webfetch": "deny" }, - "instruction": ["AGENTS.md"] + + // Project-specific AI instructions + "instructions": ["AGENTS.md"] } diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index 758ecd9..eedea59 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -49,9 +49,10 @@ def wait_for_port(port, timeout=30): def main(): OPENCODE_PORT = int(os.getenv('OPENCODE_PORT', '61164')) - OPENCODE_MODEL = os.getenv('OPENCODE_MODEL', 'claude-sonnet-4.5') + OPENCODE_MODEL = os.getenv('OPENCODE_MODEL', 'github-copilot/claude-sonnet-4-5') OPENCODE_PROVIDER = os.getenv('OPENCODE_PROVIDER', 'github-copilot') OPENCODE_TIMEOUT = int(os.getenv('OPENCODE_TIMEOUT', '90')) + OPENCODE_BEDROCK_REGION = os.getenv('OPENCODE_BEDROCK_REGION', 'us-east-1') if not (1024 <= OPENCODE_PORT <= 65535): print(f"Error: Invalid port {OPENCODE_PORT}. Port must be between 1024 and 65535.", file=sys.stderr) @@ -61,6 +62,49 @@ def main(): print(f"Error: Invalid timeout {OPENCODE_TIMEOUT}. Timeout must be between 1 and 300 seconds.", file=sys.stderr) return 3 + # Validate Bedrock region if using Bedrock provider + if OPENCODE_PROVIDER == 'bedrock': + if not OPENCODE_BEDROCK_REGION: + print("Error: OPENCODE_BEDROCK_REGION environment variable is required when using bedrock provider.", file=sys.stderr) + print("", file=sys.stderr) + print("Amazon Bedrock requires explicit region configuration for security and compliance.", file=sys.stderr) + print("", file=sys.stderr) + print("Allowed regions:", file=sys.stderr) + print(" - us-east-1 (US East - N. Virginia)", file=sys.stderr) + print(" - us-gov-west-1 (AWS GovCloud US-West)", file=sys.stderr) + print(" - us-gov-east-1 (AWS GovCloud US-East)", file=sys.stderr) + print("", file=sys.stderr) + print("To use Amazon Bedrock:", file=sys.stderr) + print(" 1. Authenticate to AWS with the correct region:", file=sys.stderr) + print(" aws configure set region us-east-1", file=sys.stderr) + print("", file=sys.stderr) + print(" 2. Set the region environment variable:", file=sys.stderr) + print(" export OPENCODE_BEDROCK_REGION=us-east-1", file=sys.stderr) + print("", file=sys.stderr) + print(" 3. Authenticate with OpenCode:", file=sys.stderr) + print(" opencode auth login", file=sys.stderr) + print(" Select: Amazon Bedrock", file=sys.stderr) + print("", file=sys.stderr) + return 3 + + # Validate region is in allowed list + allowed_regions = ['us-east-1'] + is_govcloud = OPENCODE_BEDROCK_REGION.startswith('us-gov-') + + if not (OPENCODE_BEDROCK_REGION in allowed_regions or is_govcloud): + print(f"Error: Amazon Bedrock region '{OPENCODE_BEDROCK_REGION}' is not allowed.", file=sys.stderr) + print("", file=sys.stderr) + print("For security and compliance reasons, only the following regions are permitted:", file=sys.stderr) + print(" āœ“ us-east-1 (US East - N. Virginia)", file=sys.stderr) + print(" āœ“ us-gov-* (AWS GovCloud regions)", file=sys.stderr) + print("", file=sys.stderr) + print(f"Current region: {OPENCODE_BEDROCK_REGION}", file=sys.stderr) + print("", file=sys.stderr) + print("To fix:", file=sys.stderr) + print(" export OPENCODE_BEDROCK_REGION=us-east-1", file=sys.stderr) + print("", file=sys.stderr) + return 3 + serve_process = None try: repo_root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip() @@ -109,6 +153,11 @@ def main(): OPENCODE_BASE_URL = f'http://127.0.0.1:{OPENCODE_PORT}' os.environ['OPENCODE_BASE_URL'] = OPENCODE_BASE_URL + # Set AWS region environment variables for Bedrock if needed + if OPENCODE_PROVIDER == 'bedrock': + os.environ['AWS_DEFAULT_REGION'] = OPENCODE_BEDROCK_REGION + os.environ['AWS_REGION'] = OPENCODE_BEDROCK_REGION + try: serve_process = subprocess.Popen( ['opencode', 'serve', '--port', str(OPENCODE_PORT)], From f5f54c2cfb0a2cffbd70821bc2c5759d8d9178e2 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Mon, 1 Dec 2025 15:07:12 -0500 Subject: [PATCH 10/22] adding internal opencode config file for forced universal configuration --- AGENTS.md | 4 +- CONFIGURATION_GUIDE.md | 27 +++++++++----- README.md | 45 ++++++++++++----------- hooks/ai_commit_check.py | 45 +++++++---------------- hooks/opencode.jsonc | 79 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 62 deletions(-) create mode 100644 hooks/opencode.jsonc diff --git a/AGENTS.md b/AGENTS.md index c1eecda..8a8abd3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -115,7 +115,9 @@ The `ai_commit_check` hook has special security requirements and dual-mode opera - **Mode Detection**: Checks `git diff --cached` for staged changes to determine mode - **Pre-commit mode**: Reviews staged changes using `git diff --cached` - **Pre-push mode**: Compares current branch to `origin/main` or `origin/master` using `git diff origin/{branch}...HEAD` -- **Security**: Requires `opencode.jsonc` configuration file with denied dangerous operations +- **Security**: Uses bundled `hooks/opencode.jsonc` configuration file with denied dangerous operations +- **Config enforcement**: Sets `OPENCODE_CONFIG` environment variable to force use of bundled config +- **Runtime verification**: Checks config file exists at startup and exits with error if missing - **Model Restrictions**: Only allows GitHub Copilot and Amazon Bedrock with Claude Sonnet 4.5 - **Exit behavior**: Blocks commits/pushes with `-COMMIT REJECTED-` prefix in response diff --git a/CONFIGURATION_GUIDE.md b/CONFIGURATION_GUIDE.md index 402ec2d..a966cda 100644 --- a/CONFIGURATION_GUIDE.md +++ b/CONFIGURATION_GUIDE.md @@ -53,20 +53,29 @@ This runs automatically on `git push` and reviews all changes in your branch. #### Required Setup -**āš ļø Security Requirement:** You must create an `opencode.jsonc` file in your repository root. +**āœ… Built-in Security Configuration:** -```bash -# Download the recommended configuration -curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/examples/opencode.jsonc +The AI commit check hook includes a **bundled `opencode.jsonc`** configuration file that enforces security automatically. The hook sets the `OPENCODE_CONFIG` environment variable to point to this bundled config, ensuring it's always used. + +**What's included:** +- Share disabled +- Auto-updates disabled +- Only GitHub Copilot and Amazon Bedrock allowed +- All other AI providers blocked +- Dangerous operations denied (webfetch, cloud CLIs, curl, wget, terraform) + +**Runtime Verification:** -# Commit the config file -git add opencode.jsonc -git commit -m "Add opencode security configuration" +The hook verifies the configuration file exists and displays: ``` +āœ“ Using bundled security configuration: /path/to/hooks/opencode.jsonc +``` + +If the config file is missing, the hook exits with an error. -The hook will fail if `opencode.jsonc` is not present to ensure safe operation. +**Optional: Project-Specific Settings:** -See `examples/opencode.jsonc` for the full configuration with security defaults. +You can create an `opencode.jsonc` in your repo root to add project-specific settings. OpenCode will merge it with the bundled config using [config merging](https://opencode.ai/docs/config#config-merging), so the bundled security settings are always enforced. #### Environment Variables diff --git a/README.md b/README.md index 48050e4..48184b4 100644 --- a/README.md +++ b/README.md @@ -523,17 +523,6 @@ opencode auth login # In the opencode interface, enter: /models # Select: claude-sonnet-4.5 -# REQUIRED: Copy the security configuration to your repository root -curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/examples/opencode.jsonc - -# Optional: Initialize AI instructions for your project -# Run: opencode -# Then in the opencode window, type: /init - -# Commit the configuration files -git add opencode.jsonc -git commit -m "Add AI commit check configuration" - # Install pre-commit hooks pre-commit install @@ -541,18 +530,32 @@ pre-commit install pre-commit install --hook-type pre-push ``` -**āš ļø Security Requirement:** +**āœ… Built-in Security Configuration:** -The hook **requires** an `opencode.jsonc` file in your repository root for security. This file must deny dangerous operations: +The hook includes a **bundled `opencode.jsonc` configuration** that enforces security by default. You don't need to create this file in your repository - it's automatically used via the `OPENCODE_CONFIG` environment variable. + +The bundled configuration enforces: +- `share: "disabled"` - No sharing of conversations +- `autoupdate: false` - No automatic updates - `webfetch: "deny"` - Prevents external network requests -- `aws *: "deny"` - Blocks AWS CLI commands -- `az *: "deny"` - Blocks Azure CLI commands -- `gcloud *: "deny"` - Blocks Google Cloud CLI commands -- `terraform *: "deny"` - Blocks Terraform commands -- `curl *: "deny"` - Blocks curl requests -- `wget *: "deny"` - Blocks wget requests - -See the [OpenCode permissions documentation](https://opencode.ai/docs/permissions/) for details. +- Model restrictions - Only GitHub Copilot and Amazon Bedrock Sonnet 4.5 +- Provider restrictions - All other AI providers blocked +- Cloud CLI blocks - Denies `aws`, `az`, `gcloud`, `terraform`, `curl`, `wget` + +**Runtime Verification:** + +When the hook runs, it verifies the bundled configuration file exists and displays: +``` +āœ“ Using bundled security configuration: /path/to/hooks/opencode.jsonc +``` + +If the config file is missing or unreadable, the hook exits with an error. + +**Optional: Custom Configuration:** + +If you want to add project-specific settings, you can create an `opencode.jsonc` in your repo root. OpenCode will merge your project config with the bundled config using its [config merging strategy](https://opencode.ai/docs/config#config-merging). The bundled config always provides the security baseline. + +See the [OpenCode permissions documentation](https://opencode.ai/docs/permissions/) for customization options. **Environment Variables:** ```bash diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index eedea59..72db45f 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -12,6 +12,7 @@ import socket import os from datetime import datetime +from pathlib import Path try: from rich.console import Console @@ -109,38 +110,20 @@ def main(): try: repo_root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip() - opencode_config_path = os.path.join(repo_root, 'opencode.jsonc') - if not os.path.exists(opencode_config_path): - print("Error: opencode.jsonc configuration file not found in repository root.", file=sys.stderr) - print("", file=sys.stderr) - print("The AI commit check hook requires an opencode.jsonc file to ensure secure operation.", file=sys.stderr) - print("This file controls AI permissions and prevents unsafe operations.", file=sys.stderr) - print("", file=sys.stderr) - print("To fix this:", file=sys.stderr) - print(" 1. Copy the example configuration:", file=sys.stderr) - print(" curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/examples/opencode.jsonc", file=sys.stderr) - print("", file=sys.stderr) - print(" 2. Or create opencode.jsonc with minimum required security settings:", file=sys.stderr) - print(' {', file=sys.stderr) - print(' "$schema": "https://opencode.ai/config.json",', file=sys.stderr) - print(' "permission": {', file=sys.stderr) - print(' "webfetch": "deny",', file=sys.stderr) - print(' "bash": {', file=sys.stderr) - print(' "aws *": "deny",', file=sys.stderr) - print(' "az *": "deny",', file=sys.stderr) - print(' "gcloud *": "deny",', file=sys.stderr) - print(' "terraform *": "deny",', file=sys.stderr) - print(' "curl *": "deny",', file=sys.stderr) - print(' "wget *": "deny"', file=sys.stderr) - print(' }', file=sys.stderr) - print(' }', file=sys.stderr) - print(' }', file=sys.stderr) - print("", file=sys.stderr) - print(" 3. Review and customize permissions: https://opencode.ai/docs/permissions/", file=sys.stderr) - print("", file=sys.stderr) - print(" 4. Commit the opencode.jsonc file to your repository", file=sys.stderr) + # Use the bundled opencode.jsonc from the hooks directory + hook_script_path = Path(__file__).resolve() + bundled_config_path = hook_script_path.parent / 'opencode.jsonc' + + # Verify bundled config exists + if not bundled_config_path.exists(): + print(f"Error: Bundled config not found at {bundled_config_path}", file=sys.stderr) + print("This is a bug in the pre-commit hook installation.", file=sys.stderr) return 3 + # Set OPENCODE_CONFIG to force OpenCode to use our bundled config + os.environ['OPENCODE_CONFIG'] = str(bundled_config_path) + print(f"āœ“ Using bundled security configuration: {bundled_config_path}", file=sys.stderr) + available_port = find_available_port(OPENCODE_PORT) if not available_port: print(f"Error: No available ports found starting from port {OPENCODE_PORT}. " @@ -163,7 +146,7 @@ def main(): ['opencode', 'serve', '--port', str(OPENCODE_PORT)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - cwd=repo_root + cwd=repo_root # Run in repo root so it can see git diffs ) except FileNotFoundError: print("Error: opencode is not installed.", file=sys.stderr) diff --git a/hooks/opencode.jsonc b/hooks/opencode.jsonc new file mode 100644 index 0000000..8e7457f --- /dev/null +++ b/hooks/opencode.jsonc @@ -0,0 +1,79 @@ +{ + "$schema": "https://opencode.ai/config.json", + + // Security: Disable sharing and auto-updates + "share": "disabled", + "autoupdate": false, + + // Model configuration - ONLY allow approved models + "model": "github-copilot/claude-sonnet-4-5", + "small_model": "github-copilot/claude-sonnet-4-5", + + // Disable all providers except approved ones (github-copilot and bedrock) + "disabled_providers": [ + "openai", + "anthropic", + "gemini", + "azure", + "openrouter", + "ollama", + "lmstudio", + "together", + "fireworks", + "groq", + "deepseek", + "cohere", + "mistral", + "perplexity" + ], + + // Provider-specific configuration + "provider": { + "github-copilot": { + "models": { + "claude-sonnet-4-5": { + "options": {} + } + } + }, + "bedrock": { + "models": { + "anthropic.claude-sonnet-4-5-v2:0": { + "options": {} + } + } + } + }, + + // Strict permissions - deny dangerous operations + "permission": { + "edit": "allow", + "bash": { + "*": "deny", + "git status *": "allow", + "git diff *": "allow", + "git log *": "allow", + "git show *": "allow", + "git rev-parse *": "allow", + "ls *": "allow", + "pwd": "allow", + "cat *": "allow", + "grep *": "allow", + "find *": "allow", + "rg *": "allow", + "npm run test": "ask", + "npm run build": "ask", + "pytest *": "ask", + "aws *": "deny", + "az *": "deny", + "gcloud *": "deny", + "terraform *": "deny", + "curl *": "deny", + "wget *": "deny" + }, + "webfetch": "deny" + }, + + // Project-specific AI instructions + "instructions": ["AGENTS.md"] +} From 76d033d6a4fc187d16c1152b8cd92db4a18150a5 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Mon, 1 Dec 2025 15:12:16 -0500 Subject: [PATCH 11/22] adding temp opencode config file --- AGENTS.md | 5 +-- CONFIGURATION_GUIDE.md | 12 +++--- README.md | 22 ++++++++--- hooks/ai_commit_check.py | 81 ++++++++++++++++++++++++++++++++++------ 4 files changed, 93 insertions(+), 27 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8a8abd3..7dce5cc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -115,9 +115,8 @@ The `ai_commit_check` hook has special security requirements and dual-mode opera - **Mode Detection**: Checks `git diff --cached` for staged changes to determine mode - **Pre-commit mode**: Reviews staged changes using `git diff --cached` - **Pre-push mode**: Compares current branch to `origin/main` or `origin/master` using `git diff origin/{branch}...HEAD` -- **Security**: Uses bundled `hooks/opencode.jsonc` configuration file with denied dangerous operations -- **Config enforcement**: Sets `OPENCODE_CONFIG` environment variable to force use of bundled config -- **Runtime verification**: Checks config file exists at startup and exits with error if missing +- **Security**: Embedded configuration with denied dangerous operations +- **Config enforcement**: Writes config to temporary file and sets `OPENCODE_CONFIG` environment variable - **Model Restrictions**: Only allows GitHub Copilot and Amazon Bedrock with Claude Sonnet 4.5 - **Exit behavior**: Blocks commits/pushes with `-COMMIT REJECTED-` prefix in response diff --git a/CONFIGURATION_GUIDE.md b/CONFIGURATION_GUIDE.md index a966cda..2b2b6c6 100644 --- a/CONFIGURATION_GUIDE.md +++ b/CONFIGURATION_GUIDE.md @@ -55,7 +55,7 @@ This runs automatically on `git push` and reviews all changes in your branch. **āœ… Built-in Security Configuration:** -The AI commit check hook includes a **bundled `opencode.jsonc`** configuration file that enforces security automatically. The hook sets the `OPENCODE_CONFIG` environment variable to point to this bundled config, ensuring it's always used. +The AI commit check hook includes an **embedded security configuration** that is automatically applied by writing it to a temporary file and loading it via the `OPENCODE_CONFIG` environment variable. **What's included:** - Share disabled @@ -64,18 +64,16 @@ The AI commit check hook includes a **bundled `opencode.jsonc`** configuration f - All other AI providers blocked - Dangerous operations denied (webfetch, cloud CLIs, curl, wget, terraform) -**Runtime Verification:** +**Runtime Output:** -The hook verifies the configuration file exists and displays: +The hook displays: ``` -āœ“ Using bundled security configuration: /path/to/hooks/opencode.jsonc +āœ“ Using bundled security configuration ``` -If the config file is missing, the hook exits with an error. - **Optional: Project-Specific Settings:** -You can create an `opencode.jsonc` in your repo root to add project-specific settings. OpenCode will merge it with the bundled config using [config merging](https://opencode.ai/docs/config#config-merging), so the bundled security settings are always enforced. +You can create an `opencode.jsonc` in your repo root to add project-specific settings. OpenCode will merge it with the embedded config using [config merging](https://opencode.ai/docs/config#config-merging), so the embedded security settings are always enforced. #### Environment Variables diff --git a/README.md b/README.md index 48184b4..5bf733b 100644 --- a/README.md +++ b/README.md @@ -542,18 +542,28 @@ The bundled configuration enforces: - Provider restrictions - All other AI providers blocked - Cloud CLI blocks - Denies `aws`, `az`, `gcloud`, `terraform`, `curl`, `wget` -**Runtime Verification:** +**āœ… Built-in Security Configuration:** + +The hook includes an **embedded security configuration** that is automatically applied. The configuration is written to a temporary file and loaded via the `OPENCODE_CONFIG` environment variable. -When the hook runs, it verifies the bundled configuration file exists and displays: +The embedded configuration enforces: +- `share: "disabled"` - No sharing of conversations +- `autoupdate: false` - No automatic updates +- `webfetch: "deny"` - Prevents external network requests +- Model restrictions - Only GitHub Copilot and Amazon Bedrock Sonnet 4.5 +- Provider restrictions - All other AI providers blocked +- Cloud CLI blocks - Denies `aws`, `az`, `gcloud`, `terraform`, `curl`, `wget` + +**Runtime Output:** + +When the hook runs, you'll see: ``` -āœ“ Using bundled security configuration: /path/to/hooks/opencode.jsonc +āœ“ Using bundled security configuration ``` -If the config file is missing or unreadable, the hook exits with an error. - **Optional: Custom Configuration:** -If you want to add project-specific settings, you can create an `opencode.jsonc` in your repo root. OpenCode will merge your project config with the bundled config using its [config merging strategy](https://opencode.ai/docs/config#config-merging). The bundled config always provides the security baseline. +If you want to add project-specific settings, you can create an `opencode.jsonc` in your repo root. OpenCode will merge your project config with the embedded config using its [config merging strategy](https://opencode.ai/docs/config#config-merging). The embedded config provides the security baseline. See the [OpenCode permissions documentation](https://opencode.ai/docs/permissions/) for customization options. diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index 72db45f..e767df3 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -11,9 +11,64 @@ import time import socket import os +import tempfile from datetime import datetime from pathlib import Path +# Bundled security configuration - embedded in the script +BUNDLED_CONFIG = """{ + "$schema": "https://opencode.ai/config.json", + "share": "disabled", + "autoupdate": false, + "model": "github-copilot/claude-sonnet-4-5", + "small_model": "github-copilot/claude-sonnet-4-5", + "disabled_providers": [ + "openai", "anthropic", "gemini", "azure", "openrouter", + "ollama", "lmstudio", "together", "fireworks", "groq", + "deepseek", "cohere", "mistral", "perplexity" + ], + "provider": { + "github-copilot": { + "models": { + "claude-sonnet-4-5": { "options": {} } + } + }, + "bedrock": { + "models": { + "anthropic.claude-sonnet-4-5-v2:0": { "options": {} } + } + } + }, + "permission": { + "edit": "allow", + "bash": { + "*": "deny", + "git status *": "allow", + "git diff *": "allow", + "git log *": "allow", + "git show *": "allow", + "git rev-parse *": "allow", + "ls *": "allow", + "pwd": "allow", + "cat *": "allow", + "grep *": "allow", + "find *": "allow", + "rg *": "allow", + "npm run test": "ask", + "npm run build": "ask", + "pytest *": "ask", + "aws *": "deny", + "az *": "deny", + "gcloud *": "deny", + "terraform *": "deny", + "curl *": "deny", + "wget *": "deny" + }, + "webfetch": "deny" + }, + "instructions": ["AGENTS.md"] +}""" + try: from rich.console import Console from rich.markdown import Markdown @@ -107,22 +162,18 @@ def main(): return 3 serve_process = None + config_file = None try: repo_root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip() - # Use the bundled opencode.jsonc from the hooks directory - hook_script_path = Path(__file__).resolve() - bundled_config_path = hook_script_path.parent / 'opencode.jsonc' - - # Verify bundled config exists - if not bundled_config_path.exists(): - print(f"Error: Bundled config not found at {bundled_config_path}", file=sys.stderr) - print("This is a bug in the pre-commit hook installation.", file=sys.stderr) - return 3 + # Write bundled config to a temporary file + config_file = tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) + config_file.write(BUNDLED_CONFIG) + config_file.close() # Set OPENCODE_CONFIG to force OpenCode to use our bundled config - os.environ['OPENCODE_CONFIG'] = str(bundled_config_path) - print(f"āœ“ Using bundled security configuration: {bundled_config_path}", file=sys.stderr) + os.environ['OPENCODE_CONFIG'] = config_file.name + print(f"āœ“ Using bundled security configuration", file=sys.stderr) available_port = find_available_port(OPENCODE_PORT) if not available_port: @@ -366,6 +417,14 @@ def render_markdown_terminal(message: Optional[str]) -> None: return 0 finally: + # Clean up temporary config file + if config_file and os.path.exists(config_file.name): + try: + os.unlink(config_file.name) + except: + pass # Ignore cleanup errors + + # Terminate OpenCode server if serve_process: serve_process.terminate() try: From 18328ef11cedbfe1bc9051eb3bf142afa56be630 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Mon, 1 Dec 2025 15:21:23 -0500 Subject: [PATCH 12/22] setting model and provider ids --- AGENTS.md | 6 +++--- CONFIGURATION_GUIDE.md | 12 ++++++------ README.md | 10 +++++----- hooks/ai_commit_check.py | 18 +++++++++--------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7dce5cc..afa69e4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -121,8 +121,8 @@ The `ai_commit_check` hook has special security requirements and dual-mode opera - **Exit behavior**: Blocks commits/pushes with `-COMMIT REJECTED-` prefix in response **Supported Providers:** -- **GitHub Copilot** (default): `github-copilot/claude-sonnet-4-5` -- **Amazon Bedrock**: `bedrock/anthropic.claude-sonnet-4-5-v2:0` +- **GitHub Copilot** (default): `github-copilot/claude-sonnet-4.5` +- **Amazon Bedrock**: `amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0` - Required region: `us-east-1` or `us-gov-*` - Enforced via `OPENCODE_BEDROCK_REGION` environment variable - Hook exits with error code 3 if region is invalid or not us-east-1/us-gov-* @@ -130,7 +130,7 @@ The `ai_commit_check` hook has special security requirements and dual-mode opera **Key Implementation Details:** - Exit codes: 0 (pass), 1 (fail/rejected), 3 (tool missing/error) - Denies: `webfetch`, cloud CLIs (`aws`, `az`, `gcloud`), `curl`, `wget`, `terraform` -- Disables all AI providers except: `github-copilot` and `bedrock` +- Disables all AI providers except: `github-copilot` and `amazon-bedrock` - Starts temporary opencode server on available port (default: 61164) - Creates AI session with commit context and security review prompt - Sets AWS region environment variables (`AWS_DEFAULT_REGION`, `AWS_REGION`) when using Bedrock diff --git a/CONFIGURATION_GUIDE.md b/CONFIGURATION_GUIDE.md index 2b2b6c6..1b7b464 100644 --- a/CONFIGURATION_GUIDE.md +++ b/CONFIGURATION_GUIDE.md @@ -79,10 +79,10 @@ You can create an `opencode.jsonc` in your repo root to add project-specific set ```bash export OPENCODE_PORT=61164 # Port for opencode server (default: 61164) -export OPENCODE_MODEL=github-copilot/claude-sonnet-4-5 # AI model to use (default) -export OPENCODE_PROVIDER=github-copilot # Provider: github-copilot, bedrock (default: github-copilot) +export OPENCODE_MODEL=github-copilot/claude-sonnet-4.5 # AI model to use (default) +export OPENCODE_PROVIDER=github-copilot # Provider: github-copilot, amazon-bedrock (default: github-copilot) export OPENCODE_TIMEOUT=90 # Timeout in seconds (default: 90) -export OPENCODE_BEDROCK_REGION=us-east-1 # Required for bedrock provider +export OPENCODE_BEDROCK_REGION=us-east-1 # Required for amazon-bedrock provider ``` #### Permissions and Custom Instructions @@ -126,8 +126,8 @@ repos: **Set environment variables:** ```bash -export OPENCODE_PROVIDER=bedrock -export OPENCODE_MODEL=bedrock/anthropic.claude-sonnet-4-5-v2:0 +export OPENCODE_PROVIDER=amazon-bedrock +export OPENCODE_MODEL=amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0 export OPENCODE_BEDROCK_REGION=us-east-1 ``` @@ -153,7 +153,7 @@ opencode run "hello world" **Example Usage:** ```bash # Set environment for the session -export OPENCODE_PROVIDER=bedrock +export OPENCODE_PROVIDER=amazon-bedrock export OPENCODE_BEDROCK_REGION=us-east-1 # Run the AI commit check diff --git a/README.md b/README.md index 5bf733b..31484b6 100644 --- a/README.md +++ b/README.md @@ -571,11 +571,11 @@ See the [OpenCode permissions documentation](https://opencode.ai/docs/permission ```bash # GitHub Copilot Configuration (Default) export OPENCODE_PROVIDER=github-copilot -export OPENCODE_MODEL=github-copilot/claude-sonnet-4-5 +export OPENCODE_MODEL=github-copilot/claude-sonnet-4.5 # Amazon Bedrock Configuration -export OPENCODE_PROVIDER=bedrock -export OPENCODE_MODEL=bedrock/anthropic.claude-sonnet-4-5-v2:0 +export OPENCODE_PROVIDER=amazon-bedrock +export OPENCODE_MODEL=amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0 export OPENCODE_BEDROCK_REGION=us-east-1 # Required: us-east-1 or us-gov-* # Server Configuration @@ -659,8 +659,8 @@ The AI commit check hook supports Amazon Bedrock with Claude Sonnet 4.5. For sec 3. **Set environment variables:** ```bash - export OPENCODE_PROVIDER=bedrock - export OPENCODE_MODEL=bedrock/anthropic.claude-sonnet-4-5-v2:0 + export OPENCODE_PROVIDER=amazon-bedrock + export OPENCODE_MODEL=amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0 export OPENCODE_BEDROCK_REGION=us-east-1 ``` diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index e767df3..9dbf981 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -20,8 +20,8 @@ "$schema": "https://opencode.ai/config.json", "share": "disabled", "autoupdate": false, - "model": "github-copilot/claude-sonnet-4-5", - "small_model": "github-copilot/claude-sonnet-4-5", + "model": "github-copilot/claude-sonnet-4.5", + "small_model": "github-copilot/claude-sonnet-4.5", "disabled_providers": [ "openai", "anthropic", "gemini", "azure", "openrouter", "ollama", "lmstudio", "together", "fireworks", "groq", @@ -30,12 +30,12 @@ "provider": { "github-copilot": { "models": { - "claude-sonnet-4-5": { "options": {} } + "github-copilot/claude-sonnet-4.5": { "options": {} } } }, - "bedrock": { + "amazon-bedrock": { "models": { - "anthropic.claude-sonnet-4-5-v2:0": { "options": {} } + "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0": { "options": {} } } } }, @@ -105,7 +105,7 @@ def wait_for_port(port, timeout=30): def main(): OPENCODE_PORT = int(os.getenv('OPENCODE_PORT', '61164')) - OPENCODE_MODEL = os.getenv('OPENCODE_MODEL', 'github-copilot/claude-sonnet-4-5') + OPENCODE_MODEL = os.getenv('OPENCODE_MODEL', 'github-copilot/claude-sonnet-4.5') OPENCODE_PROVIDER = os.getenv('OPENCODE_PROVIDER', 'github-copilot') OPENCODE_TIMEOUT = int(os.getenv('OPENCODE_TIMEOUT', '90')) OPENCODE_BEDROCK_REGION = os.getenv('OPENCODE_BEDROCK_REGION', 'us-east-1') @@ -119,9 +119,9 @@ def main(): return 3 # Validate Bedrock region if using Bedrock provider - if OPENCODE_PROVIDER == 'bedrock': + if OPENCODE_PROVIDER == 'amazon-bedrock': if not OPENCODE_BEDROCK_REGION: - print("Error: OPENCODE_BEDROCK_REGION environment variable is required when using bedrock provider.", file=sys.stderr) + print("Error: OPENCODE_BEDROCK_REGION environment variable is required when using amazon-bedrock provider.", file=sys.stderr) print("", file=sys.stderr) print("Amazon Bedrock requires explicit region configuration for security and compliance.", file=sys.stderr) print("", file=sys.stderr) @@ -188,7 +188,7 @@ def main(): os.environ['OPENCODE_BASE_URL'] = OPENCODE_BASE_URL # Set AWS region environment variables for Bedrock if needed - if OPENCODE_PROVIDER == 'bedrock': + if OPENCODE_PROVIDER == 'amazon-bedrock': os.environ['AWS_DEFAULT_REGION'] = OPENCODE_BEDROCK_REGION os.environ['AWS_REGION'] = OPENCODE_BEDROCK_REGION From 3b1f07a624ed46273b648f6900bb695ee0c106f9 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Mon, 1 Dec 2025 15:42:23 -0500 Subject: [PATCH 13/22] going back to file config in repo --- AGENTS.md | 5 ++- CONFIGURATION_GUIDE.md | 22 ++++++---- README.md | 34 +++++++--------- examples/opencode.jsonc | 12 +++--- hooks/ai_commit_check.py | 88 ++++++++-------------------------------- hooks/opencode.jsonc | 28 ++++++------- 6 files changed, 70 insertions(+), 119 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index afa69e4..2703f26 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -115,8 +115,8 @@ The `ai_commit_check` hook has special security requirements and dual-mode opera - **Mode Detection**: Checks `git diff --cached` for staged changes to determine mode - **Pre-commit mode**: Reviews staged changes using `git diff --cached` - **Pre-push mode**: Compares current branch to `origin/main` or `origin/master` using `git diff origin/{branch}...HEAD` -- **Security**: Embedded configuration with denied dangerous operations -- **Config enforcement**: Writes config to temporary file and sets `OPENCODE_CONFIG` environment variable +- **Security**: Requires `opencode.jsonc` file in repository root with security policies +- **Config enforcement**: Hook checks for `opencode.jsonc` and exits with error if missing - **Model Restrictions**: Only allows GitHub Copilot and Amazon Bedrock with Claude Sonnet 4.5 - **Exit behavior**: Blocks commits/pushes with `-COMMIT REJECTED-` prefix in response @@ -131,6 +131,7 @@ The `ai_commit_check` hook has special security requirements and dual-mode opera - Exit codes: 0 (pass), 1 (fail/rejected), 3 (tool missing/error) - Denies: `webfetch`, cloud CLIs (`aws`, `az`, `gcloud`), `curl`, `wget`, `terraform` - Disables all AI providers except: `github-copilot` and `amazon-bedrock` +- Requires `opencode.jsonc` in repository root - Starts temporary opencode server on available port (default: 61164) - Creates AI session with commit context and security review prompt - Sets AWS region environment variables (`AWS_DEFAULT_REGION`, `AWS_REGION`) when using Bedrock diff --git a/CONFIGURATION_GUIDE.md b/CONFIGURATION_GUIDE.md index 1b7b464..0aae6f7 100644 --- a/CONFIGURATION_GUIDE.md +++ b/CONFIGURATION_GUIDE.md @@ -53,9 +53,19 @@ This runs automatically on `git push` and reviews all changes in your branch. #### Required Setup -**āœ… Built-in Security Configuration:** +**Required: Security Configuration File:** -The AI commit check hook includes an **embedded security configuration** that is automatically applied by writing it to a temporary file and loading it via the `OPENCODE_CONFIG` environment variable. +The AI commit check hook **requires** an `opencode.jsonc` configuration file in your repository root to enforce security policies. + +**Setup:** +```bash +# Download the security configuration to your repo root +curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/hooks/opencode.jsonc + +# Commit the configuration +git add opencode.jsonc +git commit -m "Add OpenCode security configuration" +``` **What's included:** - Share disabled @@ -68,12 +78,10 @@ The AI commit check hook includes an **embedded security configuration** that is The hook displays: ``` -āœ“ Using bundled security configuration +āœ“ Using opencode.jsonc from repository root ``` -**Optional: Project-Specific Settings:** - -You can create an `opencode.jsonc` in your repo root to add project-specific settings. OpenCode will merge it with the embedded config using [config merging](https://opencode.ai/docs/config#config-merging), so the embedded security settings are always enforced. +If the file is missing, the hook will exit with an error and instructions on how to set it up. #### Environment Variables @@ -87,7 +95,7 @@ export OPENCODE_BEDROCK_REGION=us-east-1 # Required for amazon-bedrock provider #### Permissions and Custom Instructions -The recommended `opencode.jsonc` includes safe defaults that deny dangerous operations. +The required `opencode.jsonc` includes safe defaults that deny dangerous operations. **To customize permissions**: See [OpenCode Permissions Documentation](https://opencode.ai/docs/permissions/) diff --git a/README.md b/README.md index 31484b6..04e6afb 100644 --- a/README.md +++ b/README.md @@ -530,27 +530,25 @@ pre-commit install pre-commit install --hook-type pre-push ``` -**āœ… Built-in Security Configuration:** +**Required: Security Configuration File:** -The hook includes a **bundled `opencode.jsonc` configuration** that enforces security by default. You don't need to create this file in your repository - it's automatically used via the `OPENCODE_CONFIG` environment variable. +This hook **requires** an `opencode.jsonc` configuration file in your repository root to enforce security policies. -The bundled configuration enforces: -- `share: "disabled"` - No sharing of conversations -- `autoupdate: false` - No automatic updates -- `webfetch: "deny"` - Prevents external network requests -- Model restrictions - Only GitHub Copilot and Amazon Bedrock Sonnet 4.5 -- Provider restrictions - All other AI providers blocked -- Cloud CLI blocks - Denies `aws`, `az`, `gcloud`, `terraform`, `curl`, `wget` - -**āœ… Built-in Security Configuration:** +**Setup:** +```bash +# Download the security configuration to your repo root +curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/hooks/opencode.jsonc -The hook includes an **embedded security configuration** that is automatically applied. The configuration is written to a temporary file and loaded via the `OPENCODE_CONFIG` environment variable. +# Commit the configuration +git add opencode.jsonc +git commit -m "Add OpenCode security configuration" +``` -The embedded configuration enforces: +The `opencode.jsonc` file enforces: - `share: "disabled"` - No sharing of conversations - `autoupdate: false` - No automatic updates - `webfetch: "deny"` - Prevents external network requests -- Model restrictions - Only GitHub Copilot and Amazon Bedrock Sonnet 4.5 +- Model restrictions - Only GitHub Copilot and Amazon Bedrock Claude Sonnet 4.5 - Provider restrictions - All other AI providers blocked - Cloud CLI blocks - Denies `aws`, `az`, `gcloud`, `terraform`, `curl`, `wget` @@ -558,14 +556,10 @@ The embedded configuration enforces: When the hook runs, you'll see: ``` -āœ“ Using bundled security configuration +āœ“ Using opencode.jsonc from repository root ``` -**Optional: Custom Configuration:** - -If you want to add project-specific settings, you can create an `opencode.jsonc` in your repo root. OpenCode will merge your project config with the embedded config using its [config merging strategy](https://opencode.ai/docs/config#config-merging). The embedded config provides the security baseline. - -See the [OpenCode permissions documentation](https://opencode.ai/docs/permissions/) for customization options. +If the file is missing, the hook will exit with an error and instructions. **Environment Variables:** ```bash diff --git a/examples/opencode.jsonc b/examples/opencode.jsonc index 8e7457f..a7e94e3 100644 --- a/examples/opencode.jsonc +++ b/examples/opencode.jsonc @@ -6,10 +6,10 @@ "autoupdate": false, // Model configuration - ONLY allow approved models - "model": "github-copilot/claude-sonnet-4-5", - "small_model": "github-copilot/claude-sonnet-4-5", + "model": "github-copilot/claude-sonnet-4.5", + "small_model": "github-copilot/claude-sonnet-4.5", - // Disable all providers except approved ones (github-copilot and bedrock) + // Disable all providers except approved ones (github-copilot and amazon-bedrock) "disabled_providers": [ "openai", "anthropic", @@ -31,14 +31,14 @@ "provider": { "github-copilot": { "models": { - "claude-sonnet-4-5": { + "github-copilot/claude-sonnet-4.5": { "options": {} } } }, - "bedrock": { + "amazon-bedrock": { "models": { - "anthropic.claude-sonnet-4-5-v2:0": { + "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0": { "options": {} } } diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index 9dbf981..6b9ee5b 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -11,64 +11,9 @@ import time import socket import os -import tempfile from datetime import datetime from pathlib import Path -# Bundled security configuration - embedded in the script -BUNDLED_CONFIG = """{ - "$schema": "https://opencode.ai/config.json", - "share": "disabled", - "autoupdate": false, - "model": "github-copilot/claude-sonnet-4.5", - "small_model": "github-copilot/claude-sonnet-4.5", - "disabled_providers": [ - "openai", "anthropic", "gemini", "azure", "openrouter", - "ollama", "lmstudio", "together", "fireworks", "groq", - "deepseek", "cohere", "mistral", "perplexity" - ], - "provider": { - "github-copilot": { - "models": { - "github-copilot/claude-sonnet-4.5": { "options": {} } - } - }, - "amazon-bedrock": { - "models": { - "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0": { "options": {} } - } - } - }, - "permission": { - "edit": "allow", - "bash": { - "*": "deny", - "git status *": "allow", - "git diff *": "allow", - "git log *": "allow", - "git show *": "allow", - "git rev-parse *": "allow", - "ls *": "allow", - "pwd": "allow", - "cat *": "allow", - "grep *": "allow", - "find *": "allow", - "rg *": "allow", - "npm run test": "ask", - "npm run build": "ask", - "pytest *": "ask", - "aws *": "deny", - "az *": "deny", - "gcloud *": "deny", - "terraform *": "deny", - "curl *": "deny", - "wget *": "deny" - }, - "webfetch": "deny" - }, - "instructions": ["AGENTS.md"] -}""" - try: from rich.console import Console from rich.markdown import Markdown @@ -162,18 +107,28 @@ def main(): return 3 serve_process = None - config_file = None try: repo_root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip() - # Write bundled config to a temporary file - config_file = tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) - config_file.write(BUNDLED_CONFIG) - config_file.close() + # Check for opencode.jsonc in repo root + config_path = os.path.join(repo_root, 'opencode.jsonc') + if not os.path.exists(config_path): + print("Error: opencode.jsonc not found in repository root.", file=sys.stderr) + print("", file=sys.stderr) + print("The AI commit check requires an opencode.jsonc configuration file", file=sys.stderr) + print("to enforce security policies and restrict AI capabilities.", file=sys.stderr) + print("", file=sys.stderr) + print("To set up:", file=sys.stderr) + print(" 1. Download the security configuration:", file=sys.stderr) + print(" curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/hooks/opencode.jsonc", file=sys.stderr) + print("", file=sys.stderr) + print(" 2. Commit the file:", file=sys.stderr) + print(" git add opencode.jsonc && git commit -m 'Add OpenCode security config'", file=sys.stderr) + print("", file=sys.stderr) + print("See CONFIGURATION_GUIDE.md for more details.", file=sys.stderr) + return 3 - # Set OPENCODE_CONFIG to force OpenCode to use our bundled config - os.environ['OPENCODE_CONFIG'] = config_file.name - print(f"āœ“ Using bundled security configuration", file=sys.stderr) + print(f"āœ“ Using opencode.jsonc from repository root", file=sys.stderr) available_port = find_available_port(OPENCODE_PORT) if not available_port: @@ -417,13 +372,6 @@ def render_markdown_terminal(message: Optional[str]) -> None: return 0 finally: - # Clean up temporary config file - if config_file and os.path.exists(config_file.name): - try: - os.unlink(config_file.name) - except: - pass # Ignore cleanup errors - # Terminate OpenCode server if serve_process: serve_process.terminate() diff --git a/hooks/opencode.jsonc b/hooks/opencode.jsonc index 8e7457f..fb32cb1 100644 --- a/hooks/opencode.jsonc +++ b/hooks/opencode.jsonc @@ -1,18 +1,18 @@ { "$schema": "https://opencode.ai/config.json", - + // Security: Disable sharing and auto-updates "share": "disabled", "autoupdate": false, - + // Model configuration - ONLY allow approved models - "model": "github-copilot/claude-sonnet-4-5", - "small_model": "github-copilot/claude-sonnet-4-5", - - // Disable all providers except approved ones (github-copilot and bedrock) + "model": "github-copilot/claude-sonnet-4.5", + "small_model": "github-copilot/claude-sonnet-4.5", + + // Disable all providers except approved ones (github-copilot and amazon-bedrock) "disabled_providers": [ "openai", - "anthropic", + "anthropic", "gemini", "azure", "openrouter", @@ -26,28 +26,28 @@ "mistral", "perplexity" ], - + // Provider-specific configuration "provider": { "github-copilot": { "models": { - "claude-sonnet-4-5": { + "github-copilot/claude-sonnet-4.5": { "options": {} } } }, - "bedrock": { + "amazon-bedrock": { "models": { - "anthropic.claude-sonnet-4-5-v2:0": { + "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0": { "options": {} } } } }, - + // Strict permissions - deny dangerous operations "permission": { - "edit": "allow", + "edit": "deny", "bash": { "*": "deny", "git status *": "allow", @@ -73,7 +73,7 @@ }, "webfetch": "deny" }, - + // Project-specific AI instructions "instructions": ["AGENTS.md"] } From 436e82b9b8241ee0f3cd8041e9991ebcb2b3da05 Mon Sep 17 00:00:00 2001 From: Matt Donnelly <118205053+MattDonnellySoftrams@users.noreply.github.com> Date: Mon, 1 Dec 2025 15:49:00 -0500 Subject: [PATCH 14/22] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04e6afb..fa5f954 100644 --- a/README.md +++ b/README.md @@ -537,7 +537,7 @@ This hook **requires** an `opencode.jsonc` configuration file in your repository **Setup:** ```bash # Download the security configuration to your repo root -curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/hooks/opencode.jsonc +curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/examples/opencode.jsonc # Commit the configuration git add opencode.jsonc From 2cc67913133b47dbbe42d35891cdf985a930e7bd Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Mon, 1 Dec 2025 15:53:10 -0500 Subject: [PATCH 15/22] using only examples config file --- CONFIGURATION_GUIDE.md | 2 +- hooks/ai_commit_check.py | 2 +- hooks/opencode.jsonc | 79 ---------------------------------------- 3 files changed, 2 insertions(+), 81 deletions(-) delete mode 100644 hooks/opencode.jsonc diff --git a/CONFIGURATION_GUIDE.md b/CONFIGURATION_GUIDE.md index 0aae6f7..a20577a 100644 --- a/CONFIGURATION_GUIDE.md +++ b/CONFIGURATION_GUIDE.md @@ -60,7 +60,7 @@ The AI commit check hook **requires** an `opencode.jsonc` configuration file in **Setup:** ```bash # Download the security configuration to your repo root -curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/hooks/opencode.jsonc +curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/examples/opencode.jsonc # Commit the configuration git add opencode.jsonc diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index 6b9ee5b..39ecea3 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -120,7 +120,7 @@ def main(): print("", file=sys.stderr) print("To set up:", file=sys.stderr) print(" 1. Download the security configuration:", file=sys.stderr) - print(" curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/hooks/opencode.jsonc", file=sys.stderr) + print(" curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/examples/opencode.jsonc", file=sys.stderr) print("", file=sys.stderr) print(" 2. Commit the file:", file=sys.stderr) print(" git add opencode.jsonc && git commit -m 'Add OpenCode security config'", file=sys.stderr) diff --git a/hooks/opencode.jsonc b/hooks/opencode.jsonc deleted file mode 100644 index fb32cb1..0000000 --- a/hooks/opencode.jsonc +++ /dev/null @@ -1,79 +0,0 @@ -{ - "$schema": "https://opencode.ai/config.json", - - // Security: Disable sharing and auto-updates - "share": "disabled", - "autoupdate": false, - - // Model configuration - ONLY allow approved models - "model": "github-copilot/claude-sonnet-4.5", - "small_model": "github-copilot/claude-sonnet-4.5", - - // Disable all providers except approved ones (github-copilot and amazon-bedrock) - "disabled_providers": [ - "openai", - "anthropic", - "gemini", - "azure", - "openrouter", - "ollama", - "lmstudio", - "together", - "fireworks", - "groq", - "deepseek", - "cohere", - "mistral", - "perplexity" - ], - - // Provider-specific configuration - "provider": { - "github-copilot": { - "models": { - "github-copilot/claude-sonnet-4.5": { - "options": {} - } - } - }, - "amazon-bedrock": { - "models": { - "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0": { - "options": {} - } - } - } - }, - - // Strict permissions - deny dangerous operations - "permission": { - "edit": "deny", - "bash": { - "*": "deny", - "git status *": "allow", - "git diff *": "allow", - "git log *": "allow", - "git show *": "allow", - "git rev-parse *": "allow", - "ls *": "allow", - "pwd": "allow", - "cat *": "allow", - "grep *": "allow", - "find *": "allow", - "rg *": "allow", - "npm run test": "ask", - "npm run build": "ask", - "pytest *": "ask", - "aws *": "deny", - "az *": "deny", - "gcloud *": "deny", - "terraform *": "deny", - "curl *": "deny", - "wget *": "deny" - }, - "webfetch": "deny" - }, - - // Project-specific AI instructions - "instructions": ["AGENTS.md"] -} From 7cf638df8ba846dee8a30272be12cef2a74e4a09 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Fri, 12 Dec 2025 13:00:42 -0500 Subject: [PATCH 16/22] docs(ai): fixing install docs --- CONFIGURATION_GUIDE.md | 11 ++++------- DEPENDENCIES.md | 15 ++++++--------- README.md | 28 +++++++++++++++++----------- examples/opencode.jsonc | 6 +++--- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/CONFIGURATION_GUIDE.md b/CONFIGURATION_GUIDE.md index a20577a..f8081f2 100644 --- a/CONFIGURATION_GUIDE.md +++ b/CONFIGURATION_GUIDE.md @@ -58,14 +58,11 @@ This runs automatically on `git push` and reviews all changes in your branch. The AI commit check hook **requires** an `opencode.jsonc` configuration file in your repository root to enforce security policies. **Setup:** -```bash -# Download the security configuration to your repo root -curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/examples/opencode.jsonc -# Commit the configuration -git add opencode.jsonc -git commit -m "Add OpenCode security configuration" -``` +1. Copy the contents of [`examples/opencode.jsonc`](https://github.com/TriaFed/pre-commit-library/blob/main/examples/opencode.jsonc) from this repository +2. Create a file named `opencode.jsonc` in your repository root +3. Paste the contents into your `opencode.jsonc` file +4. Commit the configuration to your repository **What's included:** - Share disabled diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index 694d85a..972e70f 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -149,17 +149,14 @@ opencode auth login Download the security configuration to your repository root: -```bash -cd /path/to/your/repo -curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/examples/opencode.jsonc +**Setup:** -# Optional: Initialize AI instructions for your project -# Run: opencode -# Then in the opencode window, type: /init +1. Copy the contents of [`examples/opencode.jsonc`](https://github.com/TriaFed/pre-commit-library/blob/main/examples/opencode.jsonc) from this repository +2. Create a file named `opencode.jsonc` in your repository root +3. Paste the contents into your `opencode.jsonc` file +4. Commit the configuration to your repository -git add opencode.jsonc -git commit -m "Add opencode security configuration" -``` +**Optional:** Initialize AI instructions for your project by running `opencode` and typing `/init` in the OpenCode window. The `opencode.jsonc` file is **required** and must deny dangerous operations: - `webfetch: "deny"` - Blocks external network requests diff --git a/README.md b/README.md index fa5f954..1b6dbf2 100644 --- a/README.md +++ b/README.md @@ -535,14 +535,11 @@ pre-commit install --hook-type pre-push This hook **requires** an `opencode.jsonc` configuration file in your repository root to enforce security policies. **Setup:** -```bash -# Download the security configuration to your repo root -curl -o opencode.jsonc https://raw.githubusercontent.com/TriaFed/pre-commit-library/main/examples/opencode.jsonc -# Commit the configuration -git add opencode.jsonc -git commit -m "Add OpenCode security configuration" -``` +1. Copy the contents of [`examples/opencode.jsonc`](https://github.com/TriaFed/pre-commit-library/blob/main/examples/opencode.jsonc) from this repository +2. Create a file named `opencode.jsonc` in your repository root +3. Paste the contents into your `opencode.jsonc` file +4. Commit the configuration to your repository The `opencode.jsonc` file enforces: - `share: "disabled"` - No sharing of conversations @@ -561,22 +558,31 @@ When the hook runs, you'll see: If the file is missing, the hook will exit with an error and instructions. +**Important:** The `opencode.jsonc` file defines security policies and available models, but does NOT hardcode which model to use. Model selection is controlled via environment variables (see below), allowing you to switch between GitHub Copilot and Amazon Bedrock. + **Environment Variables:** ```bash -# GitHub Copilot Configuration (Default) -export OPENCODE_PROVIDER=github-copilot -export OPENCODE_MODEL=github-copilot/claude-sonnet-4.5 +# GitHub Copilot Configuration (Default - no env vars needed) +# The hook defaults to GitHub Copilot if no variables are set # Amazon Bedrock Configuration export OPENCODE_PROVIDER=amazon-bedrock export OPENCODE_MODEL=amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0 export OPENCODE_BEDROCK_REGION=us-east-1 # Required: us-east-1 or us-gov-* -# Server Configuration +# Server Configuration (optional) export OPENCODE_PORT=61164 # Default: 61164 export OPENCODE_TIMEOUT=90 # Default: 90 seconds ``` +**Pro Tip:** Add Bedrock variables to your `~/.zshrc` or `~/.bashrc` to avoid setting them every time: +```bash +# Add to ~/.zshrc or ~/.bashrc +export OPENCODE_PROVIDER=amazon-bedrock +export OPENCODE_MODEL=amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0 +export OPENCODE_BEDROCK_REGION=us-east-1 +``` + **Usage:** ```bash # Pre-commit mode (review staged changes) diff --git a/examples/opencode.jsonc b/examples/opencode.jsonc index a7e94e3..0710e93 100644 --- a/examples/opencode.jsonc +++ b/examples/opencode.jsonc @@ -5,9 +5,9 @@ "share": "disabled", "autoupdate": false, - // Model configuration - ONLY allow approved models - "model": "github-copilot/claude-sonnet-4.5", - "small_model": "github-copilot/claude-sonnet-4.5", + // Note: Model selection is controlled via OPENCODE_MODEL environment variable + // Default: github-copilot/claude-sonnet-4.5 + // Bedrock: amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0 // Disable all providers except approved ones (github-copilot and amazon-bedrock) "disabled_providers": [ From 185a8126f57a7343a510716ab8c8ee908dee1492 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Fri, 12 Dec 2025 13:34:01 -0500 Subject: [PATCH 17/22] fixing docs and bedrock integration --- README.md | 88 +++++++++++++++++++--------------------- hooks/ai_commit_check.py | 21 +++++++++- 2 files changed, 61 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 1b6dbf2..af10b46 100644 --- a/README.md +++ b/README.md @@ -130,9 +130,9 @@ pre-commit run --all-files ### šŸ¤– AI-Powered Hooks -| Hook ID | Description | Languages | -| ------------------ | ------------------------------------------------ | --------- | -| `ai_commit_check` | AI-powered commit validation with Opencode AI | All | +| Hook ID | Description | Languages | +| ----------------- | --------------------------------------------- | --------- | +| `ai_commit_check` | AI-powered commit validation with Opencode AI | All | ### šŸ”’ Security Hooks @@ -484,13 +484,14 @@ The `ai_commit_check` hook uses Opencode AI to review your code changes. It supp - **Pre-push mode**: Reviews all branch changes against `origin/main` or `origin/master` before pushing **Pre-commit Configuration:** + ```yaml repos: - repo: https://github.com/TriaFed/pre-commit-library rev: v1.1.7 hooks: - id: ai_commit_check - stages: [manual] # Default: Run manually (opt-in) + stages: [manual] # Default: Run manually (opt-in) # To enable automatic pre-commit: stages: [pre-commit] # To enable automatic pre-push: stages: [pre-push] ``` @@ -498,10 +499,11 @@ repos: **Note:** The AI commit check is **disabled by default** (manual stage). You must explicitly opt-in by changing the `stages` configuration. **Pre-push Configuration (Recommended for comprehensive review):** + ```yaml repos: - repo: https://github.com/TriaFed/pre-commit-library - rev: # Replace with latest release version + rev: # Replace with latest release version hooks: - id: ai_commit_check stages: @@ -509,6 +511,7 @@ repos: ``` **Setup:** + ```bash # Install opencode pip install opencode-ai rich @@ -517,7 +520,7 @@ npm install -g @sst/opencode # Authenticate with Opencode opencode auth login -# Select: github-copilot +# Select: github, choose the Github Public option and follow the instructions, login in with your TriaFederal account. # Configure the AI model # In the opencode interface, enter: /models @@ -542,6 +545,7 @@ This hook **requires** an `opencode.jsonc` configuration file in your repository 4. Commit the configuration to your repository The `opencode.jsonc` file enforces: + - `share: "disabled"` - No sharing of conversations - `autoupdate: false` - No automatic updates - `webfetch: "deny"` - Prevents external network requests @@ -552,6 +556,7 @@ The `opencode.jsonc` file enforces: **Runtime Output:** When the hook runs, you'll see: + ``` āœ“ Using opencode.jsonc from repository root ``` @@ -561,6 +566,7 @@ If the file is missing, the hook will exit with an error and instructions. **Important:** The `opencode.jsonc` file defines security policies and available models, but does NOT hardcode which model to use. Model selection is controlled via environment variables (see below), allowing you to switch between GitHub Copilot and Amazon Bedrock. **Environment Variables:** + ```bash # GitHub Copilot Configuration (Default - no env vars needed) # The hook defaults to GitHub Copilot if no variables are set @@ -576,6 +582,7 @@ export OPENCODE_TIMEOUT=90 # Default: 90 seconds ``` **Pro Tip:** Add Bedrock variables to your `~/.zshrc` or `~/.bashrc` to avoid setting them every time: + ```bash # Add to ~/.zshrc or ~/.bashrc export OPENCODE_PROVIDER=amazon-bedrock @@ -584,6 +591,7 @@ export OPENCODE_BEDROCK_REGION=us-east-1 ``` **Usage:** + ```bash # Pre-commit mode (review staged changes) pre-commit run --hook-stage manual ai_commit_check @@ -600,42 +608,24 @@ pre-commit run --hook-stage pre-push ai_commit_check ``` The hook automatically detects its mode: + - **Pre-commit**: If there are staged changes, reviews only those changes - **Pre-push**: If no staged changes, compares current branch against `origin/main` or `origin/master` The hook will: + - Review code quality and best practices - Identify potential bugs or security concerns - Suggest improvements - Block commits/pushes with critical issues (starting with "-COMMIT REJECTED-") - Provide commands to continue the AI session or auto-fix issues -**Configuring Permissions (Optional):** - -The required `opencode.jsonc` file includes safe defaults. You can customize permissions further: - -```jsonc -{ - "$schema": "https://opencode.ai/config.json", - "permission": { - "edit": "allow", // Allow file edits for auto-fixing - "bash": { - "*": "deny", // Deny all commands by default - "git status": "allow", - "npm run test": "ask" - }, - "webfetch": "deny" // Required: Must be denied - } -} -``` - -Learn more: [OpenCode Permissions Documentation](https://opencode.ai/docs/permissions/) - ### Using Amazon Bedrock The AI commit check hook supports Amazon Bedrock with Claude Sonnet 4.5. For security and compliance, only specific AWS regions are allowed. **Allowed Regions:** + - `us-east-1` (US East - N. Virginia) - `us-gov-west-1` (AWS GovCloud US-West) - `us-gov-east-1` (AWS GovCloud US-East) @@ -643,34 +633,31 @@ The AI commit check hook supports Amazon Bedrock with Claude Sonnet 4.5. For sec **Setup Steps:** 1. **Configure AWS credentials and region:** + ```bash # Set your AWS region aws configure set region us-east-1 - + # Verify your configuration aws sts get-caller-identity ``` -2. **Authenticate with OpenCode:** - ```bash - opencode auth login - # Select: Amazon Bedrock - ``` +2. **Set environment variables:** -3. **Set environment variables:** ```bash export OPENCODE_PROVIDER=amazon-bedrock export OPENCODE_MODEL=amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0 export OPENCODE_BEDROCK_REGION=us-east-1 ``` -4. **Verify the configuration:** +3. **Verify the configuration:** + ```bash # Test that OpenCode can access Bedrock - opencode run "hello world" + opencode run "hello world" --model amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0 ``` -5. **Run the AI commit check:** +4. **Run the AI commit check:** ```bash pre-commit run --hook-stage manual ai_commit_check ``` @@ -678,11 +665,13 @@ The AI commit check hook supports Amazon Bedrock with Claude Sonnet 4.5. For sec **Troubleshooting:** If you get a region error: + ``` Error: Amazon Bedrock region 'us-west-2' is not allowed. ``` Fix by setting the correct region: + ```bash export OPENCODE_BEDROCK_REGION=us-east-1 aws configure set region us-east-1 @@ -691,6 +680,7 @@ aws configure set region us-east-1 **Why only specific regions?** For security and compliance reasons, this hook restricts Amazon Bedrock to: + - `us-east-1` - Standard AWS region with Claude Sonnet 4.5 availability - `us-gov-*` - AWS GovCloud regions for government compliance @@ -701,25 +691,30 @@ If you need a different region, please file an issue with your compliance requir Provide project-specific context to the AI by creating instruction files: 1. **Initialize default instructions:** + ```bash # Start opencode opencode - + # Then in the opencode window, type: /init ``` + This creates instruction files (like `CLAUDE.md`) with AI guidelines for your project. 2. **Or create custom instruction files manually:** + ```markdown # AGENTS.md - Project-specific AI review guidelines - + ## Code Standards + - Use TypeScript strict mode - Follow React hooks best practices - All API calls must include error handling - + ## Security Requirements + - No hardcoded credentials or API keys - All user input must be validated - Database queries must use parameterized statements @@ -728,11 +723,12 @@ Provide project-specific context to the AI by creating instruction files: 3. **Reference instruction files in `opencode.jsonc`:** ```jsonc { - "instruction": ["AGENTS.md", "CLAUDE.md", "docs/CODING_STANDARDS.md"] + "instruction": ["AGENTS.md", "CLAUDE.md", "docs/CODING_STANDARDS.md"], } ``` **Example Files:** + - See `examples/opencode.jsonc` for recommended permission settings **Troubleshooting:** @@ -785,8 +781,8 @@ Finds hardcoded passwords, API keys, and tokens: ```javascript // āŒ Will be flagged -const apiKey = 'sk-1234567890abcdef'; -const password = 'mySecretPassword123'; +const apiKey = "sk-1234567890abcdef"; +const password = "mySecretPassword123"; // āœ… Safe alternatives const apiKey = process.env.API_KEY; @@ -794,7 +790,7 @@ const password = process.env.PASSWORD; // āœ… For false positives, use inline comments to suppress persistState(store, { - key: 'AppPreferences', // pragma: allowlist secret + key: "AppPreferences", // pragma: allowlist secret storage: sessionStorage, }); ``` @@ -813,7 +809,7 @@ config = { ```javascript // JavaScript example const settings = { - key: 'PreferenceKey', // pragma: allowlist secret + key: "PreferenceKey", // pragma: allowlist secret }; ``` @@ -1001,7 +997,7 @@ detect-secrets scan --baseline .secrets.baseline --force-use-all-plugins ```javascript // eslint-disable-next-line rule-name -const problematicCode = 'value'; +const problematicCode = "value"; ``` #### Semgrep diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index 39ecea3..81a7ba7 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -54,6 +54,15 @@ def main(): OPENCODE_PROVIDER = os.getenv('OPENCODE_PROVIDER', 'github-copilot') OPENCODE_TIMEOUT = int(os.getenv('OPENCODE_TIMEOUT', '90')) OPENCODE_BEDROCK_REGION = os.getenv('OPENCODE_BEDROCK_REGION', 'us-east-1') + + # Parse model and provider from OPENCODE_MODEL if it contains a slash + # Format: "provider/model-name" -> provider="provider", model="provider/model-name" + # The SDK expects model_id to include the provider prefix + if '/' in OPENCODE_MODEL: + parsed_provider = OPENCODE_MODEL.split('/')[0] + # Use the parsed provider only if OPENCODE_PROVIDER wasn't explicitly set + if os.getenv('OPENCODE_PROVIDER') is None: + OPENCODE_PROVIDER = parsed_provider if not (1024 <= OPENCODE_PORT <= 65535): print(f"Error: Invalid port {OPENCODE_PORT}. Port must be between 1024 and 65535.", file=sys.stderr) @@ -129,6 +138,8 @@ def main(): return 3 print(f"āœ“ Using opencode.jsonc from repository root", file=sys.stderr) + print(f"āœ“ Model: {OPENCODE_MODEL}", file=sys.stderr) + print(f"āœ“ Provider: {OPENCODE_PROVIDER}", file=sys.stderr) available_port = find_available_port(OPENCODE_PORT) if not available_port: @@ -147,12 +158,18 @@ def main(): os.environ['AWS_DEFAULT_REGION'] = OPENCODE_BEDROCK_REGION os.environ['AWS_REGION'] = OPENCODE_BEDROCK_REGION + # Create environment for the opencode server subprocess + server_env = os.environ.copy() + try: + # Start OpenCode server with explicit model selection + # The --model flag accepts format: provider/model-name serve_process = subprocess.Popen( - ['opencode', 'serve', '--port', str(OPENCODE_PORT)], + ['opencode', 'serve', '--port', str(OPENCODE_PORT), '--model', OPENCODE_MODEL], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - cwd=repo_root # Run in repo root so it can see git diffs + cwd=repo_root, # Run in repo root so it can see git diffs + env=server_env ) except FileNotFoundError: print("Error: opencode is not installed.", file=sys.stderr) From dcf819fcf7e626856c7c02a169512d6f8ea5a4d5 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Fri, 12 Dec 2025 14:21:00 -0500 Subject: [PATCH 18/22] another fix --- hooks/ai_commit_check.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index 81a7ba7..5c4249d 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -54,7 +54,7 @@ def main(): OPENCODE_PROVIDER = os.getenv('OPENCODE_PROVIDER', 'github-copilot') OPENCODE_TIMEOUT = int(os.getenv('OPENCODE_TIMEOUT', '90')) OPENCODE_BEDROCK_REGION = os.getenv('OPENCODE_BEDROCK_REGION', 'us-east-1') - + # Parse model and provider from OPENCODE_MODEL if it contains a slash # Format: "provider/model-name" -> provider="provider", model="provider/model-name" # The SDK expects model_id to include the provider prefix @@ -96,11 +96,11 @@ def main(): print(" Select: Amazon Bedrock", file=sys.stderr) print("", file=sys.stderr) return 3 - + # Validate region is in allowed list allowed_regions = ['us-east-1'] is_govcloud = OPENCODE_BEDROCK_REGION.startswith('us-gov-') - + if not (OPENCODE_BEDROCK_REGION in allowed_regions or is_govcloud): print(f"Error: Amazon Bedrock region '{OPENCODE_BEDROCK_REGION}' is not allowed.", file=sys.stderr) print("", file=sys.stderr) @@ -118,7 +118,7 @@ def main(): serve_process = None try: repo_root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip() - + # Check for opencode.jsonc in repo root config_path = os.path.join(repo_root, 'opencode.jsonc') if not os.path.exists(config_path): @@ -136,11 +136,11 @@ def main(): print("", file=sys.stderr) print("See CONFIGURATION_GUIDE.md for more details.", file=sys.stderr) return 3 - + print(f"āœ“ Using opencode.jsonc from repository root", file=sys.stderr) print(f"āœ“ Model: {OPENCODE_MODEL}", file=sys.stderr) print(f"āœ“ Provider: {OPENCODE_PROVIDER}", file=sys.stderr) - + available_port = find_available_port(OPENCODE_PORT) if not available_port: print(f"Error: No available ports found starting from port {OPENCODE_PORT}. " @@ -165,7 +165,7 @@ def main(): # Start OpenCode server with explicit model selection # The --model flag accepts format: provider/model-name serve_process = subprocess.Popen( - ['opencode', 'serve', '--port', str(OPENCODE_PORT), '--model', OPENCODE_MODEL], + ['opencode', 'serve', '--port', str(OPENCODE_PORT)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=repo_root, # Run in repo root so it can see git diffs @@ -227,13 +227,13 @@ def main(): review_target = "the staged changes for this commit" review_context = """ **REVIEW MODE:** Pre-commit (staged changes) - + Use `git diff --cached` to see the staged changes that are about to be committed. """ else: # Pre-push mode: compare current branch to origin/main or origin/master review_mode = "pre-push" - + # Determine the default branch (main or master) default_branch = None for branch in ['main', 'master']: @@ -248,17 +248,17 @@ def main(): break except subprocess.CalledProcessError: continue - + if not default_branch: print("Error: Could not find origin/main or origin/master branch for comparison.", file=sys.stderr) print("Please ensure you have a remote tracking branch set up.", file=sys.stderr) return 3 - + review_target = f"all changes in the current branch compared to origin/{default_branch}" review_context = f""" **REVIEW MODE:** Pre-push (branch comparison) - - Use `git diff origin/{default_branch}...HEAD` to see all changes in the current branch + + Use `git diff origin/{default_branch}...HEAD` to see all changes in the current branch that differ from origin/{default_branch}. Review ALL commits and changes since branching. """ @@ -327,11 +327,11 @@ def render_markdown_terminal(message: Optional[str]) -> None: print(f"{title}\n\n{message}") last_msg = get_last_assistant_message(new_session_chat_messages) - + is_rejected = last_msg and '-COMMIT REJECTED-' in last_msg if is_rejected and last_msg: last_msg = last_msg.replace('-COMMIT REJECTED-', '').strip() - + render_markdown_terminal(last_msg) continue_command = f"opencode --session {new_session_id}" From 2dbc30d9749e85309fc699aed90a6d568e87938c Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Fri, 12 Dec 2025 14:42:57 -0500 Subject: [PATCH 19/22] fixing documentation, bedrock guide --- README.md | 5 +++-- examples/opencode.jsonc | 27 +++++++++++++++++---------- hooks/ai_commit_check.py | 4 ---- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index af10b46..64cd304 100644 --- a/README.md +++ b/README.md @@ -624,6 +624,8 @@ The hook will: The AI commit check hook supports Amazon Bedrock with Claude Sonnet 4.5. For security and compliance, only specific AWS regions are allowed. +You need to set the model in your opencode.jsonc. There is an example in examples/opencode.jsonc. You can set it with an env variable or set it directly to bedrock. + **Allowed Regions:** - `us-east-1` (US East - N. Virginia) @@ -645,7 +647,6 @@ The AI commit check hook supports Amazon Bedrock with Claude Sonnet 4.5. For sec 2. **Set environment variables:** ```bash - export OPENCODE_PROVIDER=amazon-bedrock export OPENCODE_MODEL=amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0 export OPENCODE_BEDROCK_REGION=us-east-1 ``` @@ -729,7 +730,7 @@ Provide project-specific context to the AI by creating instruction files: **Example Files:** -- See `examples/opencode.jsonc` for recommended permission settings +- See [`examples/opencode.jsonc`](examples/opencode.jsonc) for recommended permission settings **Troubleshooting:** If the hook fails with authentication errors, ensure you've completed the `opencode auth login` setup above. diff --git a/examples/opencode.jsonc b/examples/opencode.jsonc index 0710e93..b85a571 100644 --- a/examples/opencode.jsonc +++ b/examples/opencode.jsonc @@ -1,18 +1,25 @@ { "$schema": "https://opencode.ai/config.json", - + // Security: Disable sharing and auto-updates "share": "disabled", "autoupdate": false, - - // Note: Model selection is controlled via OPENCODE_MODEL environment variable - // Default: github-copilot/claude-sonnet-4.5 - // Bedrock: amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0 - + + "model": "{env:OPENCODE_MODEL}", + "small_model": "{env:OPENCODE_MODEL}", + // You can set the model using env vars if you need to alternate between bedrock and github-copilot. + + // "model": "{env:OPENCODE_MODEL}", + // "small_model": "{env:OPENCODE_MODEL}", + + // Set bedrock model + // "model": "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0", + // "small_model": "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0", + // Disable all providers except approved ones (github-copilot and amazon-bedrock) "disabled_providers": [ "openai", - "anthropic", + "anthropic", "gemini", "azure", "openrouter", @@ -26,7 +33,7 @@ "mistral", "perplexity" ], - + // Provider-specific configuration "provider": { "github-copilot": { @@ -44,7 +51,7 @@ } } }, - + // Strict permissions - deny dangerous operations "permission": { "edit": "allow", @@ -73,7 +80,7 @@ }, "webfetch": "deny" }, - + // Project-specific AI instructions "instructions": ["AGENTS.md"] } diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index 5c4249d..49c4d45 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -137,10 +137,6 @@ def main(): print("See CONFIGURATION_GUIDE.md for more details.", file=sys.stderr) return 3 - print(f"āœ“ Using opencode.jsonc from repository root", file=sys.stderr) - print(f"āœ“ Model: {OPENCODE_MODEL}", file=sys.stderr) - print(f"āœ“ Provider: {OPENCODE_PROVIDER}", file=sys.stderr) - available_port = find_available_port(OPENCODE_PORT) if not available_port: print(f"Error: No available ports found starting from port {OPENCODE_PORT}. " From e10b7758dd1758017e069c3734d4fa842bf17ad3 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Fri, 12 Dec 2025 14:49:05 -0500 Subject: [PATCH 20/22] setting copilot to default --- examples/opencode.jsonc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/opencode.jsonc b/examples/opencode.jsonc index b85a571..24f6b98 100644 --- a/examples/opencode.jsonc +++ b/examples/opencode.jsonc @@ -5,8 +5,8 @@ "share": "disabled", "autoupdate": false, - "model": "{env:OPENCODE_MODEL}", - "small_model": "{env:OPENCODE_MODEL}", + "model": "github-copilot/claude-sonnet-4.5", + "small_model": "github-copilot/claude-sonnet-4.5", // You can set the model using env vars if you need to alternate between bedrock and github-copilot. // "model": "{env:OPENCODE_MODEL}", @@ -15,7 +15,7 @@ // Set bedrock model // "model": "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0", // "small_model": "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0", - + // Note: Model selection is controlled via OPENCODE_MODEL environment variable // Disable all providers except approved ones (github-copilot and amazon-bedrock) "disabled_providers": [ "openai", From ce48e7d67f3c2d03cfa69ce119ce25c837a8d22d Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Fri, 12 Dec 2025 14:53:17 -0500 Subject: [PATCH 21/22] adding xai to blcoked providers --- examples/opencode.jsonc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/opencode.jsonc b/examples/opencode.jsonc index 24f6b98..f6b766b 100644 --- a/examples/opencode.jsonc +++ b/examples/opencode.jsonc @@ -31,7 +31,8 @@ "deepseek", "cohere", "mistral", - "perplexity" + "perplexity", + "xai" ], // Provider-specific configuration From 5d6f459f19fa50b95fabf52c41ff3ad0e620a3b1 Mon Sep 17 00:00:00 2001 From: Matt Donnelly Date: Fri, 12 Dec 2025 15:08:39 -0500 Subject: [PATCH 22/22] fixing fail closed instead of fail open, requires ai signoff --- README.md | 8 +++++- hooks/ai_commit_check.py | 54 ++++++++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 64cd304..0a1fc12 100644 --- a/README.md +++ b/README.md @@ -617,9 +617,15 @@ The hook will: - Review code quality and best practices - Identify potential bugs or security concerns - Suggest improvements -- Block commits/pushes with critical issues (starting with "-COMMIT REJECTED-") +- **Require explicit approval**: The AI must include "-COMMIT APPROVED-" in its response to allow the commit +- **Block commits/pushes** in these cases: + - Critical issues found (response contains "-COMMIT REJECTED-") + - No response received from AI + - Response doesn't include explicit "-COMMIT APPROVED-" marker - Provide commands to continue the AI session or auto-fix issues +**Important:** The hook enforces a strict approval model. The AI must explicitly approve each commit with "-COMMIT APPROVED-" in its response. This ensures the AI has actively reviewed your changes and found no blocking issues, rather than passively accepting by default. + ### Using Amazon Bedrock The AI commit check hook supports Amazon Bedrock with Claude Sonnet 4.5. For security and compliance, only specific AWS regions are allowed. diff --git a/hooks/ai_commit_check.py b/hooks/ai_commit_check.py index 49c4d45..ea46869 100755 --- a/hooks/ai_commit_check.py +++ b/hooks/ai_commit_check.py @@ -284,8 +284,9 @@ def main(): **RESPONSE FORMAT:** - If you find critical issues that should block the commit, start your response with "-COMMIT REJECTED-" followed by a clear explanation of the blocking issues with specific file names and line numbers - - If the code has no significant issues, respond with exactly: "āœ… **Code looks good!** No significant issues found." - - If you have suggestions but no blockers, provide constructive feedback with specific suggestions + - If the code has no significant issues that should block the commit, you MUST include "-COMMIT APPROVED-" + in your response, followed by any optional feedback or suggestions + - Always provide specific, actionable feedback """} ], model_id=OPENCODE_MODEL, provider_id=OPENCODE_PROVIDER) @@ -324,14 +325,27 @@ def render_markdown_terminal(message: Optional[str]) -> None: last_msg = get_last_assistant_message(new_session_chat_messages) - is_rejected = last_msg and '-COMMIT REJECTED-' in last_msg - if is_rejected and last_msg: - last_msg = last_msg.replace('-COMMIT REJECTED-', '').strip() + # Check for valid AI response + if not last_msg: + render_markdown_terminal(None) + print("\nāŒ ERROR: No response received from AI review. Blocking commit.", file=sys.stderr) + return 1 + + is_rejected = '-COMMIT REJECTED-' in last_msg + is_approved = '-COMMIT APPROVED-' in last_msg - render_markdown_terminal(last_msg) + # Clean up markers for display + display_msg = last_msg + if is_rejected: + display_msg = display_msg.replace('-COMMIT REJECTED-', '').strip() + if is_approved: + display_msg = display_msg.replace('-COMMIT APPROVED-', '').strip() + + render_markdown_terminal(display_msg) continue_command = f"opencode --session {new_session_id}" + # If explicitly rejected, block the commit if is_rejected: fix_command = f"opencode run \"resolve these issues\" --session {new_session_id}" @@ -358,6 +372,30 @@ def render_markdown_terminal(message: Optional[str]) -> None: print('='*80) return 1 + + # If not explicitly approved, block the commit + elif not is_approved: + if Console and Panel: + console = Console() + console.print('\n') + console.print(Panel( + f"[bold red]AI review did not explicitly approve this commit.[/bold red]", + border_style='bold red', + padding=(1, 2) + )) + console.print('\n[bold yellow]To continue chatting with this session:[/bold yellow]') + console.print(f'[bold cyan]{continue_command}[/bold cyan]') + else: + print('\n' + '='*80) + print('COMMIT REJECTED - AI review did not explicitly approve') + print('='*80) + print('\nTo continue chatting with this session:') + print(f"{continue_command}") + print('='*80) + + return 1 + + # Explicitly approved - allow commit else: improve_command = f"opencode run \"apply the suggested improvements\" --session {new_session_id}" @@ -365,7 +403,7 @@ def render_markdown_terminal(message: Optional[str]) -> None: console = Console() console.print('\n') console.print(Panel( - f"[bold green]āœ“ No blocking issues found.[/bold green]", + f"[bold green]āœ“ Commit approved by AI review.[/bold green]", border_style='green', padding=(1, 2) )) @@ -375,7 +413,7 @@ def render_markdown_terminal(message: Optional[str]) -> None: console.print(f'[bold cyan]{continue_command}[/bold cyan]') else: print('\n' + '='*80) - print('No blocking issues found') + print('Commit approved by AI review') print('='*80) print('\nTo apply suggested improvements:') print(f"{improve_command}\n")