Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions examples/skills_code_review_agent/DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Skills Code Review Agent 方案设计

本示例以 `code-review` Skill 描述审查流程和规则,由 Agent 通过受控工具加载 Skill,并调用固定扫描脚本处理统一格式的 diff。输入层支持 diff 文件、文件列表、Git 工作区及内置 fixture,保留文件、hunk 和候选行号,二进制及非法路径转为明确告警或错误。

生产默认使用禁网 Container workspace;local runtime 仅在显式环境变量开启时作为开发后备。执行计划固定命令、工作目录、输入输出路径、环境变量名、超时和输出上限,并计算摘要。Filter 在执行前校验计划完整性、路径、命令、环境、网络与预算,先持久化决定;`DENY` 和 `NEEDS_HUMAN_REVIEW` 不进入沙箱。

SQLite 默认保存 review task、输入摘要、Filter 决定、sandbox run、finding 和最终报告,存储接口复用 SQLAlchemy/`SqlStorage`,可通过 SQL URL 切换受支持后端。Finding 使用固定结构,按任务、文件、行号和类别去重;低置信结果进入 warning 与人工复核,不混入高置信发现。

统一脱敏器覆盖沙箱输出、异常、数据库及 JSON/Markdown 报告,原始 diff 不入库。报告记录总耗时、沙箱耗时、工具调用、拦截次数、finding 数量、严重级别和异常类型分布。超时、输出超限或沙箱失败均形成可查询记录,不使评审流程崩溃;fake model 无需 API Key,仍执行解析、Filter、沙箱和落库主链。
166 changes: 166 additions & 0 deletions examples/skills_code_review_agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Skills Code Review Agent

This example implements Issue #92 as a standalone, policy-gated code review
workflow. It accepts a unified diff, a file list, a Git workspace, or one of
eight fixtures and writes queryable JSON/Markdown reports plus SQLite audit
records. It does not change framework production APIs.

## What it includes

- a `code-review` Skill with deterministic rules and a sandbox script;
- unified diff, file-list, fixture, and Git input adapters;
- structured findings with deduplication and confidence routing;
- a Filter for command, path, environment, network, and budget policy;
- Container execution by default and an explicit local development fallback;
- SQLite-backed task, Filter, sandbox, finding, and report records;
- fake-model and dry-run modes that require no model API key.

## Quick start

Docker must be available for the default Container runtime:

```powershell
uv run python examples/skills_code_review_agent/run_agent.py run `
--fixture security `
--runtime container `
--fake-model
```

The command prints the task ID and the two report paths:

```text
reports/<task-id>/review_report.json
reports/<task-id>/review_report.md
```

Use `--dry-run` to parse, filter, audit, and generate a report without invoking
the sandbox:

```powershell
uv run python examples/skills_code_review_agent/run_agent.py run `
--fixture clean `
--dry-run
```

## Input modes

Exactly one input option is required:

```powershell
# Unified diff
uv run python examples/skills_code_review_agent/run_agent.py run `
--diff-file change.diff --fake-model

# UTF-8 file list; paths are relative to the list file's directory
uv run python examples/skills_code_review_agent/run_agent.py run `
--file-list changed-files.txt --fake-model

# Staged Git changes
uv run python examples/skills_code_review_agent/run_agent.py run `
--repo-path . --staged --fake-model

# Worktree-only Git changes
uv run python examples/skills_code_review_agent/run_agent.py run `
--repo-path . --worktree --fake-model

# Revision range
uv run python examples/skills_code_review_agent/run_agent.py run `
--repo-path . --base main --head HEAD --fake-model
```

With `--repo-path` and no range option, the input is `git diff HEAD`. Untracked
files are not included.

## Runtime security

Container is the production default and starts with Docker
`network_mode=none`. Only the fixed Skill files and normalized review input are
staged. The Filter audits the immutable execution plan before the sandbox is
called; denied or human-review decisions do not execute.

Local execution is an unsafe development fallback and is rejected unless it is
explicitly enabled:

```powershell
$env:TRPC_CODE_REVIEW_ALLOW_UNSAFE_LOCAL = "1"
uv run python examples/skills_code_review_agent/run_agent.py run `
--fixture security `
--runtime local `
--fake-model
```

Do not enable the local runtime in production.

## Real model

The real-model path uses the same Skill, Filter, sandbox, storage, and report
contracts. Configure an OpenAI-compatible endpoint:

```powershell
$env:OPENAI_API_KEY = "<your-key>"
$env:OPENAI_BASE_URL = "https://api.example.invalid/v1"
$env:MODEL_NAME = "model-name"

uv run python examples/skills_code_review_agent/run_agent.py run `
--diff-file change.diff `
--runtime container
```

Model credentials are not included in the sandbox environment.

## Database and query

The default database is:

```text
sqlite:///skills_code_review.db
```

Override it with `--db-url` or `TRPC_CODE_REVIEW_DB_URL`. Initialize tables
without running a review:

```powershell
uv run python examples/skills_code_review_agent/scripts/init_db.py `
--db-url sqlite:///skills_code_review.db
```

Query a task and its persisted report:

```powershell
uv run python examples/skills_code_review_agent/run_agent.py show `
--task-id <task-id>
```

The report contains findings, warnings requiring human review, Filter
decisions, sandbox summaries, exceptions, metrics, and the final conclusion.
Sensitive text is redacted before report or database persistence.

## Tests

Run the lightweight suite:

```powershell
uv run pytest tests/examples/skills_code_review_agent -q
```

Run the coverage gate:

```powershell
uv run pytest tests/examples/skills_code_review_agent `
--cov=examples.skills_code_review_agent `
--cov-report=term-missing `
--cov-fail-under=90
```

Run the optional real Container integration:

```powershell
$env:TRPC_CODE_REVIEW_CONTAINER_TEST = "1"
uv run pytest `
tests/examples/skills_code_review_agent/test_acceptance.py `
-k container -q
```

Without that environment variable, only the Container integration test is
skipped. SQLite concurrency tests use simultaneous writes and unique-key merge
handling; Python has no Go-style race detector.
19 changes: 19 additions & 0 deletions examples/skills_code_review_agent/agent/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Core components for the skills code review example."""

from .input_parser import load_diff_file
from .input_parser import load_file_list
from .input_parser import load_repo_diff
from .input_parser import parse_diff_text
from .input_parser import GitDiffOptions
from .models import Finding
from .models import ReviewInput

__all__ = [
"Finding",
"GitDiffOptions",
"ReviewInput",
"load_diff_file",
"load_file_list",
"load_repo_diff",
"parse_diff_text",
]
65 changes: 65 additions & 0 deletions examples/skills_code_review_agent/agent/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""Named limits and defaults used by the code review example."""

from pathlib import Path

APP_NAME = "skills_code_review_agent"
DEFAULT_DB_URL = "sqlite:///skills_code_review.db"
DEFAULT_OUTPUT_DIR = Path("reports")
DEFAULT_CONTEXT_LINES = 3

MAX_DIFF_BYTES = 2 * 1024 * 1024
MAX_FILE_BYTES = 512 * 1024
MAX_FILES = 200
MAX_HUNKS_PER_FILE = 500
MAX_CHANGED_LINES = 20_000
MAX_PATH_LENGTH = 512
MAX_OUTPUT_BYTES = 64 * 1024
MAX_JSONL_LINES = 2_000
MAX_JSONL_LINE_BYTES = 16 * 1024
MAX_JSON_DEPTH = 12
MAX_TEXT_FIELD_LENGTH = 8_192

GIT_TIMEOUT_SECONDS = 15.0
FILTER_TIMEOUT_SECONDS = 5.0
SANDBOX_TIMEOUT_SECONDS = 30.0
CLEANUP_TIMEOUT_SECONDS = 5.0
MODEL_TIMEOUT_SECONDS = 45.0
REPORT_TIMEOUT_SECONDS = 5.0
TOTAL_TIMEOUT_SECONDS = 90.0

MIN_ACTIONABLE_CONFIDENCE = 0.70
MAX_RETRY_ATTEMPTS = 2
SQLITE_BUSY_TIMEOUT_MILLISECONDS = 5_000
DB_ID_LENGTH = 64
DB_STATUS_LENGTH = 32
DB_CATEGORY_LENGTH = 64
DB_PATH_LENGTH = 512
DB_TITLE_LENGTH = 512
DB_SOURCE_LENGTH = 256

ALLOWED_ENV_NAMES = frozenset({
"LANG",
"LC_ALL",
"PYTHONIOENCODING",
"PYTHONUTF8",
})
ALLOWED_INTERPRETERS = frozenset({"python", "python3"})
BLOCKED_PATH_PARTS = frozenset({
".git",
".ssh",
".aws",
".config",
"etc",
"proc",
"sys",
})

REDACTION_MARKER = "[REDACTED]"
LOCAL_RUNTIME_OPT_IN_ENV = "TRPC_CODE_REVIEW_ALLOW_UNSAFE_LOCAL"
CONTAINER_TEST_ENV = "TRPC_CODE_REVIEW_CONTAINER_TEST"
DB_URL_ENV = "TRPC_CODE_REVIEW_DB_URL"
ASYNC_SQL_DRIVER_MARKERS = (
"+aiosqlite",
"+asyncpg",
"+aiomysql",
)
Loading
Loading