mirror of
https://github.com/alibaba/open-code-review.git
synced 2026-07-09 17:28:58 +00:00
feat(pages): add Docs page with search, markdown rendering, and i18n support (#273)
* feat(pages): add Docs page with search, markdown rendering, and i18n support - Add DocsPage with full-text search modal (⌘K trigger) - Add MarkdownRenderer with DOMPurify sanitization - Add bilingual docs content (en/zh) for all sections - Add shared headingId utility for consistent TOC anchors - Add search keyboard hints with i18n support - Update Navbar with Docs navigation link - Add icon-search.svg asset - Configure webpack for markdown imports * fix(pages): address PR #273 code review feedback - Replace marked.setOptions() with new Marked instance (no global mutation) - Escape heading ID attribute value to prevent XSS - Use crypto.randomUUID() for mermaid diagram IDs (no collisions) - Add cancellation flag for async mermaid renders on unmount - Move inline <pre> styles to CSS class (only dynamic align-items inline) - Move @types/dompurify to devDependencies - Remove @ts-nocheck from docs/index.ts - Extract getRawContent helper to reduce duplication - Fix searchDocs fallback consistency (add enDocs fallback) - Fix heading ID mismatch by stripping markdown links before ID generation - Separate sidebar chevron (expand) from label (navigate) - Guard ⌘K shortcut against input/textarea focus interception
This commit is contained in:
parent
589a7249f5
commit
022ed75682
49 changed files with 9402 additions and 668 deletions
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Open Code Review — AI Code Review</title>
|
||||
<title>Open Code Review — Agent Native Code Review</title>
|
||||
<link rel="icon" type="image/svg+xml" href="<%= require('./logo.svg') %>" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@agentscope-ai/icons": "^1.0.68",
|
||||
"dompurify": "^3.4.11",
|
||||
"marked": "^18.0.5",
|
||||
"mermaid": "^11.16.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.8.0",
|
||||
|
|
@ -22,6 +25,7 @@
|
|||
"@babel/preset-env": "^7.29.5",
|
||||
"@babel/preset-react": "^7.23.5",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@types/three": "^0.185.0",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const App: React.FC = () => {
|
|||
<Route path="/" element={<LandingPage><FeaturesPage /></LandingPage>} />
|
||||
<Route path="/benchmark" element={<LandingPage><BenchmarkPage /></LandingPage>} />
|
||||
<Route path="/quickstart" element={<LandingPage><QuickStartPage /></LandingPage>} />
|
||||
<Route path="/docs" element={<LandingPage><DocsPage /></LandingPage>} />
|
||||
<Route path="/docs" element={<DocsPage />} />
|
||||
</Routes>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
1
pages/src/assets/icons/icon-search.svg
Normal file
1
pages/src/assets/icons/icon-search.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg viewBox="0 0 1024 1024" width="64" height="64" xmlns="http://www.w3.org/2000/svg"><path fill="#FFFFFF" fill-opacity="0.8" d="M809.6 486.4c0 176.7296-143.2704 320-320 320S169.6 663.1296 169.6 486.4 312.8704 166.4 489.6 166.4s320 143.2704 320 320z m-64 0a253.728 253.728 0 0 0-17.9744-94.2336c-13.0112-32.864-32.0128-61.792-57.0048-86.784-24.9952-24.9952-53.9232-44-86.7872-57.008A253.7664 253.7664 0 0 0 489.6 230.4a253.7536 253.7536 0 0 0-94.2336 17.9744c-32.864 13.0112-61.792 32.0128-86.784 57.0048-24.9952 24.9952-44 53.9232-57.008 86.7872A253.7536 253.7536 0 0 0 233.6 486.4a253.7664 253.7664 0 0 0 17.9744 94.2336c13.0112 32.864 32.0128 61.792 57.0048 86.7872 24.9952 24.992 53.9232 43.9936 86.7872 57.0048A253.728 253.728 0 0 0 489.6 742.4a253.7408 253.7408 0 0 0 94.2336-17.9744c32.864-13.0112 61.792-32.0128 86.7872-57.0048 24.992-24.9952 43.9936-53.9232 57.0048-86.7872A253.7408 253.7408 0 0 0 745.6 486.4z"/><path fill="#FFFFFF" fill-opacity="0.8" d="M822.0608 867.3376l-148.688-148.7104a32 32 0 0 1 45.2544-45.2544l148.6912 148.7104a32 32 0 1 1-45.2576 45.2544z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
187
pages/src/components/MarkdownRenderer.tsx
Normal file
187
pages/src/components/MarkdownRenderer.tsx
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
import React, { useMemo, useEffect, useRef, useState, useCallback, useId } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Marked, Renderer } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import mermaid from 'mermaid';
|
||||
import { useTranslation } from '../i18n';
|
||||
import copyIcon from '../assets/icons/icon-copy.svg';
|
||||
import { generateHeadingId } from '../utils/headingId';
|
||||
|
||||
// Initialize mermaid with dark theme
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'dark',
|
||||
themeVariables: {
|
||||
primaryColor: '#1a1a2e',
|
||||
primaryTextColor: 'rgba(255,255,255,0.85)',
|
||||
primaryBorderColor: 'rgba(255,255,255,0.2)',
|
||||
lineColor: 'rgba(255,255,255,0.4)',
|
||||
secondaryColor: '#16213e',
|
||||
tertiaryColor: '#0f3460',
|
||||
background: '#000000',
|
||||
mainBkg: 'rgba(255,255,255,0.04)',
|
||||
nodeBorder: 'rgba(255,255,255,0.16)',
|
||||
clusterBkg: 'rgba(255,255,255,0.02)',
|
||||
titleColor: '#FFFFFF',
|
||||
edgeLabelBackground: '#000000',
|
||||
},
|
||||
flowchart: {
|
||||
htmlLabels: true,
|
||||
curve: 'basis',
|
||||
},
|
||||
});
|
||||
|
||||
interface MarkdownRendererProps {
|
||||
content: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders markdown content with dark theme styling matching the existing DocsPage design.
|
||||
* Uses `marked` to parse markdown into HTML, then renders with styled container.
|
||||
* Mermaid diagrams are rendered client-side after mount.
|
||||
*/
|
||||
const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({ content }) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const { t } = useTranslation();
|
||||
const [toastVisible, setToastVisible] = useState(false);
|
||||
|
||||
const handleCopy = useCallback((text: string) => {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
setToastVisible(true);
|
||||
}).catch(() => fallbackCopy(text));
|
||||
} else {
|
||||
fallbackCopy(text);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const fallbackCopy = (text: string) => {
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = text;
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.opacity = '0';
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
const success = document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
if (success) setToastVisible(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!toastVisible) return;
|
||||
const timer = setTimeout(() => setToastVisible(false), 1200);
|
||||
return () => clearTimeout(timer);
|
||||
}, [toastVisible]);
|
||||
|
||||
const html = useMemo(() => {
|
||||
// Custom renderer to generate heading IDs matching the TOC extraction logic
|
||||
const renderer = new Renderer();
|
||||
renderer.heading = function ({ text, depth }: { text: string; depth: number }) {
|
||||
const id = generateHeadingId(text);
|
||||
// Escape id attribute value to prevent XSS
|
||||
const safeId = id.replace(/"/g, '"');
|
||||
return `<h${depth} id="${safeId}">${text}</h${depth}>\n`;
|
||||
};
|
||||
// Strip trailing newlines from code blocks to avoid empty line at bottom
|
||||
renderer.code = function ({ text, lang, escaped }: { text: string; lang?: string; escaped?: boolean }) {
|
||||
const trimmed = text.replace(/^\n+|\n+$/g, '');
|
||||
const content = escaped ? trimmed : trimmed.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
const langClass = lang ? ` class="language-${lang}"` : '';
|
||||
const isMultiline = trimmed.includes('\n');
|
||||
const alignItems = isMultiline ? 'flex-start' : 'center';
|
||||
return `<pre style="align-items:${alignItems};"><code${langClass}>${content}</code></pre>\n`;
|
||||
};
|
||||
const instance = new Marked({ gfm: true, breaks: false, renderer });
|
||||
return DOMPurify.sanitize(instance.parse(content) as string);
|
||||
}, [content]);
|
||||
|
||||
// Render mermaid diagrams and add copy buttons to code blocks after DOM update
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
let cancelled = false;
|
||||
|
||||
// Add copy buttons to all pre > code blocks (except mermaid)
|
||||
const preBlocks = containerRef.current.querySelectorAll('pre');
|
||||
preBlocks.forEach((pre) => {
|
||||
const codeEl = pre.querySelector('code');
|
||||
if (!codeEl || codeEl.classList.contains('language-mermaid')) return;
|
||||
if (pre.querySelector('.code-copy-btn')) return; // already added
|
||||
|
||||
// Create copy button matching reference HTML
|
||||
const btn = document.createElement('div');
|
||||
btn.className = 'code-copy-btn';
|
||||
btn.style.cssText = 'display:flex;flex-shrink:0;justify-content:flex-start;align-items:flex-start;flex-direction:column;padding-top:4px;padding-bottom:4px;cursor:pointer;';
|
||||
btn.innerHTML = `<img src="${copyIcon}" alt="copy" style="width:16px;height:16px;" />`;
|
||||
btn.addEventListener('click', () => {
|
||||
const text = codeEl.textContent || '';
|
||||
handleCopy(text);
|
||||
});
|
||||
pre.appendChild(btn);
|
||||
});
|
||||
|
||||
// Render mermaid diagrams
|
||||
const mermaidBlocks = containerRef.current.querySelectorAll('code.language-mermaid');
|
||||
if (mermaidBlocks.length === 0) return;
|
||||
|
||||
const renderPromises = Array.from(mermaidBlocks).map(async (block) => {
|
||||
const pre = block.parentElement;
|
||||
if (!pre) return;
|
||||
const code = block.textContent || '';
|
||||
try {
|
||||
const id = `mermaid-diagram-${crypto.randomUUID()}`;
|
||||
const { svg } = await mermaid.render(id, code);
|
||||
if (cancelled) return;
|
||||
// Replace the <pre> with rendered SVG
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'mermaid-rendered';
|
||||
wrapper.innerHTML = svg;
|
||||
pre.replaceWith(wrapper);
|
||||
} catch (e) {
|
||||
if (cancelled) return;
|
||||
// If rendering fails, show the code block normally
|
||||
(block as HTMLElement).style.display = 'block';
|
||||
console.warn('[Mermaid] render failed:', e);
|
||||
}
|
||||
});
|
||||
|
||||
return () => { cancelled = true; };
|
||||
}, [html]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="docs-markdown"
|
||||
dangerouslySetInnerHTML={{ __html: html }}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
{ReactDOM.createPortal(
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 88,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
background: 'rgba(255,255,255,0.1)',
|
||||
border: '1px solid rgba(255,255,255,0.2)',
|
||||
color: 'rgba(255,255,255,0.85)',
|
||||
padding: '5px 8px 5px 10px',
|
||||
borderRadius: 6,
|
||||
fontSize: 12,
|
||||
fontWeight: 500,
|
||||
pointerEvents: 'none',
|
||||
opacity: toastVisible ? 1 : 0,
|
||||
transition: 'opacity 0.15s ease',
|
||||
zIndex: 9999,
|
||||
backdropFilter: 'blur(8px)',
|
||||
}}
|
||||
>
|
||||
{t('quickstart.copied')}
|
||||
</div>,
|
||||
document.body
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownRenderer;
|
||||
|
|
@ -187,7 +187,7 @@ const Navbar: React.FC = () => {
|
|||
rel="noopener noreferrer"
|
||||
style={{ display: 'flex', alignItems: 'center', opacity: 0.6 }}
|
||||
>
|
||||
<img src={socialIcon} alt="Social" style={{ width: 20, height: 20 }} />
|
||||
<img src={socialIcon} alt="Social" style={{ width: 22, height: 22 }} />
|
||||
</a>
|
||||
<button
|
||||
onClick={() => navigate('/quickstart')}
|
||||
|
|
|
|||
374
pages/src/content/docs/en/architecture.md
Normal file
374
pages/src/content/docs/en/architecture.md
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
---
|
||||
title: Architecture
|
||||
sidebar:
|
||||
order: 8
|
||||
---
|
||||
|
||||
A walk-through of how `ocr review` actually works inside, from the moment
|
||||
you press Enter to the JSON that lands in your terminal. The goal is to
|
||||
give you enough mental model to debug behaviour, tune flags, and read
|
||||
the source code with confidence.
|
||||
|
||||
## High-level pipeline
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["<b>ocr review</b>"]
|
||||
B["<b>bootstrap</b><br/><span style='font-size:0.85em'>Resolve LLM endpoint (config → env → rc files)<br/>Load template, tool registry, system rules</span>"]
|
||||
C["<b>diff provider</b><br/><span style='font-size:0.85em'>git diff / ls-files / show — produce []model.Diff<br/>Modes: Workspace · Commit · Range</span>"]
|
||||
D["<b>filter & rules</b><br/><span style='font-size:0.85em'>5-gate filter (preview.go) — drop binaries,<br/>excluded paths, unsupported extensions. Pick rule per file.</span>"]
|
||||
E["<b>subtask dispatch</b><br/><span style='font-size:0.85em'>For every diff in parallel (concurrency=N):<br/>Plan phase (optional) → Main loop → Comments</span>"]
|
||||
F["<b>output writer</b><br/><span style='font-size:0.85em'>Synchronous line-resolution & review-filter; renders text<br/>or JSON depending on --format / --audience.</span>"]
|
||||
|
||||
A --> B --> C --> D --> E --> F
|
||||
```
|
||||
|
||||
The orchestration lives in the
|
||||
[`internal/agent/`](https://github.com/alibaba/open-code-review/blob/main/internal/agent/)
|
||||
package, which spans four files: `agent.go` (main loop & dispatch),
|
||||
`compression.go` (memory compression), `preview.go` (the file filter),
|
||||
and `util.go` (helpers). Two entry points matter: `Agent.Run` (top of
|
||||
pipeline) and `Agent.dispatchSubtasks` (per-file fan-out).
|
||||
|
||||
## The diff provider
|
||||
|
||||
`internal/diff/git.go` defines a `Provider` struct whose unexported
|
||||
`mode` field (of type `Mode`, an `int` enum) selects one of three modes
|
||||
that mirror the CLI flags:
|
||||
|
||||
| Mode | Triggered by | What it returns |
|
||||
|---|---|---|
|
||||
| `Workspace` | no flags | staged + unstaged + untracked changes |
|
||||
| `Commit` | `--commit <sha>` / `-c <sha>` | the changes introduced by `<sha>` (via `git show <sha>`, equivalent to the `<sha>^..<sha>` diff) |
|
||||
| `Range` | `--from <a> --to <b>` | `merge-base(a, b)..b` |
|
||||
|
||||
Each diff carries: old/new path, old/new hunks, insertion/deletion counts,
|
||||
binary flag, and rename detection. `DiffContextLines` is fixed at **3** —
|
||||
the same default Git uses.
|
||||
|
||||
Untracked files are read from disk and treated as full-file additions so
|
||||
they're reviewed pre-commit.
|
||||
|
||||
## The five-gate file filter
|
||||
|
||||
Once diffs are loaded, every file passes through
|
||||
[`whyExcluded`](https://github.com/alibaba/open-code-review/blob/main/internal/agent/preview.go).
|
||||
The function returns one of:
|
||||
|
||||
```
|
||||
binary — file is binary
|
||||
user_exclude — matched a pattern in your `exclude` list
|
||||
unsupported_ext — extension is not in supported_file_types.json
|
||||
default_path — matched a built-in test-file exclude pattern
|
||||
```
|
||||
|
||||
…or empty if the file is kept. `deleted` is **not** returned by
|
||||
`whyExcluded`; it's computed afterwards in `Preview()` when a kept
|
||||
file's diff reports `IsDeleted`. The gates run in this order:
|
||||
|
||||
1. `binary` — binary files are dropped first.
|
||||
2. `user_exclude` — your project's `exclude` always wins.
|
||||
3. `user_include` — if the filter has include patterns **and** the file
|
||||
matches one, it's kept immediately (returns empty), bypassing the
|
||||
`unsupported_ext` and `default_path` gates below.
|
||||
4. `unsupported_ext` filters by extension allowlist.
|
||||
5. `default_path` is the last gate: it matches built-in **test-file**
|
||||
exclude patterns (`**/*_test.go`, `**/*.test.{js,jsx,ts,tsx}`,
|
||||
`**/__tests__/**`, `**/*_test.py`, `**/*_spec.rb`, `**/*.test.ets`, …).
|
||||
Every pattern is rooted with a `**/` prefix.
|
||||
|
||||
The noisy-directory filtering (`vendor/`, `node_modules/`, `target/`, …)
|
||||
happens earlier, at the diff-provider level, via the
|
||||
`providerDirIgnoreDirs` list in `internal/diff/git.go` — diffs for those
|
||||
directories are parsed and then stripped out by `filterDiffs` before
|
||||
they ever reach the per-file filter.
|
||||
|
||||
Run `ocr review --preview` to see the full filter result without spending
|
||||
a token. See [Review Rules](../review-rules/#how-files-are-filtered) for
|
||||
the full algorithm.
|
||||
|
||||
## Per-file subtask: plan + main
|
||||
|
||||
For every file that survives filtering, OCR fires a sub-agent. Each
|
||||
sub-agent runs in its own goroutine, bounded by `--concurrency` (default
|
||||
**8**), and has its own LLM message buffer.
|
||||
|
||||
A subtask has up to **two phases**:
|
||||
|
||||
### Phase 1 — Plan (optional)
|
||||
|
||||
```go
|
||||
threshold := template.PlanModeLineThreshold // 50
|
||||
changeLines := d.Insertions + d.Deletions
|
||||
if changeLines < threshold { skip plan }
|
||||
```
|
||||
|
||||
For small diffs the plan adds latency without value, so it's skipped
|
||||
silently and the main loop runs straight away. For larger diffs OCR
|
||||
makes a **single** `PLAN_TASK` LLM call — no `Tools` field is sent, so
|
||||
the model cannot call tools during planning. The read-only tool subset
|
||||
(`code_search`, `file_read_diff`, `file_find` — the three tools whose
|
||||
`plan_task` flag is `true` in `tools.json`) is embedded as plain text
|
||||
via the `{{plan_tools}}` placeholder (rendered by
|
||||
`formatToolDefs`) so the model knows what's available later. The model
|
||||
returns a checklist that becomes `{{plan_guidance}}`
|
||||
in the main prompt.
|
||||
|
||||
### Phase 2 — Main loop
|
||||
|
||||
The main loop assembles the `MAIN_TASK` prompt and runs a tool-use
|
||||
conversation with the model. The full tool set adds **`task_done`**,
|
||||
**`code_comment`**, and **`file_read`** to the plan-phase tools — see
|
||||
[Tools](../tools/) for the full catalogue.
|
||||
|
||||
```
|
||||
loop up to MAX_TOOL_REQUEST_TIMES (default 30):
|
||||
response = llm.complete(messages, tools)
|
||||
if response.toolCalls is empty:
|
||||
nudge model with "You did not successfully call any tools.
|
||||
Please try again or use task_done if finished."
|
||||
continue
|
||||
for each call: execute → collect result
|
||||
if any call was task_done: break
|
||||
addNextMessage(...) # may trigger compression
|
||||
```
|
||||
|
||||
The loop has five exit conditions:
|
||||
|
||||
1. `task_done` was called.
|
||||
2. `MAX_TOOL_REQUEST_TIMES` ran out.
|
||||
3. 3 consecutive rounds produced no valid tool results
|
||||
(`maxConsecutiveEmptyRounds = 3`).
|
||||
4. The context was cancelled.
|
||||
5. `addNextMessage` returned false — compression couldn't bring the
|
||||
message buffer back under the warning threshold.
|
||||
|
||||
In all cases collected `code_comment` calls become review comments.
|
||||
|
||||
## Memory compression
|
||||
|
||||
A long tool-use loop will eventually overflow the context window. OCR
|
||||
manages this with a **three-zone partitioning** strategy that triggers
|
||||
on a token budget defined in `MAX_TOKENS = 58888`:
|
||||
|
||||
| Threshold | Constant | Action |
|
||||
|---|---|---|
|
||||
| 60 % of MAX_TOKENS | `tokenSoftThreshold` | Kick off **async** background compression; current loop continues uninterrupted. |
|
||||
| 80 % of MAX_TOKENS | `tokenWarningThreshold` | Run compression **synchronously** before sending the next request. |
|
||||
|
||||
### The three zones
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph messages["messages"]
|
||||
direction LR
|
||||
F["<b>frozen</b><br/>first 2 msgs<br/>(system +<br/>initial user)"]
|
||||
C["<b>compress</b><br/>summarized<br/>into one<br/>user msg"]
|
||||
A["<b>active</b><br/>K most recent<br/>complete<br/>rounds"]
|
||||
end
|
||||
F --- C --- A
|
||||
```
|
||||
|
||||
A "round" is one assistant message plus the tool result messages that
|
||||
followed it. `partitionMessages` walks rounds from the end, keeping as
|
||||
many as fit within `(0.80 × MAX_TOKENS) - reservedTokens`. Everything
|
||||
older becomes the **compress zone**.
|
||||
|
||||
The compress zone is rendered as XML and fed to the model with the
|
||||
`MEMORY_COMPRESSION_TASK` prompt; the returned summary is appended to
|
||||
the original user message inside `<previous_review_summary>` tags.
|
||||
|
||||
After compression: `messages = frozen[2] + compressed_user_msg + active`.
|
||||
|
||||
```go
|
||||
// compression.go
|
||||
func (a *Agent) runCompression(ctx context.Context, msgs []llm.Message, filePath string) ([]llm.Message, error) {
|
||||
part := partitionMessages(msgs, a.args.Template.MaxTokens, 0)
|
||||
contextXML := buildMessageXML(msgs[part.frozenEnd:part.compressEnd])
|
||||
// … call MEMORY_COMPRESSION_TASK …
|
||||
rebuilt[1] = llm.NewTextMessage(role, currentText+
|
||||
"\n\n<previous_review_summary>\n"+rawSummary+"\n</previous_review_summary>")
|
||||
for i := part.compressEnd; i < len(msgs); i++ {
|
||||
rebuilt = append(rebuilt, msgs[i])
|
||||
}
|
||||
return rebuilt, nil
|
||||
}
|
||||
```
|
||||
|
||||
### Async vs sync
|
||||
|
||||
The async path lets the main loop keep emitting tool calls while
|
||||
compression runs in the background; when the next token check happens, a
|
||||
ready summary is swapped in via `tryApplyPendingCompression`. If the
|
||||
ratio crosses the warning threshold before the async job finishes, the
|
||||
loop stalls and runs `runCompression` synchronously — guaranteeing the
|
||||
next request always fits.
|
||||
|
||||
## Comment processing pipeline
|
||||
|
||||
Every `code_comment` tool call produces one or more raw comments. They
|
||||
go through a **CommentWorkerPool** (a fixed-size goroutine pool) so the
|
||||
main tool-use loop never blocks on post-processing:
|
||||
|
||||
1. **Line resolution** (in-worker) — `existing_code` is matched against
|
||||
the diff using a sliding-window algorithm to compute precise
|
||||
`start_line` / `end_line`. If matching fails, both default to `0` — a
|
||||
`0` line range is the implicit signal for an "unanchored" comment the
|
||||
user must locate manually (there is no stored flag; downstream
|
||||
consumers check `start_line == 0`).
|
||||
2. **Re-location task** *(optional fallback)* — when line resolution
|
||||
fails on a non-trivial diff, OCR runs the `RE_LOCATION_TASK` prompt
|
||||
asking the model to re-anchor the snippet. Useful for paraphrased
|
||||
`existing_code` strings.
|
||||
3. **Review filter** — after the main loop finishes (and the worker pool
|
||||
drains), the `REVIEW_FILTER_TASK` LLM call inspects the collected
|
||||
comments against the diff and removes ones that are provably
|
||||
incorrect. Errors here are logged and ignored.
|
||||
4. **Second line-resolution pass** — once `Agent.Run` returns, the
|
||||
top-level command re-runs `diff.ResolveLineNumbers` over the full
|
||||
comment set (see `cmd/opencodereview/review_cmd.go`) to catch
|
||||
comments whose `existing_code` spans multiple files or was updated by
|
||||
the re-location step.
|
||||
5. **Render** — into text or JSON depending on `--format`.
|
||||
|
||||
## Token budget guards
|
||||
|
||||
Before the LLM is even called, OCR runs a fail-fast check:
|
||||
|
||||
```go
|
||||
tokenLimit := MaxTokens * 4 / 5 // 80 %
|
||||
if countMessagesTokens(messages) > tokenLimit {
|
||||
record warning "token_threshold_exceeded"
|
||||
return nil // skip this file
|
||||
}
|
||||
```
|
||||
|
||||
This catches monstrous diffs (auto-generated lock files, refactors
|
||||
touching thousands of lines) before they cost a request. The skipped
|
||||
file is reported as a non-fatal warning in stdout and added to the JSON
|
||||
`warnings` array.
|
||||
|
||||
A second check runs in `filterLargeDiffs`: if the diff alone exceeds
|
||||
80 % of `MAX_TOKENS` it's filtered out before the per-file dispatcher is
|
||||
even spawned.
|
||||
|
||||
## The template & placeholders
|
||||
|
||||
`internal/config/template/task_template.json` holds **five prompts**:
|
||||
|
||||
| Key | Purpose |
|
||||
|---|---|
|
||||
| `PLAN_TASK` | Planning phase — produces a checklist. |
|
||||
| `MAIN_TASK` | Main review loop — emits `code_comment` calls. |
|
||||
| `MEMORY_COMPRESSION_TASK` | Summarises the compress zone. |
|
||||
| `REVIEW_FILTER_TASK` | Post-loop pass that removes provably-incorrect comments. |
|
||||
| `RE_LOCATION_TASK` | Re-anchors a comment whose `existing_code` couldn't be matched. |
|
||||
|
||||
Each prompt is a list of `{role, prompt_file}` references that point to
|
||||
`.md` files in the template directory (e.g.
|
||||
`{"role": "system", "prompt_file": "main_task_system.md"}`). At load
|
||||
time `resolveConversation` reads those files into in-memory
|
||||
`{role, content}` messages, and template placeholders are then resolved
|
||||
per-file:
|
||||
|
||||
| Placeholder | Replaced with |
|
||||
|---|---|
|
||||
| `{{system_rule}}` | The rule body resolved from the four-layer chain. |
|
||||
| `{{change_files}}` | Status + path of every other changed file in the PR. |
|
||||
| `{{diff}}` | This file's diff (raw `git diff` output). |
|
||||
| `{{current_file_path}}` | The new path of this file. |
|
||||
| `{{plan_guidance}}` | Output of the plan phase, or removed when plan is skipped. |
|
||||
| `{{plan_tools}}` | Plan-phase tool definitions as plain text (rendered by `formatToolDefs`), used in the `PLAN_TASK` system prompt. |
|
||||
| `{{requirement_background}}` | The `--background` flag content. |
|
||||
| `{{current_system_date_time}}` | Local timestamp for the run, formatted `YYYY-MM-DD HH:MM` (no seconds or timezone). |
|
||||
| `{{context}}` | (compression only) the XML-rendered messages to summarise. |
|
||||
| `{{path}}` | File path, used in `REVIEW_FILTER_TASK`. |
|
||||
| `{{comments}}` | Accumulated comments (JSON), used in `REVIEW_FILTER_TASK`. |
|
||||
|
||||
The placeholder substitution lives in
|
||||
[`agent.go`](https://github.com/alibaba/open-code-review/blob/main/internal/agent/agent.go).
|
||||
The template itself isn't a CLI override — to change prompts you edit
|
||||
[`task_template.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/template/task_template.json)
|
||||
and rebuild. The `--tools` flag is a *tool-registry* override (it
|
||||
swaps the JSON consumed by `internal/config/toolsconfig`), not a
|
||||
template override — see [Tools](../tools/#customizing-tools).
|
||||
|
||||
> **Placeholder syntax caveat.** All the placeholders above use
|
||||
> double-brace `{{…}}` syntax *except* `RE_LOCATION_TASK`, which
|
||||
> substitutes single-brace `{diff}`, `{existing_code}`, and
|
||||
> `{suggestion_content}` (see `internal/diff/relocation.go`).
|
||||
|
||||
## Persistence
|
||||
|
||||
Every review is written to disk as JSONL:
|
||||
|
||||
```
|
||||
~/.opencodereview/sessions/<encoded-repo-path>/<session-id>.jsonl
|
||||
```
|
||||
|
||||
The repo path is **not** base64-encoded; `encodeRepoPath` (in
|
||||
`internal/session/persist.go`) replaces `/` and `\` with `-` and `:` with
|
||||
`_` so the path is filesystem-safe.
|
||||
|
||||
Each line is one event: prompt sent, LLM response, tool call, tool
|
||||
result, comment emitted, etc. The Web UI (`ocr viewer`) reads these
|
||||
files directly — there's no database, just append-only logs. See
|
||||
[Session Viewer](../viewer/) for the UI tour and event schema.
|
||||
|
||||
## Telemetry
|
||||
|
||||
When telemetry is enabled the agent emits three pipeline-level spans
|
||||
(`review.run` wrapping the whole job, `diff.parse` wrapping diff
|
||||
loading, and one `subtask.execute.<file>` per reviewed file) plus a
|
||||
short-lived `event.<name>` span at each decision point (`plan.skipped`,
|
||||
`token.threshold.exceeded`, `subtask.error`, …). LLM round trips and
|
||||
tool calls are recorded only as metrics — not as spans. Prompt and
|
||||
response content is **never** attached to telemetry; the
|
||||
`OCR_CONTENT_LOGGING` flag is plumbed but currently dead. See
|
||||
[Telemetry](../telemetry/) for the full schema.
|
||||
|
||||
## What's *not* automated
|
||||
|
||||
A few decisions are deliberately manual:
|
||||
|
||||
- **Endpoint discovery has no fallback.** If your config + env + rc
|
||||
files don't yield a complete `(URL, token, model)` triple, OCR exits
|
||||
with a non-zero code rather than guessing.
|
||||
- **Sub-agent failures are isolated, not retried.** One failing file
|
||||
produces a warning; the rest continue. Retries belong in the wrapping
|
||||
CI pipeline, not the agent.
|
||||
- **No cross-file reasoning.** Every file is reviewed in its own LLM
|
||||
conversation. Cross-file questions go through `file_read_diff` /
|
||||
`code_search` tool calls, not shared context. Findings in those
|
||||
*other* files are also off-limits as comment targets — the
|
||||
`main_task` prompt instructs the model to use context tools for
|
||||
understanding only, and to ignore issues that surface in files
|
||||
outside the current diff.
|
||||
|
||||
These choices keep the run **deterministic per-file** and keep cost
|
||||
predictable.
|
||||
|
||||
## Source-code map
|
||||
|
||||
If you want to read along:
|
||||
|
||||
| Concern | File |
|
||||
|---|---|
|
||||
| Top-level command dispatch | `cmd/opencodereview/main.go` |
|
||||
| `review` flag parsing | `cmd/opencodereview/flags.go` |
|
||||
| Agent orchestration & compression | `internal/agent/` (agent.go, compression.go, util.go) |
|
||||
| File filter / preview | `internal/agent/preview.go` |
|
||||
| Diff loading (Git modes) | `internal/diff/git.go` |
|
||||
| Rule resolution chain | `internal/config/rules/system_rules.go` |
|
||||
| Tool registry & impls | `internal/tool/` |
|
||||
| LLM endpoint resolver | `internal/llm/resolver.go` |
|
||||
| Session JSONL writer | `internal/session/persist.go` |
|
||||
| Web viewer | `internal/viewer/server.go` |
|
||||
|
||||
See [Contributing](../contributing/) for build & test instructions.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Tools](../tools/) — the six tools the agent loop calls.
|
||||
- [Review Rules](../review-rules/) — how per-file rule text is resolved.
|
||||
- [Session Viewer](../viewer/) — inspect the transcripts this pipeline writes.
|
||||
396
pages/src/content/docs/en/cli-reference.md
Normal file
396
pages/src/content/docs/en/cli-reference.md
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
---
|
||||
title: CLI Reference
|
||||
sidebar:
|
||||
order: 6
|
||||
---
|
||||
|
||||
The complete reference for every `ocr` subcommand, flag, and exit
|
||||
behaviour.
|
||||
|
||||
## Global usage
|
||||
|
||||
```text
|
||||
OpenCodeReview - AI-Powered Code Review CLI
|
||||
|
||||
Usage:
|
||||
ocr [command]
|
||||
|
||||
Commands:
|
||||
review, r Start a code review
|
||||
rules Inspect and debug review rules
|
||||
config Manage configuration settings
|
||||
llm LLM utility commands
|
||||
viewer Start the WebUI session viewer
|
||||
version Show version information
|
||||
|
||||
Examples:
|
||||
ocr review --from master --to dev Review diff range
|
||||
ocr review --commit abc123 Review a single commit
|
||||
ocr config provider Interactive provider setup
|
||||
ocr config model Interactive model selection
|
||||
ocr config set llm.model opus-4-6 Set a config value
|
||||
ocr llm test Test LLM connectivity
|
||||
ocr llm providers List built-in providers
|
||||
ocr version Show version info
|
||||
|
||||
Use "ocr review -h" for more information about review.
|
||||
Use "ocr rules -h" for more information about rules.
|
||||
Use "ocr config" for more information about config.
|
||||
Use "ocr llm" for more information about LLM utilities.
|
||||
|
||||
GitHub: https://github.com/alibaba/open-code-review
|
||||
```
|
||||
|
||||
## Command summary
|
||||
|
||||
| Command | Alias | What it does |
|
||||
|---|---|---|
|
||||
| `ocr review` | `ocr r` | Run a code review and emit comments. |
|
||||
| `ocr rules check <file>` | — | Show which rule applies to a given file path and where it came from. |
|
||||
| `ocr config set <key> <value>` | — | Persist a config value to `~/.opencodereview/config.json`. |
|
||||
| `ocr config unset custom_providers.<name>` | — | Delete a custom provider (clears active `provider`/`model` if it was active). |
|
||||
| `ocr config provider` | — | Interactive provider-setup TUI. |
|
||||
| `ocr config model` | — | Interactive model-selection TUI. |
|
||||
| `ocr llm test` | — | Send a small chat request to verify the configured endpoint. |
|
||||
| `ocr llm providers` | — | List all built-in LLM providers. |
|
||||
| `ocr viewer` | — | Launch the local web UI for past review sessions (`localhost:5483`). |
|
||||
| `ocr version` | — | Print version, commit, platform, build date, and GitHub URL. |
|
||||
|
||||
`ocr` and `ocr -h` print top-level usage. Each subcommand also accepts
|
||||
`-h` / `--help`.
|
||||
|
||||
## `ocr review`
|
||||
|
||||
The main command. Resolves a Git diff, dispatches per-file sub-agents,
|
||||
collects review comments, and prints them.
|
||||
|
||||
### Synopsis
|
||||
|
||||
```text
|
||||
ocr review [flags]
|
||||
ocr r [flags] (alias)
|
||||
```
|
||||
|
||||
If no flags are passed, OCR runs in **workspace mode** — review of all
|
||||
staged + unstaged + untracked changes in the current directory's repo.
|
||||
|
||||
### Flags
|
||||
|
||||
| Flag | Short | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `--repo <path>` | — | current dir | Git repository root. |
|
||||
| `--from <ref>` | — | — | Source ref to start the diff from (e.g., `main`). |
|
||||
| `--to <ref>` | — | — | Target ref to end the diff at (e.g., `feature-branch`). When set, OCR computes `merge-base(from, to)..to`. |
|
||||
| `--commit <sha>` | `-c` | — | Single commit to review (vs its parent). |
|
||||
| `--preview` | `-p` | `false` | Run the filter pipeline but skip the LLM. Prints the file list and exclusion reasons. |
|
||||
| `--format <fmt>` | `-f` | `text` | `text` (human-readable) or `json` (machine-readable comment array). |
|
||||
| `--audience <who>` | — | `human` | `human` streams progress lines; `agent` quiets stdout and prints only the final summary / JSON. |
|
||||
| `--background <text>` | `-b` | — | Optional requirement / business context injected into the plan + main prompts. |
|
||||
| `--concurrency <n>` | — | `8` | Maximum number of files reviewed in parallel. |
|
||||
| `--timeout <minutes>` | — | `10` | Per-file deadline. `0` disables the timeout. |
|
||||
| `--rule <path>` | — | — | Path to a custom JSON review rule file. Overrides the project-level and global `rule.json`. |
|
||||
| `--max-tools <n>` | — | template default | Max tool-call rounds per file. `0` uses the template default (`30`); values 1–9 are clamped up to `10`; any value `≥ 10` overrides the template default (even if smaller than `30`). |
|
||||
| `--model <name>` | — | — | Override the resolved LLM model for this review (e.g., `claude-opus-4-6`). |
|
||||
| `--max-git-procs <n>` | — | `16` | Maximum number of concurrent git subprocesses. |
|
||||
| `--tools <path>` | — | embedded | Path to a custom JSON tool-config file. Overrides the embedded tool definitions. |
|
||||
|
||||
> Mode flags are mutually exclusive: pass either `--from`/`--to`, or
|
||||
> `--commit`, or neither (workspace mode). Mixing them is a hard error.
|
||||
|
||||
### Modes
|
||||
|
||||
#### Workspace mode (default)
|
||||
|
||||
```bash
|
||||
ocr review
|
||||
```
|
||||
|
||||
OCR assembles the working-tree changes from two git commands:
|
||||
|
||||
- tracked changes via `git diff HEAD` (staged + unstaged combined against
|
||||
`HEAD`; if that comes back empty, OCR falls back to `git diff --staged`)
|
||||
- untracked files via `git ls-files --others --exclude-standard`, read
|
||||
from disk and treated as full-file additions
|
||||
|
||||
This is what you usually want pre-commit. Stage selectively if you want
|
||||
narrower scope.
|
||||
|
||||
#### Range mode
|
||||
|
||||
```bash
|
||||
ocr review --from main --to feature-branch
|
||||
```
|
||||
|
||||
OCR computes `merge-base(main, feature-branch)..feature-branch` so you only
|
||||
see the diff *introduced by* the feature branch — not unrelated changes
|
||||
that landed on `main` since branching.
|
||||
|
||||
#### Commit mode
|
||||
|
||||
```bash
|
||||
ocr review --commit abc123
|
||||
ocr review -c abc123
|
||||
```
|
||||
|
||||
Reviews the diff produced by `git show abc123` (i.e., the changes that
|
||||
single commit introduced).
|
||||
|
||||
### Output
|
||||
|
||||
#### Text (default, `--audience human`)
|
||||
|
||||
Progress lines stream as the review runs, followed by one block per
|
||||
comment (a dim Unicode-rule header with `path:start-end`, the comment
|
||||
body wrapped to 100 columns, and — when present — a colored inline diff
|
||||
of the suggested replacement). A run summary lands on stdout at the end:
|
||||
|
||||
```
|
||||
[ocr] 17 file(s) changed, reviewing 9 in /path/to/repo
|
||||
[ocr] Skipping image.png — filtered by path/extension rules
|
||||
[ocr] ▶ file_read "src/foo.go"
|
||||
[ocr] ✔ file_read (12ms)
|
||||
[ocr] Plan completed for src/foo.go
|
||||
…
|
||||
|
||||
─── src/foo.go:42-47 ───
|
||||
Concurrent map access without a lock — wrap with sync.RWMutex.
|
||||
|
||||
- m[k] = v
|
||||
+ mu.Lock(); defer mu.Unlock(); m[k] = v
|
||||
|
||||
…
|
||||
[ocr] Summary: 9 file(s) reviewed, 14 comment(s), ~21344 token(s) used (input: ~18012, output: ~3332), 1m12s elapsed
|
||||
```
|
||||
|
||||
#### Text (agent, `--audience agent`)
|
||||
|
||||
Identical comment output, but progress lines are suppressed via an internal
|
||||
quiet-able stdout writer ([`internal/stdout`](https://github.com/alibaba/open-code-review/blob/main/internal/stdout/stdout.go)).
|
||||
Use this in CI / when piping into another agent.
|
||||
|
||||
#### JSON
|
||||
|
||||
```bash
|
||||
ocr review --format json --audience agent
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"summary": {
|
||||
"files_reviewed": 9,
|
||||
"comments": 1,
|
||||
"total_tokens": 21344,
|
||||
"input_tokens": 18012,
|
||||
"output_tokens": 3332,
|
||||
"elapsed": "1m12s"
|
||||
},
|
||||
"comments": [
|
||||
{
|
||||
"path": "src/foo.go",
|
||||
"content": "Concurrent map access without a lock — wrap with sync.RWMutex.",
|
||||
"start_line": 42,
|
||||
"end_line": 47,
|
||||
"existing_code": "m[k] = v",
|
||||
"suggestion_code": "mu.Lock(); defer mu.Unlock(); m[k] = v",
|
||||
"thinking": "Looking at line 42, the map …"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Top-level fields:
|
||||
|
||||
| Field | Notes |
|
||||
|---|---|
|
||||
| `status` | `success`, `completed_with_warnings`, `completed_with_errors`, or `skipped`. |
|
||||
| `message` | Optional. Human-readable summary, e.g. `"No comments generated. Looks good to me."`. |
|
||||
| `summary` | Optional. Run aggregates: `files_reviewed`, `comments`, `total_tokens`, `input_tokens`, `output_tokens`, `cache_read_tokens` (omitempty), `cache_write_tokens` (omitempty), `elapsed`. Omitted for `skipped` runs. |
|
||||
| `comments` | Always present, possibly empty. Per-comment fields are the ones in the example above. |
|
||||
| `warnings` | Optional. Present when one or more sub-agents failed; each entry describes the affected file and the error. |
|
||||
|
||||
When no files were eligible for review, JSON mode emits a `skipped`
|
||||
envelope instead so callers can distinguish "no changes" from "no findings":
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "skipped",
|
||||
"message": "No supported files changed.",
|
||||
"comments": []
|
||||
}
|
||||
```
|
||||
|
||||
### Exit codes
|
||||
|
||||
| Code | Meaning |
|
||||
|---|---|
|
||||
| `0` | Review completed (possibly with zero comments, possibly with non-fatal warnings). |
|
||||
| `1` | Fatal error — bad flags, can't resolve LLM endpoint, all per-file sub-agents failed, etc. The error text is printed to stderr. |
|
||||
|
||||
Non-fatal warnings (a single sub-agent failed, a file exceeded the token
|
||||
threshold, etc.) are printed inline; in JSON mode they're added to the
|
||||
`warnings` array.
|
||||
|
||||
## `ocr rules`
|
||||
|
||||
Rule introspection. There is exactly one subcommand:
|
||||
|
||||
```text
|
||||
ocr rules check [flags] <file-path>
|
||||
|
||||
Flags:
|
||||
--repo <path> Git repository root (default: current dir)
|
||||
--rule <path> Path to a custom rule JSON file
|
||||
```
|
||||
|
||||
For the given file path, OCR:
|
||||
|
||||
1. Walks the four-layer rule chain (custom → project → global → system).
|
||||
2. Picks the first match.
|
||||
3. Prints the **source layer**, the **glob pattern** that matched, and the
|
||||
resolved **rule text**.
|
||||
|
||||
```bash
|
||||
$ ocr rules check src/main/java/com/example/Foo.java
|
||||
File: src/main/java/com/example/Foo.java
|
||||
Source: System built-in
|
||||
Pattern: **/*.java
|
||||
Rule:
|
||||
────────────────────────────────────────
|
||||
<contents of internal/config/rules/rule_docs/java.md>
|
||||
────────────────────────────────────────
|
||||
```
|
||||
|
||||
Useful for debugging "why isn't my custom rule firing?" — see
|
||||
[Review Rules](../review-rules/) for the full priority story.
|
||||
|
||||
## `ocr config`
|
||||
|
||||
Persists keys to `~/.opencodereview/config.json` and offers interactive
|
||||
setup TUIs. Four subcommands:
|
||||
|
||||
```text
|
||||
ocr config set <key> <value>
|
||||
ocr config unset custom_providers.<name> Delete a custom provider
|
||||
ocr config provider Interactive provider setup
|
||||
ocr config model Interactive model selection
|
||||
```
|
||||
|
||||
- **`set`** — write a single config value non-interactively.
|
||||
- **`unset`** — delete a custom provider. Only
|
||||
`custom_providers.<name>` is supported. If the deleted provider was the
|
||||
active one, `provider` and `model` are cleared (run `ocr config provider`
|
||||
to pick a new one).
|
||||
- **`provider`** — launch the interactive provider-setup TUI (no extra
|
||||
arguments; use `ocr config set provider <name>` for non-interactive
|
||||
setup).
|
||||
- **`model`** — launch the interactive model-selection TUI (no extra
|
||||
arguments; use `ocr config set model <name>` for non-interactive
|
||||
setup).
|
||||
|
||||
See [Configuration](../configuration/) for the full key reference,
|
||||
schemas, and examples.
|
||||
|
||||
## `ocr llm`
|
||||
|
||||
LLM utility commands. Two subcommands:
|
||||
|
||||
```text
|
||||
ocr llm <sub-command>
|
||||
|
||||
Sub-commands:
|
||||
test Send a test conversation to the configured LLM model
|
||||
providers List all built-in LLM providers
|
||||
```
|
||||
|
||||
### `ocr llm test`
|
||||
|
||||
```text
|
||||
ocr llm test
|
||||
```
|
||||
|
||||
Resolves the LLM endpoint exactly the way `ocr review` does, sends a single
|
||||
canned chat request from
|
||||
[`internal/config/testconnection/task.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/testconnection/task.json),
|
||||
and prints:
|
||||
|
||||
```
|
||||
Source: <which strategy was used>
|
||||
URL: <endpoint URL>
|
||||
Model: <effective model>
|
||||
<the model's reply>
|
||||
✓ Connection test successful
|
||||
```
|
||||
|
||||
A non-zero exit means either the endpoint isn't fully configured or the
|
||||
request failed (network / auth / model error). The error message tells you
|
||||
which.
|
||||
|
||||
### `ocr llm providers`
|
||||
|
||||
```text
|
||||
ocr llm providers
|
||||
```
|
||||
|
||||
Lists every built-in LLM provider in a three-column table:
|
||||
|
||||
```
|
||||
Built-in providers:
|
||||
NAME PROTOCOL BASE URL
|
||||
---- -------- --------
|
||||
anthropic anthropic https://api.anthropic.com
|
||||
…
|
||||
```
|
||||
|
||||
Followed by a hint to configure one interactively with `ocr config
|
||||
provider` or non-interactively with `ocr config set provider <name>`.
|
||||
|
||||
## `ocr viewer`
|
||||
|
||||
```text
|
||||
ocr viewer [flags]
|
||||
|
||||
Flags:
|
||||
--addr <address> listen address (default: localhost:5483)
|
||||
|
||||
Examples:
|
||||
ocr viewer # start on default port
|
||||
ocr viewer --addr :3000 # bind to all interfaces on port 3000
|
||||
```
|
||||
|
||||
Starts an embedded HTTP server that reads
|
||||
`~/.opencodereview/sessions/...` and renders past review sessions in a
|
||||
browser-friendly UI. See [Session Viewer](../viewer/).
|
||||
|
||||
## `ocr version`
|
||||
|
||||
```text
|
||||
ocr version
|
||||
ocr --version
|
||||
ocr -V
|
||||
```
|
||||
|
||||
Prints the version stamped at build time, the short Git commit (when
|
||||
present), the platform (`<GOOS>/<GOARCH>`), the build date (when present),
|
||||
and the GitHub URL (`https://github.com/alibaba/open-code-review`).
|
||||
|
||||
## Tips & gotchas
|
||||
|
||||
- `--audience agent` does **not** imply `--format json`. They control
|
||||
different things — quiet UI vs structured payload. Combine them when you
|
||||
want both.
|
||||
- `--background` is one of the highest-leverage flags for review quality —
|
||||
always pass the requirement / PR description when invoking from another
|
||||
agent.
|
||||
- A file whose diff alone exceeds 80 % of `MAX_TOKENS` (`58888` by default)
|
||||
is dropped before the LLM is called. This is logged but does not fail
|
||||
the run.
|
||||
- The plan phase is **automatically skipped** when changed lines for a file
|
||||
fall below `PLAN_MODE_LINE_THRESHOLD` (`50`).
|
||||
|
||||
## See Also
|
||||
|
||||
- [QuickStart](../quickstart/) — install and run your first review.
|
||||
- [Configuration](../configuration/) — env vars and config keys behind the flags.
|
||||
- [Review Rules](../review-rules/) — the `--rule` flag and rule resolution.
|
||||
- [Integrations](../integrations/) — calling `ocr review` from agents and CI.
|
||||
283
pages/src/content/docs/en/configuration.md
Normal file
283
pages/src/content/docs/en/configuration.md
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
---
|
||||
title: Configuration
|
||||
sidebar:
|
||||
order: 5
|
||||
---
|
||||
|
||||
## Endpoint resolution
|
||||
|
||||
When `ocr review` or `ocr llm test` runs, it tries four sources **in order**
|
||||
and uses the first one that yields a complete `(URL, token, model)` triple:
|
||||
|
||||
| Priority | Source | What it reads |
|
||||
|---|---|---|
|
||||
| 1 | `~/.opencodereview/config.json` | If `provider` is set, resolves via the `providers`/`custom_providers` maps (provider-first; see [Built-in providers](#built-in-providers)). Only falls back to the legacy `llm` section when no provider is set. |
|
||||
| 2 | OCR environment variables | `OCR_LLM_URL`, `OCR_LLM_TOKEN`, `OCR_LLM_MODEL`, `OCR_USE_ANTHROPIC`, `OCR_LLM_AUTH_HEADER`. |
|
||||
| 3 | Claude Code environment variables | `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_MODEL`. |
|
||||
| 4 | Shell rc files | `export ANTHROPIC_*=…` lines parsed out of `~/.zshrc`, `~/.bashrc`, `~/.bash_profile`, `~/.profile`. |
|
||||
|
||||
For Claude Code-style sources, if `ANTHROPIC_BASE_URL` lacks a versioned
|
||||
path (`/v1/...`), OCR appends `/v1/messages` automatically.
|
||||
|
||||
If no strategy yields a complete triple, OCR exits with:
|
||||
|
||||
```
|
||||
no valid LLM endpoint configured; one of OCR_LLM_URL/OCR_LLM_TOKEN/OCR_LLM_MODEL,
|
||||
~/.opencodereview/config.json, or ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN/
|
||||
ANTHROPIC_MODEL must be set
|
||||
```
|
||||
|
||||
> Resolution stops at the first source that **errors**, not just the first
|
||||
> that's empty. In particular, if `provider` is set in `config.json` but the
|
||||
> entry is misconfigured (unknown provider name, missing `api_key` with no
|
||||
> env-var fallback, missing `model`, a custom provider lacking `url`/`protocol`),
|
||||
> OCR exits with that error and does **not** fall through to the OCR env,
|
||||
> Claude Code, or rc-file sources. To switch to env-based config, unset the
|
||||
> `provider` key first.
|
||||
|
||||
> Source priority means the **environment overrides nothing** when the
|
||||
> config file is fully populated. To force the environment to win, either
|
||||
> delete the relevant `llm.*` keys from `~/.opencodereview/config.json` or
|
||||
> use `ocr config set` to switch to the new values.
|
||||
|
||||
## `ocr config set` — managing `~/.opencodereview/config.json`
|
||||
|
||||
```bash
|
||||
ocr config set <key> <value>
|
||||
```
|
||||
|
||||
`config set` mutates the file via key/value pairs with schema-aware
|
||||
parsing. The interactive TUI commands `ocr config provider` and
|
||||
`ocr config model` also write to the same file (see
|
||||
[Interactive setup](#interactive-setup--ocr-config-provider--ocr-config-model)).
|
||||
Recognised keys:
|
||||
|
||||
| Key | Type | Notes |
|
||||
|---|---|---|
|
||||
| `provider` | string | Set the active provider (built-in name or custom). Switching provider clears the model. |
|
||||
| `model` | string | Set the model for the active provider (stored under the provider entry, or top-level `model` if no provider is set). |
|
||||
| `providers.<name>.<field>` | varies | Per-provider fields for built-in providers: `api_key`, `url`, `protocol`, `model`, `models`, `auth_header`, `extra_body`. |
|
||||
| `custom_providers.<name>.<field>` | varies | Same fields as above, for custom (non-built-in) providers. Custom providers must set at least `url` and `protocol`. |
|
||||
| `llm.url` | string | Endpoint URL. For Anthropic, full Messages URL like `https://api.anthropic.com/v1/messages`. For OpenAI-compatible, the chat-completions URL. |
|
||||
| `llm.auth_token` | string | API key. Sent as `Authorization: Bearer …` (OpenAI) or, for the legacy Anthropic path, `Authorization: Bearer …` by default (the preset `anthropic` provider defaults to `x-api-key` instead). Use `x-api-key` only by explicitly setting `llm.auth_header`. |
|
||||
| `llm.auth_header` | string | Auth header name (`x-api-key`, `authorization`, or `bearer`). Anthropic-only; required for some Anthropic setups that need `x-api-key`. |
|
||||
| `llm.model` | string | Model name. A `[<digits>m]` suffix is stripped automatically. |
|
||||
| `llm.use_anthropic` | boolean | `true` (default) → Anthropic Messages protocol. `false` → OpenAI Chat Completions. |
|
||||
| `llm.extra_body` | JSON object | Vendor-specific request fields merged into every chat request body. Example: `'{"thinking":{"type":"disabled"}}'`. |
|
||||
| `language` | string | Forwarded into a directive appended to the system prompt; defaults to `English` when unset. See [Choosing a language](#choosing-a-language). |
|
||||
| `telemetry.enabled` | boolean | Master switch for OpenTelemetry export. Off by default. |
|
||||
| `telemetry.exporter` | string | `console` or `otlp`. |
|
||||
| `telemetry.otlp_endpoint` | string | OTLP collector address (e.g., `localhost:4317`). |
|
||||
| `telemetry.content_logging` | boolean | Include LLM prompts / responses in exported event data. |
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
ocr config set llm.url https://api.anthropic.com/v1/messages
|
||||
ocr config set llm.auth_token sk-ant-xxxxxxxxxx
|
||||
ocr config set llm.model claude-opus-4-6
|
||||
ocr config set llm.use_anthropic true
|
||||
ocr config set llm.extra_body '{"thinking":{"type":"disabled"}}'
|
||||
ocr config set language English
|
||||
ocr config set telemetry.enabled true
|
||||
ocr config set telemetry.exporter otlp
|
||||
ocr config set telemetry.otlp_endpoint localhost:4317
|
||||
|
||||
# Provider-based setup (recommended)
|
||||
ocr config set provider anthropic
|
||||
ocr config set model claude-opus-4-6
|
||||
ocr config set providers.anthropic.api_key "$ANTHROPIC_API_KEY"
|
||||
|
||||
# Custom (non-built-in) provider
|
||||
ocr config set provider my-gateway
|
||||
ocr config set custom_providers.my-gateway.url https://gateway.internal.com/v1
|
||||
ocr config set custom_providers.my-gateway.protocol openai
|
||||
ocr config set custom_providers.my-gateway.model llama-3-70b
|
||||
ocr config set custom_providers.my-gateway.api_key "$MY_API_KEY"
|
||||
```
|
||||
|
||||
Booleans accept anything Go's `strconv.ParseBool` accepts (`true`, `false`,
|
||||
`1`, `0`, `t`, `f`, …). `llm.extra_body` must be valid JSON.
|
||||
|
||||
## File schema reference
|
||||
|
||||
After the commands above, `~/.opencodereview/config.json` looks like:
|
||||
|
||||
```json
|
||||
{
|
||||
"llm": {
|
||||
"url": "https://api.anthropic.com/v1/messages",
|
||||
"auth_token": "sk-ant-xxxxxxxxxx",
|
||||
"auth_header": "x-api-key",
|
||||
"model": "claude-opus-4-6",
|
||||
"use_anthropic": true,
|
||||
"extra_body": {
|
||||
"thinking": { "type": "disabled" }
|
||||
}
|
||||
},
|
||||
"language": "English",
|
||||
"telemetry": {
|
||||
"enabled": true,
|
||||
"exporter": "otlp",
|
||||
"otlp_endpoint": "localhost:4317"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The provider-based form uses `provider`, `model`, `providers`, and
|
||||
`custom_providers` instead of the legacy `llm` block:
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "anthropic",
|
||||
"model": "claude-opus-4-6",
|
||||
"providers": {
|
||||
"anthropic": {
|
||||
"api_key": "sk-ant-xxxxxxxxxx",
|
||||
"model": "claude-opus-4-6"
|
||||
}
|
||||
},
|
||||
"custom_providers": {
|
||||
"my-gateway": {
|
||||
"url": "https://gateway.internal.com/v1",
|
||||
"protocol": "openai",
|
||||
"model": "llama-3-70b",
|
||||
"models": ["llama-3-70b", "llama-3-8b"],
|
||||
"api_key": "gw-xxxxxxxxxx",
|
||||
"auth_header": "authorization"
|
||||
}
|
||||
},
|
||||
"language": "English"
|
||||
}
|
||||
```
|
||||
|
||||
When `provider` is set, the `providers`/`custom_providers` maps drive
|
||||
resolution; the legacy `llm` section is ignored for that config.
|
||||
|
||||
You can edit this file by hand if you prefer, but `ocr config set` will
|
||||
remarshal with `" "` indent on the next write.
|
||||
|
||||
## Interactive setup — `ocr config provider` / `ocr config model`
|
||||
|
||||
For provider and model selection without typing keys, OCR ships two
|
||||
interactive Bubble Tea TUIs that also mutate `~/.opencodereview/config.json`.
|
||||
|
||||
```bash
|
||||
ocr config provider
|
||||
ocr config model
|
||||
```
|
||||
|
||||
- `ocr config provider` — Interactive TUI for selecting a built-in or custom
|
||||
provider, then entering URL / protocol / API key / model. Saves the choice
|
||||
to config and runs `ocr llm test` automatically to verify the endpoint.
|
||||
For built-in providers, the API key may be read from the provider's env var
|
||||
(see [Built-in providers](#built-in-providers)) when not entered directly.
|
||||
Selecting a manual configuration populates the legacy `llm.*` block instead.
|
||||
- `ocr config model` — Interactive TUI for selecting a model from the current
|
||||
provider's preset list, plus any user-added models stored under
|
||||
`providers.<name>.models` / `custom_providers.<name>.models`. Requires a
|
||||
provider to be set first (`ocr config provider`).
|
||||
|
||||
## Built-in providers
|
||||
|
||||
The following providers ship with OCR. Each has a preset `BaseURL`,
|
||||
`Protocol`, and (where applicable) an API key environment variable used as a
|
||||
fallback when `providers.<name>.api_key` is unset.
|
||||
|
||||
| Name | Protocol | Base URL | API key env var |
|
||||
|---|---|---|---|
|
||||
| `anthropic` | anthropic | `https://api.anthropic.com` | `ANTHROPIC_API_KEY` |
|
||||
| `openai` | openai | `https://api.openai.com/v1` | `OPENAI_API_KEY` |
|
||||
| `dashscope` | openai | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `DASHSCOPE_API_KEY` |
|
||||
| `dashscope-tokenplan` | openai | `https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1` | `DASHSCOPE_TOKENPLAN_KEY` |
|
||||
| `volcengine` | openai | `https://ark.cn-beijing.volces.com/api/v3` | `ARK_API_KEY` |
|
||||
| `deepseek` | openai | `https://api.deepseek.com` | `DEEPSEEK_API_KEY` |
|
||||
| `tencent-tokenhub` | openai | `https://tokenhub.tencentmaas.com/v1` | `TENCENT_TOKENHUB_API_KEY` |
|
||||
| `hy-tokenplan` | openai | `https://api.lkeap.cloud.tencent.com/plan/v3` | `TENCENT_HUNYUAN_TOKENPLAN_KEY` |
|
||||
| `kimi` | openai | `https://api.moonshot.cn/v1` | `MOONSHOT_API_KEY` |
|
||||
| `z-ai` | openai | `https://open.bigmodel.cn/api/paas/v4` | `Z_AI_API_KEY` |
|
||||
| `mimo` | openai | `https://api.xiaomimimo.com/v1` | `MIMO_API_KEY` |
|
||||
| `minimax` | openai | `https://api.minimaxi.com/v1` | `MINIMAX_API_KEY` |
|
||||
| `baidu-qianfan` | openai | `https://qianfan.baidubce.com/v2` | `QIANFAN_API_KEY` |
|
||||
|
||||
Any other provider name is treated as custom and must be configured under
|
||||
`custom_providers` with at least `url` and `protocol`.
|
||||
|
||||
## Environment variable reference
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `OCR_LLM_URL` | Endpoint URL — same shape as `llm.url`. |
|
||||
| `OCR_LLM_TOKEN` | API key — same as `llm.auth_token`. |
|
||||
| `OCR_LLM_MODEL` | Model name. |
|
||||
| `OCR_LLM_AUTH_HEADER` | Auth header name (`x-api-key`, `authorization`, or `bearer`). Anthropic-only; same as `llm.auth_header`. Defaults to `authorization` when unset. |
|
||||
| `OCR_USE_ANTHROPIC` | Unset → Anthropic protocol (default). Set to `true` / `1` / `yes` (case-insensitive) → Anthropic. Set to anything else (`false`, `0`, `no`, typos, …) → OpenAI. |
|
||||
| `ANTHROPIC_BASE_URL` | Claude Code-compatible base URL. |
|
||||
| `ANTHROPIC_AUTH_TOKEN` | Claude Code-compatible API key. |
|
||||
| `ANTHROPIC_MODEL` | Claude Code-compatible model. |
|
||||
| `OCR_ENABLE_TELEMETRY` | `1` to enable telemetry from env. |
|
||||
| `OTEL_SERVICE_NAME` | Override service name in spans/metrics. |
|
||||
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector address — also forces the exporter to `otlp`. |
|
||||
| `OTEL_EXPORTER_OTLP_PROTOCOL` | OTLP transport protocol (`grpc`, `http/protobuf`, or `http/json`). Defaults to `grpc`. |
|
||||
| `OCR_CONTENT_LOGGING` | `1` to include prompts/responses in telemetry events. |
|
||||
|
||||
Per-provider API keys (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
|
||||
`DASHSCOPE_API_KEY`, …) are used as a fallback when a built-in provider's
|
||||
`api_key` field is unset. See the [Built-in providers](#built-in-providers)
|
||||
table for each provider's env var name.
|
||||
|
||||
## Why `extra_body` exists
|
||||
|
||||
Some hosted providers add non-standard fields to the request body (for
|
||||
example, Bedrock-style `thinking`, vendor-specific `temperature_strategy`,
|
||||
streaming options). `llm.extra_body` is merged into every outgoing request,
|
||||
so you can ship those fields without patching the source.
|
||||
|
||||
```bash
|
||||
ocr config set llm.extra_body '{"thinking":{"type":"enabled","budget_tokens":2048}}'
|
||||
```
|
||||
|
||||
## Choosing a language
|
||||
|
||||
The `language` key controls one thing only: a directive appended to every
|
||||
system-role message in the review and `ocr llm test` prompts. The exact
|
||||
string injected is:
|
||||
|
||||
```
|
||||
\n\nAlways respond in <language>.
|
||||
```
|
||||
|
||||
- *Unset* or empty — treated as `English`.
|
||||
- `Chinese`, `English`, or any other string — passed through verbatim.
|
||||
|
||||
There is no language switching on built-in rule docs. The files embedded
|
||||
under `internal/config/rules/rule_docs/` are loaded by fixed filename and
|
||||
are mostly written in Chinese (with `default.md` as an English exception);
|
||||
they appear in the prompt as-is regardless of the `language` setting. When
|
||||
`language` is `English`, the prompt therefore contains an English directive
|
||||
on top of mostly-Chinese rule text — strong models honour the directive and
|
||||
produce English comments, weaker models may emit mixed output.
|
||||
|
||||
`language` has no environment-variable, CLI-flag, or per-project override —
|
||||
the only place it can be set is the global `~/.opencodereview/config.json`,
|
||||
via [`ocr config set`](#ocr-config-set--managing-opencodereviewconfigjson):
|
||||
|
||||
```bash
|
||||
ocr config set language English
|
||||
```
|
||||
|
||||
If you need fully English rule text, supply your own rules via `--rule`,
|
||||
`<repo>/.opencodereview/rule.json`, or `~/.opencodereview/rule.json` (see
|
||||
[Review Rules](../review-rules/#priority-chain)).
|
||||
|
||||
## Per-project vs. global config
|
||||
|
||||
The CLI itself is configured globally (`~/.opencodereview/config.json`) —
|
||||
there is no project-local LLM config. **Review rules** *are* per-project,
|
||||
however; see [Review Rules](../review-rules/#priority-chain).
|
||||
|
||||
## See Also
|
||||
|
||||
- [QuickStart](../quickstart/) — minimal setup and first review.
|
||||
- [CLI Reference](../cli-reference/) — every flag the review command accepts.
|
||||
- [Telemetry](../telemetry/) — how to wire up OTLP / console exporters.
|
||||
220
pages/src/content/docs/en/contributing.md
Normal file
220
pages/src/content/docs/en/contributing.md
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
---
|
||||
title: Contributing
|
||||
sidebar:
|
||||
order: 13
|
||||
---
|
||||
|
||||
OCR is open source under the Apache-2.0 license. Bug reports, doc fixes,
|
||||
and code contributions are all welcome. This page is a quick reference;
|
||||
the canonical version lives in
|
||||
[`CONTRIBUTING.md`](https://github.com/alibaba/open-code-review/blob/main/CONTRIBUTING.md).
|
||||
|
||||
## Ways to contribute
|
||||
|
||||
You don't have to write Go to be useful:
|
||||
|
||||
- **Bug reports** — open a [GitHub issue](https://github.com/alibaba/open-code-review/issues/new/choose)
|
||||
with reproduction steps.
|
||||
- **Feature requests** — start a thread in
|
||||
[Discussions](https://github.com/alibaba/open-code-review/discussions/categories/ideas)
|
||||
or open a feature-request issue.
|
||||
- **Docs** — typo fixes, missing examples, broken links — these PRs
|
||||
often merge fastest.
|
||||
- **Reviewing other PRs** — comments from non-maintainers help reduce
|
||||
reviewer load.
|
||||
- **Code** — bug fixes, performance work, new features.
|
||||
|
||||
## Local development setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [Go ≥ 1.25](https://go.dev/dl/)
|
||||
- [Git](https://git-scm.com/)
|
||||
- [Make](https://www.gnu.org/software/make/)
|
||||
|
||||
### Getting the source
|
||||
|
||||
```bash
|
||||
# Fork on GitHub, then:
|
||||
git clone https://github.com/<your-username>/open-code-review.git
|
||||
cd open-code-review
|
||||
git remote add upstream https://github.com/alibaba/open-code-review.git
|
||||
|
||||
make build # writes dist/opencodereview
|
||||
make test # LC_ALL=C go test -v -race -count=1 ./...
|
||||
```
|
||||
|
||||
> The `upstream` remote is read-only. Push to `origin` (your fork) and
|
||||
> open PRs from there.
|
||||
|
||||
### Running your local build
|
||||
|
||||
```bash
|
||||
./dist/opencodereview review --preview
|
||||
```
|
||||
|
||||
For convenience, drop a symlink at `~/bin/ocr-dev` pointing at
|
||||
`dist/opencodereview` so you can invoke `ocr-dev` from any repo.
|
||||
|
||||
### Make targets
|
||||
|
||||
| Target | What it does |
|
||||
|---|---|
|
||||
| `make build` | Build for current platform → `dist/opencodereview`. |
|
||||
| `make build-darwin-amd64` | Cross-compile for macOS Intel. |
|
||||
| `make build-darwin-arm64` | Cross-compile for macOS Apple Silicon. |
|
||||
| `make build-linux-amd64` | Cross-compile for Linux x86_64. |
|
||||
| `make build-linux-arm64` | Cross-compile for Linux ARM64. |
|
||||
| `make build-windows-amd64` | Cross-compile for Windows x86_64. |
|
||||
| `make build-windows-arm64` | Cross-compile for Windows ARM64. |
|
||||
| `make build-all` | All six cross-compiled binaries (linux/darwin/windows × amd64/arm64). |
|
||||
| `make sha256sum` | Generate `sha256sum.txt` for build artifacts. |
|
||||
| `make dist` | `clean → build-all → sha256sum`. What CI runs. |
|
||||
| `make test` | Run tests with race detector. |
|
||||
| `make clean` | Remove `dist/`. |
|
||||
|
||||
## Branching and commit conventions
|
||||
|
||||
### Branch prefixes
|
||||
|
||||
| Prefix | Purpose |
|
||||
|---|---|
|
||||
| `feat/` | New feature |
|
||||
| `fix/` | Bug fix |
|
||||
| `docs/` | Documentation only |
|
||||
| `refactor/` | Refactor with no behaviour change |
|
||||
| `test/` | Test-only changes |
|
||||
| `chore/` | Build / CI / tooling |
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git pull upstream main
|
||||
git checkout -b feat/anthropic-streaming
|
||||
```
|
||||
|
||||
### Commit messages
|
||||
|
||||
[Conventional Commits](https://www.conventionalcommits.org/) format:
|
||||
|
||||
```
|
||||
<type>(<scope>): <short summary>
|
||||
|
||||
[optional body explaining the why]
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
feat(agent): add support for custom tool definitions
|
||||
fix(llm): handle timeout errors in Anthropic API calls
|
||||
docs(readme): clarify endpoint resolution priority
|
||||
refactor(viewer): extract task-card rendering into helper
|
||||
```
|
||||
|
||||
The same format is used for **PR titles** so they show up cleanly in the
|
||||
generated changelog.
|
||||
|
||||
## Project layout
|
||||
|
||||
```
|
||||
open-code-review/
|
||||
├── cmd/opencodereview/ # CLI entry point — flag parsing, dispatch
|
||||
├── internal/
|
||||
│ ├── agent/ # Review agent logic, sub-agent dispatch
|
||||
│ ├── config/ # Template, rules, allowlist, embedded JSON
|
||||
│ ├── diff/ # Git diff parsing, three modes
|
||||
│ ├── gitcmd/ # Git subprocess runner
|
||||
│ ├── llm/ # LLM client (Anthropic & OpenAI), endpoint resolver
|
||||
│ ├── model/ # Data structs (LlmComment, Diff, …)
|
||||
│ ├── pathutil/ # Path utilities
|
||||
│ ├── release/ # Release-notes generation
|
||||
│ ├── session/ # JSONL session writer
|
||||
│ ├── stdout/ # Quiet-able stdout writer
|
||||
│ ├── suggestdiff/ # Suggestion diff rendering
|
||||
│ ├── telemetry/ # OpenTelemetry config + helpers
|
||||
│ ├── tool/ # Tool registry + provider impls
|
||||
│ └── viewer/ # Embedded HTTP UI
|
||||
├── pages/ # WebUI marketing page (separate React app)
|
||||
├── plugins/ # Claude Code slash command
|
||||
├── extensions/ # Editor extensions (VS Code)
|
||||
├── examples/ # CI recipes (GitHub Actions, GitLab CI)
|
||||
├── skills/ # Agent SDK skill manifest
|
||||
├── scripts/ # NPM postinstall + cross-build scripts
|
||||
├── npm/ # Per-platform optional dependency packages
|
||||
└── bin/ # NPM wrapper (Node)
|
||||
```
|
||||
|
||||
Most contributions touch `internal/agent/`, `internal/tool/`, or
|
||||
`internal/llm/`. The CLI surface in `cmd/opencodereview/` is
|
||||
intentionally thin — flag parsing then dispatch to the agent package.
|
||||
|
||||
## Code quality checks
|
||||
|
||||
Before opening a PR:
|
||||
|
||||
```bash
|
||||
go fmt ./...
|
||||
go vet ./...
|
||||
make test # race-enabled, runs in CI on every push
|
||||
make build # smoke test the binary builds
|
||||
```
|
||||
|
||||
CI runs the same set on every push; nothing surprising.
|
||||
|
||||
## Adding new tools
|
||||
|
||||
A tool has two parts:
|
||||
|
||||
1. **JSON definition** in
|
||||
[`internal/config/toolsconfig/tools.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/toolsconfig/tools.json):
|
||||
the name, description, and JSON-schema parameters the LLM sees.
|
||||
2. **Go provider** registered in `internal/tool/definitions.go` with
|
||||
the actual implementation.
|
||||
|
||||
Both have to be present for a new tool name to work. See [Tools](../tools/)
|
||||
for the existing six and treat them as templates.
|
||||
|
||||
## Adding new rule patterns
|
||||
|
||||
Edit `internal/config/rules/system_rules.json` to map a new glob to a
|
||||
rule doc, and add the corresponding markdown under
|
||||
`internal/config/rules/rule_docs/`. Rule docs are single-file per
|
||||
pattern (English). The `language` config only appends a directive to the
|
||||
system prompt instructing the model to respond in that language; it does
|
||||
not switch rule-doc files.
|
||||
|
||||
## PR process
|
||||
|
||||
1. **Open an issue first for big changes.** Aligning on the approach
|
||||
beats discovering misalignment in code review.
|
||||
2. **One logical change per PR.** If you have two unrelated fixes,
|
||||
submit two PRs.
|
||||
3. **Update tests.** Behaviour changes need test coverage — `make test`
|
||||
has to pass.
|
||||
4. **Update docs.** If the change affects flags, config keys, or the
|
||||
review pipeline, update both this docs site (in [`docs/`](https://github.com/alibaba/open-code-review))
|
||||
and any relevant inline help.
|
||||
5. **Fill in the PR template.** A maintainer will review, usually
|
||||
within a few business days.
|
||||
|
||||
## Contributor License Agreement (CLA)
|
||||
|
||||
The project requires the Alibaba Open Source CLA. The first time you
|
||||
open a PR, a bot will post a link — sign electronically (takes a
|
||||
minute). Subsequent PRs don't require re-signing.
|
||||
|
||||
## First contribution?
|
||||
|
||||
Look for issues labeled
|
||||
[`good first issue`](https://github.com/alibaba/open-code-review/labels/good%20first%20issue)
|
||||
or [`help wanted`](https://github.com/alibaba/open-code-review/labels/help%20wanted).
|
||||
Most are small, self-contained, and have enough context in the issue
|
||||
description to get started.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Architecture](../architecture/) — the mental model you'll need
|
||||
before touching `internal/agent/`.
|
||||
- [Tools](../tools/) — what the existing tools look like.
|
||||
- Full contributing guide:
|
||||
[CONTRIBUTING.md](https://github.com/alibaba/open-code-review/blob/main/CONTRIBUTING.md)
|
||||
329
pages/src/content/docs/en/faq.md
Normal file
329
pages/src/content/docs/en/faq.md
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
---
|
||||
title: FAQ
|
||||
sidebar:
|
||||
order: 14
|
||||
---
|
||||
|
||||
Common errors, surprises, and "is this supposed to do that?" questions.
|
||||
If your problem isn't here, open a
|
||||
[GitHub issue](https://github.com/alibaba/open-code-review/issues) with
|
||||
the steps you ran and the full output.
|
||||
|
||||
## Configuration & startup
|
||||
|
||||
### `no valid LLM endpoint configured`
|
||||
|
||||
```
|
||||
no valid LLM endpoint configured; one of OCR_LLM_URL/OCR_LLM_TOKEN/OCR_LLM_MODEL,
|
||||
~/.opencodereview/config.json, or ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN/
|
||||
ANTHROPIC_MODEL must be set
|
||||
```
|
||||
|
||||
OCR ran the four-source resolution chain ([Configuration](../configuration/#endpoint-resolution))
|
||||
and didn't find a complete `(URL, token, model)` triple. Either:
|
||||
|
||||
- Run `ocr config set llm.url …` / `llm.auth_token …` / `llm.model …`
|
||||
to populate `~/.opencodereview/config.json`, **or**
|
||||
- Export `OCR_LLM_URL` / `OCR_LLM_TOKEN` / `OCR_LLM_MODEL`, **or**
|
||||
- Export `ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN` /
|
||||
`ANTHROPIC_MODEL` if you already use Claude Code.
|
||||
|
||||
Then `ocr llm test` to verify connectivity before retrying the review.
|
||||
|
||||
### `ocr llm test` shows the wrong source
|
||||
|
||||
OCR uses the **first** complete triple, not the last. So if your
|
||||
config file has all three llm.* keys, env vars are ignored. To make
|
||||
env wins, either delete the config keys (`rm` the file or unset by
|
||||
hand) or use `ocr config set` to switch to the new values.
|
||||
|
||||
### 401 / 403 from `ocr llm test`
|
||||
|
||||
The token is missing scope, expired, or wrong vendor. Anthropic and
|
||||
OpenAI use different auth headers and different URL shapes — make sure
|
||||
`llm.use_anthropic` matches the URL you're pointing at:
|
||||
|
||||
- Anthropic: URL ends `/v1/messages`, `use_anthropic=true`.
|
||||
- OpenAI / OpenAI-compatible: URL ends `/v1/chat/completions`,
|
||||
`use_anthropic=false`.
|
||||
|
||||
### `not a git repository`
|
||||
|
||||
`ocr review` runs `git diff` (and `git ls-files` for untracked files)
|
||||
against the current directory. If you're not inside a Git working tree,
|
||||
it exits early. Either `cd` into a repo, or pass `--repo /path/to/repo`.
|
||||
|
||||
## Filtering & rules
|
||||
|
||||
### My file isn't being reviewed
|
||||
|
||||
Run `ocr review --preview` (no LLM cost). The output lists every
|
||||
candidate file with the **reason** it was kept or dropped:
|
||||
|
||||
```
|
||||
src/foo.go modified
|
||||
src/foo_test.go modified (excluded: user_exclude)
|
||||
node_modules/lib.js added (excluded: default_path)
|
||||
imgs/logo.png binary (excluded: unsupported_ext)
|
||||
```
|
||||
|
||||
The five exclusion reasons map to gates in the
|
||||
[file filter](../review-rules/#how-files-are-filtered):
|
||||
|
||||
| Reason | Fix |
|
||||
|---|---|
|
||||
| `binary` | Nothing to do — binary files have no reviewable text. |
|
||||
| `user_exclude` | Remove the pattern from your `exclude` list. |
|
||||
| `unsupported_ext` | Add the extension to your `include` list to bypass the allowlist gate. |
|
||||
| `default_path` | Add the file to `include` — that overrides built-in test-file exclude patterns. |
|
||||
| `deleted` | Nothing to do — there's no new content to review. |
|
||||
|
||||
### My custom rule isn't firing
|
||||
|
||||
Run `ocr rules check <file-path>`. It prints the **layer** and
|
||||
**glob pattern** that matched, end-to-end:
|
||||
|
||||
```
|
||||
File: src/api/UserHandler.go
|
||||
Source: Project (.opencodereview/rule.json)
|
||||
Pattern: src/api/**/*.go
|
||||
Rule: …
|
||||
```
|
||||
|
||||
If the layer is wrong (e.g., showing "System built-in" when you expected
|
||||
your project rule), most likely the **declaration order** matters — the
|
||||
first matching pattern wins. Move your more-specific rule earlier in the
|
||||
`rules` array, or fix the glob.
|
||||
|
||||
### Brace expansion isn't working
|
||||
|
||||
`bmatcuk/doublestar/v4` supports `{ts,tsx}` braces. If they're not
|
||||
matching, check for stray spaces — `{ts, tsx}` with a space silently
|
||||
fails to match `tsx`.
|
||||
|
||||
## Reviews
|
||||
|
||||
### A file shows zero comments — was it actually reviewed?
|
||||
|
||||
Open the [Session Viewer](../viewer/) (`ocr viewer`), find the session,
|
||||
and look at the file's `main_task` lane:
|
||||
|
||||
- Tool calls present + ends in `task_done` → reviewed cleanly.
|
||||
- Tool calls present + ends mid-loop → look for an error card.
|
||||
- No `main_task` cards at all → the file was filtered out before review;
|
||||
see [Filtering & rules](#filtering--rules) above.
|
||||
|
||||
### Comments have `start_line: 0` and `end_line: 0`
|
||||
|
||||
OCR couldn't anchor the comment to a precise line in the diff. Two
|
||||
common causes:
|
||||
|
||||
- The model paraphrased `existing_code` instead of copying it verbatim
|
||||
from the diff. The model is told not to, but it sometimes does.
|
||||
- The diff had unusual formatting (CRLF, mixed tabs/spaces) that broke
|
||||
the sliding-window match.
|
||||
|
||||
The comment is still real — it just wasn't placed automatically. Most
|
||||
agent integrations (the SKILL, the Claude Code plugin) read the
|
||||
`existing_code` field and locate the spot in the file themselves.
|
||||
|
||||
### Token threshold exceeded
|
||||
|
||||
```
|
||||
[ocr] WARNING: prompt tokens (94000) exceed 80% of max_tokens(58888) for src/big.sql
|
||||
```
|
||||
|
||||
The initial prompt for that file (rule + diff + change-files list) was
|
||||
already past 80 % of `MAX_TOKENS = 58888` before the model could even
|
||||
respond. OCR skips the file and continues — you'll see this in
|
||||
`warnings` in JSON mode too.
|
||||
|
||||
Mitigations:
|
||||
|
||||
- Add the file to your `exclude` list if it's autogenerated.
|
||||
- Split a large refactor into smaller commits.
|
||||
- Use `--commit` mode for a series of small commits rather than
|
||||
reviewing them all at once via workspace mode.
|
||||
|
||||
### Plan phase took forever and the file is small
|
||||
|
||||
Run `ocr review --preview` first. If the file's `lines.changed` is
|
||||
above `PLAN_MODE_LINE_THRESHOLD` (default **50**), the plan phase runs.
|
||||
That's by design — large diffs benefit from a planning pass. To skip
|
||||
it for a single review, run with a smaller diff, or temporarily edit
|
||||
the embedded template (advanced; you'll need to override `--tools`).
|
||||
|
||||
### "Max tool requests reached"
|
||||
|
||||
```
|
||||
[ocr] Max tool requests reached for src/foo.go.
|
||||
```
|
||||
|
||||
The model spent 30 (`MAX_TOOL_REQUEST_TIMES`) tool-use rounds without
|
||||
calling `task_done`. Comments emitted up to that point are still
|
||||
collected and rendered. If this happens on most files, the issue is
|
||||
usually one of:
|
||||
|
||||
- Model isn't great at following the "call `task_done` when finished"
|
||||
instruction. Switch to a stronger model (e.g., Claude Opus).
|
||||
- A tool keeps erroring and the model keeps retrying. Look at the
|
||||
session JSONL — if the same tool result repeats, that's why.
|
||||
- The file is genuinely large or context-heavy and 30 rounds isn't
|
||||
enough. Raise or lower the cap with `--max-tools <n>` (e.g.,
|
||||
`--max-tools 40` for more, `--max-tools 15` for fewer). Values 1–9
|
||||
are clamped up to 10; `0` (the default) uses the template default of
|
||||
30.
|
||||
|
||||
### Some sub-agents fail; the run still exits 0
|
||||
|
||||
By design. OCR isolates per-file failures so one bad file doesn't kill
|
||||
a 20-file review. The aggregate exit code is `0` if *anything*
|
||||
succeeded; only a fully-failed run (zero successful sub-agents) exits
|
||||
non-zero. Check the `warnings` array in JSON mode or stderr in text
|
||||
mode to see which files failed.
|
||||
|
||||
### CI run is much slower than local
|
||||
|
||||
Two usual suspects:
|
||||
|
||||
- **Model rate limits** — under throttling, the LLM client backs off
|
||||
and retries. Lower `--concurrency` (e.g., to `4`) so you don't hit
|
||||
the limit in the first place.
|
||||
- **Cold cache** — if your provider supports prompt caching, the first
|
||||
run after deploy doesn't benefit from it. Subsequent runs in the
|
||||
same window are faster.
|
||||
|
||||
## Output & integration
|
||||
|
||||
### `--audience agent` still has progress lines
|
||||
|
||||
Make sure you're not seeing **stderr**. Progress messages occasionally
|
||||
go to stderr (warnings, errors). The clean stdout that `--audience
|
||||
agent` guarantees is *parser-friendly* — to suppress everything,
|
||||
redirect: `ocr review --audience agent 2>/dev/null`.
|
||||
|
||||
### JSON output is `{ "files_reviewed": 0, "comments": [] }`
|
||||
|
||||
Workspace had no eligible files. This is intentional — the explicit
|
||||
shape lets callers distinguish "nothing to review" from "no findings
|
||||
found in the reviewed files". A normal review with zero comments
|
||||
produces a regular empty array `[]` instead.
|
||||
|
||||
### Where do session JSONLs live?
|
||||
|
||||
```
|
||||
~/.opencodereview/sessions/<path-encoded-repo-path>/<session-id>.jsonl
|
||||
```
|
||||
|
||||
The repo path is encoded by replacing `/` and `\` with `-` and `:` with
|
||||
`_` (e.g. `/Users/foo/my-repo` → `Users-foo-my-repo`). Browse sessions
|
||||
with `ocr viewer`. Delete the directory to wipe history; OCR regenerates
|
||||
the encoded path on the next run.
|
||||
|
||||
## Performance & cost
|
||||
|
||||
### How can I tell what tokens cost what?
|
||||
|
||||
Enable telemetry:
|
||||
|
||||
```bash
|
||||
ocr config set telemetry.enabled true
|
||||
ocr config set telemetry.exporter console
|
||||
ocr review
|
||||
```
|
||||
|
||||
LLM calls don't get their own spans — they're recorded as metrics
|
||||
instead. Watch `ocr.llm.tokens_used` (counter, labelled `model` +
|
||||
`type`), `ocr.llm.requests_total` (counter, labelled `model` +
|
||||
`status`), and `ocr.llm.request_duration_seconds` (histogram, labelled
|
||||
`model`). The console exporter prints these aggregates inline. For
|
||||
dashboards, switch to the OTLP exporter and ship to your metrics
|
||||
stack — see [Telemetry](../telemetry/).
|
||||
|
||||
### Why are my reviews so expensive?
|
||||
|
||||
Common levers:
|
||||
|
||||
- Plan phase is on for files ≥ 50 lines. It costs an extra LLM call
|
||||
per file. Lowering the threshold reduces cost; raising it improves
|
||||
small-PR speed.
|
||||
- `MAX_TOOL_REQUEST_TIMES = 30` is generous. A model that uses every
|
||||
round will produce a longer (more tokens) conversation than one that
|
||||
finishes in 3 rounds. Stronger models tend to finish faster.
|
||||
Conversely, if you raised it with `--max-tools` to fight "max tool
|
||||
requests reached", expect cost per file to grow roughly linearly.
|
||||
- Memory compression itself is an LLM call. Long subtasks pay for
|
||||
compression rounds in addition to review rounds.
|
||||
|
||||
### How do I reduce LLM calls?
|
||||
|
||||
- Add an `include` list so OCR doesn't review files you don't care
|
||||
about.
|
||||
- Set `--concurrency` lower if your account has burst-mode pricing.
|
||||
- Pass `--background` — better context up-front sometimes lets the
|
||||
model finish without `file_read` / `code_search` round-trips.
|
||||
|
||||
## Privacy & security
|
||||
|
||||
### Does OCR send my code anywhere?
|
||||
|
||||
OCR sends your **diffs** (and optional read-tool snippets) to whatever
|
||||
LLM endpoint you configured. Nothing else leaves your machine —
|
||||
session JSONLs and rule files are local-only.
|
||||
|
||||
If telemetry is enabled, the `content_logging` flag is plumbed through
|
||||
the config layer but currently gates **no** code path — prompt and
|
||||
response content is never exported to your collector regardless of the
|
||||
flag's value. Treat it as reserved. Leave it `false` in production. See
|
||||
[Telemetry](../telemetry/#content-logging) for details.
|
||||
|
||||
### Can I redact secrets before they're sent to the LLM?
|
||||
|
||||
Not built-in. The recommended workflow:
|
||||
|
||||
1. Don't commit secrets to your repo (the usual rule).
|
||||
2. Add files known to contain hash material to `exclude`.
|
||||
3. Use `git diff --no-textconv` filters or pre-commit redaction to keep
|
||||
secrets out of diffs.
|
||||
|
||||
A "redaction rule" feature is on the roadmap; track
|
||||
[the issue tracker](https://github.com/alibaba/open-code-review/issues).
|
||||
|
||||
## Misc
|
||||
|
||||
### Where's the changelog?
|
||||
|
||||
[GitHub Releases](https://github.com/alibaba/open-code-review/releases)
|
||||
— each release has notes generated from Conventional Commits.
|
||||
|
||||
### Does OCR support non-Git VCS?
|
||||
|
||||
No. The diff providers shell out to `git`. SVN / Mercurial / etc. would
|
||||
need new providers; an issue for Hg support is open
|
||||
[here](https://github.com/alibaba/open-code-review/issues).
|
||||
|
||||
### Why is the binary called `opencodereview` but the CLI is `ocr`?
|
||||
|
||||
The static binary published in releases is named after the project
|
||||
(`opencodereview`); the NPM wrapper installs it as `ocr` for
|
||||
ergonomics. If you build from source you get `dist/opencodereview` —
|
||||
copy it to `ocr` on your `$PATH`.
|
||||
|
||||
### How do I uninstall?
|
||||
|
||||
```bash
|
||||
npm uninstall -g @alibaba-group/open-code-review # NPM install
|
||||
sudo rm /usr/local/bin/ocr # binary install
|
||||
rm -rf ~/.opencodereview # all state
|
||||
```
|
||||
|
||||
OCR doesn't write outside `~/.opencodereview` (apart from the binary
|
||||
download via NPM), so removing that directory wipes history, config,
|
||||
and per-user rules.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Configuration](../configuration/) — LLM endpoint resolution and config keys.
|
||||
- [Review Rules](../review-rules/) — the file filter and rule resolution chain.
|
||||
- [Session Viewer](../viewer/) — inspect past review sessions.
|
||||
- [Telemetry](../telemetry/) — token usage and LLM metrics.
|
||||
183
pages/src/content/docs/en/installation.md
Normal file
183
pages/src/content/docs/en/installation.md
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
---
|
||||
title: Installation
|
||||
sidebar:
|
||||
order: 4
|
||||
---
|
||||
|
||||
There are four supported ways to install the `ocr` CLI. They all produce
|
||||
the same binary — pick whichever fits your environment.
|
||||
|
||||
## NPM (recommended)
|
||||
|
||||
```bash
|
||||
npm install -g @alibaba-group/open-code-review
|
||||
```
|
||||
|
||||
The NPM package ships a small wrapper script (`bin/ocr.js`) plus a
|
||||
[postinstall hook](https://github.com/alibaba/open-code-review/blob/main/scripts/install.js)
|
||||
that:
|
||||
|
||||
1. Detects your platform (`darwin-amd64`, `darwin-arm64`, `linux-amd64`,
|
||||
`linux-arm64`, `windows-amd64`, `windows-arm64`).
|
||||
2. Downloads the matching binary from GitHub Releases.
|
||||
3. Verifies it (when checksum data is present) and places it next to the
|
||||
wrapper.
|
||||
|
||||
If a platform-specific npm package (e.g. `@alibaba-group/ocr-darwin-arm64`)
|
||||
is installed as an optional dependency, the binary is used directly and the
|
||||
download is skipped.
|
||||
|
||||
When you run `ocr`, the wrapper just `exec`s the downloaded binary, so the
|
||||
overhead is effectively zero after first run.
|
||||
|
||||
### Updating
|
||||
|
||||
```bash
|
||||
npm update -g @alibaba-group/open-code-review
|
||||
# or pin a specific version:
|
||||
npm install -g @alibaba-group/open-code-review@<version>
|
||||
```
|
||||
|
||||
### Uninstalling
|
||||
|
||||
```bash
|
||||
npm uninstall -g @alibaba-group/open-code-review
|
||||
```
|
||||
|
||||
## GitHub Release binary
|
||||
|
||||
If you don't want Node.js, grab the static binary directly from the
|
||||
[releases page](https://github.com/alibaba/open-code-review/releases):
|
||||
|
||||
```bash
|
||||
# macOS (Apple Silicon)
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-darwin-arm64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# macOS (Intel)
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-darwin-amd64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Linux x86_64
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-linux-amd64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Linux ARM64
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-linux-arm64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Windows (AMD64)
|
||||
curl -Lo ocr.exe https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-windows-amd64.exe
|
||||
|
||||
# Windows (ARM64)
|
||||
curl -Lo ocr.exe https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-windows-arm64.exe
|
||||
```
|
||||
|
||||
Each release also publishes `sha256sum.txt` next to the binaries so you can
|
||||
verify integrity:
|
||||
|
||||
```bash
|
||||
curl -LO https://github.com/alibaba/open-code-review/releases/latest/download/sha256sum.txt
|
||||
shasum -a 256 -c sha256sum.txt --ignore-missing
|
||||
```
|
||||
|
||||
## Install script (curl | sh)
|
||||
|
||||
A convenience installer that wraps the GitHub Release binary download
|
||||
(with checksum verification) — handy for CI base images and headless
|
||||
machines:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/alibaba/open-code-review/main/install.sh | sh
|
||||
```
|
||||
|
||||
It honours two environment variables:
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
|---|---|---|
|
||||
| `OCR_INSTALL_DIR` | `/usr/local/bin` | Where to place the `ocr` binary. |
|
||||
| `OCR_VERSION` | latest release | Pin a specific release tag (e.g. `v1.2.3`). |
|
||||
|
||||
The script supports `darwin` and `linux` on `amd64` / `arm64`; for
|
||||
Windows, use the [GitHub Release binary](#github-release-binary) or
|
||||
[NPM](#npm-recommended) path instead.
|
||||
|
||||
## Build from source
|
||||
|
||||
You only need this path if you're hacking on OCR or running on a platform
|
||||
without a pre-built binary.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [Go ≥ 1.25](https://go.dev/dl/)
|
||||
- [Git](https://git-scm.com/)
|
||||
- [Make](https://www.gnu.org/software/make/)
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alibaba/open-code-review.git
|
||||
cd open-code-review
|
||||
make build # writes dist/opencodereview
|
||||
sudo cp dist/opencodereview /usr/local/bin/ocr
|
||||
```
|
||||
|
||||
### Build for another platform
|
||||
|
||||
```bash
|
||||
make build-linux-amd64
|
||||
make build-linux-arm64
|
||||
make build-darwin-amd64
|
||||
make build-darwin-arm64
|
||||
make build-windows-amd64 # Windows (x86_64)
|
||||
make build-windows-arm64 # Windows (ARM64)
|
||||
make build-all # all six at once
|
||||
make sha256sum # also produce sha256sum.txt
|
||||
```
|
||||
|
||||
`make dist` runs `clean → build-all → sha256sum` and writes a `VERSION`
|
||||
file alongside the binaries — that's exactly what the release pipeline
|
||||
runs.
|
||||
|
||||
### Run tests
|
||||
|
||||
```bash
|
||||
make test # LC_ALL=C go test -v -race -count=1 ./...
|
||||
```
|
||||
|
||||
## Verifying the install
|
||||
|
||||
Wherever you got the binary from:
|
||||
|
||||
```bash
|
||||
ocr version # prints version + git commit + build date
|
||||
ocr --help # top-level usage
|
||||
ocr review --help # full review-command flag list
|
||||
```
|
||||
|
||||
If you see a "command not found" error, double-check that the install
|
||||
location is on your `$PATH`:
|
||||
|
||||
```bash
|
||||
which ocr
|
||||
echo $PATH
|
||||
```
|
||||
|
||||
## Where OCR stores state
|
||||
|
||||
| Path | What it holds |
|
||||
|---|---|
|
||||
| `~/.opencodereview/config.json` | LLM endpoint, language, telemetry config (managed by `ocr config set`). |
|
||||
| `~/.opencodereview/rule.json` | Optional global review rules. |
|
||||
| `~/.opencodereview/sessions/<encoded-repo-path>/<session-id>.jsonl` | Streaming JSONL transcript of every review session, used by `ocr viewer`. |
|
||||
| `~/.opencodereview/{last-update-check,update.lock,update-available}` | NPM wrapper's background update-check state. The wrapper polls for a newer release (every ~18 min by default) and prints an upgrade hint. Disable with `OCR_NO_UPDATE=1`, or tune the interval with `OCR_UPDATE_INTERVAL` (seconds). Not written by the static binary. |
|
||||
| `<repo>/.opencodereview/rule.json` | Optional per-project review rules — safe to commit. |
|
||||
|
||||
OCR never writes outside `~/.opencodereview/` (besides the transient binary
|
||||
download via NPM). Removing the directory is a clean uninstall.
|
||||
|
||||
## See Also
|
||||
|
||||
- [QuickStart](../quickstart/) — configure an LLM and run your first review.
|
||||
- [Configuration](../configuration/) — every env var and config key OCR honors.
|
||||
- [Contributing](../contributing/) — build from source, run tests, and hack on OCR.
|
||||
66
pages/src/content/docs/en/integrations.md
Normal file
66
pages/src/content/docs/en/integrations.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
title: Integrations
|
||||
sidebar:
|
||||
order: 12
|
||||
---
|
||||
|
||||
OCR is a CLI; it composes with anything that can spawn a process. This
|
||||
section covers the first-class ways to wire it into agentic workflows
|
||||
and CI, with one page per integration method.
|
||||
|
||||
## Why these particular integrations?
|
||||
|
||||
OCR's `--audience agent` mode is purpose-built for being driven by
|
||||
another agent: stdout carries only the JSON / final summary, no
|
||||
progress UI. That makes three composition patterns natural:
|
||||
|
||||
1. **Agent skill** — register OCR as a skill the calling agent can
|
||||
invoke (e.g., the Anthropic Agent SDK).
|
||||
2. **Command (Claude Code plugin)** — install the bundled command so
|
||||
`/open-code-review:review` runs `ocr review` end-to-end. Also works
|
||||
in any other agent that supports a Claude-Code-style command
|
||||
convention.
|
||||
3. **Direct subprocess** — any framework that can call `subprocess.run`
|
||||
(LangChain tool, custom shell, CI step) just shells out.
|
||||
|
||||
You can mix and match. The skill and plugin both end up calling the
|
||||
same binary.
|
||||
|
||||
## Pick a pattern
|
||||
|
||||
| Method | Best when | Page |
|
||||
|---|---|---|
|
||||
| Agent skill | You're building on the Anthropic Agent SDK or another framework that consumes a `SKILL.md`. | [Agent Skill](agent-skill/) |
|
||||
| Command (Claude Code plugin) | You use Claude Code (or any agent with a Claude-Code-style command convention) and want `/open-code-review:review` to do the right thing. | [Command(Claude Code Plugin)](claude-code/) |
|
||||
| Direct subprocess | You need to call OCR from a custom script, LangChain tool, or non-Anthropic agent. | [Direct Subprocess](subprocess/) |
|
||||
| CI/CD | You want OCR to run on every PR or pre-commit. | [CI/CD](ci/) |
|
||||
|
||||
## What about MCP?
|
||||
|
||||
OCR doesn't expose a Model Context Protocol server today. The intended
|
||||
integration surface is "agent calls CLI", which is simpler and avoids
|
||||
the long-running-process issues an MCP server would introduce. If your
|
||||
agent platform requires MCP specifically, wrap the CLI with a thin
|
||||
shim — a 30-line Node script that exposes a single `review` tool is
|
||||
enough.
|
||||
|
||||
## Tips that apply to every pattern
|
||||
|
||||
- **Always pass `--audience agent`** when the caller is non-human.
|
||||
Otherwise progress lines pollute the parsed output.
|
||||
- **Always pass `--background`** when you have PR / requirement
|
||||
context. Quality gain is large, cost is one tool argument.
|
||||
- **Set `--concurrency`** lower in CI (`--concurrency 4`) to stay below
|
||||
vendor rate limits. Default is 8.
|
||||
- **Prefer `--from origin/main --to HEAD`** in CI over `--commit HEAD`
|
||||
— the merge-base computation excludes unrelated changes that landed
|
||||
on `main` since the branch was cut.
|
||||
- **Keep `OCR_LLM_TOKEN` out of stdout/logs.** OCR doesn't print it,
|
||||
but a misconfigured shell may. Use CI secret masking.
|
||||
|
||||
## See Also
|
||||
|
||||
- [CLI Reference](../cli-reference/) — every flag the review command
|
||||
takes.
|
||||
- [Configuration](../configuration/) — env vars and config keys.
|
||||
- [QuickStart](../quickstart/) — minimal setup for a first review.
|
||||
119
pages/src/content/docs/en/integrations/agent-skill.md
Normal file
119
pages/src/content/docs/en/integrations/agent-skill.md
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
---
|
||||
title: Agent Skill
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
Register OCR as a callable skill so an agent framework can invoke it
|
||||
with the right flags, prerequisite checks, and triage rubric — without
|
||||
you re-deriving any of that on the calling side.
|
||||
|
||||
## What ships in the repo
|
||||
|
||||
The repo ships a SKILL manifest at
|
||||
[`skills/open-code-review/SKILL.md`](https://github.com/alibaba/open-code-review/blob/main/skills/open-code-review/SKILL.md).
|
||||
It declares OCR as a callable skill, with prerequisite checks, an
|
||||
invocation workflow, and a comment-triage rubric (High/Medium/Low).
|
||||
|
||||
## Install
|
||||
|
||||
### Option 1: `npx skills add` (recommended)
|
||||
|
||||
Run from inside the project where you want the skill available:
|
||||
|
||||
```bash
|
||||
npx skills add alibaba/open-code-review --skill open-code-review
|
||||
```
|
||||
|
||||
This pulls the manifest from the
|
||||
[skills registry](https://github.com/alibaba/open-code-review/blob/main/skills/open-code-review/SKILL.md)
|
||||
and drops it into the project so any coding agent that respects the
|
||||
skills convention picks it up on the next invocation. Re-run the
|
||||
command to update the skill to the latest version.
|
||||
|
||||
> **Prerequisite:** the skill will install the `ocr` CLI itself the
|
||||
> first time it runs (via `npm install -g @alibaba-group/open-code-review`)
|
||||
> if the binary isn't on `PATH` — see [What the skill does](#what-the-skill-does)
|
||||
> below. You **do** need an LLM configured up front; the skill cannot
|
||||
> do that for you and will stop and ask. See [Configuration](../../configuration/).
|
||||
|
||||
### Option 2: Manual copy (system-wide)
|
||||
|
||||
If you'd rather install the skill globally instead of per-project, copy
|
||||
the folder into your skills directory:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.claude/skills
|
||||
cp -R /path/to/open-code-review/skills/open-code-review ~/.claude/skills/
|
||||
```
|
||||
|
||||
This makes the skill available to every project on the machine.
|
||||
|
||||
## What the skill does
|
||||
|
||||
The SKILL.md is a prompt: when the calling agent loads it, the agent
|
||||
itself executes the steps. End-to-end, a single `/open-code-review`
|
||||
(or equivalent) request unfolds like this:
|
||||
|
||||
1. **Prerequisite check.** Run `which ocr` to confirm the CLI is on
|
||||
`PATH`, then `ocr llm test` to confirm an LLM is reachable.
|
||||
2. **Auto-install the CLI if missing.** If `which ocr` reports
|
||||
"NOT INSTALLED", the agent runs
|
||||
`npm install -g @alibaba-group/open-code-review` and continues. No
|
||||
user prompt — this is treated as a routine setup step.
|
||||
3. **Stop and ask if no LLM is configured.** If `ocr llm test` fails,
|
||||
the agent will *not* invent credentials. It shows the user the two
|
||||
supported options (environment variables or `ocr config set …`) and
|
||||
waits for the user to provide an API key.
|
||||
4. **Extract business context.** Inspect the review target (commits,
|
||||
branch, working copy) and synthesise a short `--background` string.
|
||||
5. **Run the review.** Invoke
|
||||
`ocr review --audience agent --background "…" [--commit | --from/--to]`,
|
||||
picking flags based on whether the user asked to review the working
|
||||
copy, a specific commit, or a branch range.
|
||||
6. **Classify and report.** Group the JSON comments into **High** /
|
||||
**Medium** / **Low** using the rubric in SKILL.md (bugs and
|
||||
security issues are High; nitpicks and likely false positives are
|
||||
silently dropped), then render a Markdown summary.
|
||||
7. **Fix on request.** If the user said "review **and** fix" (or
|
||||
similar), apply safe fixes to High/Medium items inline; otherwise
|
||||
ask before touching the code.
|
||||
|
||||
The full prompt — including the exact triage rubric, output template,
|
||||
and gotchas — lives in
|
||||
[`skills/open-code-review/SKILL.md`](https://github.com/alibaba/open-code-review/blob/main/skills/open-code-review/SKILL.md).
|
||||
Edit your local copy if you want to tighten any of the above (e.g.,
|
||||
flip the default to always-ask before fixing).
|
||||
|
||||
## Anthropic Agent SDK
|
||||
|
||||
Point your SDK init at the installed skill path:
|
||||
|
||||
```python
|
||||
from anthropic_agent_sdk import Agent
|
||||
|
||||
agent = Agent(
|
||||
skill_paths=["/path/to/open-code-review/skills/open-code-review"],
|
||||
)
|
||||
|
||||
agent.run("Review my staged changes — focus on race conditions.")
|
||||
```
|
||||
|
||||
The SDK loads the SKILL.md prompt and the agent executes the workflow
|
||||
described in [What the skill does](#what-the-skill-does) — including
|
||||
the `npm install` fallback and the prompt-for-credentials step if no
|
||||
LLM is configured.
|
||||
|
||||
## Other agent frameworks
|
||||
|
||||
Any framework with a "register external skill" surface can ingest the
|
||||
SKILL.md — it's just markdown with frontmatter. If your framework
|
||||
expects a different schema, the markdown body is still useful as a
|
||||
prompt template.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Command(Claude Code Plugin)](../claude-code/) — the
|
||||
slash-command flavor of the same skill.
|
||||
- [Direct Subprocess](../subprocess/) — bypass the manifest and call
|
||||
the CLI yourself.
|
||||
459
pages/src/content/docs/en/integrations/ci.md
Normal file
459
pages/src/content/docs/en/integrations/ci.md
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
---
|
||||
title: CI/CD
|
||||
sidebar:
|
||||
order: 4
|
||||
---
|
||||
|
||||
Run OCR on every Pull Request or Merge Request. The upstream repo
|
||||
ships two ready-made pipelines you copy and configure — one for
|
||||
GitHub Actions, one for GitLab CI. Both are thin wrappers around the
|
||||
core command from [Direct Subprocess](../subprocess/).
|
||||
|
||||
## How CI/CD integration works
|
||||
|
||||
Every recipe on this page follows the same pattern — the GitHub
|
||||
Actions and GitLab CI sections below are just the concrete
|
||||
implementations of it:
|
||||
|
||||
1. **Trigger on a PR / MR event.** A new pull request, an updated
|
||||
merge request, or a manual `/open-code-review` comment kicks off
|
||||
the job.
|
||||
2. **Install `ocr`** in the runner, typically
|
||||
`npm install -g @alibaba-group/open-code-review`. The runner is
|
||||
ephemeral, so this happens on every run.
|
||||
3. **Configure the LLM** from CI secrets via `ocr config set`
|
||||
(endpoint, token, model). There is no persisted
|
||||
`~/.opencodereview` to fall back on.
|
||||
4. **Run the review in range mode** with machine-readable output, so
|
||||
stdout is a clean JSON envelope:
|
||||
|
||||
```bash
|
||||
ocr review \
|
||||
--from "origin/<base-branch>" \
|
||||
--to "origin/<head-branch>" \
|
||||
--format json \
|
||||
--audience agent
|
||||
```
|
||||
|
||||
`--format json` gives a parseable payload; `--audience agent`
|
||||
suppresses progress lines. See the
|
||||
[JSON shape](../subprocess/#json-shape) for the envelope every
|
||||
recipe consumes.
|
||||
5. **Parse the JSON** and walk `comments[]`.
|
||||
6. **Post comments back** to the PR / MR via the provider's review
|
||||
API. Entries without valid line info (file-level findings) are
|
||||
folded into a summary note instead of being posted inline; the
|
||||
posting step also falls back to a plain summary comment if the
|
||||
inline-batch API rejects the request.
|
||||
|
||||
Two kinds of credentials are always in play: the **LLM credentials**
|
||||
OCR uses to generate findings, and a **PR/MR write token** the
|
||||
posting step uses to comment back. The GitHub recipe gets the latter
|
||||
for free via `GITHUB_TOKEN`; GitLab recommends an explicit
|
||||
`GITLAB_API_TOKEN`, but the built-in `CI_JOB_TOKEN` is used as a
|
||||
fallback for fork MRs (it can post discussions via `/discussions`) —
|
||||
a dedicated token is recommended for reliability.
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
The upstream workflow lives at
|
||||
[`examples/github_actions/ocr-review.yml`](https://github.com/alibaba/open-code-review/blob/main/examples/github_actions/ocr-review.yml).
|
||||
|
||||
### What it does
|
||||
|
||||
- Triggers on `pull_request_target` (`opened`) **and** `issue_comment` events
|
||||
whose body starts with `/open-code-review` or `@open-code-review` —
|
||||
the latter lets reviewers re-run OCR on demand by commenting on a PR.
|
||||
(`pull_request_target` is used instead of `pull_request` so that
|
||||
secrets are available even for PRs opened from forks; OCR only reads
|
||||
the diff and does not execute code from the PR.)
|
||||
- Installs OCR via `npm install -g @alibaba-group/open-code-review`,
|
||||
writes config with `ocr config set`, then runs the core command in
|
||||
branch-range mode.
|
||||
- Parses the JSON envelope and posts each finding as an inline review
|
||||
comment via the GitHub Pull Request Review API. Comments without
|
||||
line info are folded into the summary body. If batch submission
|
||||
fails, it falls back to posting comments one-by-one and surfaces
|
||||
statistics in a summary comment.
|
||||
|
||||
### Install
|
||||
|
||||
Drop the workflow into your repo:
|
||||
|
||||
```bash
|
||||
mkdir -p .github/workflows
|
||||
curl -o .github/workflows/ocr-review.yml \
|
||||
https://raw.githubusercontent.com/alibaba/open-code-review/main/examples/github_actions/ocr-review.yml
|
||||
```
|
||||
|
||||
### Required secrets
|
||||
|
||||
Set under **Settings → Secrets and variables → Actions**:
|
||||
|
||||
| Secret | Required | Description |
|
||||
|---|---|---|
|
||||
| `OCR_LLM_URL` | Yes | LLM API endpoint (e.g. `https://api.openai.com/v1/chat/completions`). |
|
||||
| `OCR_LLM_AUTH_TOKEN` | Yes | Authentication token for the LLM API. This CI secret is passed to `ocr config set llm.auth_token`. (OCR's direct env var is `OCR_LLM_TOKEN`, not `OCR_LLM_AUTH_TOKEN`.) |
|
||||
| `OCR_LLM_MODEL` | No | Model name. No default — must be set explicitly. |
|
||||
| `OCR_LLM_USE_ANTHROPIC` | No | Set to `true` for Anthropic Claude models. |
|
||||
|
||||
`GITHUB_TOKEN` is auto-provided; the workflow declares
|
||||
`pull-requests: write` so it can post review comments.
|
||||
|
||||
> The workflow also runs
|
||||
> `ocr config set llm.extra_body '{"thinking": {"type": "disabled"}}'`
|
||||
> at startup, which turns off thinking-mode requests for
|
||||
> compatibility across LLM providers that don't support that field.
|
||||
> Remove the line if your provider needs thinking-mode left on.
|
||||
|
||||
### Customization
|
||||
|
||||
All of the following are edits to the workflow file you just copied
|
||||
(`.github/workflows/ocr-review.yml`).
|
||||
|
||||
#### Background context
|
||||
|
||||
`--background` is the single highest-leverage flag — see the
|
||||
[tips that apply to every pattern](../#tips-that-apply-to-every-pattern).
|
||||
Feed the PR title (works especially well when titles follow a
|
||||
semantic convention like `feat(auth): add OAuth2 support`):
|
||||
|
||||
```yaml
|
||||
- name: Run OCR review
|
||||
run: |
|
||||
ocr review \
|
||||
--background "${{ github.event.pull_request.title }}" \
|
||||
--from "origin/${{ github.base_ref }}" \
|
||||
--to "origin/${{ github.head_ref }}" \
|
||||
--format json --audience agent
|
||||
```
|
||||
|
||||
#### Custom rules
|
||||
|
||||
Pass a project-specific rule file with `--rule`:
|
||||
|
||||
```yaml
|
||||
- name: Run OCR review
|
||||
run: |
|
||||
ocr review --rule ./my-rules.json \
|
||||
--from "origin/${{ github.base_ref }}" \
|
||||
--to "origin/${{ github.head_ref }}"
|
||||
```
|
||||
|
||||
See [Review Rules](../../review-rules/) for the schema.
|
||||
|
||||
#### Concurrency
|
||||
|
||||
The default is 8 parallel per-file sub-agents. Lower it on large PRs
|
||||
to stay under your LLM provider's rate limits:
|
||||
|
||||
```yaml
|
||||
- name: Run OCR review
|
||||
run: |
|
||||
ocr review --concurrency 5 \
|
||||
--from "origin/${{ github.base_ref }}" \
|
||||
--to "origin/${{ github.head_ref }}"
|
||||
```
|
||||
|
||||
#### Trigger pattern
|
||||
|
||||
The default workflow triggers on PR **opened** and on PR comments
|
||||
beginning with `/open-code-review` or `@open-code-review`. Two common
|
||||
adjustments:
|
||||
|
||||
Run on more PR lifecycle events (e.g., re-review when new commits
|
||||
are pushed):
|
||||
|
||||
```yaml
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
```
|
||||
|
||||
Use a different comment keyword:
|
||||
|
||||
```yaml
|
||||
if: |
|
||||
github.event_name == 'pull_request' ||
|
||||
(github.event_name == 'issue_comment'
|
||||
&& github.event.issue.pull_request
|
||||
&& startsWith(github.event.comment.body, '/review'))
|
||||
```
|
||||
|
||||
The `github.event.issue.pull_request` check ensures the comment is
|
||||
on a PR, not a regular issue.
|
||||
|
||||
#### Pin the OCR version
|
||||
|
||||
The default workflow installs the latest published version. To pin:
|
||||
|
||||
```yaml
|
||||
- name: Install OpenCodeReview
|
||||
run: npm install -g @alibaba-group/open-code-review@1.0.0
|
||||
```
|
||||
|
||||
#### Post under a GitHub App identity
|
||||
|
||||
By default, review comments come from `github-actions[bot]`. To post
|
||||
under a branded bot like `OpenCodeReview Bot`, swap `GITHUB_TOKEN`
|
||||
for a GitHub App installation token.
|
||||
|
||||
1. **Create the app** at *Settings → Developer settings → GitHub
|
||||
Apps → New GitHub App*. Disable the webhook (not needed for this
|
||||
use case). Under *Repository permissions* grant:
|
||||
- **Pull requests**: Read and write
|
||||
- **Contents**: Read-only (for fetching diffs)
|
||||
- **Metadata**: Read-only (required)
|
||||
|
||||
2. **Generate a private key** from the app settings page and download
|
||||
the `.pem` file. Note the **App ID** from the same page.
|
||||
|
||||
3. **Install the app** on the repositories you want OCR to review.
|
||||
The Installation ID appears in the post-install URL, e.g.
|
||||
`https://github.com/settings/installations/12345` → ID is `12345`.
|
||||
|
||||
4. **Add three secrets** under *Settings → Secrets and variables →
|
||||
Actions*:
|
||||
|
||||
| Secret | Value |
|
||||
|---|---|
|
||||
| `GITHUB_APP_ID` | The App ID. |
|
||||
| `GITHUB_APP_PRIVATE_KEY` | Full contents of the `.pem` file, including the `-----BEGIN RSA PRIVATE KEY-----` and `-----END RSA PRIVATE KEY-----` lines. |
|
||||
| `GITHUB_APP_INSTALLATION_ID` | The Installation ID. |
|
||||
|
||||
5. **Mint a token and use it** in the comment-posting step:
|
||||
|
||||
```yaml
|
||||
- name: Get GitHub App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.GITHUB_APP_ID }}
|
||||
private-key: ${{ secrets.GITHUB_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Post review comments to PR
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
# ...existing post script...
|
||||
```
|
||||
|
||||
Reviews will now appear as posted by your app's name instead of
|
||||
`github-actions[bot]`.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
| Symptom | Cause / Fix |
|
||||
|---|---|
|
||||
| `Cannot find merge-base` | The checkout step used a shallow clone, but range-mode review needs full history. The upstream workflow sets `fetch-depth: 0` on `actions/checkout` — preserve that setting if you edit the file. |
|
||||
| `Failed to parse OCR output` | `OCR_LLM_URL` or `OCR_LLM_AUTH_TOKEN` is missing or wrong. Re-check the values under *Settings → Secrets and variables → Actions*. |
|
||||
| Review comments land on the wrong lines | Usually means the diff shifted between the moment the review started and when comments were posted. The posting script falls back to a plain issue comment in that case — no action needed. |
|
||||
|
||||
> **Note.** The `OCR_DEBUG` env var is **not currently implemented**
|
||||
> in OCR — setting `OCR_DEBUG: "1"` has no effect. It's documented
|
||||
> here in case it is wired up later. For verbose output today, inspect
|
||||
> the raw review JSON and stderr that the workflow writes to
|
||||
> `/tmp/ocr-result.json` and `/tmp/ocr-stderr.log` (see troubleshooting
|
||||
> below), or run `ocr review` locally.
|
||||
|
||||
## GitLab CI
|
||||
|
||||
The upstream pipeline lives at
|
||||
[`examples/gitlab_ci/.gitlab-ci.yml`](https://github.com/alibaba/open-code-review/blob/main/examples/gitlab_ci/.gitlab-ci.yml).
|
||||
|
||||
### What it does
|
||||
|
||||
- Triggers on `merge_requests` events (all MR events — creation,
|
||||
updates, reopen).
|
||||
- Runs in a `node:20` image, installs OCR, configures it via
|
||||
`ocr config set`, then runs the core command in MR diff mode.
|
||||
- Parses the JSON envelope with an inlined Python script and posts
|
||||
each finding as a GitLab Discussion (inline on the diff), using
|
||||
the MR's `versions` endpoint to compute correct `base_sha` /
|
||||
`start_sha` / `head_sha` for accurate positioning. Falls back to
|
||||
regular MR notes for any comment that can't be posted inline, and
|
||||
closes with a summary note.
|
||||
|
||||
### Install
|
||||
|
||||
Drop the pipeline into your repo root:
|
||||
|
||||
```bash
|
||||
curl -o .gitlab-ci.yml \
|
||||
https://raw.githubusercontent.com/alibaba/open-code-review/main/examples/gitlab_ci/.gitlab-ci.yml
|
||||
```
|
||||
|
||||
If you already have a `.gitlab-ci.yml` and want to keep it, vendor
|
||||
the recipe to a different path and pull it in with `include:`:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- local: 'ci/ocr-review.gitlab-ci.yml'
|
||||
```
|
||||
|
||||
### Required CI/CD variables
|
||||
|
||||
Set under **Settings → CI/CD → Variables**:
|
||||
|
||||
| Variable | Required | Masked | Description |
|
||||
|---|---|---|---|
|
||||
| `OCR_LLM_URL` | Yes | No | LLM API endpoint URL. |
|
||||
| `OCR_LLM_AUTH_TOKEN` | Yes | Yes | API authentication token. This CI variable is passed to `ocr config set llm.auth_token`. (OCR's direct env var is `OCR_LLM_TOKEN`, not `OCR_LLM_AUTH_TOKEN`.) |
|
||||
| `OCR_LLM_MODEL` | No | No | Model name. No default — must be set explicitly. |
|
||||
| `GITLAB_API_TOKEN` | No | Yes | Project / personal / group access token with `api` scope. Optional — the built-in `CI_JOB_TOKEN` is used as a fallback when this is absent (e.g. for fork MRs). A dedicated `GITLAB_API_TOKEN` is recommended for reliability. |
|
||||
|
||||
> GitLab rejects variables shorter than 8 characters, so
|
||||
> `llm.use_anthropic` is hardcoded to `false` in the pipeline. To use
|
||||
> Anthropic Claude models, edit the script directly.
|
||||
|
||||
> The pipeline also runs
|
||||
> `ocr config set llm.extra_body '{"thinking": {"type": "disabled"}}'`
|
||||
> at startup, which turns off thinking-mode requests for
|
||||
> compatibility across LLM providers that don't support that field.
|
||||
> Remove the line if your provider needs thinking-mode left on.
|
||||
|
||||
> **Quick bot-naming tip.** For Project Access Tokens and Group
|
||||
> Access Tokens, the token's **name** is what appears next to MR
|
||||
> discussions. Naming the token `OpenCodeReview Bot` is a fast way
|
||||
> to brand the reviewer without setting up anything else — handy
|
||||
> when you don't need the more durable service-account setup
|
||||
> documented under [Post under a service account identity](#post-under-a-service-account-identity).
|
||||
|
||||
### Customization
|
||||
|
||||
All of the following are edits to the `.gitlab-ci.yml` you just
|
||||
copied.
|
||||
|
||||
#### Background context
|
||||
|
||||
Pass the MR title to `--background` — especially useful when titles
|
||||
follow a semantic convention like `feat(auth): add OAuth2 support`:
|
||||
|
||||
```yaml
|
||||
script:
|
||||
- |
|
||||
ocr review \
|
||||
--background "$CI_MERGE_REQUEST_TITLE" \
|
||||
--from "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" \
|
||||
--to "${CI_COMMIT_SHA}" \
|
||||
--format json --audience agent
|
||||
```
|
||||
|
||||
#### Custom rules and concurrency
|
||||
|
||||
Same flags as the GitHub Actions recipe — pass `--rule` for a
|
||||
project-specific rule file, and `--concurrency` to throttle parallel
|
||||
sub-agents (default 8):
|
||||
|
||||
```yaml
|
||||
script:
|
||||
- |
|
||||
ocr review --rule ./my-rules.json --concurrency 5 \
|
||||
--from "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" \
|
||||
--to "${CI_COMMIT_SHA}"
|
||||
```
|
||||
|
||||
See [Review Rules](../../review-rules/) for the rule schema.
|
||||
|
||||
#### Pin the OCR version
|
||||
|
||||
```yaml
|
||||
script:
|
||||
- npm install -g @alibaba-group/open-code-review@1.0.0
|
||||
```
|
||||
|
||||
#### Avoid re-reviewing on every push
|
||||
|
||||
`only: [merge_requests]` triggers on **every** MR update, which can
|
||||
burn a lot of LLM tokens on long-running MRs. GitLab has no native
|
||||
"only on creation" event, so the recommended pattern is to detect
|
||||
existing OCR notes before running the review and bail out if any are
|
||||
found. Replace the `ocr review` invocation with a Python wrapper:
|
||||
|
||||
```python
|
||||
import json, os, sys, urllib.request
|
||||
|
||||
GITLAB_URL = os.environ.get("CI_SERVER_URL", "https://gitlab.com")
|
||||
PROJECT_ID = os.environ["CI_PROJECT_ID"]
|
||||
MR_IID = os.environ["CI_MERGE_REQUEST_IID"]
|
||||
API_TOKEN = os.environ["GITLAB_API_TOKEN"]
|
||||
|
||||
url = (
|
||||
f"{GITLAB_URL}/api/v4/projects/{PROJECT_ID}"
|
||||
f"/merge_requests/{MR_IID}/notes?per_page=100"
|
||||
)
|
||||
req = urllib.request.Request(url, headers={"PRIVATE-TOKEN": API_TOKEN})
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
notes = json.loads(resp.read().decode())
|
||||
|
||||
if any("OpenCodeReview" in n.get("body", "") for n in notes):
|
||||
print("OCR already reviewed this MR. Skipping to save tokens.")
|
||||
sys.exit(0)
|
||||
|
||||
# ...otherwise call `ocr review ...` as usual and write the JSON to
|
||||
# the file the posting step expects.
|
||||
```
|
||||
|
||||
To force a re-review after this, delete the previous OCR notes from
|
||||
the MR — the next pipeline run will see no OCR notes and proceed.
|
||||
|
||||
#### Self-hosted GitLab
|
||||
|
||||
No code change needed. The posting script reads `CI_SERVER_URL`
|
||||
(which GitLab sets automatically on every runner), so it talks to
|
||||
your own instance out of the box. Just make sure
|
||||
`GITLAB_API_TOKEN` is issued by your self-hosted instance, not
|
||||
`gitlab.com`.
|
||||
|
||||
#### Post under a service account identity
|
||||
|
||||
By default, review discussions appear under whichever user owns
|
||||
`GITLAB_API_TOKEN`. Swap in a project-scoped service account for a
|
||||
branded bot identity like `OpenCodeReview Bot`.
|
||||
|
||||
1. **Create the service account** at *Project → Settings → Service
|
||||
Accounts → New service account*. The name you pick (e.g.
|
||||
`OpenCodeReview Bot`) is what appears next to MR discussions.
|
||||
|
||||
2. **Invite it to the project** at *Settings → Members → Invite
|
||||
member*. Search for the service-account name and assign
|
||||
`Developer` or `Maintainer` — both have the permissions needed
|
||||
to post discussions.
|
||||
|
||||
3. **Issue an access token** at *Settings → Service Accounts → (the
|
||||
account) → Add new token*. Required scope: `api`. Copy the token
|
||||
immediately — GitLab only shows it once.
|
||||
|
||||
4. **Swap the token value** at *Settings → CI/CD → Variables* —
|
||||
replace the existing `GITLAB_API_TOKEN` value with the service
|
||||
account's token (keep the variable name the same).
|
||||
|
||||
Discussions are now posted under the service account name instead
|
||||
of the user who originally created the token.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
| Symptom | Cause / Fix |
|
||||
|---|---|
|
||||
| `Cannot find merge-base` | The runner used a shallow clone. The upstream pipeline sets `GIT_DEPTH: 0` to force a full clone — preserve that setting if you edit the file. |
|
||||
| `API error 403` when posting | `GITLAB_API_TOKEN` is missing the `api` scope, isn't a member of the project, or — on self-hosted — was issued by a different instance. Reissue with `api` scope and re-add it under *Settings → CI/CD → Variables*. |
|
||||
| `Failed to parse OCR output` | `OCR_LLM_URL` or `OCR_LLM_AUTH_TOKEN` is wrong. Re-check the values under *Settings → CI/CD → Variables*. |
|
||||
| Inline comments land on the wrong lines | GitLab requires exact SHA matching for inline discussions; the posting script fetches `versions` metadata to get the right `base_sha` / `start_sha` / `head_sha`. If a finding still can't be anchored, it falls back to a plain MR note. |
|
||||
|
||||
The pipeline writes raw review JSON to `/tmp/ocr-result.json` and
|
||||
stderr to `/tmp/ocr-stderr.log`. Cat them in a debug step to inspect
|
||||
what OCR returned:
|
||||
|
||||
```yaml
|
||||
script:
|
||||
- cat /tmp/ocr-result.json
|
||||
- cat /tmp/ocr-stderr.log
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Direct Subprocess](../subprocess/) — the JSON shape both pipelines
|
||||
consume, useful when writing your own CI script from scratch.
|
||||
- [Configuration](../../configuration/) — every env var and config
|
||||
key OCR honors.
|
||||
119
pages/src/content/docs/en/integrations/claude-code.md
Normal file
119
pages/src/content/docs/en/integrations/claude-code.md
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
---
|
||||
title: Command(Claude Code Plugin)
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
Install the bundled command so OCR runs end-to-end inside
|
||||
[Claude Code](https://docs.anthropic.com/en/docs/claude-code) — review
|
||||
the diff, classify findings, and automatically apply fixes for the
|
||||
ones worth adopting.
|
||||
|
||||
## What ships in the repo
|
||||
|
||||
The repo ships a Claude Code plugin under
|
||||
[`plugins/open-code-review/`](https://github.com/alibaba/open-code-review/tree/main/plugins/open-code-review).
|
||||
The command prompt itself lives at
|
||||
[`plugins/open-code-review/commands/review.md`](https://github.com/alibaba/open-code-review/blob/main/plugins/open-code-review/commands/review.md)
|
||||
and is the source of truth for the workflow described below.
|
||||
|
||||
## Install
|
||||
|
||||
### Option 1: Plugin marketplace (recommended)
|
||||
|
||||
Run these two commands **inside Claude Code**:
|
||||
|
||||
```bash
|
||||
/plugin marketplace add alibaba/open-code-review
|
||||
/plugin install open-code-review@open-code-review
|
||||
```
|
||||
|
||||
This registers the `/open-code-review:review` slash command and keeps
|
||||
it updateable through `/plugin`.
|
||||
|
||||
### Option 2: Copy the command file directly
|
||||
|
||||
If you'd rather skip the plugin marketplace, drop the command file
|
||||
straight into `.claude/commands/`. This registers as `/open-code-review`
|
||||
(without the `:review` suffix).
|
||||
|
||||
**Project-level** (commit alongside the repo so the team shares it):
|
||||
|
||||
```bash
|
||||
mkdir -p .claude/commands
|
||||
curl -o .claude/commands/open-code-review.md \
|
||||
https://raw.githubusercontent.com/alibaba/open-code-review/main/plugins/open-code-review/commands/review.md
|
||||
```
|
||||
|
||||
**User-level** (available in every project on the machine):
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.claude/commands
|
||||
curl -o ~/.claude/commands/open-code-review.md \
|
||||
https://raw.githubusercontent.com/alibaba/open-code-review/main/plugins/open-code-review/commands/review.md
|
||||
```
|
||||
|
||||
### Other agents with command support
|
||||
|
||||
The command file is plain markdown with a single frontmatter field —
|
||||
nothing about it is Claude-Code-specific. If your agent supports a
|
||||
similar **command** convention (markdown prompts loaded as invokable
|
||||
commands from a directory), the file-copy recipe above is the install
|
||||
path: drop `open-code-review.md` into whichever directory your agent
|
||||
reads commands from, and invoke it the way your agent invokes
|
||||
commands. The prompt body is agent-agnostic — it just tells the model
|
||||
which `ocr` flags to pick and how to triage the output.
|
||||
|
||||
> **Prerequisite:** the command will install the `ocr` CLI itself the
|
||||
> first time it runs (via `npm install -g @alibaba-group/open-code-review`)
|
||||
> if the binary isn't on `PATH`. You **do** need an LLM configured up
|
||||
> front — the command will fail if `ocr llm test` can't reach one. See
|
||||
> [Configuration](../../configuration/).
|
||||
|
||||
## Use
|
||||
|
||||
In Claude Code, invoke the command by name. Use `/open-code-review:review`
|
||||
if you installed via the plugin marketplace, or `/open-code-review` if
|
||||
you copied the file directly:
|
||||
|
||||
```
|
||||
/open-code-review:review
|
||||
/open-code-review:review review this PR against main
|
||||
/open-code-review:review focus on race conditions in commit abc123
|
||||
```
|
||||
|
||||
The prompt parses your request and picks the right `ocr review` flags:
|
||||
no arguments → workspace mode (staged + unstaged + untracked), mention
|
||||
of a commit → `--commit`, mention of a branch range → `--from` / `--to`.
|
||||
You can also pass OCR flags through directly (e.g.
|
||||
`/open-code-review:review --commit abc123` or `--from main --to feature`).
|
||||
|
||||
## What the command does
|
||||
|
||||
The command prompt is short — three steps:
|
||||
|
||||
1. **Run the review.** Invoke `ocr review --audience agent` with the
|
||||
flags inferred from your request (plus an optional `--background`
|
||||
when you've described requirement context). If the `ocr` binary
|
||||
isn't on `PATH`, the command auto-installs it via
|
||||
`npm i -g @alibaba-group/open-code-review` and continues. Output is
|
||||
captured with a 5-minute timeout.
|
||||
2. **Filter and evaluate.** Classify each comment as **High** /
|
||||
**Medium** / **Low**. Low-confidence comments (likely false
|
||||
positives, nitpicks, lacking context) are dropped silently; the
|
||||
rest are displayed.
|
||||
3. **Fix.** Automatically apply fixes for the High/Medium items worth
|
||||
adopting. Unlike the [Agent Skill](../agent-skill/), this command
|
||||
**auto-fixes by default** — it's the right surface for a "review
|
||||
and clean up" workflow, not a "show me a diff" workflow.
|
||||
|
||||
If you want the command to ask before touching code, or to tighten the
|
||||
triage rubric, edit your local copy of the prompt. Claude Code
|
||||
re-reads commands on every invocation, so no restart is needed.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Agent Skill](../agent-skill/) — the SDK-level equivalent; same
|
||||
underlying CLI, different defaults (asks before fixing).
|
||||
- [Direct Subprocess](../subprocess/) — bypass the slash command and
|
||||
call the CLI yourself.
|
||||
171
pages/src/content/docs/en/integrations/subprocess.md
Normal file
171
pages/src/content/docs/en/integrations/subprocess.md
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
---
|
||||
title: Direct Subprocess
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
Shell out to `ocr` and parse the JSON. This is the lowest-level
|
||||
integration path — every other method on this site ultimately reduces
|
||||
to it. The [Agent Skill](../agent-skill/) and [Command](../claude-code/)
|
||||
methods are prompt templates that tell a calling agent to do exactly
|
||||
this; the [CI/CD](../ci/) recipes are GitHub Actions and GitLab CI
|
||||
pipelines that do the same thing from a script — no orchestrating
|
||||
agent, just subprocess invocation, JSON parsing, and posting comments
|
||||
back to the PR / MR. Use this page directly when you're calling OCR
|
||||
from a custom script, a LangChain tool, or any other framework that
|
||||
isn't already covered.
|
||||
|
||||
## Bash
|
||||
|
||||
```bash
|
||||
result=$(ocr review --format json --audience agent)
|
||||
status=$(echo "$result" | jq -r '.status')
|
||||
total=$(echo "$result" | jq '.comments | length')
|
||||
echo "Status: $status — $total comments"
|
||||
echo "$result" | jq -r '.comments[] | "\(.path):\(.start_line) — \(.content)"'
|
||||
```
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
import json, subprocess
|
||||
|
||||
proc = subprocess.run(
|
||||
["ocr", "review", "--format", "json", "--audience", "agent",
|
||||
"--from", "origin/main", "--to", "HEAD",
|
||||
"--background", pr_description],
|
||||
capture_output=True, text=True, check=True,
|
||||
)
|
||||
data = json.loads(proc.stdout)
|
||||
for c in data["comments"]:
|
||||
if c["start_line"] > 0:
|
||||
post_line_comment(c["path"], c["start_line"], c["content"])
|
||||
```
|
||||
|
||||
## JSON shape
|
||||
|
||||
OCR emits a single top-level **object** (not a bare array). Here is a
|
||||
complete `success` envelope with one finding:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"summary": {
|
||||
"files_reviewed": 1,
|
||||
"comments": 1,
|
||||
"total_tokens": 12770,
|
||||
"input_tokens": 12450,
|
||||
"output_tokens": 320,
|
||||
"elapsed": "9s"
|
||||
},
|
||||
"comments": [
|
||||
{
|
||||
"path": "internal/cache/store.go",
|
||||
"content": "Concurrent map access without a lock — wrap reads and writes with `sync.RWMutex` to avoid a race on the shared cache.",
|
||||
"start_line": 42,
|
||||
"end_line": 47,
|
||||
"existing_code": "func (s *Store) Get(k string) string {\n return s.m[k]\n}",
|
||||
"suggestion_code": "func (s *Store) Get(k string) string {\n s.mu.RLock()\n defer s.mu.RUnlock()\n return s.m[k]\n}",
|
||||
"thinking": "The struct exposes `m map[string]string` without a guarding mutex, and Get/Set are called from concurrent request handlers."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Top-level fields
|
||||
|
||||
| Field | Type | Always present | Notes |
|
||||
|---|---|---|---|
|
||||
| `status` | string | Yes | One of `success`, `completed_with_warnings`, `completed_with_errors`, `skipped`. |
|
||||
| `message` | string | No | Short human-readable summary. Set on empty / skipped runs, e.g. `"No comments generated. Looks good to me."`. |
|
||||
| `summary` | object | No | Run aggregates. Present on completed runs; omitted on `skipped`. Fields below. |
|
||||
| `comments` | array | Yes | Possibly empty. Per-comment schema below. |
|
||||
| `warnings` | array | No | Present only when one or more sub-agents failed or were skipped. Schema below. |
|
||||
|
||||
### Summary shape (`summary`)
|
||||
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| `files_reviewed` | int | Number of files that survived all filters and were sent to the model. |
|
||||
| `comments` | int | Total comments emitted across all files (matches `comments.length`). |
|
||||
| `total_tokens` | int | Sum of prompt + completion tokens across every LLM call in the run. |
|
||||
| `input_tokens` | int | Prompt tokens (including cache-read tokens) across every LLM call. |
|
||||
| `output_tokens` | int | Completion tokens (including cache-write tokens) across every LLM call. |
|
||||
| `cache_read_tokens` | int | Total cache-read tokens across every LLM call. Omitted (`omitempty`) when zero. |
|
||||
| `cache_write_tokens` | int | Total cache-write tokens across every LLM call. Omitted (`omitempty`) when zero. |
|
||||
| `elapsed` | string | Wall-clock duration rounded to whole seconds, formatted by Go's `time.Duration.String()` (e.g. `"1m12s"`). |
|
||||
|
||||
### Per-comment fields (`comments[]`)
|
||||
|
||||
| Field | Type | Always present | Notes |
|
||||
|---|---|---|---|
|
||||
| `path` | string | Yes | Repo-relative file path. |
|
||||
| `content` | string | Yes | The review comment, in Markdown. |
|
||||
| `start_line` | int | Yes | First line of the affected range. A value `< 1` means the comment has no line anchor (file-level) — fold these into the summary instead of trying to post them inline. |
|
||||
| `end_line` | int | Yes | Last line of the affected range. Equal to `start_line` for single-line comments. |
|
||||
| `existing_code` | string | No | Original code snippet to be replaced. Omitted for advisory comments with no diff. |
|
||||
| `suggestion_code` | string | No | Proposed replacement for `existing_code`. Always paired with `existing_code` when present. |
|
||||
| `thinking` | string | No | Model's reasoning trail. Useful for triage / debugging; safe to drop before displaying to humans. |
|
||||
|
||||
### Warnings shape (`warnings[]`)
|
||||
|
||||
A run where some files were skipped or failed looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "completed_with_errors",
|
||||
"message": "Some files could not be reviewed due to errors.",
|
||||
"comments": [],
|
||||
"warnings": [
|
||||
{
|
||||
"file": "src/very_long_file.go",
|
||||
"message": "diff size exceeds 80% of MAX_TOKENS; skipped",
|
||||
"type": "token_threshold_exceeded"
|
||||
},
|
||||
{
|
||||
"file": "src/broken.py",
|
||||
"message": "sub-agent failed: context deadline exceeded",
|
||||
"type": "subtask_error"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| `file` | string | Repo-relative path of the file that triggered the warning. |
|
||||
| `message` | string | Short human-readable description. |
|
||||
| `type` | string | Stable kind for filtering. Currently emitted: `subtask_error` (a sub-agent run failed) and `token_threshold_exceeded` (diff too large for the model). |
|
||||
|
||||
When `warnings` contains at least one `subtask_error`, `status` is
|
||||
`completed_with_errors`; otherwise it's `completed_with_warnings`.
|
||||
|
||||
### No severity / priority field
|
||||
|
||||
OCR does **not** emit a `severity` or `priority` field. The
|
||||
High/Medium/Low triage you see in the [Agent Skill](../agent-skill/)
|
||||
and [Command](../claude-code/) docs is added by the calling agent
|
||||
after it receives the raw comments — don't try to `jq '.comments[].severity'`,
|
||||
it won't exist.
|
||||
|
||||
## Empty-result handling
|
||||
|
||||
A workspace with **no eligible files** is reported via `status`, so
|
||||
callers can distinguish "nothing changed" from "no findings":
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "skipped",
|
||||
"message": "No supported files changed.",
|
||||
"comments": []
|
||||
}
|
||||
```
|
||||
|
||||
Always check `status == "skipped"` before declaring "all clean".
|
||||
|
||||
## See Also
|
||||
|
||||
- [CI/CD](../ci/) — ready-made GitHub Actions and pre-commit recipes
|
||||
built on top of subprocess invocation.
|
||||
- [Agent Skill](../agent-skill/) — when the caller is an Anthropic
|
||||
SDK agent rather than a plain script.
|
||||
139
pages/src/content/docs/en/overview.md
Normal file
139
pages/src/content/docs/en/overview.md
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
---
|
||||
title: Overview
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
## What is Open Code Review?
|
||||
|
||||
Open Code Review (**OCR** for short, distinct from Optical Character
|
||||
Recognition) is an AI-powered code review CLI distributed as the
|
||||
[`@alibaba-group/open-code-review`](https://www.npmjs.com/package/@alibaba-group/open-code-review)
|
||||
NPM package and as standalone Go binaries. The CLI binary is named `ocr`.
|
||||
|
||||
In a single command (`ocr review`) it:
|
||||
|
||||
1. Resolves a Git diff — workspace, branch range, or single commit.
|
||||
2. Filters the changed files using both system defaults and any user rules.
|
||||
3. Spawns one **per-file sub-agent** for each changed file, in parallel.
|
||||
4. Each sub-agent runs an LLM tool-use loop, optionally preceded by a
|
||||
**plan phase** for larger diffs.
|
||||
5. The model calls `code_comment` to record findings, optionally `file_read`,
|
||||
`code_search`, `file_find`, `file_read_diff` to gather context, and
|
||||
`task_done` when finished.
|
||||
6. OCR resolves each comment to exact line numbers, runs an optional
|
||||
re-positioning pass for any comments that didn't match cleanly, and
|
||||
prints (or JSON-emits) the final list.
|
||||
|
||||
## The problem with general-purpose agents
|
||||
|
||||
If you've used a general-purpose coding agent (Claude Code with a Skill,
|
||||
Cursor, Cline, etc.) for code review, you've likely run into:
|
||||
|
||||
- **Incomplete coverage** — on larger changesets the agent quietly cuts
|
||||
corners, reviewing only some files.
|
||||
- **Position drift** — comments don't line up with the code they refer to;
|
||||
line numbers and file paths drift off target.
|
||||
- **Unstable quality** — natural-language Skills are hard to debug, and
|
||||
output quality fluctuates with minor prompt edits.
|
||||
|
||||
The root cause: a purely language-driven architecture lacks **hard
|
||||
constraints** on the review process.
|
||||
|
||||
## Core design: deterministic engineering × agent
|
||||
|
||||
OCR's core philosophy is to combine **deterministic engineering** with an
|
||||
**agent** — each handling what it does best.
|
||||
|
||||
### Deterministic engineering — hard constraints
|
||||
|
||||
For steps that *must not go wrong*, engineering logic — not the model —
|
||||
guarantees correctness:
|
||||
|
||||
- **Precise file selection** — a [five-gate filter](../review-rules/#how-files-are-filtered)
|
||||
decides exactly which files are reviewed, with explicit `include`/`exclude`
|
||||
controls.
|
||||
- **Smart file bundling** — related files (e.g., `message_en.properties` and
|
||||
`message_zh.properties`) can be grouped into a single review unit. Each
|
||||
bundle runs as a sub-agent with isolated context — divide and conquer that
|
||||
stays stable on very large changesets and naturally supports concurrent
|
||||
review.
|
||||
- **Fine-grained rule matching** — review rules are matched per file path
|
||||
with first-match-wins, keeping the model's attention sharply focused and
|
||||
eliminating noise. Template-based matching is more stable than purely
|
||||
language-driven rule guidance.
|
||||
- **External positioning and reflection modules** — independent comment
|
||||
positioning ([`internal/diff/relocation.go`](https://github.com/alibaba/open-code-review/blob/main/internal/diff/relocation.go))
|
||||
and re-location passes systematically improve both location and content
|
||||
accuracy.
|
||||
|
||||
### Agent — dynamic decision-making
|
||||
|
||||
The agent's strengths are concentrated where they matter most:
|
||||
|
||||
- **Scenario-tuned prompts** — prompt templates deeply optimized for code
|
||||
review, improving effectiveness while reducing token consumption (see
|
||||
[`internal/config/template/task_template.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/template/task_template.json)).
|
||||
- **Scenario-tuned toolset** — distilled from analysis of tool-call traces in
|
||||
large-scale production data (call-frequency distributions, per-tool
|
||||
repetition rates, the impact of each tool on the overall call chain). The
|
||||
result is a purpose-built set of [six tools](../tools/) that is more stable
|
||||
and predictable than a generic agent toolkit.
|
||||
|
||||
## How the pipeline fits together
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start["<b>ocr review --from main --to feature</b>"]
|
||||
S1["<b>1. Resolve LLM endpoint</b><br/>config / env / shell rc"]
|
||||
S2["<b>2. Load diffs from git</b><br/>workspace / commit / range"]
|
||||
S3["<b>3. Filter files</b><br/>binary → user_exclude → user_include<br/>→ ext allowlist → default path"]
|
||||
S4["<b>4. Drop diffs > 80% of MAX_TOKENS</b>"]
|
||||
S5["<b>5. Dispatch per-file sub-agents</b> (concurrent)<br/><br/>For each file:<br/> a. Plan phase (if changed lines ≥ 50)<br/> b. Main loop: LLM → tool calls → … → task_done<br/> c. code_comment results collected (async via worker pool)<br/><br/>Memory compression triggers when context<br/>exceeds 60 % (async) or 80 % (sync) of MAX_TOKENS."]
|
||||
S6["<b>6. Resolve line numbers</b><br/>from <code>existing_code</code> against diffs.<br/>Re-locate via LLM if needed."]
|
||||
S7["<b>7. Emit text or JSON output</b><br/>(and persist session to disk)"]
|
||||
|
||||
Start --> S1 --> S2 --> S3 --> S4 --> S5 --> S6 --> S7
|
||||
```
|
||||
|
||||
## Project layout
|
||||
|
||||
```
|
||||
open-code-review/
|
||||
├── cmd/opencodereview/ # CLI entry point: dispatch, flags, commands
|
||||
├── internal/
|
||||
│ ├── agent/ # Per-file sub-agent loop + memory compression
|
||||
│ ├── config/
|
||||
│ │ ├── allowlist/ # Default file-extension allowlist & exclusions
|
||||
│ │ ├── rules/ # Layered rule resolver, system rule docs
|
||||
│ │ ├── template/ # Plan / main / memory_compression prompts
|
||||
│ │ ├── testconnection/ # Built-in `ocr llm test` task
|
||||
│ │ └── toolsconfig/ # Tool definitions sent to the model
|
||||
│ ├── diff/ # Git diff parsing, hunk math, relocation
|
||||
│ ├── gitcmd/ # Git subprocess runner
|
||||
│ ├── llm/ # Anthropic + OpenAI protocols, retries, BPE tokens
|
||||
│ ├── model/ # Diff / Comment data structures
|
||||
│ ├── pathutil/ # Path utilities
|
||||
│ ├── release/ # Release-notes generation
|
||||
│ ├── session/ # JSONL persistence of every review session
|
||||
│ ├── stdout/ # Quiet-able stdout writer for `--audience agent`
|
||||
│ ├── suggestdiff/ # Build "Apply suggestion" diffs
|
||||
│ ├── telemetry/ # OpenTelemetry spans, metrics, exporters
|
||||
│ ├── tool/ # The six built-in tools + comment collector
|
||||
│ └── viewer/ # `ocr viewer` — local web UI for past sessions
|
||||
├── pages/ # React-based marketing landing page (separate)
|
||||
├── plugins/ # Claude Code plugin manifest + commands
|
||||
├── extensions/ # Editor extensions (VS Code)
|
||||
├── examples/ # CI recipes (GitHub Actions, GitLab CI)
|
||||
├── skills/ # Generic agent Skill manifest
|
||||
├── scripts/ # NPM install/update helpers, publish scripts
|
||||
├── npm/ # Per-platform optional dependency packages
|
||||
└── bin/ # NPM wrapper that shells out to the binary
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [QuickStart](../quickstart/) — install and run your first review.
|
||||
- [Architecture](../architecture/) — the agent loop, plan phase, and memory compression.
|
||||
- [CLI Reference](../cli-reference/) — every flag and sub-command.
|
||||
- [Integrations](../integrations/) — call OCR from Claude Code or any agent.
|
||||
242
pages/src/content/docs/en/quickstart.md
Normal file
242
pages/src/content/docs/en/quickstart.md
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
---
|
||||
title: QuickStart
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
Install OCR, point it at any LLM that speaks the Anthropic Messages API or
|
||||
the OpenAI Chat Completions API, and run your first code review.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A working **Git** install — OCR drives Git as a subprocess to read diffs.
|
||||
- An **API key** for an Anthropic-compatible or OpenAI-compatible LLM.
|
||||
- One of:
|
||||
- **Node.js ≥ 18** (recommended; minimum supported: Node 14 — installs via NPM).
|
||||
- Or just `curl` + `chmod` to drop the static binary into `$PATH`.
|
||||
- Or **Go ≥ 1.25** if you prefer to build from source.
|
||||
|
||||
## Step 1 — Install the CLI
|
||||
|
||||
### Option A: NPM (recommended)
|
||||
|
||||
```bash
|
||||
npm install -g @alibaba-group/open-code-review
|
||||
```
|
||||
|
||||
The NPM package installs a small wrapper that downloads the right binary for
|
||||
your OS / architecture on install (via a postinstall hook). If the binary is
|
||||
missing at run time, the wrapper errors out rather than downloading. After
|
||||
install, you have a global `ocr` command:
|
||||
|
||||
```bash
|
||||
ocr --version
|
||||
```
|
||||
|
||||
### Option B: GitHub Release binary
|
||||
|
||||
Pick the binary for your platform from the
|
||||
[releases page](https://github.com/alibaba/open-code-review/releases) and
|
||||
drop it into your `$PATH`:
|
||||
|
||||
```bash
|
||||
# macOS (Apple Silicon)
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-darwin-arm64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# macOS (Intel)
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-darwin-amd64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Linux x86_64
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-linux-amd64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Linux ARM64
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-linux-arm64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Windows (AMD64)
|
||||
curl -Lo ocr.exe https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-windows-amd64.exe
|
||||
|
||||
# Windows (ARM64)
|
||||
curl -Lo ocr.exe https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-windows-arm64.exe
|
||||
```
|
||||
|
||||
### Option C: Build from source
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alibaba/open-code-review.git
|
||||
cd open-code-review
|
||||
make build
|
||||
sudo cp dist/opencodereview /usr/local/bin/ocr
|
||||
```
|
||||
|
||||
> See the [Installation](../installation/) page for details on each option,
|
||||
> including how the NPM wrapper resolves the platform binary.
|
||||
|
||||
## Step 2 — Configure an LLM
|
||||
|
||||
OCR will refuse to run a review until it can resolve a complete LLM
|
||||
endpoint (URL + token + model). It searches four sources in priority order:
|
||||
|
||||
1. `~/.opencodereview/config.json`
|
||||
2. OCR-specific environment variables (`OCR_LLM_*`)
|
||||
3. Claude Code environment variables (`ANTHROPIC_*`)
|
||||
4. `export ANTHROPIC_*` lines parsed out of your shell rc files
|
||||
(`~/.zshrc`, `~/.bashrc`, `~/.bash_profile`, `~/.profile`)
|
||||
|
||||
### Quickest path: `ocr config set`
|
||||
|
||||
```bash
|
||||
ocr config set llm.url https://api.anthropic.com/v1/messages
|
||||
ocr config set llm.auth_token sk-ant-xxxxxxxxxx
|
||||
ocr config set llm.model claude-opus-4-6
|
||||
ocr config set llm.use_anthropic true
|
||||
```
|
||||
|
||||
These values are persisted to `~/.opencodereview/config.json`.
|
||||
|
||||
### Alternative: environment variables
|
||||
|
||||
Highest priority — useful in CI / containers where you don't want a config
|
||||
file on disk:
|
||||
|
||||
```bash
|
||||
export OCR_LLM_URL=https://api.anthropic.com/v1/messages
|
||||
export OCR_LLM_TOKEN=sk-ant-xxxxxxxxxx
|
||||
export OCR_LLM_MODEL=claude-opus-4-6
|
||||
export OCR_USE_ANTHROPIC=true # default true; set false for OpenAI protocol
|
||||
```
|
||||
|
||||
### Already using Claude Code?
|
||||
|
||||
OCR transparently picks up the same vars Claude Code uses, so no extra setup:
|
||||
|
||||
```bash
|
||||
export ANTHROPIC_BASE_URL=https://api.anthropic.com
|
||||
export ANTHROPIC_AUTH_TOKEN=sk-ant-xxxxxxxxxx
|
||||
export ANTHROPIC_MODEL=claude-opus-4-6
|
||||
```
|
||||
|
||||
If `ANTHROPIC_BASE_URL` lacks a versioned path, OCR appends `/v1/messages`
|
||||
automatically.
|
||||
|
||||
### Using an OpenAI-compatible endpoint?
|
||||
|
||||
Set `llm.use_anthropic` to `false` (or `OCR_USE_ANTHROPIC=false`):
|
||||
|
||||
```bash
|
||||
ocr config set llm.url https://api.openai.com/v1/chat/completions
|
||||
ocr config set llm.auth_token sk-xxxxxxxxxx
|
||||
ocr config set llm.model gpt-4o
|
||||
ocr config set llm.use_anthropic false
|
||||
```
|
||||
|
||||
> See [Configuration](../configuration/) for the full key reference,
|
||||
> including `llm.extra_body` for vendor-specific request fields and
|
||||
> `language` for switching review-comment language.
|
||||
|
||||
## Step 3 — Test connectivity
|
||||
|
||||
```bash
|
||||
ocr llm test
|
||||
```
|
||||
|
||||
Expected output (model name varies):
|
||||
|
||||
```
|
||||
Source: OCR config file
|
||||
URL: https://api.anthropic.com/v1/messages
|
||||
Model: claude-opus-4-6
|
||||
Hello! …
|
||||
```
|
||||
|
||||
If you instead get an error like `no valid LLM endpoint configured`, recheck
|
||||
the config keys above. A 401 / 403 means the token is wrong or expired.
|
||||
|
||||
## Step 4 — Run your first review
|
||||
|
||||
Move into any Git repository and run:
|
||||
|
||||
```bash
|
||||
cd path/to/your-repo
|
||||
|
||||
# Workspace mode — reviews staged + unstaged + untracked changes (default)
|
||||
ocr review
|
||||
|
||||
# Branch range — reviews `main..feature-branch`
|
||||
ocr review --from main --to feature-branch
|
||||
|
||||
# Single commit — reviews the diff that commit introduced
|
||||
ocr review --commit abc123
|
||||
```
|
||||
|
||||
You should see a stream of progress lines, finishing with one or more review
|
||||
comments per file.
|
||||
|
||||
> Workspace mode includes **untracked** files. If you only want to review
|
||||
> what you've staged, `git add` selectively beforehand.
|
||||
|
||||
> The three modes above are the basics. See [CLI Reference](../cli-reference/)
|
||||
> for the complete list of `ocr review` flags — concurrency tuning, output
|
||||
> format, audience mode, background context, and more — plus every other
|
||||
> sub-command (`config`, `rules`, `llm test`, `viewer`).
|
||||
|
||||
### Want to see what *would* be reviewed first?
|
||||
|
||||
```bash
|
||||
ocr review --preview # workspace
|
||||
ocr review -c abc123 -p # commit
|
||||
```
|
||||
|
||||
`--preview` runs every filter step but never calls the LLM, so it's free.
|
||||
It prints the file list with each file's status (`added` / `modified` /
|
||||
`deleted` / `renamed` / `binary`) and, for excluded files, the reason
|
||||
(`binary`, `unsupported_ext`, `default_path`, `user_exclude`, `deleted`).
|
||||
|
||||
### JSON output for tooling
|
||||
|
||||
```bash
|
||||
ocr review --format json --audience agent > review.json
|
||||
```
|
||||
|
||||
- `--format json` emits a machine-readable array of comments, each with
|
||||
`path`, `content`, `start_line`, `end_line`, `existing_code`,
|
||||
`suggestion_code` and optional `thinking`.
|
||||
- `--audience agent` suppresses the human-friendly progress UI so the only
|
||||
thing on stdout is the JSON / final summary — exactly what an upstream
|
||||
agent or CI script wants.
|
||||
|
||||
## Step 5 — Review the results
|
||||
|
||||
Each comment includes:
|
||||
|
||||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `path` | File that the comment is about. |
|
||||
| `content` | The review comment itself, in the configured `language`. |
|
||||
| `start_line` / `end_line` | Line range in the **new** version of the file. Both `0` means OCR couldn't precisely position the comment — the issue is real but you'll need to find the exact spot yourself. |
|
||||
| `existing_code` | The snippet from the diff the comment refers to. Used internally for line resolution; useful when `start_line` is `0`. |
|
||||
| `suggestion_code` | Optional fix snippet. |
|
||||
| `thinking` | Optional model reasoning. Only present for some models. |
|
||||
|
||||
## Step 6 — Inspect a past session
|
||||
|
||||
Every review is persisted to `~/.opencodereview/sessions/...` as a
|
||||
JSONL transcript. Browse them in a local web UI:
|
||||
|
||||
```bash
|
||||
ocr viewer # http://localhost:5483
|
||||
ocr viewer --addr :3000
|
||||
```
|
||||
|
||||
> See [Session Viewer](../viewer/) for the full UI tour.
|
||||
|
||||
## See Also
|
||||
|
||||
- [CLI Reference](../cli-reference/) — every sub-command, flag, and output mode.
|
||||
- [Review Rules](../review-rules/) — customize what gets reviewed.
|
||||
- [Integrations](../integrations/) — embed OCR in Claude Code, an Agent skill, or CI.
|
||||
- [Telemetry](../telemetry/) — ship traces and metrics over OTLP.
|
||||
- [FAQ](../faq/) — known errors and remedies.
|
||||
261
pages/src/content/docs/en/review-rules.md
Normal file
261
pages/src/content/docs/en/review-rules.md
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
---
|
||||
title: Review Rules
|
||||
sidebar:
|
||||
order: 7
|
||||
---
|
||||
|
||||
Rules tell OCR **what to focus on** when reviewing each file. They live
|
||||
in JSON files at three layers, plus an embedded system default that ships
|
||||
with the binary.
|
||||
|
||||
## Priority chain
|
||||
|
||||
OCR resolves rules using a **four-layer priority chain**. For each file
|
||||
path, the layers are tried in order; the first matching pattern wins.
|
||||
|
||||
| Priority | Source | Path | Notes |
|
||||
|---|---|---|---|
|
||||
| 1 (highest) | `--rule` flag | user-specified | CLI override; always wins when supplied. |
|
||||
| 2 | Project config | `<repoDir>/.opencodereview/rule.json` | Per-project rules — safe to commit. |
|
||||
| 3 | Global config | `~/.opencodereview/rule.json` | User-wide preferences. |
|
||||
| 4 (lowest) | System default | embedded `system_rules.json` | Built-in rules covering common languages. |
|
||||
|
||||
If a higher-priority layer's file doesn't exist, it's silently skipped —
|
||||
not an error. So a project that never adds `.opencodereview/rule.json`
|
||||
just falls through to the global / system layers.
|
||||
|
||||
The system layer is **always** present (it ships in the binary), so there
|
||||
is always *some* rule resolved.
|
||||
|
||||
## Rule file format (layers 1–3)
|
||||
|
||||
```json
|
||||
{
|
||||
"include": ["src/**/*.{ts,tsx}", "src/**/*.go"],
|
||||
"exclude": ["**/*.test.ts", "**/generated/**"],
|
||||
"rules": [
|
||||
{
|
||||
"path": "src/api/**/*.go",
|
||||
"rule": "All exported handlers must validate request bodies before use."
|
||||
},
|
||||
{
|
||||
"path": "**/*mapper*.xml",
|
||||
"rule": "Check SQL for injection risks, parameter errors, and missing closing tags."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Three independent fields:
|
||||
|
||||
- `include` — optional. Glob patterns that *bypass* built-in default
|
||||
exclude patterns (test-file exclusions — see below). It is not a
|
||||
whitelist: files not matching any `include` pattern still proceed
|
||||
through the `unsupported_ext` and `default_path` checks and may still
|
||||
be reviewed.
|
||||
- `exclude` — optional. Glob patterns for files OCR must *not* review.
|
||||
Highest precedence within the filter.
|
||||
- `rules` — array of `{path, rule}` entries, evaluated **in declaration
|
||||
order**. The first `path` whose glob matches the file determines the
|
||||
prompt OCR sends to the model for that file.
|
||||
|
||||
### Glob features
|
||||
|
||||
OCR uses [`bmatcuk/doublestar/v4`](https://pkg.go.dev/github.com/bmatcuk/doublestar/v4)
|
||||
for matching:
|
||||
|
||||
- `*` — match any characters except `/`.
|
||||
- `**` — match across directory boundaries (`src/**/*.go` covers any
|
||||
depth).
|
||||
- `{a,b,c}` — brace expansion. `*.{ts,tsx,js,jsx}` is expanded to four
|
||||
patterns and matched in turn.
|
||||
- `?` — match a single character.
|
||||
- `[abc]` — character class.
|
||||
|
||||
> Patterns are matched **case-insensitively** (file path is lowercased
|
||||
> before matching). When in doubt, use `ocr rules check <path>` to confirm.
|
||||
|
||||
## How files are filtered
|
||||
|
||||
The filter is a five-gate algorithm in
|
||||
[`internal/agent/preview.go`](https://github.com/alibaba/open-code-review/blob/main/internal/agent/preview.go).
|
||||
For each diff, OCR asks:
|
||||
|
||||
1. **`binary`** — Is the file binary? Excluded.
|
||||
2. **`user_exclude`** — Does the path match any user `exclude` pattern?
|
||||
Excluded.
|
||||
3. **`user_include`** — If the user defined `include`, does the path
|
||||
match? If yes, **kept immediately** (bypasses the `unsupported_ext`
|
||||
and `default_path` gates below).
|
||||
4. **`unsupported_ext`** — Is the file extension in the
|
||||
[allowlist](https://github.com/alibaba/open-code-review/blob/main/internal/config/allowlist/supported_file_types.json)?
|
||||
Excluded if not.
|
||||
5. **`default_path`** — Does the path match a built-in test-file exclude
|
||||
pattern (`**/*_test.go`, `**/*.test.{js,jsx,ts,tsx}`, `**/*_spec.rb`,
|
||||
…)? Excluded.
|
||||
|
||||
Files that survive all five gates are sent to the LLM. A `deleted`
|
||||
reason (not a gate — it's computed separately in `Preview()`) marks
|
||||
files whose new path is `/dev/null`; there's no new content to review.
|
||||
Use `ocr review --preview` to print the result of this filter without
|
||||
spending a token.
|
||||
|
||||
### Default path exclusions
|
||||
|
||||
The built-in exclude list (see
|
||||
[`internal/config/allowlist/default_exclude_patterns.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/allowlist/default_exclude_patterns.json))
|
||||
matches test-file patterns:
|
||||
|
||||
- `**/*_test.go`
|
||||
- `**/src/test/java/**/*.java`
|
||||
- `**/src/test/**/*.kt`
|
||||
- `**/*.test.{js,jsx,ts,tsx}`
|
||||
- `**/*.spec.{js,jsx,ts,tsx}`
|
||||
- `**/__tests__/**`
|
||||
- `**/test/**/*_test.py`
|
||||
- `**/tests/**/*_test.py`
|
||||
- `**/*_test.py`
|
||||
- `**/*_spec.rb`
|
||||
- `**/spec/**/*_spec.rb`
|
||||
- `**/*Test.java`
|
||||
- `**/*Tests.java`
|
||||
- `**/*_test.rs`
|
||||
- `**/oh_modules/**`
|
||||
- `**/*.test.ets`
|
||||
|
||||
Noisy-directory filtering (`vendor/`, `node_modules/`, `target/`, …)
|
||||
happens earlier, at the diff level in
|
||||
[`internal/diff/git.go`](https://github.com/alibaba/open-code-review/blob/main/internal/diff/git.go),
|
||||
before the per-file filter runs.
|
||||
|
||||
To **review** a file that matches one of these test-file patterns, add
|
||||
it to the user `include` list — that overrides the default-path gate.
|
||||
|
||||
## Rule resolution per file
|
||||
|
||||
After filtering decides a file *will* be reviewed, OCR picks the rule
|
||||
text the agent should follow:
|
||||
|
||||
1. Try `--rule` (custom) layer in declaration order.
|
||||
2. Try `<repo>/.opencodereview/rule.json` in declaration order.
|
||||
3. Try `~/.opencodereview/rule.json` in declaration order.
|
||||
4. Fall back to the embedded system rule layer.
|
||||
|
||||
The embedded `system_rules.json` ships with these patterns (in order):
|
||||
|
||||
| Pattern | Rule doc |
|
||||
|---|---|
|
||||
| `**/*.properties` | `properties.md` — i18n / configuration files. |
|
||||
| `**/*{mapper,dao}*.xml` | `mapper_dao_xml.md` — MyBatis-style mapper SQL. |
|
||||
| `**/pom.xml` | `pom_xml.md` — Maven dependencies. |
|
||||
| `**/build.gradle` | `build_gradle.md` — Gradle dependencies. |
|
||||
| `**/package.json` | `package_json.md` — NPM dependencies / scripts. |
|
||||
| `**/Cargo.toml` | `cargo_toml.md` — Rust manifest. |
|
||||
| `**/*.{json,json5}` | `json.md` — generic JSON (also matches `.json5`). |
|
||||
| `.github/workflows/**/*.{yaml,yml}` | `github_workflows.md` — GitHub Actions workflow YAML. |
|
||||
| `.github/**/*.{yaml,yml}` | `github_config.md` — other `.github` config YAML. |
|
||||
| `**/*.{yaml,yml}` | `yaml.md` |
|
||||
| `**/*.java` | `java.md` |
|
||||
| `**/*.ets` | `arkts.md` — ArkTS / HarmonyOS. |
|
||||
| `**/*.{ts,js,tsx,jsx}` | `ts_js_tsx_jsx.md` |
|
||||
| `**/*.{kt}` | `kotlin.md` |
|
||||
| `**/*.rs` | `rust.md` |
|
||||
| `**/*.{cpp,cc,hpp}` | `cpp.md` |
|
||||
| `**/*.c` | `c.md` |
|
||||
| *(fallback)* | `default.md` |
|
||||
|
||||
The resolved rule body becomes the `{{system_rule}}` placeholder in the
|
||||
plan and main task prompts.
|
||||
|
||||
## Inspecting which rule wins: `ocr rules check`
|
||||
|
||||
```bash
|
||||
$ ocr rules check src/main/java/com/example/UserService.java
|
||||
File: src/main/java/com/example/UserService.java
|
||||
Source: System built-in
|
||||
Pattern: **/*.java
|
||||
Rule:
|
||||
────────────────────────────────────────
|
||||
…contents of java.md…
|
||||
────────────────────────────────────────
|
||||
```
|
||||
|
||||
```bash
|
||||
$ ocr rules check --rule custom.json src/main/resources/mapper/UserMapper.xml
|
||||
File: src/main/resources/mapper/UserMapper.xml
|
||||
Source: Custom (--rule)
|
||||
Pattern: **/*mapper*.xml
|
||||
Rule:
|
||||
────────────────────────────────────────
|
||||
…contents of your custom rule…
|
||||
────────────────────────────────────────
|
||||
```
|
||||
|
||||
Use this whenever a rule isn't behaving the way you expected — it tells
|
||||
you the **layer** and the **pattern** that won.
|
||||
|
||||
## Recipes
|
||||
|
||||
### Project-level: enforce a coding standard
|
||||
|
||||
Save as `<repo>/.opencodereview/rule.json` and commit:
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"path": "src/api/**/*.go",
|
||||
"rule": "Every public handler must `defer tx.Rollback()` immediately after starting a transaction."
|
||||
},
|
||||
{
|
||||
"path": "**/*mapper*.xml",
|
||||
"rule": "Check SQL for injection risks, missing parameter binding, and unclosed XML tags."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Project-level: skip generated code, focus on src
|
||||
|
||||
```json
|
||||
{
|
||||
"include": ["src/**/*.{ts,tsx,js,jsx}"],
|
||||
"exclude": ["**/*.gen.ts", "**/generated/**"]
|
||||
}
|
||||
```
|
||||
|
||||
With `include` set, files inside `src/` are kept even if they'd otherwise
|
||||
be dropped by a built-in default exclude pattern (e.g., a test file).
|
||||
Files outside `src/` still go through the normal ext / default checks —
|
||||
`include` is a bypass, not a whitelist.
|
||||
|
||||
### Per-PR override
|
||||
|
||||
```bash
|
||||
ocr review --rule ./.review-rules-only-for-this-pr.json
|
||||
```
|
||||
|
||||
Bypasses both the project and global layers — handy when a single PR
|
||||
needs a totally different review checklist (e.g., security-only review).
|
||||
|
||||
### Global personal preferences
|
||||
|
||||
Put them at `~/.opencodereview/rule.json` so every repo on your machine
|
||||
inherits them:
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"path": "**/*.{ts,tsx,js,jsx}",
|
||||
"rule": "Always check for unhandled promise rejections; warn on `// eslint-disable` without a reason comment."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [CLI Reference](../cli-reference/) — `ocr review --rule`, `--preview`, and `ocr rules check`.
|
||||
- [Configuration](../configuration/) — config file locations and the layered resolution chain.
|
||||
- [Architecture](../architecture/) — how the resolved rule feeds the agent prompt.
|
||||
281
pages/src/content/docs/en/telemetry.md
Normal file
281
pages/src/content/docs/en/telemetry.md
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
---
|
||||
title: Telemetry
|
||||
sidebar:
|
||||
order: 11
|
||||
---
|
||||
|
||||
OCR ships with first-class **OpenTelemetry** support. Every review run
|
||||
produces structured spans, metrics, and events. Wired up to a collector,
|
||||
the data is enough to answer "what did the agent spend time on?",
|
||||
"which models cost what?", and "why did this run fail?".
|
||||
|
||||
## Overview
|
||||
|
||||
Telemetry is **off by default**. Once enabled, OCR exports:
|
||||
|
||||
- **Spans** — three pipeline-level spans (`review.run`, `diff.parse`,
|
||||
`subtask.execute.<file>`) plus one short-lived `event.*` span per
|
||||
decision-point event.
|
||||
- **Metrics** — aggregated counts and histograms for review duration,
|
||||
files reviewed, comments generated, LLM requests / tokens / latency,
|
||||
and tool calls / latency.
|
||||
- **Events** — discrete in-span events like `plan.skipped`,
|
||||
`token.threshold.exceeded`, `review.started`.
|
||||
|
||||
Two exporters are supported:
|
||||
|
||||
| Exporter | When to use |
|
||||
|---|---|
|
||||
| `console` | Personal use / debugging. Pretty-prints spans to stdout. |
|
||||
| `otlp` | System integration. Sends to any OTLP-compatible collector (Jaeger, Tempo, OTel Collector, Datadog Agent, …). |
|
||||
|
||||
## Enabling telemetry
|
||||
|
||||
Like the LLM endpoint, telemetry is configured by either persistent
|
||||
config or environment variables — env wins on conflict.
|
||||
|
||||
### Config-file approach
|
||||
|
||||
```bash
|
||||
ocr config set telemetry.enabled true
|
||||
ocr config set telemetry.exporter otlp
|
||||
ocr config set telemetry.otlp_endpoint localhost:4317
|
||||
ocr config set telemetry.content_logging false
|
||||
```
|
||||
|
||||
The result in `~/.opencodereview/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"telemetry": {
|
||||
"enabled": true,
|
||||
"exporter": "otlp",
|
||||
"otlp_endpoint": "localhost:4317",
|
||||
"content_logging": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Environment-variable approach
|
||||
|
||||
```bash
|
||||
export OCR_ENABLE_TELEMETRY=1
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317 # implies exporter=otlp
|
||||
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc # default. NOTE: only grpc is currently
|
||||
# implemented; http/protobuf and http/json
|
||||
# are accepted but not yet wired up.
|
||||
export OTEL_SERVICE_NAME=open-code-review-prod # optional; default: open-code-review
|
||||
export OCR_CONTENT_LOGGING=0 # reserved / currently a no-op (see Content logging)
|
||||
```
|
||||
|
||||
Setting `OTEL_EXPORTER_OTLP_ENDPOINT` also forces `exporter=otlp` —
|
||||
useful for one-off `OTEL_EXPORTER_OTLP_ENDPOINT=… ocr review` runs.
|
||||
|
||||
## What gets exported
|
||||
|
||||
### Spans
|
||||
|
||||
The full span tree for a review:
|
||||
|
||||
```
|
||||
review.run
|
||||
├── diff.parse
|
||||
├── event.review.started (decision-point event)
|
||||
├── subtask.execute.<file1>
|
||||
│ ├── event.plan.skipped (when changes are below threshold)
|
||||
│ ├── event.plan.failed (when plan phase errored)
|
||||
│ ├── event.token.threshold.exceeded (when prompt > 80% of max_tokens)
|
||||
│ └── event.subtask.error (when the subtask errored)
|
||||
├── subtask.execute.<file2>
|
||||
└── …
|
||||
```
|
||||
|
||||
LLM round trips and tool executions are **not** emitted as separate
|
||||
spans — they show up only in metrics (see below). Decision-point events
|
||||
fire as short-lived `event.<name>` spans attached to the current
|
||||
context.
|
||||
|
||||
Each span carries useful attributes:
|
||||
|
||||
| Span | Key attributes |
|
||||
|---|---|
|
||||
| `review.run` | `error` (set when the run failed) |
|
||||
| `diff.parse` | `files.changed`, `lines.inserted`, `lines.deleted` |
|
||||
| `subtask.execute.<file>` | `file.path`, `lines.changed`, `lines.inserted`, `lines.deleted` |
|
||||
| `event.review.started` | `file.count`, `review.count`, `repo.dir` |
|
||||
| `event.plan.skipped` | `file.path`, `lines.changed`, `threshold` |
|
||||
| `event.plan.failed` | `file.path`, `message` |
|
||||
| `event.token.threshold.exceeded` | `file.path`, `tokens`, `max_tokens` |
|
||||
| `event.subtask.error` | `file.path`, `error` |
|
||||
|
||||
### Metrics
|
||||
|
||||
OCR records numeric metrics via the OTel meter — counts and histograms
|
||||
the collector aggregates downstream:
|
||||
|
||||
| Metric | Type | Unit | Labels |
|
||||
|---|---|---|---|
|
||||
| `ocr.review.duration_seconds` | histogram | `s` | — |
|
||||
| `ocr.files_reviewed_total` | counter | — | — |
|
||||
| `ocr.comments_generated_total` | counter | — | — |
|
||||
| `ocr.llm.requests_total` | counter | — | `model`, `status` (`ok` / `error`) |
|
||||
| `ocr.llm.request_duration_seconds` | histogram | `s` | `model` |
|
||||
| `ocr.llm.tokens_used` | counter | — | `model`, `type` (currently always `total`) |
|
||||
| `ocr.tool.calls_total` | counter | — | `tool.name`, `status` (`ok` / `error`) |
|
||||
| `ocr.tool.execution_duration_seconds` | histogram | `s` | `tool.name` |
|
||||
|
||||
### Events
|
||||
|
||||
Events fire as short-lived `event.<name>` spans at decision points.
|
||||
The full list:
|
||||
|
||||
| Event | Meaning |
|
||||
|---|---|
|
||||
| `review.started` | Diffs loaded; we know how many files we'll review. |
|
||||
| `no.files.changed` | The diff resolved to zero files. |
|
||||
| `plan.skipped` | A file was below `PLAN_MODE_LINE_THRESHOLD`. |
|
||||
| `plan.failed` | The plan phase errored; main loop ran without a plan. |
|
||||
| `token.threshold.exceeded` | Initial prompt tokens > 80 % of `MAX_TOKENS`; file skipped. |
|
||||
| `subtask.error` | A per-file subtask errored — emitted with `Error` span status. |
|
||||
|
||||
Use these to alert on degraded review quality long before a user
|
||||
notices.
|
||||
|
||||
## Content logging
|
||||
|
||||
Telemetry exports the **shape** of LLM traffic (counts, durations,
|
||||
statuses) but **never** the actual prompts or responses. OCR makes no
|
||||
attempt to attach LLM message content to spans or events — the data
|
||||
that leaves the process is the metric / event schema documented above
|
||||
and nothing else.
|
||||
|
||||
The `content_logging` config key (and `OCR_CONTENT_LOGGING=1` env
|
||||
override) is plumbed through the config layer but currently does **not**
|
||||
gate any code path that emits prompt content. Treat the flag as
|
||||
reserved.
|
||||
|
||||
If you need to inspect what was sent to or returned from the LLM, use
|
||||
the local JSONL transcripts that the [Session Viewer](../viewer/)
|
||||
reads. Those live entirely on disk under `~/.opencodereview/` and are
|
||||
never shipped to the collector.
|
||||
|
||||
## Recipes
|
||||
|
||||
### Console exporter for local debugging
|
||||
|
||||
```bash
|
||||
ocr config set telemetry.enabled true
|
||||
ocr config set telemetry.exporter console
|
||||
ocr review --commit HEAD
|
||||
```
|
||||
|
||||
Spans print to stdout in human-readable form. Pipe through `less` to
|
||||
read a long run.
|
||||
|
||||
### OTel Collector with Tempo + Prometheus
|
||||
|
||||
```yaml
|
||||
# otel-collector-config.yaml
|
||||
receivers:
|
||||
otlp:
|
||||
protocols: { grpc: { endpoint: 0.0.0.0:4317 } }
|
||||
|
||||
exporters:
|
||||
otlp/tempo:
|
||||
endpoint: tempo:4317
|
||||
tls: { insecure: true }
|
||||
prometheus:
|
||||
endpoint: 0.0.0.0:9464
|
||||
|
||||
service:
|
||||
pipelines:
|
||||
traces: { receivers: [otlp], exporters: [otlp/tempo] }
|
||||
metrics: { receivers: [otlp], exporters: [prometheus] }
|
||||
```
|
||||
|
||||
Then in your shell:
|
||||
|
||||
```bash
|
||||
export OCR_ENABLE_TELEMETRY=1
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317
|
||||
ocr review --from main --to feature/branch
|
||||
```
|
||||
|
||||
Open Tempo → search by `service.name=open-code-review` → click any
|
||||
trace to see the full span tree.
|
||||
|
||||
### Datadog
|
||||
|
||||
The Datadog Agent's OTLP receiver speaks OTLP/gRPC by default:
|
||||
|
||||
```bash
|
||||
export OCR_ENABLE_TELEMETRY=1
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317
|
||||
export OTEL_SERVICE_NAME=open-code-review
|
||||
```
|
||||
|
||||
Spans show up under APM with the service name; LLM metrics show up
|
||||
under Metrics with the labels above.
|
||||
|
||||
### CI run, results in your dashboard
|
||||
|
||||
Inject the env in your pipeline step:
|
||||
|
||||
```yaml
|
||||
- name: Code review
|
||||
env:
|
||||
OCR_LLM_URL: ${{ secrets.OCR_LLM_URL }}
|
||||
OCR_LLM_TOKEN: ${{ secrets.OCR_LLM_TOKEN }}
|
||||
OCR_LLM_MODEL: claude-opus-4-6
|
||||
OCR_ENABLE_TELEMETRY: "1"
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ vars.OTEL_COLLECTOR_URL }}
|
||||
OTEL_SERVICE_NAME: open-code-review-ci
|
||||
run: ocr review --from origin/main --to HEAD --audience agent
|
||||
```
|
||||
|
||||
The `OTEL_SERVICE_NAME` separates CI traces from human dev runs.
|
||||
|
||||
## Resolution priority
|
||||
|
||||
When OCR builds the final telemetry config:
|
||||
|
||||
1. Defaults (`enabled=false`, `exporter=console`, no endpoint).
|
||||
2. `telemetry.*` keys from `~/.opencodereview/config.json`.
|
||||
3. Environment variables (highest priority, **overrides** the file).
|
||||
|
||||
So you can leave `telemetry.enabled=false` in the config and flip it
|
||||
per-run with `OCR_ENABLE_TELEMETRY=1`.
|
||||
|
||||
## Sampling and overhead
|
||||
|
||||
OCR exports **everything**. There is no sampling configuration; OTel's
|
||||
sampling is the responsibility of your collector. For a typical review
|
||||
run that's:
|
||||
|
||||
- 1 `review.run` span + 1 `diff.parse` span + 1 `subtask.execute.<file>`
|
||||
span per reviewed file + 1 short-lived `event.*` span per
|
||||
decision-point event.
|
||||
- A 10-file PR produces ~15–25 spans total. LLM round trips and tool
|
||||
calls add to the metric counters but do not create extra spans.
|
||||
|
||||
The export is **batched and asynchronous** — telemetry doesn't block
|
||||
the review loop. If the collector is unreachable, OCR logs a warning
|
||||
and continues; the review still produces its normal output.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Likely cause |
|
||||
|---|---|
|
||||
| Nothing exported | `OCR_ENABLE_TELEMETRY` / `telemetry.enabled` is unset. The default is **off**. |
|
||||
| OTLP works locally, fails in prod | OCR currently only implements OTLP/gRPC — `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf` (or `http/json`) is accepted but not yet wired up, so switching it won't help. Verify the endpoint and that the collector is listening for gRPC. |
|
||||
| Spans show but no metrics | Some collectors only enable the traces pipeline by default; add a `metrics` pipeline in the config. |
|
||||
| Prompts missing from spans | OCR never attaches prompt content to telemetry — see [Content logging](#content-logging). Inspect transcripts via [Session Viewer](../viewer/) instead. |
|
||||
|
||||
## See Also
|
||||
|
||||
- [Configuration](../configuration/) — full key reference for the
|
||||
`telemetry.*` namespace.
|
||||
- [Architecture](../architecture/) — what each span actually
|
||||
measures.
|
||||
- [OpenTelemetry docs](https://opentelemetry.io/docs/) — collector
|
||||
setup and exporters.
|
||||
389
pages/src/content/docs/en/tools.md
Normal file
389
pages/src/content/docs/en/tools.md
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
---
|
||||
title: Tools
|
||||
sidebar:
|
||||
order: 9
|
||||
---
|
||||
|
||||
OCR ships with **six built-in tools** the LLM can call during a review.
|
||||
This page documents each tool's purpose, input schema, and example
|
||||
input/output. The full machine-readable definitions live in
|
||||
[`internal/config/toolsconfig/tools.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/toolsconfig/tools.json).
|
||||
|
||||
## Tool availability per phase
|
||||
|
||||
Each tool declares whether it's exposed during the **plan phase**, the
|
||||
**main task**, or both:
|
||||
|
||||
| Tool | Plan | Main | Purpose |
|
||||
|---|---|---|---|
|
||||
| `task_done` | ✗ | ✓ | Signal "I'm finished" — terminates the loop. |
|
||||
| `code_comment` | ✗ | ✓ | Emit a review comment with line range + suggestion. |
|
||||
| `file_read` | ✗ | ✓ | Read a slice of a file from the post-change snapshot. |
|
||||
| `file_read_diff` | ✓ | ✓ | Read another file's diff to confirm a cross-file concern. |
|
||||
| `file_find` | ✓ | ✓ | Locate files by filename keyword. |
|
||||
| `code_search` | ✓ | ✓ | Grep across the repo (literal or regex). |
|
||||
|
||||
`task_done` and `code_comment` are intentionally **not** available
|
||||
during the plan phase: planning is read-only.
|
||||
|
||||
> **Context tools are read-only context, not comment targets.** The
|
||||
> `main_task` prompt explicitly forbids commenting on findings in
|
||||
> *other* files. `file_read`, `file_read_diff`, `file_find`, and
|
||||
> `code_search` exist so the model can understand the current file's
|
||||
> diff better — any issue spotted while gathering that context is
|
||||
> ignored by design. Cross-file concerns surface as comments only when
|
||||
> they're observable from the **current file's diff**.
|
||||
|
||||
To override the tool registry, pass `--tools <path>` to a JSON file with
|
||||
the same shape as the embedded one. This lets you disable a tool, edit
|
||||
a description, or add a new tool backed by an existing provider.
|
||||
|
||||
## `task_done`
|
||||
|
||||
Terminate the main loop.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "task_done",
|
||||
"input": { "state": "DONE" }
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Required | Meaning |
|
||||
|---|---|---|
|
||||
| `state` | yes | `DONE` (default) or `FAILED`. `FAILED` is for "I literally cannot use the available tools to do this" — almost never the right choice. |
|
||||
|
||||
When the agent sees `task_done`, it stops calling the LLM and starts
|
||||
processing accumulated `code_comment` calls. `task_done` returns
|
||||
immediately (before the result is recorded in the session log), so the
|
||||
`state` value is accepted but **not** persisted — it doesn't affect exit
|
||||
codes either.
|
||||
|
||||
## `code_comment`
|
||||
|
||||
Emit one or more review comments. Each comment is anchored to a code
|
||||
snippet (`existing_code`) so OCR can compute line numbers automatically.
|
||||
|
||||
### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "code_comment",
|
||||
"input": {
|
||||
"path": "string — optional, override the file path for this comment",
|
||||
"comments": [
|
||||
{
|
||||
"content": "string — the comment in the configured language",
|
||||
"existing_code": "string — snippet from the diff to anchor on",
|
||||
"suggestion_code": "string — optional fix snippet",
|
||||
"thinking": "string — optional, the model's reasoning for this comment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`comments` is an array, so the model can emit several comments in one
|
||||
tool call. `content` and `existing_code` are required; `suggestion_code`
|
||||
is optional but encouraged. `path` is a top-level optional override —
|
||||
if omitted, the agent injects the file currently under review. The
|
||||
agent also injects `path` automatically when the model leaves it out, so
|
||||
the model rarely needs to set it explicitly. `thinking` (per-comment)
|
||||
captures the model's reasoning and is preserved on the comment but not
|
||||
shown in the final review output.
|
||||
|
||||
> **`thinking` is a runtime-only field.** OCR parses and stores it, but
|
||||
> it is deliberately **not** listed in the `code_comment` schema
|
||||
> advertised to the model in `tools.json` (only `content`,
|
||||
> `existing_code`, and `suggestion_code` are). Stronger models that
|
||||
> emit a `thinking` block anyway will have it persisted; most won't send
|
||||
> it, which is fine.
|
||||
|
||||
### Anchoring algorithm
|
||||
|
||||
OCR walks the diff looking for the text in `existing_code` using a
|
||||
**dynamic sliding window**. The match tries, in order:
|
||||
|
||||
1. **Hunk new-side** — a run of consecutive **context + added** lines
|
||||
(not deleted-only, not unchanged-only), yielding new-file line
|
||||
numbers. If that fails, OCR retries the **hunk old-side** — context +
|
||||
deleted lines — yielding old-file line numbers.
|
||||
2. **Full new-file scan** — if no hunk matched, OCR scans the entire
|
||||
post-change file content line-by-line for consecutive matches
|
||||
(`resolveFromFileContent`).
|
||||
3. **Re-location task** — if text matching still fails on a non-trivial
|
||||
diff, OCR runs the `RE_LOCATION_TASK` prompt asking the model to
|
||||
re-anchor the snippet.
|
||||
|
||||
Matching is **whitespace-insensitive**: lines are trimmed and diff
|
||||
`+`/`-` markers stripped before comparison, so indentation need not
|
||||
match exactly. As a last resort the comment is delivered with
|
||||
`start_line=0`, telling the user "the issue is real but you'll need to
|
||||
find the spot yourself".
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"comments": [
|
||||
{
|
||||
"content": "`tx.Rollback()` is never deferred — early returns leak the transaction.",
|
||||
"existing_code": "tx, err := db.Begin()\nif err != nil {\n return err\n}",
|
||||
"suggestion_code": "tx, err := db.Begin()\nif err != nil {\n return err\n}\ndefer tx.Rollback()"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## `file_read`
|
||||
|
||||
Read a range of lines from a file in its **post-change** form.
|
||||
|
||||
### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "file_read",
|
||||
"input": {
|
||||
"file_path": "src/foo.go",
|
||||
"start_line": 10,
|
||||
"end_line": 80
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Required | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `file_path` | yes | — | Path relative to the repo root. |
|
||||
| `start_line` | no | `1` | 1-indexed. |
|
||||
| `end_line` | no | end of file | Inclusive. |
|
||||
|
||||
### Output
|
||||
|
||||
```
|
||||
File: src/foo.go (Total lines: 220)
|
||||
IS_TRUNCATED: false
|
||||
LINE_RANGE: 10-80
|
||||
10|package foo
|
||||
11|
|
||||
12|import (
|
||||
13| "fmt"
|
||||
…
|
||||
```
|
||||
|
||||
Each content line is prefixed with its 1-indexed line number and a `|`
|
||||
separator so the model can quote line numbers precisely in subsequent
|
||||
`code_comment` calls.
|
||||
|
||||
### Limits
|
||||
|
||||
- **500 lines max per call.** Larger ranges are truncated, `IS_TRUNCATED:
|
||||
true` is set, and a trailing `Note: Results truncated to 500 lines.
|
||||
Please narrow your line range.` is appended.
|
||||
- Reads only the **modified version** of the file. To see the old
|
||||
version, use `file_read_diff`.
|
||||
|
||||
When the model needs surrounding context (for a comment about a
|
||||
function it can only see in the diff), it should compute the range
|
||||
from the diff hunk header `@@ -x,y +m,n @@` — typically `m-50` to
|
||||
`m+n+50`.
|
||||
|
||||
## `file_read_diff`
|
||||
|
||||
Read the diff for one or more *other* files in the same change set —
|
||||
useful when a comment hinges on whether a related file was updated.
|
||||
|
||||
### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "file_read_diff",
|
||||
"input": {
|
||||
"path_array": ["src/api/handler.go", "src/db/queries.go"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
```
|
||||
==== FILE: src/api/handler.go ====
|
||||
--- a/src/api/handler.go
|
||||
+++ b/src/api/handler.go
|
||||
@@ -10,1 +10,2 @@
|
||||
- old line
|
||||
+ new line 1
|
||||
+ new line 2
|
||||
|
||||
==== FILE: src/db/queries.go ====
|
||||
@@ -5,1 +5,1 @@
|
||||
- query := "SELECT *"
|
||||
+ query := "SELECT id"
|
||||
```
|
||||
|
||||
If a path isn't in the change set, that entry is silently omitted. If
|
||||
**none** of the requested paths are in the change set the tool returns
|
||||
`Error: diff not found for the requested paths`; an empty `path_array`
|
||||
returns `Error: no files found`.
|
||||
|
||||
## `file_find`
|
||||
|
||||
Find files in the repo by filename keyword (substring match).
|
||||
|
||||
### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "file_find",
|
||||
"input": {
|
||||
"query_name": "UserService",
|
||||
"case_sensitive": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Required | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `query_name` | yes | — | Substring matched against each file's **basename** (the part after the last `/`), not the full path. |
|
||||
| `case_sensitive` | no | `false` | Set to `true` for exact-case matching. |
|
||||
|
||||
The candidate set comes from `git ls-files --cached --others
|
||||
--exclude-standard` in workspace mode, or `git ls-tree -r --name-only
|
||||
<ref>` in range / commit mode. Extensionless files are skipped except
|
||||
for `Makefile`, `Dockerfile`, `LICENSE`, `Vagrantfile`, `Containerfile`.
|
||||
|
||||
### Output
|
||||
|
||||
A newline-separated list of paths:
|
||||
|
||||
```
|
||||
src/main/java/com/example/UserService.java
|
||||
src/test/java/com/example/UserServiceTest.java
|
||||
src/main/java/com/example/internal/UserServiceImpl.java
|
||||
```
|
||||
|
||||
When no file matches (or `query_name` is blank), the tool returns the
|
||||
literal string `// The file was not found`.
|
||||
|
||||
### Limits
|
||||
|
||||
Returns up to **100** matches; excess is silently truncated. If the
|
||||
model needs broader search, it should fall through to `code_search`.
|
||||
|
||||
## `code_search`
|
||||
|
||||
Full-text search across the repo. Backed by `git grep`, so it
|
||||
understands `pathspec` syntax and respects `.gitignore`.
|
||||
|
||||
### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "code_search",
|
||||
"input": {
|
||||
"search_text": "TODO|FIXME",
|
||||
"file_patterns": ["*.go", ":(exclude)vendor/"],
|
||||
"case_sensitive": false,
|
||||
"use_perl_regexp": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Required | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `search_text` | yes | — | Literal string or PCRE pattern (see `use_perl_regexp`). |
|
||||
| `file_patterns` | no | whole repo | Array of pathspec entries. Use `:(exclude)pat` to subtract. |
|
||||
| `case_sensitive` | no | `false` | — |
|
||||
| `use_perl_regexp` | no | `false` | When `true`, `search_text` is treated as a regex. |
|
||||
|
||||
### Output
|
||||
|
||||
Results are grouped by file. Each group starts with `File: <path>` and
|
||||
`Match lines: <n>`, followed by one `line|content` line per hit:
|
||||
|
||||
```
|
||||
File: path/to/example.java
|
||||
Match lines: 2
|
||||
433| String name = toolRequest.get().getName();
|
||||
438| logToolRequest(newPath, tool, toolRequest.get());
|
||||
|
||||
File: path/to/other.java
|
||||
Match lines: 1
|
||||
22| var req = new ToolRequest();
|
||||
```
|
||||
|
||||
When there are no matches, the tool returns the literal string
|
||||
`No matches found`.
|
||||
|
||||
### Pathspec cookbook
|
||||
|
||||
| Goal | `file_patterns` |
|
||||
|---|---|
|
||||
| Single file | `["src/main.go"]` |
|
||||
| All Go files | `["*.go"]` |
|
||||
| All Go except tests | `["*.go", ":(exclude)*_test.go"]` |
|
||||
| Only one directory | `["src/api/"]` |
|
||||
| Multiple types, no vendor | `["*.go", "*.ts", ":(exclude)vendor/", ":(exclude)node_modules/"]` |
|
||||
|
||||
### Limits
|
||||
|
||||
- Caps matches at **100 per file** via `git grep --max-count 100`, so
|
||||
total output across many files can exceed 100. When the per-file cap
|
||||
is hit the output is prefixed with `Note: The results have been
|
||||
truncated. Only showing first 100 results.`.
|
||||
- Empty / whitespace-only `search_text` returns `Error: search_text is
|
||||
blank` instead of expanding to every line.
|
||||
- Searches the **current working tree** in workspace mode, or the
|
||||
resolved target ref in range / commit mode (the `FileReader.Ref` is
|
||||
passed as a positional argument to `git grep`).
|
||||
|
||||
## Tool execution & errors
|
||||
|
||||
Tools execute synchronously inside the agent loop, with two exceptions:
|
||||
|
||||
- `code_comment` is dispatched to the **CommentWorkerPool** so the loop
|
||||
doesn't block on line-resolution + reflection.
|
||||
- `task_done` short-circuits — it returns immediately without invoking
|
||||
any provider.
|
||||
|
||||
When a tool errors (network failure, malformed args, file not found),
|
||||
the result is delivered to the model as a regular tool result with text
|
||||
like `"Error: file not found: src/missing.go"`. The model then decides
|
||||
whether to retry, ask for a different file, or call `task_done`.
|
||||
|
||||
If a tool name isn't in the registry, OCR returns the constant
|
||||
`tool.NotAvailableMsg` rather than crashing. This makes runtime tool
|
||||
disabling (via `--tools`) safe.
|
||||
|
||||
## Customizing tools
|
||||
|
||||
Two paths to extend:
|
||||
|
||||
### 1. Disable a tool
|
||||
|
||||
Copy `tools.json`, drop the entry you don't want, then run:
|
||||
|
||||
```bash
|
||||
ocr review --tools ./my-tools.json
|
||||
```
|
||||
|
||||
For example, if you want a "comment-only" reviewer that never reads
|
||||
extra context, keep only `code_comment` and `task_done`.
|
||||
|
||||
### 2. Re-describe a tool
|
||||
|
||||
Keep the `name` (the providers are looked up by name internally) but
|
||||
change the `description` to nudge the model. This is the easiest way to
|
||||
inject project-specific guidance — e.g., "When using `file_read`,
|
||||
always read at least 30 lines around the change."
|
||||
|
||||
> Adding **new** tool *names* requires Go-side wiring; see
|
||||
> `internal/tool/definitions.go` and the providers under
|
||||
> `internal/tool/`. The JSON file alone can't add new behaviour.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Architecture](../architecture/) — how the agent loop drives tools.
|
||||
- [Review Rules](../review-rules/) — what the LLM is told to focus on.
|
||||
- [Session Viewer](../viewer/) — see exactly which tools fired in past
|
||||
reviews.
|
||||
182
pages/src/content/docs/en/viewer.md
Normal file
182
pages/src/content/docs/en/viewer.md
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
---
|
||||
title: Session Viewer
|
||||
sidebar:
|
||||
order: 10
|
||||
---
|
||||
|
||||
`ocr viewer` is a small embedded HTTP server that renders past review
|
||||
sessions in a browser-friendly UI. No external dependencies — sessions
|
||||
are read directly from the JSONL files OCR writes to disk during every
|
||||
review.
|
||||
|
||||
## Launching
|
||||
|
||||
```bash
|
||||
ocr viewer # binds localhost:5483
|
||||
ocr viewer --addr :3000 # bind to all interfaces on port 3000
|
||||
ocr viewer --addr 0.0.0.0:8080 # bind on all interfaces
|
||||
```
|
||||
|
||||
The default address is `localhost:5483`. The server holds the foreground
|
||||
— `Ctrl+C` stops it. Sessions are scanned lazily from
|
||||
`~/.opencodereview/sessions/` on each request, so a review running in
|
||||
another terminal shows up the moment its JSONL file appears.
|
||||
|
||||
> **DNS-rebinding protection.** The viewer checks the `Host` header
|
||||
> against a loopback allowlist (`localhost`, `127.0.0.1`, `::1`). A
|
||||
> concrete bind host (e.g. `--addr 192.168.1.10:5483`) is added
|
||||
> automatically, but **wildcard** binds (`:3000`, `0.0.0.0`, `::`) are
|
||||
> not — reaching the UI from a LAN IP or hostname then returns
|
||||
> `forbidden host`. To expose a wildcard bind, set
|
||||
> `OCR_VIEWER_ALLOWED_HOSTS` to a comma-separated list of allowed
|
||||
> hostnames (e.g. `OCR_VIEWER_ALLOWED_HOSTS=box.local,192.168.1.10`).
|
||||
|
||||
## Three pages
|
||||
|
||||
The viewer has three URLs:
|
||||
|
||||
| URL | What you see |
|
||||
|---|---|
|
||||
| `/` | List of all repositories that have sessions on disk. |
|
||||
| `/r/{repo}` | List of sessions for one repository, newest first. |
|
||||
| `/r/{repo}/{sessionID}` | Full detail for a single session. |
|
||||
|
||||
`{repo}` is a path-encoded string (separators `/` and `\` replaced with
|
||||
`-`, colons replaced with `_` — the same encoding used to name the
|
||||
on-disk directories). You don't usually type this — you click through.
|
||||
|
||||
### `/` — Repository list
|
||||
|
||||
For each repo with at least one session you see the repo path, the
|
||||
total session count, and the most recent activity timestamp.
|
||||
|
||||
### `/r/{repo}` — Session list for one repo
|
||||
|
||||
For each session: ID (a UUID), branch name (when OCR was able to
|
||||
detect it), review mode, model, file count, duration, and a started-at
|
||||
timestamp.
|
||||
|
||||
### `/r/{repo}/{sessionID}` — Session detail
|
||||
|
||||
The detail page is the interesting one. It shows:
|
||||
|
||||
1. **Header** — diff range, model, branch, total tokens, run duration.
|
||||
2. **File group** — one block per reviewed file. Inside each file, five
|
||||
"task type" lanes:
|
||||
|
||||
| Task type | When it appears |
|
||||
|---|---|
|
||||
| `plan_task` | The plan phase ran (file ≥ `PLAN_MODE_LINE_THRESHOLD`). |
|
||||
| `main_task` | Every file. The main review loop. |
|
||||
| `review_filter_task` | The post-review comment-filtering pass ran for this file. |
|
||||
| `memory_compression_task` | The active+compress zone exceeded 60 % / 80 % budget. |
|
||||
| `re_location_task` | A `code_comment` couldn't be anchored, fallback re-location ran. |
|
||||
|
||||
Each lane is a horizontal strip of **task cards** — one per LLM round
|
||||
trip. Cards are coloured by task type so you can see at a glance which
|
||||
phases dominated the run.
|
||||
|
||||
## What's in a task card
|
||||
|
||||
Click a task card to expand. Each card has:
|
||||
|
||||
- a **header row** — request number, model badge, a token badge
|
||||
(`P:` prompt / `C:` completion, plus `CR:` / `CW:` cache read/write
|
||||
when present), a duration badge, and an error badge when the round
|
||||
failed;
|
||||
- **Response** — the raw assistant response, including any reasoning /
|
||||
`thinking` blocks;
|
||||
- **Tool calls** — each tool invocation with arguments + the result that
|
||||
was returned (collapsible).
|
||||
|
||||
The full message list sent to the model and the in-scope tool
|
||||
definitions are **not** rendered in the card UI; if you need them,
|
||||
inspect the JSONL transcript directly (the `messages` field on each
|
||||
`llm_request` record).
|
||||
|
||||
## Use cases
|
||||
|
||||
The viewer is designed around three workflows:
|
||||
|
||||
### "Why did the model say that?"
|
||||
|
||||
Open a comment in your terminal output, locate the file in the viewer,
|
||||
and walk down its `main_task` lane. The card whose **tool calls**
|
||||
include the `code_comment` you care about is the round that produced
|
||||
it. The card's Response shows the model's reasoning; for the exact
|
||||
prompt + context the model was sent, open the `llm_request` record for
|
||||
that request number in the JSONL transcript (its `messages` field).
|
||||
|
||||
### "Why was this file silent?"
|
||||
|
||||
A file with **no comments** is a successful review only if the model
|
||||
*deliberately* called `task_done`. If the lane shows tool calls but no
|
||||
`code_comment`, that's an intentional clean review. If the lane ends in
|
||||
an error card, it's a failure dressed up as silence — surface it as a
|
||||
warning.
|
||||
|
||||
### "What did compression keep / drop?"
|
||||
|
||||
The `memory_compression_task` lane shows every compression round.
|
||||
Inside, the Response pane has the resulting summary; the rendered XML
|
||||
of the compress zone that was fed in lives in the round's
|
||||
`llm_request` `messages` in the JSONL transcript. Useful when debugging
|
||||
a "the model forgot earlier context" complaint — you can see whether
|
||||
compression dropped the relevant detail.
|
||||
|
||||
## Storage layout on disk
|
||||
|
||||
The viewer reads from:
|
||||
|
||||
```
|
||||
~/.opencodereview/sessions/
|
||||
└── <path-encoded-repo-path>/
|
||||
└── <session-id>.jsonl
|
||||
```
|
||||
|
||||
Each line in the JSONL file is one event:
|
||||
|
||||
```json
|
||||
{"type": "llm_request", "filePath": "src/foo.go", "taskType": "main_task", "request_no": 1, "messages": [{"role": "user", "content": "Review this diff…"}], "timestamp": "2026-06-02T10:15:23Z"}
|
||||
{"type": "llm_response", "filePath": "src/foo.go", "taskType": "main_task", "model": "claude-sonnet-4-6", "content": "Found 2 issues…", "duration_ms": 8421, "usage": {"prompt_tokens": 12450, "completion_tokens": 320}}
|
||||
{"type": "tool_call", "filePath": "src/foo.go", "tool_name": "file_read", "arguments": "{\"file_path\":\"src/foo.go\",\"start_line\":1,\"end_line\":50}", "result": "File: src/foo.go (Total lines: 220)\nIS_TRUNCATED: false\nLINE_RANGE: 1-50\n1|package foo…", "ok": true, "duration_ms": 14}
|
||||
```
|
||||
|
||||
Lines are append-only — a partial JSONL means a session was killed
|
||||
mid-run, and the viewer renders what it has.
|
||||
|
||||
To free disk space, delete entire session files; the viewer regenerates
|
||||
its index on the next request.
|
||||
|
||||
## Privacy
|
||||
|
||||
The JSONL transcripts contain **everything** sent to and received from
|
||||
the LLM, including any code that was in the diff. They live entirely on
|
||||
your machine inside `~/.opencodereview/`. OCR does not upload them
|
||||
anywhere.
|
||||
|
||||
If your reviews include code you wouldn't want stored long-term,
|
||||
either:
|
||||
|
||||
- delete the session files periodically, or
|
||||
- redirect `--audience agent --format json` output to a transient pipe
|
||||
in CI and run with a temporary `HOME` so the JSONL never persists.
|
||||
|
||||
The OpenTelemetry exporter is a separate concern — see
|
||||
[Telemetry](../telemetry/) for how to keep prompt content out of
|
||||
exported traces.
|
||||
|
||||
## When the viewer is *not* the right tool
|
||||
|
||||
- For programmatic post-processing (CI, dashboards), use
|
||||
`ocr review --format json --audience agent`. The viewer renders for
|
||||
humans, not machines.
|
||||
- For grepping across many sessions, use `jq` on the JSONL files
|
||||
directly. There's no search box in the UI yet.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Architecture](../architecture/) — what those five task types
|
||||
actually do under the hood.
|
||||
- [Tools](../tools/) — the tool calls you'll see in `main_task`
|
||||
cards.
|
||||
177
pages/src/content/docs/index.ts
Normal file
177
pages/src/content/docs/index.ts
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
/* Docs content index — imports all markdown files and provides a lookup by slug + language */
|
||||
|
||||
// English docs
|
||||
import enOverview from './en/overview.md';
|
||||
import enQuickstart from './en/quickstart.md';
|
||||
import enInstallation from './en/installation.md';
|
||||
import enConfiguration from './en/configuration.md';
|
||||
import enCliReference from './en/cli-reference.md';
|
||||
import enReviewRules from './en/review-rules.md';
|
||||
import enArchitecture from './en/architecture.md';
|
||||
import enTools from './en/tools.md';
|
||||
import enViewer from './en/viewer.md';
|
||||
import enTelemetry from './en/telemetry.md';
|
||||
import enIntegrations from './en/integrations.md';
|
||||
import enAgentSkill from './en/integrations/agent-skill.md';
|
||||
import enClaudeCode from './en/integrations/claude-code.md';
|
||||
import enSubprocess from './en/integrations/subprocess.md';
|
||||
import enCicd from './en/integrations/ci.md';
|
||||
import enContributing from './en/contributing.md';
|
||||
import enFaq from './en/faq.md';
|
||||
|
||||
// Chinese docs
|
||||
import zhOverview from './zh/overview.md';
|
||||
import zhQuickstart from './zh/quickstart.md';
|
||||
import zhInstallation from './zh/installation.md';
|
||||
import zhConfiguration from './zh/configuration.md';
|
||||
import zhCliReference from './zh/cli-reference.md';
|
||||
import zhReviewRules from './zh/review-rules.md';
|
||||
import zhArchitecture from './zh/architecture.md';
|
||||
import zhTools from './zh/tools.md';
|
||||
import zhViewer from './zh/viewer.md';
|
||||
import zhTelemetry from './zh/telemetry.md';
|
||||
import zhIntegrations from './zh/integrations.md';
|
||||
import zhAgentSkill from './zh/integrations/agent-skill.md';
|
||||
import zhClaudeCode from './zh/integrations/claude-code.md';
|
||||
import zhSubprocess from './zh/integrations/subprocess.md';
|
||||
import zhCicd from './zh/integrations/ci.md';
|
||||
import zhContributing from './zh/contributing.md';
|
||||
import zhFaq from './zh/faq.md';
|
||||
|
||||
export type DocSlug =
|
||||
| 'overview'
|
||||
| 'quickstart'
|
||||
| 'installation'
|
||||
| 'configuration'
|
||||
| 'cli-reference'
|
||||
| 'review-rules'
|
||||
| 'architecture'
|
||||
| 'tools'
|
||||
| 'viewer'
|
||||
| 'telemetry'
|
||||
| 'integrations'
|
||||
| 'agent-skill'
|
||||
| 'claude-code'
|
||||
| 'subprocess'
|
||||
| 'cicd'
|
||||
| 'contributing'
|
||||
| 'faq';
|
||||
|
||||
const enDocs: Record<DocSlug, string> = {
|
||||
'overview': enOverview,
|
||||
'quickstart': enQuickstart,
|
||||
'installation': enInstallation,
|
||||
'configuration': enConfiguration,
|
||||
'cli-reference': enCliReference,
|
||||
'review-rules': enReviewRules,
|
||||
'architecture': enArchitecture,
|
||||
'tools': enTools,
|
||||
'viewer': enViewer,
|
||||
'telemetry': enTelemetry,
|
||||
'integrations': enIntegrations,
|
||||
'agent-skill': enAgentSkill,
|
||||
'claude-code': enClaudeCode,
|
||||
'subprocess': enSubprocess,
|
||||
'cicd': enCicd,
|
||||
'contributing': enContributing,
|
||||
'faq': enFaq,
|
||||
};
|
||||
|
||||
const zhDocs: Record<DocSlug, string> = {
|
||||
'overview': zhOverview,
|
||||
'quickstart': zhQuickstart,
|
||||
'installation': zhInstallation,
|
||||
'configuration': zhConfiguration,
|
||||
'cli-reference': zhCliReference,
|
||||
'review-rules': zhReviewRules,
|
||||
'architecture': zhArchitecture,
|
||||
'tools': zhTools,
|
||||
'viewer': zhViewer,
|
||||
'telemetry': zhTelemetry,
|
||||
'integrations': zhIntegrations,
|
||||
'agent-skill': zhAgentSkill,
|
||||
'claude-code': zhClaudeCode,
|
||||
'subprocess': zhSubprocess,
|
||||
'cicd': zhCicd,
|
||||
'contributing': zhContributing,
|
||||
'faq': zhFaq,
|
||||
};
|
||||
|
||||
const docsMap: Record<string, Record<DocSlug, string>> = {
|
||||
en: enDocs,
|
||||
zh: zhDocs,
|
||||
ja: enDocs, // fallback to English for Japanese
|
||||
};
|
||||
|
||||
/**
|
||||
* Strip YAML frontmatter from markdown content
|
||||
*/
|
||||
function stripFrontmatter(md: string): string {
|
||||
if (md.startsWith('---')) {
|
||||
const end = md.indexOf('---', 3);
|
||||
if (end !== -1) {
|
||||
return md.slice(end + 3).trim();
|
||||
}
|
||||
}
|
||||
return md;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw content for a slug in the given language, with English fallback.
|
||||
*/
|
||||
function getRawContent(slug: DocSlug, language: string): string {
|
||||
const langDocs = docsMap[language] || docsMap.en;
|
||||
return langDocs[slug] || enDocs[slug] || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the markdown content for a given doc slug and language.
|
||||
* Falls back to English if the language is not found.
|
||||
*/
|
||||
export function getDocContent(slug: DocSlug, language: string): string {
|
||||
return stripFrontmatter(getRawContent(slug, language));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title from frontmatter
|
||||
*/
|
||||
export function getDocTitle(slug: DocSlug, language: string): string {
|
||||
const raw = getRawContent(slug, language);
|
||||
if (raw.startsWith('---')) {
|
||||
const end = raw.indexOf('---', 3);
|
||||
if (end !== -1) {
|
||||
const fm = raw.slice(3, end);
|
||||
const match = fm.match(/title:\s*(.+)/);
|
||||
if (match) return match[1].trim();
|
||||
}
|
||||
}
|
||||
return slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search across all docs for a query string. Returns matching slugs with context.
|
||||
*/
|
||||
export function searchDocs(query: string, language: string): { slug: DocSlug; title: string; snippet: string }[] {
|
||||
if (!query.trim()) return [];
|
||||
const langDocs = docsMap[language] || docsMap.en;
|
||||
const results: { slug: DocSlug; title: string; snippet: string }[] = [];
|
||||
const lowerQuery = query.toLowerCase();
|
||||
const slugs = Object.keys(langDocs) as DocSlug[];
|
||||
for (const slug of slugs) {
|
||||
const raw = langDocs[slug] || enDocs[slug] || '';
|
||||
const content = stripFrontmatter(raw);
|
||||
const lowerContent = content.toLowerCase();
|
||||
const idx = lowerContent.indexOf(lowerQuery);
|
||||
if (idx !== -1) {
|
||||
// Extract snippet around match
|
||||
const start = Math.max(0, idx - 30);
|
||||
const end = Math.min(content.length, idx + query.length + 60);
|
||||
let snippet = content.slice(start, end).replace(/[#*_`\[\]()]/g, '').replace(/\n/g, ' ').trim();
|
||||
if (start > 0) snippet = '...' + snippet;
|
||||
if (end < content.length) snippet = snippet + '...';
|
||||
const title = getDocTitle(slug, language);
|
||||
results.push({ slug, title, snippet });
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
4
pages/src/content/docs/md.d.ts
vendored
Normal file
4
pages/src/content/docs/md.d.ts
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
declare module '*.md' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
328
pages/src/content/docs/zh/architecture.md
Normal file
328
pages/src/content/docs/zh/architecture.md
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
---
|
||||
title: 架构
|
||||
sidebar:
|
||||
order: 8
|
||||
---
|
||||
|
||||
从你按下回车到 JSON 落在终端,`ocr review` 内部实际如何运作的导览。旨在帮你
|
||||
建立足够的心智模型,以调试行为、调优参数,并有把握地阅读源码。
|
||||
|
||||
## 高层流水线
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["<b>ocr review</b>"]
|
||||
B["<b>bootstrap</b><br/><span style='font-size:0.85em'>Resolve LLM endpoint (config → env → rc files)<br/>Load template, tool registry, system rules</span>"]
|
||||
C["<b>diff provider</b><br/><span style='font-size:0.85em'>git diff / ls-files / show — produce []model.Diff<br/>Modes: Workspace · Commit · Range</span>"]
|
||||
D["<b>filter & rules</b><br/><span style='font-size:0.85em'>5-gate filter (preview.go) — drop binaries,<br/>excluded paths, unsupported extensions. Pick rule per file.</span>"]
|
||||
E["<b>subtask dispatch</b><br/><span style='font-size:0.85em'>For every diff in parallel (concurrency=N):<br/>Plan phase (optional) → Main loop → Comments</span>"]
|
||||
F["<b>output writer</b><br/><span style='font-size:0.85em'>Synchronous line-resolution & review-filter; renders text<br/>or JSON depending on --format / --audience.</span>"]
|
||||
|
||||
A --> B --> C --> D --> E --> F
|
||||
```
|
||||
|
||||
编排逻辑位于
|
||||
[`internal/agent/`](https://github.com/alibaba/open-code-review/blob/main/internal/agent/)
|
||||
包,分布在四个文件:`agent.go`(主循环与分发)、`compression.go`(记忆压缩)、
|
||||
`preview.go`(文件过滤)和 `util.go`(辅助)。两个入口点值得关注:`Agent.Run`
|
||||
(流水线顶部)和 `Agent.dispatchSubtasks`(per-file 扇出)。
|
||||
|
||||
## diff provider
|
||||
|
||||
`internal/diff/git.go` 定义了一个 `Provider` 结构,其未导出字段 `mode`(类型为
|
||||
`Mode`,一个 `int` 枚举)选择与 CLI 参数对应的三种模式之一:
|
||||
|
||||
| 模式 | 触发方式 | 返回内容 |
|
||||
|---|---|---|
|
||||
| `Workspace` | 无参数 | staged + unstaged + untracked 变更 |
|
||||
| `Commit` | `--commit <sha>` / `-c <sha>` | `<sha>` 引入的变更(经 `git show <sha>`,等价于 `<sha>^..<sha>` diff) |
|
||||
| `Range` | `--from <a> --to <b>` | `merge-base(a, b)..b` |
|
||||
|
||||
每个 diff 携带:old/new path、old/new hunk、插入/删除计数、二进制标志、重命名
|
||||
检测。`DiffContextLines` 固定为 **3**——与 Git 默认一致。
|
||||
|
||||
untracked 文件从磁盘读取并作为整文件新增处理,以便 commit 前评审。
|
||||
|
||||
## 五重门文件过滤
|
||||
|
||||
diff 加载后,每个文件经过
|
||||
[`whyExcluded`](https://github.com/alibaba/open-code-review/blob/main/internal/agent/preview.go)。
|
||||
该函数返回以下之一:
|
||||
|
||||
```
|
||||
binary — file is binary
|
||||
user_exclude — matched a pattern in your `exclude` list
|
||||
unsupported_ext — extension is not in supported_file_types.json
|
||||
default_path — matched a built-in test-file exclude pattern
|
||||
```
|
||||
|
||||
……或文件被保留时返回空。`deleted` **不**由 `whyExcluded` 返回;它在 `Preview()`
|
||||
中随后计算——当一个被保留文件的 diff 报告 `IsDeleted` 时。各门按以下顺序执行:
|
||||
|
||||
1. `binary`——二进制文件先被丢弃。
|
||||
2. `user_exclude`——你项目的 `exclude` 总是优先。
|
||||
3. `user_include`——若配置了 include 模式**且**文件匹配其一,立即保留
|
||||
(返回空),绕过下面的 `unsupported_ext` 和 `default_path` 门。
|
||||
4. `unsupported_ext` 按扩展名白名单过滤。
|
||||
5. `default_path` 是最后一道门:匹配内置**测试文件**排除模式
|
||||
(`**/*_test.go`、`**/*.test.{js,jsx,ts,tsx}`、`**/__tests__/**`、
|
||||
`**/*_test.py`、`**/*_spec.rb`、`**/*.test.ets`……)。每个模式都以
|
||||
`**/` 作为根前缀。
|
||||
|
||||
噪声目录过滤(`vendor/`、`node_modules/`、`target/`……)发生在更早的阶段,
|
||||
位于 diff-provider 层,通过 `internal/diff/git.go` 中的 `providerDirIgnoreDirs`
|
||||
列表——这些目录的 diff 被解析后由 `filterDiffs` 剔除,永远不会到达 per-file
|
||||
过滤器。
|
||||
|
||||
运行 `ocr review --preview` 可不花 token 查看完整过滤结果。完整算法见
|
||||
[评审规则](../review-rules/#how-files-are-filtered)。
|
||||
|
||||
## per-file 子任务:plan + main
|
||||
|
||||
对每个通过过滤的文件,OCR 启动一个子 agent。每个子 agent 在自己的 goroutine
|
||||
中运行,受 `--concurrency`(默认 **8**)约束,并有独立的 LLM 消息缓冲区。
|
||||
|
||||
一个子任务最多有**两个阶段**:
|
||||
|
||||
### 阶段 1——Plan(可选)
|
||||
|
||||
```go
|
||||
threshold := template.PlanModeLineThreshold // 50
|
||||
changeLines := d.Insertions + d.Deletions
|
||||
if changeLines < threshold { skip plan }
|
||||
```
|
||||
|
||||
对小 diff,plan 只会增加延迟、没有价值,因此被静默跳过,main 循环直接运行。对较大
|
||||
diff,OCR 做一次**单次** `PLAN_TASK` LLM 调用——不发送 `Tools` 字段,因此模型
|
||||
在 plan 期间不能调用工具。只读工具子集(`code_search`、`file_read_diff`、
|
||||
`file_find`——`tools.json` 中 `plan_task` 标志为 `true` 的那三个)作为纯文本
|
||||
通过 `{{plan_tools}}` 占位符(由 `formatToolDefs` 渲染)嵌入,
|
||||
让模型知道后续可用什么。模型返回一份清单,作为 main prompt 中的
|
||||
`{{plan_guidance}}`。
|
||||
|
||||
### 阶段 2——main 循环
|
||||
|
||||
main 循环组装 `MAIN_TASK` prompt,与模型展开工具调用对话。完整工具集在
|
||||
plan 阶段工具基础上加 **`task_done`**、**`code_comment`** 和
|
||||
**`file_read`**——完整清单见[工具](../tools/)。
|
||||
|
||||
```
|
||||
loop up to MAX_TOOL_REQUEST_TIMES (default 30):
|
||||
response = llm.complete(messages, tools)
|
||||
if response.toolCalls is empty:
|
||||
nudge model with "You did not successfully call any tools.
|
||||
Please try again or use task_done if finished."
|
||||
continue
|
||||
for each call: execute → collect result
|
||||
if any call was task_done: break
|
||||
addNextMessage(...) # may trigger compression
|
||||
```
|
||||
|
||||
循环有五个退出条件:
|
||||
|
||||
1. 调用了 `task_done`。
|
||||
2. `MAX_TOOL_REQUEST_TIMES` 耗尽。
|
||||
3. 连续 3 轮未产生有效工具结果(`maxConsecutiveEmptyRounds = 3`)。
|
||||
4. context 被取消。
|
||||
5. `addNextMessage` 返回 false——压缩无法把消息缓冲区压回警告阈值以下。
|
||||
|
||||
无论哪种情况,已收集的 `code_comment` 调用都成为评审评论。
|
||||
|
||||
## 记忆压缩
|
||||
|
||||
长的工具调用循环最终会溢出上下文窗口。OCR 用**三分区**策略管理,触发于
|
||||
`MAX_TOKENS = 58888` 定义的 token 预算:
|
||||
|
||||
| 阈值 | 常量 | 动作 |
|
||||
|---|---|---|
|
||||
| MAX_TOKENS 的 60 % | `tokenSoftThreshold` | 启动**异步**后台压缩;当前循环不中断继续。 |
|
||||
| MAX_TOKENS 的 80 % | `tokenWarningThreshold` | 在发送下一个请求前**同步**运行压缩。 |
|
||||
|
||||
### 三个区
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph messages["messages"]
|
||||
direction LR
|
||||
F["<b>frozen</b><br/>first 2 msgs<br/>(system +<br/>initial user)"]
|
||||
C["<b>compress</b><br/>summarized<br/>into one<br/>user msg"]
|
||||
A["<b>active</b><br/>K most recent<br/>complete<br/>rounds"]
|
||||
end
|
||||
F --- C --- A
|
||||
```
|
||||
|
||||
一“轮”是一条 assistant 消息加上其后跟随的工具结果消息。`partitionMessages`
|
||||
从末尾向前遍历轮次,保留能装入 `(0.80 × MAX_TOKENS) - reservedTokens` 的尽可能
|
||||
多的轮。更早的内容成为 **compress 区**。
|
||||
|
||||
compress 区被渲染为 XML,用 `MEMORY_COMPRESSION_TASK` prompt 交给模型;返回的
|
||||
摘要被追加到原始 user 消息内,包在 `<previous_review_summary>` 标签里。
|
||||
|
||||
压缩后:`messages = frozen[2] + compressed_user_msg + active`。
|
||||
|
||||
```go
|
||||
// compression.go
|
||||
func (a *Agent) runCompression(ctx context.Context, msgs []llm.Message, filePath string) ([]llm.Message, error) {
|
||||
part := partitionMessages(msgs, a.args.Template.MaxTokens, 0)
|
||||
contextXML := buildMessageXML(msgs[part.frozenEnd:part.compressEnd])
|
||||
// … call MEMORY_COMPRESSION_TASK …
|
||||
rebuilt[1] = llm.NewTextMessage(role, currentText+
|
||||
"\n\n<previous_review_summary>\n"+rawSummary+"\n</previous_review_summary>")
|
||||
for i := part.compressEnd; i < len(msgs); i++ {
|
||||
rebuilt = append(rebuilt, msgs[i])
|
||||
}
|
||||
return rebuilt, nil
|
||||
}
|
||||
```
|
||||
|
||||
### 异步 vs 同步
|
||||
|
||||
异步路径让 main 循环在后台压缩运行时继续产出工具调用;当下一次 token 检查发生
|
||||
时,已就绪的摘要会通过 `tryApplyPendingCompression` 应用。若比例在异步任务完成前越过
|
||||
警告阈值,循环会停顿并同步运行 `runCompression`——保证下一个请求总是装得下。
|
||||
|
||||
## 评论处理流水线
|
||||
|
||||
每个 `code_comment` 工具调用产出一条或多条原始评论。它们经过一个
|
||||
**CommentWorkerPool**(固定大小 goroutine 池),使主工具调用循环永不阻塞在
|
||||
后处理上:
|
||||
|
||||
1. **行解析**(worker 内)——`existing_code` 用滑动窗口算法与 diff 匹配以计算
|
||||
精确的 `start_line` / `end_line`。匹配失败则两者默认为 `0`——`0` 行范围是
|
||||
“未锚定”评论的隐式信号,用户需手动定位(没有存储标志;下游消费者检查
|
||||
`start_line == 0`)。
|
||||
2. **重新定位任务** *(可选回退)*——当行解析在较复杂的 diff 上失败时,OCR 运行
|
||||
`RE_LOCATION_TASK` prompt,请模型重新锚定片段。对改写过的 `existing_code`
|
||||
字符串有用。
|
||||
3. **评审过滤**——main 循环结束后(worker 池排空),`REVIEW_FILTER_TASK` LLM
|
||||
调用对照 diff 检查收集到的评论,移除可证明为错的评论。此处错误被记录并忽略。
|
||||
4. **第二轮行解析**——`Agent.Run` 返回后,顶层命令对完整评论集重跑
|
||||
`diff.ResolveLineNumbers`(见 `cmd/opencodereview/review_cmd.go`),以捕获
|
||||
`existing_code` 跨多文件或被重新定位步骤更新的评论。
|
||||
5. **渲染**——按 `--format` 渲染为 text 或 JSON。
|
||||
|
||||
## token 预算守卫
|
||||
|
||||
在调用 LLM 之前,OCR 先做一个 fail-fast 检查:
|
||||
|
||||
```go
|
||||
tokenLimit := MaxTokens * 4 / 5 // 80 %
|
||||
if countMessagesTokens(messages) > tokenLimit {
|
||||
record warning "token_threshold_exceeded"
|
||||
return nil // skip this file
|
||||
}
|
||||
```
|
||||
|
||||
这会在巨大 diff(自动生成的 lock 文件、触及数千行的重构)耗费请求之前把它们拦截下来。
|
||||
被跳过的文件作为非致命警告在 stdout 报告,并加入 JSON `warnings` 数组。
|
||||
|
||||
第二个检查在 `filterLargeDiffs` 中运行:若 diff 单独超过 `MAX_TOKENS` 的 80 %,
|
||||
它在 per-file 分发器启动前就被过滤掉。
|
||||
|
||||
## 模板与占位符
|
||||
|
||||
`internal/config/template/task_template.json` 含**五个 prompt**:
|
||||
|
||||
| Key | 用途 |
|
||||
|---|---|
|
||||
| `PLAN_TASK` | plan 阶段——产出清单。 |
|
||||
| `MAIN_TASK` | main 评审循环——发出 `code_comment` 调用。 |
|
||||
| `MEMORY_COMPRESSION_TASK` | 摘要 compress 区。 |
|
||||
| `REVIEW_FILTER_TASK` | 循环后移除可证明为错评论的流程。 |
|
||||
| `RE_LOCATION_TASK` | 为 `existing_code` 无法匹配的评论重新锚定。 |
|
||||
|
||||
每个 prompt 是一个 `{role, prompt_file}` 引用列表,指向模板目录中的 `.md` 文件
|
||||
(如 `{"role": "system", "prompt_file": "main_task_system.md"}`)。加载时
|
||||
`resolveConversation` 把这些文件读入内存中的 `{role, content}` 消息,随后模板
|
||||
占位符按文件解析:
|
||||
|
||||
| 占位符 | 替换为 |
|
||||
|---|---|
|
||||
| `{{system_rule}}` | 从四层链解析出的规则正文。 |
|
||||
| `{{change_files}}` | PR 中其他每个变更文件的状态 + 路径。 |
|
||||
| `{{diff}}` | 本文件的 diff(原始 `git diff` 输出)。 |
|
||||
| `{{current_file_path}}` | 本文件的新路径。 |
|
||||
| `{{plan_guidance}}` | plan 阶段的输出,plan 被跳过时移除。 |
|
||||
| `{{plan_tools}}` | plan 阶段工具定义的纯文本(由 `formatToolDefs` 渲染),用于 `PLAN_TASK` system prompt。 |
|
||||
| `{{requirement_background}}` | `--background` 参数内容。 |
|
||||
| `{{current_system_date_time}}` | 运行的本地时间戳,格式 `YYYY-MM-DD HH:MM`(无秒或时区)。 |
|
||||
| `{{context}}` | (仅压缩)要摘要的 XML 渲染消息。 |
|
||||
| `{{path}}` | 文件路径,用于 `REVIEW_FILTER_TASK`。 |
|
||||
| `{{comments}}` | 累积的评论(JSON),用于 `REVIEW_FILTER_TASK`。 |
|
||||
|
||||
占位符替换位于
|
||||
[`agent.go`](https://github.com/alibaba/open-code-review/blob/main/internal/agent/agent.go)。
|
||||
模板本身不是 CLI 覆盖——要修改 prompt,你需要编辑
|
||||
[`task_template.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/template/task_template.json)
|
||||
并重新构建。`--tools` 参数是*工具注册表*覆盖(它替换 `internal/config/toolsconfig`
|
||||
消费的 JSON),不是模板覆盖——见[工具](../tools/#customizing-tools)。
|
||||
|
||||
> **占位符语法注意。** 以上所有占位符都使用双花括号
|
||||
> `{{…}}` 语法,*除了* `RE_LOCATION_TASK`,它替换单花括号
|
||||
> 的 `{diff}`、`{existing_code}` 和 `{suggestion_content}`
|
||||
> (见 `internal/diff/relocation.go`)。
|
||||
|
||||
## 持久化
|
||||
|
||||
每次评审以 JSONL 写入磁盘:
|
||||
|
||||
```
|
||||
~/.opencodereview/sessions/<encoded-repo-path>/<session-id>.jsonl
|
||||
```
|
||||
|
||||
仓库路径**不**做 base64 编码;`encodeRepoPath`(在
|
||||
`internal/session/persist.go`)把 `/` 和 `\` 替换为 `-`、`:` 替换为 `_`,使路径
|
||||
对文件系统安全。
|
||||
|
||||
每行是一个事件:发送的 prompt、LLM 响应、工具调用、工具结果、发出的评论等。
|
||||
Web UI(`ocr viewer`)直接读这些文件——没有数据库,只有 append-only 日志。UI
|
||||
导览与事件 schema 见[会话查看器](../viewer/)。
|
||||
|
||||
## 遥测
|
||||
|
||||
启用遥测后,agent 发出三个流水线级 span(`review.run` 包裹整个作业、
|
||||
`diff.parse` 包裹 diff 加载、每个被评审文件一个 `subtask.execute.<file>`),加上
|
||||
每个决策点一个短生命周期的 `event.<name>` span(`plan.skipped`、
|
||||
`token.threshold.exceeded`、`subtask.error`……)。LLM 往返和工具调用仅作为
|
||||
metrics 记录——不作为 span。prompt 与响应内容**绝不**附加到遥测;
|
||||
`OCR_CONTENT_LOGGING` 标志已接入但目前是死代码。完整 schema 见[遥测](../telemetry/)。
|
||||
|
||||
## 哪些*不*自动化
|
||||
|
||||
一些决策有意保持手动:
|
||||
|
||||
- **端点发现没有回退。** 若你的 config + env + rc 文件给不出完整的
|
||||
`(URL, token, model)` 三元组,OCR 以非零码退出,而非猜测。
|
||||
- **子 agent 失败被隔离,不重试。** 一个失败文件产生一条警告;其余继续。重试
|
||||
属于包裹它的 CI 流水线,而非 agent。
|
||||
- **无跨文件推理。** 每个文件在它自己的 LLM 对话中评审。跨文件问题通过
|
||||
`file_read_diff` / `code_search` 工具调用,而非共享上下文。那些*其他*文件中
|
||||
的发现也禁止作为评论目标——`main_task` prompt 指示模型仅将上下文工具用于
|
||||
理解,并忽略在当前 diff 之外文件中出现的问题。
|
||||
|
||||
这些选择让运行**按文件确定性**,并让成本可预测。
|
||||
|
||||
## 源码地图
|
||||
|
||||
若你想对照阅读:
|
||||
|
||||
| 关注点 | 文件 |
|
||||
|---|---|
|
||||
| 顶层命令分发 | `cmd/opencodereview/main.go` |
|
||||
| `review` 参数解析 | `cmd/opencodereview/flags.go` |
|
||||
| agent 编排与压缩 | `internal/agent/`(agent.go、compression.go、util.go) |
|
||||
| 文件过滤 / 预览 | `internal/agent/preview.go` |
|
||||
| diff 加载(Git 模式) | `internal/diff/git.go` |
|
||||
| 规则解析链 | `internal/config/rules/system_rules.go` |
|
||||
| 工具注册表与实现 | `internal/tool/` |
|
||||
| LLM 端点解析器 | `internal/llm/resolver.go` |
|
||||
| 会话 JSONL 写入器 | `internal/session/persist.go` |
|
||||
| Web 查看器 | `internal/viewer/server.go` |
|
||||
|
||||
构建与测试说明见[贡献](../contributing/)。
|
||||
|
||||
## 另见
|
||||
|
||||
- [工具](../tools/)——agent 循环调用的六种工具。
|
||||
- [评审规则](../review-rules/)——按文件的规则文本如何解析。
|
||||
- [会话查看器](../viewer/)——检查此流水线写出的转录。
|
||||
376
pages/src/content/docs/zh/cli-reference.md
Normal file
376
pages/src/content/docs/zh/cli-reference.md
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
---
|
||||
title: CLI 参考
|
||||
sidebar:
|
||||
order: 6
|
||||
---
|
||||
|
||||
每个 `ocr` 子命令、参数与退出行为的完整参考。
|
||||
|
||||
## 全局用法
|
||||
|
||||
```text
|
||||
OpenCodeReview - AI-Powered Code Review CLI
|
||||
|
||||
Usage:
|
||||
ocr [command]
|
||||
|
||||
Commands:
|
||||
review, r Start a code review
|
||||
rules Inspect and debug review rules
|
||||
config Manage configuration settings
|
||||
llm LLM utility commands
|
||||
viewer Start the WebUI session viewer
|
||||
version Show version information
|
||||
|
||||
Examples:
|
||||
ocr review --from master --to dev Review diff range
|
||||
ocr review --commit abc123 Review a single commit
|
||||
ocr config provider Interactive provider setup
|
||||
ocr config model Interactive model selection
|
||||
ocr config set llm.model opus-4-6 Set a config value
|
||||
ocr llm test Test LLM connectivity
|
||||
ocr llm providers List built-in providers
|
||||
ocr version Show version info
|
||||
|
||||
Use "ocr review -h" for more information about review.
|
||||
Use "ocr rules -h" for more information about rules.
|
||||
Use "ocr config" for more information about config.
|
||||
Use "ocr llm" for more information about LLM utilities.
|
||||
|
||||
GitHub: https://github.com/alibaba/open-code-review
|
||||
```
|
||||
|
||||
## 命令总览
|
||||
|
||||
| 命令 | 别名 | 作用 |
|
||||
|---|---|---|
|
||||
| `ocr review` | `ocr r` | 运行代码评审并输出评论。 |
|
||||
| `ocr rules check <file>` | — | 显示某文件路径适用哪条规则及其来源。 |
|
||||
| `ocr config set <key> <value>` | — | 将一个配置值持久化到 `~/.opencodereview/config.json`。 |
|
||||
| `ocr config unset custom_providers.<name>` | — | 删除一个自定义 provider(若它是当前启用的,则清空启用的 `provider`/`model`)。 |
|
||||
| `ocr config provider` | — | 交互式 provider 配置 TUI。 |
|
||||
| `ocr config model` | — | 交互式 model 选择 TUI。 |
|
||||
| `ocr llm test` | — | 发送一条简短 chat 请求以验证配置的端点。 |
|
||||
| `ocr llm providers` | — | 列出所有内置 LLM provider。 |
|
||||
| `ocr viewer` | — | 启动用于历史评审会话的本地 Web UI(`localhost:5483`)。 |
|
||||
| `ocr version` | — | 打印版本、commit、平台、构建日期与 GitHub URL。 |
|
||||
|
||||
`ocr` 和 `ocr -h` 打印顶层用法。每个子命令也接受 `-h` / `--help`。
|
||||
|
||||
## `ocr review`
|
||||
|
||||
主命令。解析 Git diff,分发 per-file 子 agent,收集评审评论并打印。
|
||||
|
||||
### 概要
|
||||
|
||||
```text
|
||||
ocr review [flags]
|
||||
ocr r [flags] (alias)
|
||||
```
|
||||
|
||||
若不传任何参数,OCR 以**工作区模式**运行——评审当前目录所在仓库中所有 staged +
|
||||
unstaged + untracked 变更。
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数 | 简写 | 默认 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `--repo <path>` | — | 当前目录 | Git 仓库根。 |
|
||||
| `--from <ref>` | — | — | diff 起始 ref(如 `main`)。 |
|
||||
| `--to <ref>` | — | — | diff 结束 ref(如 `feature-branch`)。设置后 OCR 计算 `merge-base(from, to)..to`。 |
|
||||
| `--commit <sha>` | `-c` | — | 评审单个 commit(相对其父)。 |
|
||||
| `--preview` | `-p` | `false` | 运行过滤流水线但跳过 LLM。打印文件列表与排除原因。 |
|
||||
| `--format <fmt>` | `-f` | `text` | `text`(人类可读)或 `json`(机器可读的评论数组)。 |
|
||||
| `--audience <who>` | — | `human` | `human` 流式输出进度行;`agent` 静默 stdout,只打印最终摘要 / JSON。 |
|
||||
| `--background <text>` | `-b` | — | 注入 plan + main prompt 的可选需求 / 业务上下文。 |
|
||||
| `--concurrency <n>` | — | `8` | 并行评审的最大文件数。 |
|
||||
| `--timeout <minutes>` | — | `10` | 每文件截止时间。`0` 关闭超时。 |
|
||||
| `--rule <path>` | — | — | 自定义 JSON 评审规则文件路径。覆盖项目级与全局 `rule.json`。 |
|
||||
| `--max-tools <n>` | — | 模板默认 | 每文件最大工具调用轮数。`0` 用模板默认(`30`);1–9 会被上调到 `10`;任何 `≥ 10` 的值都覆盖模板默认(即使小于 `30`)。 |
|
||||
| `--model <name>` | — | — | 为本次评审覆盖已解析出的 LLM model(如 `claude-opus-4-6`)。 |
|
||||
| `--max-git-procs <n>` | — | `16` | 并发 git 子进程的最大数。 |
|
||||
| `--tools <path>` | — | 内嵌 | 自定义 JSON 工具配置文件路径。覆盖内嵌工具定义。 |
|
||||
|
||||
> 模式参数互斥:传 `--from`/`--to`,或 `--commit`,或都不传(工作区模式)。
|
||||
> 混用会直接报错。
|
||||
|
||||
### 模式
|
||||
|
||||
#### 工作区模式(默认)
|
||||
|
||||
```bash
|
||||
ocr review
|
||||
```
|
||||
|
||||
OCR 从两条 git 命令组装工作树变更:
|
||||
|
||||
- 通过 `git diff HEAD` 获取已跟踪变更(staged + unstaged 合并对比 `HEAD`;
|
||||
若为空则回退到 `git diff --staged`)
|
||||
- 通过 `git ls-files --others --exclude-standard` 获取 untracked 文件,从磁盘
|
||||
读取并作为整文件新增处理
|
||||
|
||||
这通常是 commit 前你想要的。如需更小的范围,请选择性暂存。
|
||||
|
||||
#### 区间模式
|
||||
|
||||
```bash
|
||||
ocr review --from main --to feature-branch
|
||||
```
|
||||
|
||||
OCR 计算 `merge-base(main, feature-branch)..feature-branch`,因此你只看到
|
||||
feature 分支*引入*的 diff——而非分支切出后落到 `main` 上的无关变更。
|
||||
|
||||
#### Commit 模式
|
||||
|
||||
```bash
|
||||
ocr review --commit abc123
|
||||
ocr review -c abc123
|
||||
```
|
||||
|
||||
评审 `git show abc123` 产生的 diff(即该 commit 引入的变更)。
|
||||
|
||||
### 输出
|
||||
|
||||
#### Text(默认,`--audience human`)
|
||||
|
||||
评审运行时流式输出进度行,随后每条评论一个块(带 `path:start-end` 的暗色
|
||||
Unicode 分隔头、按 100 列折行的评论正文,以及(存在时)建议替换的彩色内联
|
||||
diff)。运行结束时 stdout 末尾打印一份摘要:
|
||||
|
||||
```
|
||||
[ocr] 17 file(s) changed, reviewing 9 in /path/to/repo
|
||||
[ocr] Skipping image.png — filtered by path/extension rules
|
||||
[ocr] ▶ file_read "src/foo.go"
|
||||
[ocr] ✔ file_read (12ms)
|
||||
[ocr] Plan completed for src/foo.go
|
||||
…
|
||||
|
||||
─── src/foo.go:42-47 ───
|
||||
Concurrent map access without a lock — wrap with sync.RWMutex.
|
||||
|
||||
- m[k] = v
|
||||
+ mu.Lock(); defer mu.Unlock(); m[k] = v
|
||||
|
||||
…
|
||||
[ocr] Summary: 9 file(s) reviewed, 14 comment(s), ~21344 token(s) used (input: ~18012, output: ~3332), 1m12s elapsed
|
||||
```
|
||||
|
||||
#### Text(agent,`--audience agent`)
|
||||
|
||||
评论输出相同,但通过一个内部可静默的 stdout writer 屏蔽进度行
|
||||
([`internal/stdout`](https://github.com/alibaba/open-code-review/blob/main/internal/stdout/stdout.go))。
|
||||
在 CI / 流水线中交给另一个 agent 时使用。
|
||||
|
||||
#### JSON
|
||||
|
||||
```bash
|
||||
ocr review --format json --audience agent
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"summary": {
|
||||
"files_reviewed": 9,
|
||||
"comments": 1,
|
||||
"total_tokens": 21344,
|
||||
"input_tokens": 18012,
|
||||
"output_tokens": 3332,
|
||||
"elapsed": "1m12s"
|
||||
},
|
||||
"comments": [
|
||||
{
|
||||
"path": "src/foo.go",
|
||||
"content": "Concurrent map access without a lock — wrap with sync.RWMutex.",
|
||||
"start_line": 42,
|
||||
"end_line": 47,
|
||||
"existing_code": "m[k] = v",
|
||||
"suggestion_code": "mu.Lock(); defer mu.Unlock(); m[k] = v",
|
||||
"thinking": "Looking at line 42, the map …"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
顶层字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `status` | `success`、`completed_with_warnings`、`completed_with_errors` 或 `skipped`。 |
|
||||
| `message` | 可选。人类可读摘要,如 `"No comments generated. Looks good to me."`。 |
|
||||
| `summary` | 可选。运行聚合:`files_reviewed`、`comments`、`total_tokens`、`input_tokens`、`output_tokens`、`cache_read_tokens`(omitempty)、`cache_write_tokens`(omitempty)、`elapsed`。`skipped` 运行时省略。 |
|
||||
| `comments` | 总是存在,可能为空。每条评论的字段如上例。 |
|
||||
| `warnings` | 可选。当一个或多个子 agent 失败时存在;每条描述受影响文件与错误。 |
|
||||
|
||||
当没有文件可评审时,JSON 模式会发一个 `skipped` 外壳,以便调用方区分“无变更”
|
||||
与“无发现”:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "skipped",
|
||||
"message": "No supported files changed.",
|
||||
"comments": []
|
||||
}
|
||||
```
|
||||
|
||||
### 退出码
|
||||
|
||||
| 码 | 含义 |
|
||||
|---|---|
|
||||
| `0` | 评审完成(可能零评论,可能有非致命警告)。 |
|
||||
| `1` | 致命错误——参数错误、无法解析 LLM 端点、所有 per-file 子 agent 失败等。错误文本打印到 stderr。 |
|
||||
|
||||
非致命警告(单个子 agent 失败、某文件超过 token 阈值等)内联打印;JSON 模式下
|
||||
会加入 `warnings` 数组。
|
||||
|
||||
## `ocr rules`
|
||||
|
||||
规则自查。只有一个子命令:
|
||||
|
||||
```text
|
||||
ocr rules check [flags] <file-path>
|
||||
|
||||
Flags:
|
||||
--repo <path> Git repository root (default: current dir)
|
||||
--rule <path> Path to a custom rule JSON file
|
||||
```
|
||||
|
||||
对给定文件路径,OCR 会:
|
||||
|
||||
1. 遍历四层规则链(custom → project → global → system)。
|
||||
2. 取第一条匹配。
|
||||
3. 打印**来源层**、匹配的 **glob 模式**,以及解析出的**规则文本**。
|
||||
|
||||
```bash
|
||||
$ ocr rules check src/main/java/com/example/Foo.java
|
||||
File: src/main/java/com/example/Foo.java
|
||||
Source: System built-in
|
||||
Pattern: **/*.java
|
||||
Rule:
|
||||
────────────────────────────────────────
|
||||
<contents of internal/config/rules/rule_docs/java.md>
|
||||
────────────────────────────────────────
|
||||
```
|
||||
|
||||
可用于排查“为什么我的自定义规则没触发?”——完整的优先级说明见
|
||||
[评审规则](../review-rules/)。
|
||||
|
||||
## `ocr config`
|
||||
|
||||
将 key 持久化到 `~/.opencodereview/config.json`,并提供交互式配置 TUI。四个
|
||||
子命令:
|
||||
|
||||
```text
|
||||
ocr config set <key> <value>
|
||||
ocr config unset custom_providers.<name> Delete a custom provider
|
||||
ocr config provider Interactive provider setup
|
||||
ocr config model Interactive model selection
|
||||
```
|
||||
|
||||
- **`set`**——非交互式写入单个配置值。
|
||||
- **`unset`**——删除一个自定义 provider。仅支持
|
||||
`custom_providers.<name>`。若删除的是当前启用的 provider,则 `provider` 和
|
||||
`model` 被清空(运行 `ocr config provider` 重新选择)。
|
||||
- **`provider`**——启动交互式 provider 配置 TUI(无额外参数;非交互式请用
|
||||
`ocr config set provider <name>`)。
|
||||
- **`model`**——启动交互式 model 选择 TUI(无额外参数;非交互式请用
|
||||
`ocr config set model <name>`)。
|
||||
|
||||
完整的 key 参考、schema 与示例见[配置](../configuration/)。
|
||||
|
||||
## `ocr llm`
|
||||
|
||||
LLM 工具命令。两个子命令:
|
||||
|
||||
```text
|
||||
ocr llm <sub-command>
|
||||
|
||||
Sub-commands:
|
||||
test Send a test conversation to the configured LLM model
|
||||
providers List all built-in LLM providers
|
||||
```
|
||||
|
||||
### `ocr llm test`
|
||||
|
||||
```text
|
||||
ocr llm test
|
||||
```
|
||||
|
||||
以与 `ocr review` 完全相同的方式解析 LLM 端点,从
|
||||
[`internal/config/testconnection/task.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/testconnection/task.json)
|
||||
发送一条预置 chat 请求,并打印:
|
||||
|
||||
```
|
||||
Source: <which strategy was used>
|
||||
URL: <endpoint URL>
|
||||
Model: <effective model>
|
||||
<the model's reply>
|
||||
✓ Connection test successful
|
||||
```
|
||||
|
||||
非零退出意味着端点未完整配置,或请求失败(网络 / 鉴权 / 模型错误)。错误信息
|
||||
会指明具体是哪一种。
|
||||
|
||||
### `ocr llm providers`
|
||||
|
||||
```text
|
||||
ocr llm providers
|
||||
```
|
||||
|
||||
以三列表格列出每个内置 LLM provider:
|
||||
|
||||
```
|
||||
Built-in providers:
|
||||
NAME PROTOCOL BASE URL
|
||||
---- -------- --------
|
||||
anthropic anthropic https://api.anthropic.com
|
||||
…
|
||||
```
|
||||
|
||||
随后是一条提示,可用 `ocr config provider` 交互式配置,或用
|
||||
`ocr config set provider <name>` 非交互式配置。
|
||||
|
||||
## `ocr viewer`
|
||||
|
||||
```text
|
||||
ocr viewer [flags]
|
||||
|
||||
Flags:
|
||||
--addr <address> listen address (default: localhost:5483)
|
||||
|
||||
Examples:
|
||||
ocr viewer # start on default port
|
||||
ocr viewer --addr :3000 # bind to all interfaces on port 3000
|
||||
```
|
||||
|
||||
启动一个内嵌 HTTP 服务器,读取 `~/.opencodereview/sessions/...`,并以浏览器友好的 UI 渲染历史评审会话。见[会话查看器](../viewer/)。
|
||||
|
||||
## `ocr version`
|
||||
|
||||
```text
|
||||
ocr version
|
||||
ocr --version
|
||||
ocr -V
|
||||
```
|
||||
|
||||
打印构建时写入的版本信息、短 Git commit(存在时)、平台
|
||||
(`<GOOS>/<GOARCH>`)、构建日期(存在时),以及 GitHub URL
|
||||
(`https://github.com/alibaba/open-code-review`)。
|
||||
|
||||
## 提示与注意
|
||||
|
||||
- `--audience agent` **并不**隐含 `--format json`。两者控制不同的事——屏蔽 UI
|
||||
vs 结构化载荷。需要二者兼得时组合使用。
|
||||
- `--background` 是提升评审质量最有效的参数之一——从其他 agent 调用时,始终传入
|
||||
需求 / PR 描述。
|
||||
- 某文件 diff 单独超过 `MAX_TOKENS` 的 80%(默认 `58888`)时,会在调用 LLM 前
|
||||
被丢弃。这会记录日志但不会使运行失败。
|
||||
- 当某文件变更行数低于 `PLAN_MODE_LINE_THRESHOLD`(`50`)时,plan 阶段会被
|
||||
**自动跳过**。
|
||||
|
||||
## 另见
|
||||
|
||||
- [快速开始](../quickstart/)——安装并完成首次评审。
|
||||
- [配置](../configuration/)——参数背后的环境变量与 config key。
|
||||
- [评审规则](../review-rules/)——`--rule` 参数与规则解析。
|
||||
- [集成](../integrations/)——从 agent 与 CI 调用 `ocr review`。
|
||||
272
pages/src/content/docs/zh/configuration.md
Normal file
272
pages/src/content/docs/zh/configuration.md
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
---
|
||||
title: 配置
|
||||
sidebar:
|
||||
order: 5
|
||||
---
|
||||
|
||||
## 端点解析
|
||||
|
||||
当 `ocr review` 或 `ocr llm test` 运行时,它会按顺序尝试四个来源,
|
||||
并使用第一个能给出完整 `(URL, token, model)` 三元组的来源:
|
||||
|
||||
| 优先级 | 来源 | 读取内容 |
|
||||
|---|---|---|
|
||||
| 1 | `~/.opencodereview/config.json` | 若设置了 `provider`,则通过 `providers`/`custom_providers` 映射解析(provider 优先;见[内置 provider](#built-in-providers))。仅当未设置 provider 时才回退到遗留的 `llm` 段。 |
|
||||
| 2 | OCR 环境变量 | `OCR_LLM_URL`、`OCR_LLM_TOKEN`、`OCR_LLM_MODEL`、`OCR_USE_ANTHROPIC`、`OCR_LLM_AUTH_HEADER`。 |
|
||||
| 3 | Claude Code 环境变量 | `ANTHROPIC_BASE_URL`、`ANTHROPIC_AUTH_TOKEN`、`ANTHROPIC_MODEL`。 |
|
||||
| 4 | Shell rc 文件 | 从 `~/.zshrc`、`~/.bashrc`、`~/.bash_profile`、`~/.profile` 中解析出的 `export ANTHROPIC_*=…` 行。 |
|
||||
|
||||
对于 Claude Code 风格的来源,若 `ANTHROPIC_BASE_URL` 缺少带版本的路径
|
||||
(`/v1/...`),OCR 会自动追加 `/v1/messages`。
|
||||
|
||||
如果没有一种策略能给出完整三元组,OCR 会以如下信息退出:
|
||||
|
||||
```
|
||||
no valid LLM endpoint configured; one of OCR_LLM_URL/OCR_LLM_TOKEN/OCR_LLM_MODEL,
|
||||
~/.opencodereview/config.json, or ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN/
|
||||
ANTHROPIC_MODEL must be set
|
||||
```
|
||||
|
||||
> 解析在第一个**报错**的来源处停止,而不仅仅是第一个为空的来源。尤其要注意:
|
||||
> 如果 `config.json` 中设置了 `provider` 但该条目配置有误(未知的 provider 名、
|
||||
> 缺少 `api_key` 且无环境变量回退、缺少 `model`、自定义 provider 缺少
|
||||
> `url`/`protocol`),OCR 会以该错误退出,并**不会**继续回退到 OCR 环境变量、
|
||||
> Claude Code 或 rc 文件来源。要切换到基于环境变量的配置,请先取消
|
||||
> 设置 `provider` key。
|
||||
|
||||
> 来源优先级意味着当配置文件已完整填充时,**环境变量不会覆盖任何值**。要让
|
||||
> 环境变量生效,要么从 `~/.opencodereview/config.json` 删除相关 `llm.*` key,
|
||||
> 要么用 `ocr config set` 切换到新值。
|
||||
|
||||
## `ocr config set` ——管理 `~/.opencodereview/config.json`
|
||||
|
||||
```bash
|
||||
ocr config set <key> <value>
|
||||
```
|
||||
|
||||
`config set` 通过 key/value 对修改文件,并做具备 schema 感知的解析。交互式 TUI
|
||||
命令 `ocr config provider` 和 `ocr config model` 也写入同一个文件(见
|
||||
[交互式设置](#interactive-setup--ocr-config-provider--ocr-config-model))。识别的
|
||||
key:
|
||||
|
||||
| Key | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `provider` | string | 设置当前 provider(内置名或自定义)。切换 provider 会清空 model。 |
|
||||
| `model` | string | 为当前 provider 设置 model(存在 provider 条目下;若无 provider 则存到顶层 `model`)。 |
|
||||
| `providers.<name>.<field>` | varies | 内置 provider 的按字段设置:`api_key`、`url`、`protocol`、`model`、`models`、`auth_header`、`extra_body`。 |
|
||||
| `custom_providers.<name>.<field>` | varies | 同上字段,用于自定义(非内置)provider。自定义 provider 至少要设置 `url` 和 `protocol`。 |
|
||||
| `llm.url` | string | 端点 URL。Anthropic 用完整的 Messages URL,如 `https://api.anthropic.com/v1/messages`。OpenAI 兼容则用 chat-completions URL。 |
|
||||
| `llm.auth_token` | string | API key。以 `Authorization: Bearer …` 发送(OpenAI);遗留 Anthropic 路径默认也是 `Authorization: Bearer …`(预设 `anthropic` provider 改为默认 `x-api-key`)。仅在显式设置 `llm.auth_header` 时才用 `x-api-key`。 |
|
||||
| `llm.auth_header` | string | Auth header 名(`x-api-key`、`authorization` 或 `bearer`)。仅 Anthropic 用;某些需要 `x-api-key` 的 Anthropic 设置必需。 |
|
||||
| `llm.model` | string | 模型名。`[<数字>m]` 后缀会被自动去除。 |
|
||||
| `llm.use_anthropic` | boolean | `true`(默认)→ Anthropic Messages 协议。`false` → OpenAI Chat Completions。 |
|
||||
| `llm.extra_body` | JSON object | 厂商专属的请求字段,合并进每次 chat 请求体。示例:`'{"thinking":{"type":"disabled"}}'`。 |
|
||||
| `language` | string | 转发为追加到 system prompt 的指令;未设置时默认 `English`。见[选择语言](#choosing-a-language)。 |
|
||||
| `telemetry.enabled` | boolean | OpenTelemetry 导出的总开关。默认关闭。 |
|
||||
| `telemetry.exporter` | string | `console` 或 `otlp`。 |
|
||||
| `telemetry.otlp_endpoint` | string | OTLP collector 地址(如 `localhost:4317`)。 |
|
||||
| `telemetry.content_logging` | boolean | 在导出的事件数据中包含 LLM prompt / 响应。 |
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
ocr config set llm.url https://api.anthropic.com/v1/messages
|
||||
ocr config set llm.auth_token sk-ant-xxxxxxxxxx
|
||||
ocr config set llm.model claude-opus-4-6
|
||||
ocr config set llm.use_anthropic true
|
||||
ocr config set llm.extra_body '{"thinking":{"type":"disabled"}}'
|
||||
ocr config set language English
|
||||
ocr config set telemetry.enabled true
|
||||
ocr config set telemetry.exporter otlp
|
||||
ocr config set telemetry.otlp_endpoint localhost:4317
|
||||
|
||||
# 基于 provider 的设置(推荐)
|
||||
ocr config set provider anthropic
|
||||
ocr config set model claude-opus-4-6
|
||||
ocr config set providers.anthropic.api_key "$ANTHROPIC_API_KEY"
|
||||
|
||||
# 自定义(非内置)provider
|
||||
ocr config set provider my-gateway
|
||||
ocr config set custom_providers.my-gateway.url https://gateway.internal.com/v1
|
||||
ocr config set custom_providers.my-gateway.protocol openai
|
||||
ocr config set custom_providers.my-gateway.model llama-3-70b
|
||||
ocr config set custom_providers.my-gateway.api_key "$MY_API_KEY"
|
||||
```
|
||||
|
||||
布尔值接受 Go `strconv.ParseBool` 接受的任何形式(`true`、`false`、`1`、`0`、
|
||||
`t`、`f`……)。`llm.extra_body` 必须是合法 JSON。
|
||||
|
||||
## 文件 schema 参考
|
||||
|
||||
执行上述命令后,`~/.opencodereview/config.json` 形如:
|
||||
|
||||
```json
|
||||
{
|
||||
"llm": {
|
||||
"url": "https://api.anthropic.com/v1/messages",
|
||||
"auth_token": "sk-ant-xxxxxxxxxx",
|
||||
"auth_header": "x-api-key",
|
||||
"model": "claude-opus-4-6",
|
||||
"use_anthropic": true,
|
||||
"extra_body": {
|
||||
"thinking": { "type": "disabled" }
|
||||
}
|
||||
},
|
||||
"language": "English",
|
||||
"telemetry": {
|
||||
"enabled": true,
|
||||
"exporter": "otlp",
|
||||
"otlp_endpoint": "localhost:4317"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
基于 provider 的形式使用 `provider`、`model`、`providers` 和
|
||||
`custom_providers`,而非遗留的 `llm` 块:
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "anthropic",
|
||||
"model": "claude-opus-4-6",
|
||||
"providers": {
|
||||
"anthropic": {
|
||||
"api_key": "sk-ant-xxxxxxxxxx",
|
||||
"model": "claude-opus-4-6"
|
||||
}
|
||||
},
|
||||
"custom_providers": {
|
||||
"my-gateway": {
|
||||
"url": "https://gateway.internal.com/v1",
|
||||
"protocol": "openai",
|
||||
"model": "llama-3-70b",
|
||||
"models": ["llama-3-70b", "llama-3-8b"],
|
||||
"api_key": "gw-xxxxxxxxxx",
|
||||
"auth_header": "authorization"
|
||||
}
|
||||
},
|
||||
"language": "English"
|
||||
}
|
||||
```
|
||||
|
||||
当设置了 `provider` 时,由 `providers`/`custom_providers` 映射驱动解析;该配置下
|
||||
遗留的 `llm` 段被忽略。
|
||||
|
||||
你也可以手动编辑此文件,但下次写入时 `ocr config set` 会以 `" "` 缩进
|
||||
重新序列化。
|
||||
|
||||
## 交互式设置——`ocr config provider` / `ocr config model`
|
||||
|
||||
为免去手动键入 key 来选择 provider 和 model,OCR 提供两个交互式 Bubble Tea TUI,
|
||||
二者同样会修改 `~/.opencodereview/config.json`。
|
||||
|
||||
```bash
|
||||
ocr config provider
|
||||
ocr config model
|
||||
```
|
||||
|
||||
- `ocr config provider`——选择内置或自定义 provider,并输入 URL / protocol /
|
||||
API key / model 的交互式 TUI。选择会保存到 config,并自动运行 `ocr llm test`
|
||||
验证端点。对于内置 provider,若未直接输入,API key 可从该 provider 的环境变量
|
||||
读取(见[内置 provider](#built-in-providers))。若选择手动配置,则改为填充遗留的
|
||||
`llm.*` 块。
|
||||
- `ocr config model`——从当前 provider 的预设列表,以及
|
||||
`providers.<name>.models` / `custom_providers.<name>.models` 下用户添加的
|
||||
model 中选择模型的交互式 TUI。需要先设置 provider(`ocr config provider`)。
|
||||
|
||||
## 内置 provider
|
||||
|
||||
以下 provider 随 OCR 发布。每个都有预设的 `BaseURL`、`Protocol`,以及
|
||||
(如适用)一个 API key 环境变量,在 `providers.<name>.api_key` 未设置时作为
|
||||
回退。
|
||||
|
||||
| 名称 | Protocol | Base URL | API key 环境变量 |
|
||||
|---|---|---|---|
|
||||
| `anthropic` | anthropic | `https://api.anthropic.com` | `ANTHROPIC_API_KEY` |
|
||||
| `openai` | openai | `https://api.openai.com/v1` | `OPENAI_API_KEY` |
|
||||
| `dashscope` | openai | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `DASHSCOPE_API_KEY` |
|
||||
| `dashscope-tokenplan` | openai | `https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1` | `DASHSCOPE_TOKENPLAN_KEY` |
|
||||
| `volcengine` | openai | `https://ark.cn-beijing.volces.com/api/v3` | `ARK_API_KEY` |
|
||||
| `deepseek` | openai | `https://api.deepseek.com` | `DEEPSEEK_API_KEY` |
|
||||
| `tencent-tokenhub` | openai | `https://tokenhub.tencentmaas.com/v1` | `TENCENT_TOKENHUB_API_KEY` |
|
||||
| `hy-tokenplan` | openai | `https://api.lkeap.cloud.tencent.com/plan/v3` | `TENCENT_HUNYUAN_TOKENPLAN_KEY` |
|
||||
| `kimi` | openai | `https://api.moonshot.cn/v1` | `MOONSHOT_API_KEY` |
|
||||
| `z-ai` | openai | `https://open.bigmodel.cn/api/paas/v4` | `Z_AI_API_KEY` |
|
||||
| `mimo` | openai | `https://api.xiaomimimo.com/v1` | `MIMO_API_KEY` |
|
||||
| `minimax` | openai | `https://api.minimaxi.com/v1` | `MINIMAX_API_KEY` |
|
||||
| `baidu-qianfan` | openai | `https://qianfan.baidubce.com/v2` | `QIANFAN_API_KEY` |
|
||||
|
||||
任何其他 provider 名都被视为自定义,必须在 `custom_providers` 下配置,且至少
|
||||
要有 `url` 和 `protocol`。
|
||||
|
||||
## 环境变量参考
|
||||
|
||||
| 变量 | 用途 |
|
||||
|---|---|
|
||||
| `OCR_LLM_URL` | 端点 URL——与 `llm.url` 同形。 |
|
||||
| `OCR_LLM_TOKEN` | API key——与 `llm.auth_token` 相同。 |
|
||||
| `OCR_LLM_MODEL` | 模型名。 |
|
||||
| `OCR_LLM_AUTH_HEADER` | Auth header 名(`x-api-key`、`authorization` 或 `bearer`)。仅 Anthropic;与 `llm.auth_header` 相同。未设置时默认 `authorization`。 |
|
||||
| `OCR_USE_ANTHROPIC` | 未设置 → Anthropic 协议(默认)。设为 `true` / `1` / `yes`(不区分大小写)→ Anthropic。设为其他值(`false`、`0`、`no`、拼写错误……)→ OpenAI。 |
|
||||
| `ANTHROPIC_BASE_URL` | Claude Code 兼容的 base URL。 |
|
||||
| `ANTHROPIC_AUTH_TOKEN` | Claude Code 兼容的 API key。 |
|
||||
| `ANTHROPIC_MODEL` | Claude Code 兼容的 model。 |
|
||||
| `OCR_ENABLE_TELEMETRY` | `1` 表示从环境变量启用遥测。 |
|
||||
| `OTEL_SERVICE_NAME` | 覆盖 span/metric 中的 service name。 |
|
||||
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector 地址——同时强制 exporter 为 `otlp`。 |
|
||||
| `OTEL_EXPORTER_OTLP_PROTOCOL` | OTLP 传输协议(`grpc`、`http/protobuf` 或 `http/json`)。默认 `grpc`。 |
|
||||
| `OCR_CONTENT_LOGGING` | `1` 表示在遥测事件中包含 prompt/响应。 |
|
||||
|
||||
各 provider 的 API key(`ANTHROPIC_API_KEY`、`OPENAI_API_KEY`、
|
||||
`DASHSCOPE_API_KEY`……)在内置 provider 的 `api_key` 字段未设置时作为回退。
|
||||
各 provider 的环境变量名见[内置 provider](#built-in-providers)表。
|
||||
|
||||
## 为什么有 `extra_body`
|
||||
|
||||
一些托管 provider 会在请求体中加入非标准字段(例如 Bedrock 风格的 `thinking`、
|
||||
厂商专属的 `temperature_strategy`、流式选项)。`llm.extra_body` 会被合并进每个
|
||||
发出的请求,因此你无需改源码即可发送这些字段。
|
||||
|
||||
```bash
|
||||
ocr config set llm.extra_body '{"thinking":{"type":"enabled","budget_tokens":2048}}'
|
||||
```
|
||||
|
||||
## 选择语言
|
||||
|
||||
`language` key 只控制一件事:追加到评审和 `ocr llm test` prompt 中每条
|
||||
system-role 消息的一条指令。注入的精确字符串是:
|
||||
|
||||
```
|
||||
\n\nAlways respond in <language>.
|
||||
```
|
||||
|
||||
- *未设置*或为空——按 `English` 对待。
|
||||
- `Chinese`、`English` 或任何其他字符串——原样透传。
|
||||
|
||||
内置 rule docs 不支持语言切换。`internal/config/rules/rule_docs/` 下嵌入的文件按
|
||||
固定文件名加载,多数以中文撰写(`default.md` 是英文例外);无论 `language`
|
||||
如何设置,它们都原样出现在 prompt 中。因此当 `language` 设为 `English` 时,prompt
|
||||
里会是一条英文指令叠加大段中文 rule 文本——强模型会遵从指令产出英文评论,
|
||||
弱模型可能输出中英混杂的内容。
|
||||
|
||||
`language` 没有环境变量、CLI 参数或项目级覆盖——唯一能设置它的地方是全局
|
||||
`~/.opencodereview/config.json`,通过
|
||||
[`ocr config set`](#ocr-config-set--managing-opencodereviewconfigjson):
|
||||
|
||||
```bash
|
||||
ocr config set language English
|
||||
```
|
||||
|
||||
如果你需要纯英文 rule 文本,请通过 `--rule`、`<repo>/.opencodereview/rule.json`
|
||||
或 `~/.opencodereview/rule.json` 提供自己的规则(见
|
||||
[评审规则](../review-rules/#priority-chain))。
|
||||
|
||||
## 项目级 vs 全局配置
|
||||
|
||||
CLI 本身是全局配置的(`~/.opencodereview/config.json`)——没有项目级 LLM 配置。
|
||||
但**评审规则**是项目级的;见[评审规则](../review-rules/#priority-chain)。
|
||||
|
||||
## 另见
|
||||
|
||||
- [快速开始](../quickstart/)——最小化设置与首次评审。
|
||||
- [CLI 参考](../cli-reference/)——review 命令接受的每个参数。
|
||||
- [遥测](../telemetry/)——如何接入 OTLP / console exporter。
|
||||
201
pages/src/content/docs/zh/contributing.md
Normal file
201
pages/src/content/docs/zh/contributing.md
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
---
|
||||
title: 贡献
|
||||
sidebar:
|
||||
order: 13
|
||||
---
|
||||
|
||||
OCR 是 Apache-2.0 许可下的开源项目。欢迎 bug 报告、文档修复与代码贡献。本页为
|
||||
快速参考;权威版本位于
|
||||
[`CONTRIBUTING.md`](https://github.com/alibaba/open-code-review/blob/main/CONTRIBUTING.md)。
|
||||
|
||||
## 贡献方式
|
||||
|
||||
不写 Go 也能帮上忙:
|
||||
|
||||
- **Bug 报告**——开一个带复现步骤的
|
||||
[GitHub issue](https://github.com/alibaba/open-code-review/issues/new/choose)。
|
||||
- **功能请求**——在
|
||||
[Discussions](https://github.com/alibaba/open-code-review/discussions/categories/ideas)
|
||||
开帖或开 feature-request issue。
|
||||
- **文档**——错别字、缺失示例、失效链接——这些 PR 通常最快被合并。
|
||||
- **评审其他 PR**——非维护者的评论有助于减轻评审者负担。
|
||||
- **代码**——bug 修复、性能优化、新功能。
|
||||
|
||||
## 本地开发设置
|
||||
|
||||
### 前置条件
|
||||
|
||||
- [Go ≥ 1.25](https://go.dev/dl/)
|
||||
- [Git](https://git-scm.com/)
|
||||
- [Make](https://www.gnu.org/software/make/)
|
||||
|
||||
### 获取源码
|
||||
|
||||
```bash
|
||||
# Fork on GitHub, then:
|
||||
git clone https://github.com/<your-username>/open-code-review.git
|
||||
cd open-code-review
|
||||
git remote add upstream https://github.com/alibaba/open-code-review.git
|
||||
|
||||
make build # writes dist/opencodereview
|
||||
make test # LC_ALL=C go test -v -race -count=1 ./...
|
||||
```
|
||||
|
||||
> `upstream` remote 是只读的。推送到 `origin`(你的 fork)并从那里发起 PR。
|
||||
|
||||
### 运行本地构建
|
||||
|
||||
```bash
|
||||
./dist/opencodereview review --preview
|
||||
```
|
||||
|
||||
为方便起见,在 `~/bin/ocr-dev` 放一个指向 `dist/opencodereview` 的符号链接,即可在
|
||||
任意仓库调用 `ocr-dev`。
|
||||
|
||||
### Make target
|
||||
|
||||
| Target | 作用 |
|
||||
|---|---|
|
||||
| `make build` | 为当前平台构建 → `dist/opencodereview`。 |
|
||||
| `make build-darwin-amd64` | 交叉编译 macOS Intel。 |
|
||||
| `make build-darwin-arm64` | 交叉编译 macOS Apple Silicon。 |
|
||||
| `make build-linux-amd64` | 交叉编译 Linux x86_64。 |
|
||||
| `make build-linux-arm64` | 交叉编译 Linux ARM64。 |
|
||||
| `make build-windows-amd64` | 交叉编译 Windows x86_64。 |
|
||||
| `make build-windows-arm64` | 交叉编译 Windows ARM64。 |
|
||||
| `make build-all` | 全部六个交叉编译二进制(linux/darwin/windows × amd64/arm64)。 |
|
||||
| `make sha256sum` | 为构建产物生成 `sha256sum.txt`。 |
|
||||
| `make dist` | `clean → build-all → sha256sum`。CI 运行的内容。 |
|
||||
| `make test` | 带 race 检测运行测试。 |
|
||||
| `make clean` | 删除 `dist/`。 |
|
||||
|
||||
## 分支与提交约定
|
||||
|
||||
### 分支前缀
|
||||
|
||||
| 前缀 | 用途 |
|
||||
|---|---|
|
||||
| `feat/` | 新功能 |
|
||||
| `fix/` | Bug 修复 |
|
||||
| `docs/` | 仅文档 |
|
||||
| `refactor/` | 无行为变更的重构 |
|
||||
| `test/` | 仅测试变更 |
|
||||
| `chore/` | 构建 / CI / 工具 |
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git pull upstream main
|
||||
git checkout -b feat/anthropic-streaming
|
||||
```
|
||||
|
||||
### 提交信息
|
||||
|
||||
[Conventional Commits](https://www.conventionalcommits.org/) 格式:
|
||||
|
||||
```
|
||||
<type>(<scope>): <short summary>
|
||||
|
||||
[optional body explaining the why]
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```
|
||||
feat(agent): add support for custom tool definitions
|
||||
fix(llm): handle timeout errors in Anthropic API calls
|
||||
docs(readme): clarify endpoint resolution priority
|
||||
refactor(viewer): extract task-card rendering into helper
|
||||
```
|
||||
|
||||
**PR 标题**也用相同格式,以便在生成的 changelog 中整洁显示。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
open-code-review/
|
||||
├── cmd/opencodereview/ # CLI 入口——参数解析、分发
|
||||
├── internal/
|
||||
│ ├── agent/ # 评审 agent 逻辑、子 agent 分发
|
||||
│ ├── config/ # 模板、规则、白名单、内嵌 JSON
|
||||
│ ├── diff/ # Git diff 解析、三种模式
|
||||
│ ├── gitcmd/ # Git 子进程运行器
|
||||
│ ├── llm/ # LLM client(Anthropic 与 OpenAI)、端点解析器
|
||||
│ ├── model/ # 数据结构(LlmComment、Diff……)
|
||||
│ ├── pathutil/ # 路径工具
|
||||
│ ├── release/ # Release notes 生成
|
||||
│ ├── session/ # JSONL 会话写入器
|
||||
│ ├── stdout/ # 可静音的 stdout writer
|
||||
│ ├── suggestdiff/ # 建议 diff 渲染
|
||||
│ ├── telemetry/ # OpenTelemetry 配置 + 辅助
|
||||
│ ├── tool/ # 工具注册表 + provider 实现
|
||||
│ └── viewer/ # 内嵌 HTTP UI
|
||||
├── pages/ # WebUI 营销页(独立 React app)
|
||||
├── plugins/ # Claude Code slash 命令
|
||||
├── extensions/ # 编辑器扩展(VS Code)
|
||||
├── examples/ # CI 配方(GitHub Actions、GitLab CI)
|
||||
├── skills/ # Agent SDK skill manifest
|
||||
├── scripts/ # NPM postinstall + 跨平台构建脚本
|
||||
├── npm/ # 各平台 optional dependency 包
|
||||
└── bin/ # NPM wrapper(Node)
|
||||
```
|
||||
|
||||
多数贡献触及 `internal/agent/`、`internal/tool/` 或 `internal/llm/`。
|
||||
`cmd/opencodereview/` 中的 CLI 层有意保持精简——参数解析后分发到 agent 包。
|
||||
|
||||
## 代码质量检查
|
||||
|
||||
开 PR 前:
|
||||
|
||||
```bash
|
||||
go fmt ./...
|
||||
go vet ./...
|
||||
make test # race-enabled, runs in CI on every push
|
||||
make build # smoke test the binary builds
|
||||
```
|
||||
|
||||
CI 在每次推送时运行同一套,不会有意外。
|
||||
|
||||
## 添加新工具
|
||||
|
||||
一个工具有两部分:
|
||||
|
||||
1. [`internal/config/toolsconfig/tools.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/toolsconfig/tools.json)
|
||||
中的 **JSON 定义**:name、description 与 LLM 看到的 JSON-schema 参数。
|
||||
2. 在 `internal/tool/definitions.go` 注册的 **Go provider**,含实际实现。
|
||||
|
||||
两者都存在,新工具名才能工作。现有六个见[工具](../tools/),可当作模板。
|
||||
|
||||
## 添加新规则模式
|
||||
|
||||
编辑 `internal/config/rules/system_rules.json` 把新 glob 映射到规则文档,并在
|
||||
`internal/config/rules/rule_docs/` 下添加对应 markdown。规则文档按模式一个文件存放
|
||||
(英文)。`language` 配置只在 system prompt 追加一条指示模型以该语言响应的指令;
|
||||
它不会切换 rule-doc 文件。
|
||||
|
||||
## PR 流程
|
||||
|
||||
1. **大改动先开 issue。** 提前对齐方向,好过在代码评审时才发现分歧。
|
||||
2. **每个 PR 一个逻辑变更。** 若有两个无关修复,提两个 PR。
|
||||
3. **更新测试。** 行为变更需测试覆盖——`make test` 必须通过。
|
||||
4. **更新文档。** 若变更影响参数、config key 或评审流水线,同时更新本文档站
|
||||
(在 [`docs/`](https://github.com/alibaba/open-code-review))与任何相关内联帮助。
|
||||
5. **填写 PR 模板。** 维护者会评审,通常几个工作日内。
|
||||
|
||||
## 贡献者许可协议(CLA)
|
||||
|
||||
本项目要求 Alibaba Open Source CLA。首次开 PR 时会有 bot 贴链接——电子签署
|
||||
(一分钟)。后续 PR 无需重签。
|
||||
|
||||
## 首次贡献?
|
||||
|
||||
找标了
|
||||
[`good first issue`](https://github.com/alibaba/open-code-review/labels/good%20first%20issue)
|
||||
或 [`help wanted`](https://github.com/alibaba/open-code-review/labels/help%20wanted)
|
||||
的 issue。多数体量小且自包含,issue 描述里有足够上下文,便于上手。
|
||||
|
||||
## 另见
|
||||
|
||||
- [架构](../architecture/)——修改 `internal/agent/` 前你需要的心智模型。
|
||||
- [工具](../tools/)——现有工具长什么样。
|
||||
- 完整贡献指南:
|
||||
[CONTRIBUTING.md](https://github.com/alibaba/open-code-review/blob/main/CONTRIBUTING.md)
|
||||
283
pages/src/content/docs/zh/faq.md
Normal file
283
pages/src/content/docs/zh/faq.md
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
---
|
||||
title: FAQ
|
||||
sidebar:
|
||||
order: 14
|
||||
---
|
||||
|
||||
常见错误、意外与“这应该这样吗?”的问题。若你的问题不在此处,开一个带运行步骤
|
||||
与完整输出的 [GitHub issue](https://github.com/alibaba/open-code-review/issues)。
|
||||
|
||||
## 配置与启动
|
||||
|
||||
### `no valid LLM endpoint configured`
|
||||
|
||||
```
|
||||
no valid LLM endpoint configured; one of OCR_LLM_URL/OCR_LLM_TOKEN/OCR_LLM_MODEL,
|
||||
~/.opencodereview/config.json, or ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN/
|
||||
ANTHROPIC_MODEL must be set
|
||||
```
|
||||
|
||||
OCR 走完了四来源解析链([配置](../configuration/#endpoint-resolution))但没
|
||||
找到完整的 `(URL, token, model)` 三元组。要么:
|
||||
|
||||
- 运行 `ocr config set llm.url …` / `llm.auth_token …` / `llm.model …` 填充
|
||||
`~/.opencodereview/config.json`,**或**
|
||||
- 导出 `OCR_LLM_URL` / `OCR_LLM_TOKEN` / `OCR_LLM_MODEL`,**或**
|
||||
- 若你已在用 Claude Code,导出 `ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN` /
|
||||
`ANTHROPIC_MODEL`。
|
||||
|
||||
然后 `ocr llm test` 验证连通性再重试评审。
|
||||
|
||||
### `ocr llm test` 显示错误的来源
|
||||
|
||||
OCR 取**第一个**完整三元组,而非最后一个。因此若配置文件已有全部三个 llm.*
|
||||
key,环境变量会被忽略。要让环境变量生效,删除配置 key(删除文件或手动 unset)或
|
||||
用 `ocr config set` 切换到新值。
|
||||
|
||||
### `ocr llm test` 返回 401 / 403
|
||||
|
||||
token 缺少 scope、已过期或厂商不匹配。Anthropic 与 OpenAI 用不同的 auth header 与
|
||||
URL 格式——确保 `llm.use_anthropic` 与你指向的 URL 相匹配:
|
||||
|
||||
- Anthropic:URL 以 `/v1/messages` 结尾,`use_anthropic=true`。
|
||||
- OpenAI / OpenAI 兼容:URL 以 `/v1/chat/completions` 结尾,
|
||||
`use_anthropic=false`。
|
||||
|
||||
### `not a git repository`
|
||||
|
||||
`ocr review` 对当前目录运行 `git diff`(以及对 untracked 文件的 `git ls-files`)。
|
||||
若你不在 Git 工作树内,它会提前退出。要么 `cd` 进仓库,要么传 `--repo /path/to/repo`。
|
||||
|
||||
## 过滤与规则
|
||||
|
||||
### 我的文件没被评审
|
||||
|
||||
运行 `ocr review --preview`(无 LLM 成本)。输出列出每个候选文件及其被保留或
|
||||
丢弃的**原因**:
|
||||
|
||||
```
|
||||
src/foo.go modified
|
||||
src/foo_test.go modified (excluded: user_exclude)
|
||||
node_modules/lib.js added (excluded: default_path)
|
||||
imgs/logo.png binary (excluded: unsupported_ext)
|
||||
```
|
||||
|
||||
五种排除原因对应[文件过滤](../review-rules/#how-files-are-filtered)中的门:
|
||||
|
||||
| 原因 | 修复 |
|
||||
|---|---|
|
||||
| `binary` | 无需处理——二进制文件无可评审文本。 |
|
||||
| `user_exclude` | 从你的 `exclude` 列表移除该模式。 |
|
||||
| `unsupported_ext` | 把扩展名加入你的 `include` 列表以绕过白名单门。 |
|
||||
| `default_path` | 把文件加入 `include`——那会覆盖内置测试文件排除模式。 |
|
||||
| `deleted` | 无需处理——没有新内容可评审。 |
|
||||
|
||||
### 我的自定义规则没触发
|
||||
|
||||
运行 `ocr rules check <file-path>`。它会完整打印匹配的**层**与 **glob 模式**:
|
||||
|
||||
```
|
||||
File: src/api/UserHandler.go
|
||||
Source: Project (.opencodereview/rule.json)
|
||||
Pattern: src/api/**/*.go
|
||||
Rule: …
|
||||
```
|
||||
|
||||
若层不对(如期望项目规则却显示 "System built-in"),多半是**声明顺序**问题——
|
||||
首条匹配模式生效。把更具体的规则在 `rules` 数组里前移,或修正 glob。
|
||||
|
||||
### 花括号展开不工作
|
||||
|
||||
`bmatcuk/doublestar/v4` 支持 `{ts,tsx}` 花括号。若不匹配,检查多余空格——
|
||||
`{ts, tsx}` 带空格会静默地无法匹配 `tsx`。
|
||||
|
||||
## 评审
|
||||
|
||||
### 某文件显示零评论——它真的被评审了吗?
|
||||
|
||||
打开[会话查看器](../viewer/)(`ocr viewer`),找到会话,看该文件的
|
||||
`main_task` 泳道:
|
||||
|
||||
- 有工具调用 + 以 `task_done` 结束 → 干净评审。
|
||||
- 有工具调用 + 循环中途结束 → 找错误卡片。
|
||||
- 完全没有 `main_task` 卡片 → 文件评审前被过滤;见上方[过滤与规则](#filtering--rules)。
|
||||
|
||||
### 评论的 `start_line: 0` 和 `end_line: 0`
|
||||
|
||||
OCR 无法把评论锚定到 diff 中的精确行。两个常见原因:
|
||||
|
||||
- 模型改写了 `existing_code` 而非从 diff 原样复制。模型被告知不要这样做,但偶尔
|
||||
仍会如此。
|
||||
- diff 有异常格式(CRLF、tab/空格混用)破坏了滑动窗口匹配。
|
||||
|
||||
评论仍是真实的——只是没被自动放置。多数 agent 集成(SKILL、Claude Code
|
||||
plugin)读 `existing_code` 字段并自行在文件中定位。
|
||||
|
||||
### Token threshold exceeded
|
||||
|
||||
```
|
||||
[ocr] WARNING: prompt tokens (94000) exceed 80% of max_tokens(58888) for src/big.sql
|
||||
```
|
||||
|
||||
该文件的初始 prompt(规则 + diff + change-files 列表)在模型能响应之前就已超过
|
||||
`MAX_TOKENS = 58888` 的 80 %。OCR 跳过该文件并继续——JSON 模式下你也会在
|
||||
`warnings` 中看到。
|
||||
|
||||
缓解:
|
||||
|
||||
- 若是自动生成的,把文件加入 `exclude` 列表。
|
||||
- 把大重构拆成更小的 commit。
|
||||
- 对一系列小 commit 用 `--commit` 模式,而非一次性工作区模式评审。
|
||||
|
||||
### plan 阶段花了很久而文件很小
|
||||
|
||||
先运行 `ocr review --preview`。若文件的 `lines.changed` 超过
|
||||
`PLAN_MODE_LINE_THRESHOLD`(默认 **50**),plan 阶段会运行。这是有意为之——大
|
||||
diff 能从 plan 中受益。要为单次评审跳过它,用更小 diff 运行,或临时编辑内嵌模板
|
||||
(高级;需覆盖 `--tools`)。
|
||||
|
||||
### "Max tool requests reached"
|
||||
|
||||
```
|
||||
[ocr] Max tool requests reached for src/foo.go.
|
||||
```
|
||||
|
||||
模型花了 30(`MAX_TOOL_REQUEST_TIMES`)轮工具调用却没调 `task_done`。到那时为
|
||||
止发出的评论仍被收集并渲染。若多数文件都这样,问题通常是:
|
||||
|
||||
- 模型不擅长遵循“完成后调 `task_done`”指令。换更强模型(如 Claude Opus)。
|
||||
- 某工具持续报错而模型持续重试。看会话 JSONL——若同一工具结果重复,即是原因。
|
||||
- 文件确实大或上下文重,30 轮不够。用 `--max-tools <n>` 调高或调低
|
||||
(如 `--max-tools 40` 更多,`--max-tools 15` 更少)。1–9 会被上调到 10;
|
||||
`0`(默认)用模板默认 30。
|
||||
|
||||
### 一些子 agent 失败;运行仍以 0 退出
|
||||
|
||||
有意为之。OCR 隔离 per-file 失败,使一个有问题的文件不会拖垮 20 文件的评审。只要*有*
|
||||
成功的,聚合退出码就是 `0`;仅当完全失败(零成功子 agent)才非零退出。查看 JSON
|
||||
模式的 `warnings` 数组或文本模式的 stderr,看哪些文件失败了。
|
||||
|
||||
### CI 运行比本地慢得多
|
||||
|
||||
两个常见原因:
|
||||
|
||||
- **模型速率限制**——限流下 LLM client 退避并重试。调低 `--concurrency`
|
||||
(如 `4`)以免一开始就触限。
|
||||
- **冷缓存**——若 provider 支持 prompt 缓存,部署后首次运行无法受益。同一窗口内
|
||||
后续运行更快。
|
||||
|
||||
## 输出与集成
|
||||
|
||||
### `--audience agent` 仍有进度行
|
||||
|
||||
确认你看到的不是 **stderr**。进度消息偶尔会到 stderr(警告、错误)。`--audience
|
||||
agent` 保证的干净 stdout 是*对解析器友好的*——要屏蔽一切,重定向:
|
||||
`ocr review --audience agent 2>/dev/null`。
|
||||
|
||||
### JSON 输出是 `{ "files_reviewed": 0, "comments": [] }`
|
||||
|
||||
工作区没有合格文件。这是有意为之——显式形状让调用方区分“无可评审内容”与“已评审
|
||||
文件中无发现”。零评论的正常评审产出的是普通空数组 `[]`。
|
||||
|
||||
### 会话 JSONL 在哪?
|
||||
|
||||
```
|
||||
~/.opencodereview/sessions/<path-encoded-repo-path>/<session-id>.jsonl
|
||||
```
|
||||
|
||||
仓库路径通过把 `/` 和 `\` 替换为 `-`、`:` 替换为 `_` 编码
|
||||
(如 `/Users/foo/my-repo` → `Users-foo-my-repo`)。用 `ocr viewer` 浏览会话。
|
||||
删除该目录清除历史;OCR 在下次运行时重新生成编码路径。
|
||||
|
||||
## 性能与成本
|
||||
|
||||
### 怎么知道哪些 token 花了多少?
|
||||
|
||||
启用遥测:
|
||||
|
||||
```bash
|
||||
ocr config set telemetry.enabled true
|
||||
ocr config set telemetry.exporter console
|
||||
ocr review
|
||||
```
|
||||
|
||||
LLM 调用没有自己的 span——它们记为 metric。关注 `ocr.llm.tokens_used`
|
||||
(counter,标 `model` + `type`)、`ocr.llm.requests_total`(counter,标 `model`
|
||||
+ `status`)、`ocr.llm.request_duration_seconds`(histogram,标 `model`)。
|
||||
console exporter 会内联打印这些聚合。如需仪表盘,切换到 OTLP exporter 并发到你的
|
||||
metrics 体系——见[遥测](../telemetry/)。
|
||||
|
||||
### 为什么我的评审这么贵?
|
||||
|
||||
常见因素:
|
||||
|
||||
- 文件 ≥ 50 行时 plan 阶段开启。它每文件多一次 LLM 调用。降低阈值可减少成本;升高
|
||||
阈值可提升小 PR 的速度。
|
||||
- `MAX_TOOL_REQUEST_TIMES = 30` 很宽松。用满轮数的模型会产出比 3 轮就完成的模型
|
||||
更长(更多 token)的对话。更强模型倾向于更快完成。反过来,若你为应对 "max tool
|
||||
requests reached" 用 `--max-tools` 调高,预期每文件成本大致线性增长。
|
||||
- 记忆压缩本身是一次 LLM 调用。较长的子任务除评审轮外,还要为压缩轮付费。
|
||||
|
||||
### 如何减少 LLM 调用?
|
||||
|
||||
- 添加 `include` 列表,使 OCR 不评审你不关心的文件。
|
||||
- 若你的账户有 burst-mode 计价,调低 `--concurrency`。
|
||||
- 传 `--background`——更充分的前期上下文有时能让模型无需 `file_read` /
|
||||
`code_search` 往返即可完成。
|
||||
|
||||
## 隐私与安全
|
||||
|
||||
### OCR 会把我的代码发到别处吗?
|
||||
|
||||
OCR 把你的 **diff**(及可选 read-tool 片段)发到你配置的 LLM 端点。其余任何内容都不
|
||||
离开你的机器——会话 JSONL 与规则文件仅存于本地。
|
||||
|
||||
若启用遥测,`content_logging` 标志已接入配置层但目前**不**控制任何代码路径——
|
||||
无论该标志值如何,prompt 与响应内容绝不导出到你的 collector。请视为保留位。生产
|
||||
环境保持 `false`。详情见[遥测](../telemetry/#content-logging)。
|
||||
|
||||
### 我能在发给 LLM 前脱敏 secret 吗?
|
||||
|
||||
非内置功能。推荐工作流:
|
||||
|
||||
1. 不要把 secret 提交到仓库(常规规则)。
|
||||
2. 把已知含敏感信息的文件加入 `exclude`。
|
||||
3. 用 `git diff --no-textconv` 过滤器或 pre-commit 脱敏,使 secret 不进入 diff。
|
||||
|
||||
“脱敏规则”功能在路线图上;关注
|
||||
[issue 跟踪器](https://github.com/alibaba/open-code-review/issues)。
|
||||
|
||||
## 杂项
|
||||
|
||||
### changelog 在哪?
|
||||
|
||||
[GitHub Releases](https://github.com/alibaba/open-code-review/releases)
|
||||
——每个 release 都附带从 Conventional Commits 生成的 notes。
|
||||
|
||||
### OCR 支持非 Git VCS 吗?
|
||||
|
||||
不支持。diff provider 通过 shell 调用 `git`。SVN / Mercurial 等需要新的 provider;Hg 支持的
|
||||
issue 已在[此](https://github.com/alibaba/open-code-review/issues)开放。
|
||||
|
||||
### 为什么二进制叫 `opencodereview` 而 CLI 是 `ocr`?
|
||||
|
||||
release 中发布的静态二进制以项目命名(`opencodereview`);NPM wrapper 为了便于使用而安装为 `ocr`。从源码构建得到 `dist/opencodereview`——复制为 `$PATH` 上的
|
||||
`ocr`。
|
||||
|
||||
### 如何卸载?
|
||||
|
||||
```bash
|
||||
npm uninstall -g @alibaba-group/open-code-review # NPM install
|
||||
sudo rm /usr/local/bin/ocr # binary install
|
||||
rm -rf ~/.opencodereview # all state
|
||||
```
|
||||
|
||||
OCR 不在 `~/.opencodereview` 之外写入(NPM 下载二进制除外),因此删除该目录即可
|
||||
清除历史、配置与每用户规则。
|
||||
|
||||
## 另见
|
||||
|
||||
- [配置](../configuration/)——LLM 端点解析与 config key。
|
||||
- [评审规则](../review-rules/)——文件过滤器与规则解析链。
|
||||
- [会话查看器](../viewer/)——查看历史评审会话。
|
||||
- [遥测](../telemetry/)——token 用量与 LLM 指标。
|
||||
177
pages/src/content/docs/zh/installation.md
Normal file
177
pages/src/content/docs/zh/installation.md
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
---
|
||||
title: 安装
|
||||
sidebar:
|
||||
order: 4
|
||||
---
|
||||
|
||||
安装 `ocr` CLI 有四种受支持的方式。它们产出的都是同一个二进制——按你的
|
||||
环境选择即可。
|
||||
|
||||
## NPM(推荐)
|
||||
|
||||
```bash
|
||||
npm install -g @alibaba-group/open-code-review
|
||||
```
|
||||
|
||||
NPM 包附带一个小的 wrapper 脚本(`bin/ocr.js`)和一个
|
||||
[postinstall hook](https://github.com/alibaba/open-code-review/blob/main/scripts/install.js),
|
||||
它会:
|
||||
|
||||
1. 探测你的平台(`darwin-amd64`、`darwin-arm64`、`linux-amd64`、
|
||||
`linux-arm64`、`windows-amd64`、`windows-arm64`)。
|
||||
2. 从 GitHub Releases 下载匹配的二进制。
|
||||
3. (当存在校验和数据时)验证它,并放到 wrapper 旁边。
|
||||
|
||||
如果某个平台专属 npm 包(如 `@alibaba-group/ocr-darwin-arm64`)作为
|
||||
optional dependency 被安装,则直接使用该二进制,跳过下载。
|
||||
|
||||
运行 `ocr` 时,wrapper 只是 `exec` 下载好的二进制,因此首次运行后实际开销
|
||||
为零。
|
||||
|
||||
### 更新
|
||||
|
||||
```bash
|
||||
npm update -g @alibaba-group/open-code-review
|
||||
# 或固定到某个版本:
|
||||
npm install -g @alibaba-group/open-code-review@<version>
|
||||
```
|
||||
|
||||
### 卸载
|
||||
|
||||
```bash
|
||||
npm uninstall -g @alibaba-group/open-code-review
|
||||
```
|
||||
|
||||
## GitHub Release 二进制
|
||||
|
||||
如果你不想装 Node.js,可直接从
|
||||
[releases 页面](https://github.com/alibaba/open-code-review/releases)获取
|
||||
静态二进制:
|
||||
|
||||
```bash
|
||||
# macOS (Apple Silicon)
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-darwin-arm64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# macOS (Intel)
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-darwin-amd64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Linux x86_64
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-linux-amd64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Linux ARM64
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-linux-arm64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Windows (AMD64)
|
||||
curl -Lo ocr.exe https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-windows-amd64.exe
|
||||
|
||||
# Windows (ARM64)
|
||||
curl -Lo ocr.exe https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-windows-arm64.exe
|
||||
```
|
||||
|
||||
每个 release 还会在二进制旁发布 `sha256sum.txt`,供你校验完整性:
|
||||
|
||||
```bash
|
||||
curl -LO https://github.com/alibaba/open-code-review/releases/latest/download/sha256sum.txt
|
||||
shasum -a 256 -c sha256sum.txt --ignore-missing
|
||||
```
|
||||
|
||||
## 安装脚本(curl | sh)
|
||||
|
||||
一个便捷安装器,封装了 GitHub Release 二进制下载(带校验)——适合 CI 基础
|
||||
镜像和无界面环境:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/alibaba/open-code-review/main/install.sh | sh
|
||||
```
|
||||
|
||||
它识别两个环境变量:
|
||||
|
||||
| 变量 | 默认值 | 用途 |
|
||||
|---|---|---|
|
||||
| `OCR_INSTALL_DIR` | `/usr/local/bin` | 放置 `ocr` 二进制的位置。 |
|
||||
| `OCR_VERSION` | 最新 release | 固定到某个 release tag(如 `v1.2.3`)。 |
|
||||
|
||||
该脚本支持 `darwin` 与 `linux` 的 `amd64` / `arm64`;Windows 请改用
|
||||
[GitHub Release 二进制](#github-release-binary)或 [NPM](#npm-recommended)
|
||||
方式。
|
||||
|
||||
## 从源码构建
|
||||
|
||||
仅当你要修改 OCR 本身,或在某个没有预编译二进制的平台上运行时才需要此方式。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- [Go ≥ 1.25](https://go.dev/dl/)
|
||||
- [Git](https://git-scm.com/)
|
||||
- [Make](https://www.gnu.org/software/make/)
|
||||
|
||||
### 构建
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alibaba/open-code-review.git
|
||||
cd open-code-review
|
||||
make build # 产出 dist/opencodereview
|
||||
sudo cp dist/opencodereview /usr/local/bin/ocr
|
||||
```
|
||||
|
||||
### 为其他平台构建
|
||||
|
||||
```bash
|
||||
make build-linux-amd64
|
||||
make build-linux-arm64
|
||||
make build-darwin-amd64
|
||||
make build-darwin-arm64
|
||||
make build-windows-amd64 # Windows (x86_64)
|
||||
make build-windows-arm64 # Windows (ARM64)
|
||||
make build-all # 一次性构建全部六个
|
||||
make sha256sum # 同时产出 sha256sum.txt
|
||||
```
|
||||
|
||||
`make dist` 会运行 `clean → build-all → sha256sum`,并在二进制旁写入一个
|
||||
`VERSION` 文件——这正是 release 流水线执行的步骤。
|
||||
|
||||
### 运行测试
|
||||
|
||||
```bash
|
||||
make test # LC_ALL=C go test -v -race -count=1 ./...
|
||||
```
|
||||
|
||||
## 验证安装
|
||||
|
||||
无论二进制来自哪里:
|
||||
|
||||
```bash
|
||||
ocr version # 打印版本 + git commit + 构建日期
|
||||
ocr --help # 顶层用法
|
||||
ocr review --help # 完整的 review 命令参数列表
|
||||
```
|
||||
|
||||
如果出现 "command not found" 错误,请确认安装位置在你的 `$PATH` 上:
|
||||
|
||||
```bash
|
||||
which ocr
|
||||
echo $PATH
|
||||
```
|
||||
|
||||
## OCR 在哪里存放状态
|
||||
|
||||
| 路径 | 存放内容 |
|
||||
|---|---|
|
||||
| `~/.opencodereview/config.json` | LLM 端点、语言、遥测配置(由 `ocr config set` 管理)。 |
|
||||
| `~/.opencodereview/rule.json` | 可选的全局评审规则。 |
|
||||
| `~/.opencodereview/sessions/<encoded-repo-path>/<session-id>.jsonl` | 每次评审会话的流式 JSONL 转录,供 `ocr viewer` 使用。 |
|
||||
| `~/.opencodereview/{last-update-check,update.lock,update-available}` | NPM wrapper 的后台更新检查状态。wrapper 会轮询是否有更新的 release(默认约每 18 分钟一次)并打印升级提示。用 `OCR_NO_UPDATE=1` 禁用,或用 `OCR_UPDATE_INTERVAL`(秒)调整间隔。静态二进制不写入这些文件。 |
|
||||
| `<repo>/.opencodereview/rule.json` | 可选的项目级评审规则——可安全提交。 |
|
||||
|
||||
OCR 永远不会写入 `~/.opencodereview/` 之外(除 NPM 临时下载二进制外)。
|
||||
删除该目录即可完成干净的卸载。
|
||||
|
||||
## 另见
|
||||
|
||||
- [快速开始](../quickstart/)——配置 LLM 并完成首次评审。
|
||||
- [配置](../configuration/)——OCR 接受的每个环境变量与 config key。
|
||||
- [贡献](../contributing/)——从源码构建、跑测试并参与开发。
|
||||
56
pages/src/content/docs/zh/integrations.md
Normal file
56
pages/src/content/docs/zh/integrations.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
title: 集成
|
||||
sidebar:
|
||||
order: 12
|
||||
---
|
||||
|
||||
OCR 是一个 CLI,能与任何能派生进程的环境组合。本节涵盖将其接入 agentic
|
||||
工作流和 CI 的主要方式,每种集成方式一页。
|
||||
|
||||
## 为什么是这些集成?
|
||||
|
||||
OCR 的 `--audience agent` 模式专为被另一个 agent 驱动而设计:stdout 只携带
|
||||
JSON / 最终摘要,无进度 UI。这让三种组合方式顺理成章:
|
||||
|
||||
1. **Agent skill**——把 OCR 注册为调用方 agent 可调用的 skill(如 Anthropic
|
||||
Agent SDK)。
|
||||
2. **Command(Claude Code plugin)**——安装打包的命令,使
|
||||
`/open-code-review:review` 端到端运行 `ocr review`。在任何其他支持
|
||||
Claude-Code 风格命令约定的 agent 中也可用。
|
||||
3. **Direct subprocess**——任何能调 `subprocess.run` 的框架(LangChain 工具、
|
||||
自定义 shell、CI 步骤)直接通过 shell 调用。
|
||||
|
||||
你可以混搭。skill 和 plugin 最终调用的都是同一个二进制。
|
||||
|
||||
## 选择模式
|
||||
|
||||
| 方式 | 最适合 | 页面 |
|
||||
|---|---|---|
|
||||
| Agent skill | 你基于 Anthropic Agent SDK 或其他消费 `SKILL.md` 的框架构建。 | [Agent Skill](agent-skill/) |
|
||||
| Command(Claude Code plugin) | 你用 Claude Code(或任何有 Claude-Code 风格命令约定的 agent),希望 `/open-code-review:review` 做正确的事。 | [Command(Claude Code Plugin)](claude-code/) |
|
||||
| Direct subprocess | 你需要从自定义脚本、LangChain 工具或非 Anthropic agent 调用 OCR。 | [Direct Subprocess](subprocess/) |
|
||||
| CI/CD | 你希望 OCR 在每个 PR 或 pre-commit 时运行。 | [CI/CD](ci/) |
|
||||
|
||||
## MCP 怎么办?
|
||||
|
||||
OCR 目前不暴露 Model Context Protocol server。预期的集成方式是“agent 调用 CLI”,
|
||||
更简单,且能避免 MCP server 引入的长期运行进程问题。如果你的 agent 平台特别
|
||||
要求 MCP,用一个薄 shim 包裹 CLI——一个 30 行的 Node 脚本暴露单个 `review`
|
||||
工具就够了。
|
||||
|
||||
## 适用于所有模式的提示
|
||||
|
||||
- **始终传 `--audience agent`**,当调用方不是人时。否则进度行会污染待解析的输出。
|
||||
- **有 PR / 需求上下文时始终传 `--background`**。质量提升显著,成本只是一个工具
|
||||
参数。
|
||||
- **CI 中把 `--concurrency` 调低**(`--concurrency 4`)以免触发厂商速率限制。默认 8。
|
||||
- **CI 中优先 `--from origin/main --to HEAD`** 而非 `--commit HEAD`——merge-base
|
||||
计算排除分支切出后落到 `main` 上的无关变更。
|
||||
- **让 `OCR_LLM_TOKEN` 远离 stdout/logs。** OCR 不打印它,但配置不当的 shell
|
||||
可能泄露。使用 CI secret 掩码。
|
||||
|
||||
## 另见
|
||||
|
||||
- [CLI 参考](../cli-reference/)——review 命令的每个参数。
|
||||
- [配置](../configuration/)——环境变量与 config key。
|
||||
- [快速开始](../quickstart/)——首次评审的最小化设置。
|
||||
102
pages/src/content/docs/zh/integrations/agent-skill.md
Normal file
102
pages/src/content/docs/zh/integrations/agent-skill.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
---
|
||||
title: Agent Skill
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
把 OCR 注册为可调用的 skill,使 agent 框架能以正确的参数、前置检查与分级标准
|
||||
调用它——无需你在调用侧重新推导这些。
|
||||
|
||||
## 仓库里有什么
|
||||
|
||||
仓库在
|
||||
[`skills/open-code-review/SKILL.md`](https://github.com/alibaba/open-code-review/blob/main/skills/open-code-review/SKILL.md)
|
||||
提供 SKILL manifest。它把 OCR 声明为可调用 skill,含前置检查、调用工作流与
|
||||
评论分级标准(High/Medium/Low)。
|
||||
|
||||
## 安装
|
||||
|
||||
### 方式 1:`npx skills add`(推荐)
|
||||
|
||||
在希望 skill 可用的项目内运行:
|
||||
|
||||
```bash
|
||||
npx skills add alibaba/open-code-review --skill open-code-review
|
||||
```
|
||||
|
||||
这从
|
||||
[skills registry](https://github.com/alibaba/open-code-review/blob/main/skills/open-code-review/SKILL.md)
|
||||
拉取 manifest 并放入项目,使任何尊重 skills 约定的编码 agent 在下次调用时加载
|
||||
它。重新运行该命令以更新 skill 到最新版本。
|
||||
|
||||
> **前置条件:** 首次运行时 skill 会自行安装 `ocr` CLI
|
||||
> (通过 `npm install -g @alibaba-group/open-code-review`),前提是二进制不在
|
||||
> `PATH` 上——见[skill 做什么](#what-the-skill-does)。你**确实**需要预先配置好
|
||||
> LLM;skill 无法替你完成,会停下来询问。见[配置](../../configuration/)。
|
||||
|
||||
### 方式 2:手动复制(系统级)
|
||||
|
||||
若想全局安装 skill 而非按项目,把文件夹复制进你的 skills 目录:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.claude/skills
|
||||
cp -R /path/to/open-code-review/skills/open-code-review ~/.claude/skills/
|
||||
```
|
||||
|
||||
这使 skill 在机器上每个项目可用。
|
||||
|
||||
## skill 做什么
|
||||
|
||||
SKILL.md 是一个 prompt:当调用方 agent 加载它时,由 agent 自身执行步骤。一次
|
||||
完整的 `/open-code-review`(或等价)请求流程如下展开:
|
||||
|
||||
1. **前置检查。** 运行 `which ocr` 确认 CLI 在 `PATH` 上,再 `ocr llm test`
|
||||
确认 LLM 可达。
|
||||
2. **CLI 缺失则自动安装。** 若 `which ocr` 报告 "NOT INSTALLED",agent 运行
|
||||
`npm install -g @alibaba-group/open-code-review` 并继续。不提示用户——这被视为
|
||||
常规设置步骤。
|
||||
3. **无 LLM 配置则停下询问。** 若 `ocr llm test` 失败,agent *不会* 编造凭证。
|
||||
它向用户展示两种受支持的方式(环境变量或 `ocr config set …`)并等待用户提供
|
||||
API key。
|
||||
4. **提取业务上下文。** 检查评审目标(commit、分支、工作副本)并生成一个简短的
|
||||
`--background` 字符串。
|
||||
5. **运行评审。** 调用
|
||||
`ocr review --audience agent --background "…" [--commit | --from/--to]`,
|
||||
根据用户是要评审工作副本、特定 commit 还是分支区间来选择参数。
|
||||
6. **分类与报告。** 用 SKILL.md 中的标准把 JSON 评论分为 **High** /
|
||||
**Medium** / **Low**(bug 与安全问题为 High;吹毛求疵与疑似误报被静默丢弃),
|
||||
再渲染 Markdown 摘要。
|
||||
7. **按需修复。** 若用户说“评审**并**修复”(或类似),对 High/Medium 项内联
|
||||
应用安全修复;否则修改代码前先询问。
|
||||
|
||||
完整 prompt——包括确切分级标准、输出模板与注意事项——位于
|
||||
[`skills/open-code-review/SKILL.md`](https://github.com/alibaba/open-code-review/blob/main/skills/open-code-review/SKILL.md)。
|
||||
如想收紧上述任一项(比如把默认行为改为修复前总先询问),编辑你本地副本。
|
||||
|
||||
## Anthropic Agent SDK
|
||||
|
||||
把你的 SDK init 指向已安装的 skill 路径:
|
||||
|
||||
```python
|
||||
from anthropic_agent_sdk import Agent
|
||||
|
||||
agent = Agent(
|
||||
skill_paths=["/path/to/open-code-review/skills/open-code-review"],
|
||||
)
|
||||
|
||||
agent.run("Review my staged changes — focus on race conditions.")
|
||||
```
|
||||
|
||||
SDK 加载 SKILL.md prompt,由 agent 执行[skill 做什么](#what-the-skill-does)中
|
||||
所述工作流——包括 `npm install` 回退与无 LLM 配置时提示输入凭证的步骤。
|
||||
|
||||
## 其他 agent 框架
|
||||
|
||||
任何有“注册外部 skill”接口的框架都能摄入 SKILL.md——它只是带 frontmatter 的
|
||||
markdown。若你的框架期望不同 schema,markdown 正文仍可用作 prompt 模板。
|
||||
|
||||
## 另见
|
||||
|
||||
- [Command(Claude Code Plugin)](../claude-code/)——同一 skill 的
|
||||
slash-command 版本。
|
||||
- [Direct Subprocess](../subprocess/)——绕过 manifest,自行调用 CLI。
|
||||
406
pages/src/content/docs/zh/integrations/ci.md
Normal file
406
pages/src/content/docs/zh/integrations/ci.md
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
---
|
||||
title: CI/CD
|
||||
sidebar:
|
||||
order: 4
|
||||
---
|
||||
|
||||
在每个 Pull Request 或 Merge Request 上运行 OCR。上游仓库提供两条现成流水线,
|
||||
你复制并配置即可——一条 GitHub Actions,一条 GitLab CI。两者都是
|
||||
[Direct Subprocess](../subprocess/)中核心命令的薄包装。
|
||||
|
||||
## CI/CD 集成如何工作
|
||||
|
||||
本页每条配方都遵循同一模式——下面的 GitHub Actions 与 GitLab CI 章节只是它的
|
||||
具体实现:
|
||||
|
||||
1. **在 PR / MR 事件上触发。** 新建 pull request、更新的 merge request,或手动
|
||||
`/open-code-review` 评论触发作业。
|
||||
2. **在 runner 中安装 `ocr`**,通常是
|
||||
`npm install -g @alibaba-group/open-code-review`。runner 是临时的,因此每次
|
||||
运行都发生。
|
||||
3. **从 CI secret 经 `ocr config set` 配置 LLM**(端点、token、model)。没有持久
|
||||
的 `~/.opencodereview` 可回退。
|
||||
4. **以区间模式运行评审**,输出机器可读,使 stdout 是干净的 JSON 外壳:
|
||||
|
||||
```bash
|
||||
ocr review \
|
||||
--from "origin/<base-branch>" \
|
||||
--to "origin/<head-branch>" \
|
||||
--format json \
|
||||
--audience agent
|
||||
```
|
||||
|
||||
`--format json` 给出可解析载荷;`--audience agent` 屏蔽进度行。每条配方消费的
|
||||
外壳见 [JSON 结构](../subprocess/#json-shape)。
|
||||
5. **解析 JSON** 并遍历 `comments[]`。
|
||||
6. **通过 provider 的 review API 把评论回贴到 PR / MR。** 无有效行信息的条目
|
||||
(文件级发现)合并到摘要备注而非内联张贴;若内联批量 API 拒绝请求,张贴步骤也
|
||||
回退为普通摘要评论。
|
||||
|
||||
始终涉及两类凭据:OCR 用来生成发现的 **LLM 凭据**,以及张贴步骤用来回贴评论的
|
||||
**PR/MR 写 token**。GitHub 配方通过 `GITHUB_TOKEN` 自动提供后者;GitLab 建议显式
|
||||
配置 `GITLAB_API_TOKEN`,但对 fork MR 会回退使用内置 `CI_JOB_TOKEN`(它可通过
|
||||
`/discussions` 发起讨论)——为可靠性推荐使用专用 token。
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
上游工作流位于
|
||||
[`examples/github_actions/ocr-review.yml`](https://github.com/alibaba/open-code-review/blob/main/examples/github_actions/ocr-review.yml)。
|
||||
|
||||
### 它做什么
|
||||
|
||||
- 在 `pull_request_target`(`opened`)**和** `issue_comment` 事件上触发,后者正文
|
||||
以 `/open-code-review` 或 `@open-code-review` 开头——后者让评审者通过在 PR 上
|
||||
评论按需重跑 OCR。(用 `pull_request_target` 而非 `pull_request`,使即便从
|
||||
fork 提交的 PR 也能用上 secret;OCR 只读 diff,不执行 PR 中的代码。)
|
||||
- 通过 `npm install -g @alibaba-group/open-code-review` 安装 OCR,用
|
||||
`ocr config set` 写配置,再以分支区间模式运行核心命令。
|
||||
- 解析 JSON 外壳并通过 GitHub Pull Request Review API 把每条发现作为内联评审评论
|
||||
张贴。无行信息的评论合并到摘要正文。若批量提交失败,回退为逐条张贴,并在摘要
|
||||
评论中呈现统计。
|
||||
|
||||
### 安装
|
||||
|
||||
把工作流放进你的仓库:
|
||||
|
||||
```bash
|
||||
mkdir -p .github/workflows
|
||||
curl -o .github/workflows/ocr-review.yml \
|
||||
https://raw.githubusercontent.com/alibaba/open-code-review/main/examples/github_actions/ocr-review.yml
|
||||
```
|
||||
|
||||
### 必需 secret
|
||||
|
||||
在 **Settings → Secrets and variables → Actions** 下设置:
|
||||
|
||||
| Secret | 必需 | 说明 |
|
||||
|---|---|---|
|
||||
| `OCR_LLM_URL` | 是 | LLM API 端点(如 `https://api.openai.com/v1/chat/completions`)。 |
|
||||
| `OCR_LLM_AUTH_TOKEN` | 是 | LLM API 的认证 token。此 CI secret 传给 `ocr config set llm.auth_token`。(OCR 的直接环境变量是 `OCR_LLM_TOKEN`,不是 `OCR_LLM_AUTH_TOKEN`。) |
|
||||
| `OCR_LLM_MODEL` | 否 | 模型名。无默认——必须显式设置。 |
|
||||
| `OCR_LLM_USE_ANTHROPIC` | 否 | Anthropic Claude 模型设为 `true`。 |
|
||||
|
||||
`GITHUB_TOKEN` 自动提供;工作流声明 `pull-requests: write` 以便张贴评审评论。
|
||||
|
||||
> 工作流启动时还会运行
|
||||
> `ocr config set llm.extra_body '{"thinking": {"type": "disabled"}}'`,
|
||||
> 为不支持该字段的 LLM provider 关闭 thinking-mode 请求。若你的 provider 需保留
|
||||
> thinking-mode,删除该行。
|
||||
|
||||
### 定制
|
||||
|
||||
以下都是对你刚复制的工作流文件
|
||||
(`.github/workflows/ocr-review.yml`)的编辑。
|
||||
|
||||
#### 背景上下文
|
||||
|
||||
`--background` 是效果最显著的单一参数——见
|
||||
[适用于所有模式的提示](../#tips-that-apply-to-every-pattern)。
|
||||
传入 PR 标题(当标题遵循 `feat(auth): add OAuth2 support` 这样的语义约定时,效果
|
||||
更好):
|
||||
|
||||
```yaml
|
||||
- name: Run OCR review
|
||||
run: |
|
||||
ocr review \
|
||||
--background "${{ github.event.pull_request.title }}" \
|
||||
--from "origin/${{ github.base_ref }}" \
|
||||
--to "origin/${{ github.head_ref }}" \
|
||||
--format json --audience agent
|
||||
```
|
||||
|
||||
#### 自定义规则
|
||||
|
||||
用 `--rule` 传入项目专属规则文件:
|
||||
|
||||
```yaml
|
||||
- name: Run OCR review
|
||||
run: |
|
||||
ocr review --rule ./my-rules.json \
|
||||
--from "origin/${{ github.base_ref }}" \
|
||||
--to "origin/${{ github.head_ref }}"
|
||||
```
|
||||
|
||||
schema 见[评审规则](../../review-rules/)。
|
||||
|
||||
#### 并发
|
||||
|
||||
默认 8 个并行 per-file 子 agent。大 PR 上调低,以免触发 LLM provider 速率限制:
|
||||
|
||||
```yaml
|
||||
- name: Run OCR review
|
||||
run: |
|
||||
ocr review --concurrency 5 \
|
||||
--from "origin/${{ github.base_ref }}" \
|
||||
--to "origin/${{ github.head_ref }}"
|
||||
```
|
||||
|
||||
#### 触发模式
|
||||
|
||||
默认工作流在 PR **opened** 时以及以 `/open-code-review` 或
|
||||
`@open-code-review` 开头的 PR 评论时触发。两种常见调整:
|
||||
|
||||
在更多 PR 生命周期事件上运行(如推送新 commit 时复审):
|
||||
|
||||
```yaml
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
```
|
||||
|
||||
使用不同评论关键字:
|
||||
|
||||
```yaml
|
||||
if: |
|
||||
github.event_name == 'pull_request' ||
|
||||
(github.event_name == 'issue_comment'
|
||||
&& github.event.issue.pull_request
|
||||
&& startsWith(github.event.comment.body, '/review'))
|
||||
```
|
||||
|
||||
`github.event.issue.pull_request` 检查确保评论在 PR 上而非普通 issue 上。
|
||||
|
||||
#### 固定 OCR 版本
|
||||
|
||||
默认工作流安装最新发布版本。固定:
|
||||
|
||||
```yaml
|
||||
- name: Install OpenCodeReview
|
||||
run: npm install -g @alibaba-group/open-code-review@1.0.0
|
||||
```
|
||||
|
||||
#### 以 GitHub App 身份发布
|
||||
|
||||
默认评审评论来自 `github-actions[bot]`。要以 `OpenCodeReview Bot` 这类自定义品牌的 bot 发布,把 `GITHUB_TOKEN` 换成 GitHub App installation token。
|
||||
|
||||
1. 在 *Settings → Developer settings → GitHub Apps → New GitHub App* **创建
|
||||
app**。禁用 webhook(此用例不需要)。在 *Repository permissions* 授予:
|
||||
- **Pull requests**:Read and write
|
||||
- **Contents**:Read-only(用于取 diff)
|
||||
- **Metadata**:Read-only(必需)
|
||||
|
||||
2. 从 app 设置页**生成私钥**并下载 `.pem` 文件。记下同页的 **App ID**。
|
||||
|
||||
3. 把 app **安装**到你想 OCR 评审的仓库。Installation ID 出现在安装后 URL 中,
|
||||
如 `https://github.com/settings/installations/12345` → ID 为 `12345`。
|
||||
|
||||
4. 在 *Settings → Secrets and variables → Actions* 下**添加三个 secret**:
|
||||
|
||||
| Secret | 值 |
|
||||
|---|---|
|
||||
| `GITHUB_APP_ID` | App ID。 |
|
||||
| `GITHUB_APP_PRIVATE_KEY` | `.pem` 文件全部内容,含 `-----BEGIN RSA PRIVATE KEY-----` 与 `-----END RSA PRIVATE KEY-----` 行。 |
|
||||
| `GITHUB_APP_INSTALLATION_ID` | Installation ID。 |
|
||||
|
||||
5. 在评论张贴步骤中**生成并使用 token**:
|
||||
|
||||
```yaml
|
||||
- name: Get GitHub App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.GITHUB_APP_ID }}
|
||||
private-key: ${{ secrets.GITHUB_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Post review comments to PR
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
# ...existing post script...
|
||||
```
|
||||
|
||||
评审现在会以你 app 的名字而非 `github-actions[bot]` 发布。
|
||||
|
||||
### 故障排查
|
||||
|
||||
| 症状 | 原因 / 修复 |
|
||||
|---|---|
|
||||
| `Cannot find merge-base` | checkout 步骤用了浅克隆,但区间模式评审需要完整历史。上游工作流在 `actions/checkout` 上设 `fetch-depth: 0`——编辑文件时保留该设置。 |
|
||||
| `Failed to parse OCR output` | `OCR_LLM_URL` 或 `OCR_LLM_AUTH_TOKEN` 缺失或错误。在 *Settings → Secrets and variables → Actions* 下复查值。 |
|
||||
| 评审评论落到错误行 | 通常意味着评审开始到评论张贴之间 diff 发生了偏移。张贴脚本此时回退为普通 issue 评论——无需处理。 |
|
||||
|
||||
> **注意。** `OCR_DEBUG` 环境变量目前在 OCR 中**未实现**——设置
|
||||
> `OCR_DEBUG: "1"` 无效。此处记录以备将来接入。当前若需详细输出,可检查工作流写
|
||||
> 到 `/tmp/ocr-result.json` 和 `/tmp/ocr-stderr.log` 的原始评审 JSON 和 stderr
|
||||
> (见下方故障排查),或本地运行 `ocr review`。
|
||||
|
||||
## GitLab CI
|
||||
|
||||
上游流水线位于
|
||||
[`examples/gitlab_ci/.gitlab-ci.yml`](https://github.com/alibaba/open-code-review/blob/main/examples/gitlab_ci/.gitlab-ci.yml)。
|
||||
|
||||
### 它做什么
|
||||
|
||||
- 在 `merge_requests` 事件上触发(所有 MR 事件——创建、更新、重开)。
|
||||
- 在 `node:20` 镜像中运行,安装 OCR,通过 `ocr config set` 配置,再以 MR diff 模式
|
||||
运行核心命令。
|
||||
- 用内联 Python 脚本解析 JSON 外壳,把每条发现作为 GitLab Discussion(在 diff
|
||||
上内联)张贴,用 MR 的 `versions` 端点计算正确的 `base_sha` / `start_sha` /
|
||||
`head_sha` 以精确定位。对无法内联张贴的评论回退为普通 MR note,并以摘要 note
|
||||
收尾。
|
||||
|
||||
### 安装
|
||||
|
||||
把流水线放进仓库根:
|
||||
|
||||
```bash
|
||||
curl -o .gitlab-ci.yml \
|
||||
https://raw.githubusercontent.com/alibaba/open-code-review/main/examples/gitlab_ci/.gitlab-ci.yml
|
||||
```
|
||||
|
||||
若已有 `.gitlab-ci.yml` 并想保留,把配方放到其他路径并用 `include:`
|
||||
引入:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- local: 'ci/ocr-review.gitlab-ci.yml'
|
||||
```
|
||||
|
||||
### 必需 CI/CD 变量
|
||||
|
||||
在 **Settings → CI/CD → Variables** 下设置:
|
||||
|
||||
| 变量 | 必需 | 掩码 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `OCR_LLM_URL` | 是 | 否 | LLM API 端点 URL。 |
|
||||
| `OCR_LLM_AUTH_TOKEN` | 是 | 是 | API 认证 token。此 CI 变量传给 `ocr config set llm.auth_token`。(OCR 的直接环境变量是 `OCR_LLM_TOKEN`,不是 `OCR_LLM_AUTH_TOKEN`。) |
|
||||
| `OCR_LLM_MODEL` | 否 | 否 | 模型名。无默认——必须显式设置。 |
|
||||
| `GITLAB_API_TOKEN` | 否 | 是 | 带 `api` scope 的 project / personal / group access token。可选——缺失时回退使用内置 `CI_JOB_TOKEN`(如对 fork MR)。为可靠性推荐专用 `GITLAB_API_TOKEN`。 |
|
||||
|
||||
> GitLab 拒绝短于 8 字符的变量,因此流水线中 `llm.use_anthropic` 硬编码为
|
||||
> `false`。要用 Anthropic Claude 模型,直接编辑脚本。
|
||||
|
||||
> 流水线启动时还会运行
|
||||
> `ocr config set llm.extra_body '{"thinking": {"type": "disabled"}}'`,
|
||||
> 为不支持该字段的 LLM provider 关闭 thinking-mode 请求。若你的 provider 需保留
|
||||
> thinking-mode,删除该行。
|
||||
|
||||
> **快速 bot 命名提示。** 对 Project Access Token 和 Group Access Token,
|
||||
> token 的**名字**会出现在 MR 讨论旁。把 token 命名为 `OpenCodeReview Bot`,
|
||||
> 即可让评审讨论带上品牌名,无需额外设置——当你不需要
|
||||
> [以服务账号身份发布](#post-under-a-service-account-identity)中记录的更持久
|
||||
> 服务账号设置时很方便。
|
||||
|
||||
### 定制
|
||||
|
||||
以下都是对你刚复制的 `.gitlab-ci.yml` 的编辑。
|
||||
|
||||
#### 背景上下文
|
||||
|
||||
把 MR 标题传给 `--background`——当标题遵循 `feat(auth): add OAuth2 support`
|
||||
这样的语义约定时,效果更好:
|
||||
|
||||
```yaml
|
||||
script:
|
||||
- |
|
||||
ocr review \
|
||||
--background "$CI_MERGE_REQUEST_TITLE" \
|
||||
--from "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" \
|
||||
--to "${CI_COMMIT_SHA}" \
|
||||
--format json --audience agent
|
||||
```
|
||||
|
||||
#### 自定义规则与并发
|
||||
|
||||
与 GitHub Actions 配方相同的参数——`--rule` 传项目专属规则文件,
|
||||
`--concurrency` 限制并行子 agent(默认 8):
|
||||
|
||||
```yaml
|
||||
script:
|
||||
- |
|
||||
ocr review --rule ./my-rules.json --concurrency 5 \
|
||||
--from "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" \
|
||||
--to "${CI_COMMIT_SHA}"
|
||||
```
|
||||
|
||||
规则 schema 见[评审规则](../../review-rules/)。
|
||||
|
||||
#### 固定 OCR 版本
|
||||
|
||||
```yaml
|
||||
script:
|
||||
- npm install -g @alibaba-group/open-code-review@1.0.0
|
||||
```
|
||||
|
||||
#### 避免每次推送都复审
|
||||
|
||||
`only: [merge_requests]` 在**每次** MR 更新时触发,对长生命周期 MR 会消耗大量
|
||||
LLM token。GitLab 无原生“仅在创建时”事件,因此推荐模式是运行评审前检测已有
|
||||
OCR note,若有则跳过。把 `ocr review` 调用替换为 Python wrapper:
|
||||
|
||||
```python
|
||||
import json, os, sys, urllib.request
|
||||
|
||||
GITLAB_URL = os.environ.get("CI_SERVER_URL", "https://gitlab.com")
|
||||
PROJECT_ID = os.environ["CI_PROJECT_ID"]
|
||||
MR_IID = os.environ["CI_MERGE_REQUEST_IID"]
|
||||
API_TOKEN = os.environ["GITLAB_API_TOKEN"]
|
||||
|
||||
url = (
|
||||
f"{GITLAB_URL}/api/v4/projects/{PROJECT_ID}"
|
||||
f"/merge_requests/{MR_IID}/notes?per_page=100"
|
||||
)
|
||||
req = urllib.request.Request(url, headers={"PRIVATE-TOKEN": API_TOKEN})
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
notes = json.loads(resp.read().decode())
|
||||
|
||||
if any("OpenCodeReview" in n.get("body", "") for n in notes):
|
||||
print("OCR already reviewed this MR. Skipping to save tokens.")
|
||||
sys.exit(0)
|
||||
|
||||
# ...otherwise call `ocr review ...` as usual and write the JSON to
|
||||
# the file the posting step expects.
|
||||
```
|
||||
|
||||
要在此之后强制复审,从 MR 删除之前的 OCR note——下次流水线运行会看不到 OCR
|
||||
note,便会继续。
|
||||
|
||||
#### 自托管 GitLab
|
||||
|
||||
无需改代码。张贴脚本读 `CI_SERVER_URL`(GitLab 在每个 runner 上自动设置),
|
||||
因此开箱即可与你自己的实例通信。只需确保 `GITLAB_API_TOKEN` 由你的自托管实例签发,
|
||||
而非 `gitlab.com`。
|
||||
|
||||
#### 以服务账号身份发布
|
||||
|
||||
默认评审讨论出现在 `GITLAB_API_TOKEN` 所属用户名下。改用项目级服务账号,即可获得
|
||||
`OpenCodeReview Bot` 这类自定义品牌的 bot 身份。
|
||||
|
||||
1. 在 *Project → Settings → Service Accounts → New service account* **创建服务
|
||||
账号**。你选的名字(如 `OpenCodeReview Bot`)会出现在 MR 讨论旁。
|
||||
|
||||
2. 在 *Settings → Members → Invite member* **邀请它到项目**。搜索服务账号名并
|
||||
分配 `Developer` 或 `Maintainer`——两者都有张贴讨论所需权限。
|
||||
|
||||
3. 在 *Settings → Service Accounts →(该账号)→ Add new token* **签发 access
|
||||
token**。所需 scope:`api`。立即复制 token——GitLab 只显示一次。
|
||||
|
||||
4. 在 *Settings → CI/CD → Variables* **替换 token 值**——用服务账号的 token
|
||||
替换现有 `GITLAB_API_TOKEN` 值(变量名保持不变)。
|
||||
|
||||
讨论现在以服务账号名而非最初创建 token 的用户名发布。
|
||||
|
||||
### 故障排查
|
||||
|
||||
| 症状 | 原因 / 修复 |
|
||||
|---|---|
|
||||
| `Cannot find merge-base` | runner 用了浅克隆。上游流水线设 `GIT_DEPTH: 0` 强制完整克隆——编辑文件时保留该设置。 |
|
||||
| 张贴时 `API error 403` | `GITLAB_API_TOKEN` 缺 `api` scope、不是项目成员,或——自托管时——由不同实例签发。以 `api` scope 重签并在 *Settings → CI/CD → Variables* 下重新添加。 |
|
||||
| `Failed to parse OCR output` | `OCR_LLM_URL` 或 `OCR_LLM_AUTH_TOKEN` 错误。在 *Settings → CI/CD → Variables* 下复查值。 |
|
||||
| 内联评论落到错误行 | GitLab 内联讨论要求精确 SHA 匹配;张贴脚本取 `versions` 元数据以得到正确的 `base_sha` / `start_sha` / `head_sha`。若某条发现仍无法锚定,回退为普通 MR note。 |
|
||||
|
||||
流水线把原始评审 JSON 写到 `/tmp/ocr-result.json`,stderr 写到
|
||||
`/tmp/ocr-stderr.log`。可在 debug 步骤中 cat 它们,检查 OCR 返回了什么:
|
||||
|
||||
```yaml
|
||||
script:
|
||||
- cat /tmp/ocr-result.json
|
||||
- cat /tmp/ocr-stderr.log
|
||||
```
|
||||
|
||||
## 另见
|
||||
|
||||
- [Direct Subprocess](../subprocess/)——两条流水线消费的 JSON 结构,从头写
|
||||
CI 脚本时有用。
|
||||
- [配置](../../configuration/)——OCR 接受的每个环境变量与 config key。
|
||||
102
pages/src/content/docs/zh/integrations/claude-code.md
Normal file
102
pages/src/content/docs/zh/integrations/claude-code.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
---
|
||||
title: Command(Claude Code Plugin)
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
安装打包的命令,使 OCR 在 [Claude Code](https://docs.anthropic.com/en/docs/claude-code)
|
||||
内端到端运行——评审 diff、分类发现,并自动应用值得采纳的修复。
|
||||
|
||||
## 仓库里有什么
|
||||
|
||||
仓库在
|
||||
[`plugins/open-code-review/`](https://github.com/alibaba/open-code-review/tree/main/plugins/open-code-review)
|
||||
下提供 Claude Code plugin。命令 prompt 本体位于
|
||||
[`plugins/open-code-review/commands/review.md`](https://github.com/alibaba/open-code-review/blob/main/plugins/open-code-review/commands/review.md),
|
||||
是下述工作流的权威依据。
|
||||
|
||||
## 安装
|
||||
|
||||
### 方式 1:plugin marketplace(推荐)
|
||||
|
||||
在 **Claude Code 内**运行这两条命令:
|
||||
|
||||
```bash
|
||||
/plugin marketplace add alibaba/open-code-review
|
||||
/plugin install open-code-review@open-code-review
|
||||
```
|
||||
|
||||
这会注册 `/open-code-review:review` slash 命令,并保持可通过 `/plugin` 更新。
|
||||
|
||||
### 方式 2:直接复制命令文件
|
||||
|
||||
若想跳过 plugin marketplace,把命令文件直接放进 `.claude/commands/`。这会注册为
|
||||
`/open-code-review`(无 `:review` 后缀)。
|
||||
|
||||
**项目级**(随仓库提交,团队共享):
|
||||
|
||||
```bash
|
||||
mkdir -p .claude/commands
|
||||
curl -o .claude/commands/open-code-review.md \
|
||||
https://raw.githubusercontent.com/alibaba/open-code-review/main/plugins/open-code-review/commands/review.md
|
||||
```
|
||||
|
||||
**用户级**(机器上每个项目可用):
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.claude/commands
|
||||
curl -o ~/.claude/commands/open-code-review.md \
|
||||
https://raw.githubusercontent.com/alibaba/open-code-review/main/plugins/open-code-review/commands/review.md
|
||||
```
|
||||
|
||||
### 其他支持命令的 agent
|
||||
|
||||
命令文件是带单个 frontmatter 字段的纯 markdown——没有任何 Claude Code 专有
|
||||
内容。如果你的 agent 支持类似的 **command** 约定(从目录加载为可调用命令的
|
||||
markdown prompt),上面的文件复制方法就是安装路径:把 `open-code-review.md`
|
||||
放进你的 agent 读取命令的目录,按你的 agent 调用命令的方式调用它。prompt 正文
|
||||
与 agent 无关——它只告诉模型选哪些 `ocr` 参数以及如何分级输出。
|
||||
|
||||
> **前置条件:** 首次运行时命令会自行安装 `ocr` CLI
|
||||
> (通过 `npm install -g @alibaba-group/open-code-review`),前提是二进制不在
|
||||
> `PATH` 上。你**确实**需要预先配置好 LLM——若 `ocr llm test` 连不上,命令会
|
||||
> 失败。见[配置](../../configuration/)。
|
||||
|
||||
## 使用
|
||||
|
||||
在 Claude Code 中按名调用命令。通过 plugin marketplace 安装的用
|
||||
`/open-code-review:review`,直接复制文件的用 `/open-code-review`:
|
||||
|
||||
```
|
||||
/open-code-review:review
|
||||
/open-code-review:review review this PR against main
|
||||
/open-code-review:review focus on race conditions in commit abc123
|
||||
```
|
||||
|
||||
prompt 解析你的请求并选择正确的 `ocr review` 参数:无参数 → 工作区模式
|
||||
(staged + unstaged + untracked),提到 commit → `--commit`,提到分支区间 →
|
||||
`--from` / `--to`。你也可以直接透传 OCR 参数
|
||||
(如 `/open-code-review:review --commit abc123` 或 `--from main --to feature`)。
|
||||
|
||||
## 命令做什么
|
||||
|
||||
命令 prompt 很短——三步:
|
||||
|
||||
1. **运行评审。** 用从你请求推断的参数调用 `ocr review --audience agent`
|
||||
(描述了需求上下文时加可选 `--background`)。若 `ocr` 二进制不在 `PATH`,
|
||||
命令通过 `npm i -g @alibaba-group/open-code-review` 自动安装并继续。输出在 5
|
||||
分钟超时内捕获。
|
||||
2. **过滤与评估。** 把每条评论分为 **High** / **Medium** / **Low**。低置信
|
||||
(疑似误报、吹毛求疵、缺上下文)评论被静默丢弃;其余展示。
|
||||
3. **修复。** 对值得采纳的 High/Medium 项自动应用修复。与
|
||||
[Agent Skill](../agent-skill/) 不同,此命令**默认自动修复**——它是“评审并
|
||||
清理”工作流的合适选择,而非“给我看 diff”工作流。
|
||||
|
||||
若你想让命令在修改代码前先询问,或收紧分级标准,编辑你本地的 prompt 副本。Claude
|
||||
Code 每次调用都重新读取命令,因此无需重启。
|
||||
|
||||
## 另见
|
||||
|
||||
- [Agent Skill](../agent-skill/)——SDK 级等价物;同一个底层 CLI,不同默认值
|
||||
(修复前先询问)。
|
||||
- [Direct Subprocess](../subprocess/)——绕过 slash 命令,自行调用 CLI。
|
||||
164
pages/src/content/docs/zh/integrations/subprocess.md
Normal file
164
pages/src/content/docs/zh/integrations/subprocess.md
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
---
|
||||
title: Direct Subprocess
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
通过 shell 调用 `ocr` 并解析 JSON。这是最低层的集成路径——本站其他方式最终都归结
|
||||
于它。[Agent Skill](../agent-skill/) 与 [Command](../claude-code/) 方式是告诉
|
||||
调用方 agent 去做这件事的 prompt 模板;[CI/CD](../ci/) 配方是从脚本做同样事情的
|
||||
GitHub Actions 和 GitLab CI 流水线——不涉及编排 agent,只有子进程调用、JSON 解析、
|
||||
把评论回贴到 PR / MR。当你从自定义脚本、LangChain 工具或任何其他尚未覆盖的
|
||||
框架调用 OCR 时,直接用本页。
|
||||
|
||||
## Bash
|
||||
|
||||
```bash
|
||||
result=$(ocr review --format json --audience agent)
|
||||
status=$(echo "$result" | jq -r '.status')
|
||||
total=$(echo "$result" | jq '.comments | length')
|
||||
echo "Status: $status — $total comments"
|
||||
echo "$result" | jq -r '.comments[] | "\(.path):\(.start_line) — \(.content)"'
|
||||
```
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
import json, subprocess
|
||||
|
||||
proc = subprocess.run(
|
||||
["ocr", "review", "--format", "json", "--audience", "agent",
|
||||
"--from", "origin/main", "--to", "HEAD",
|
||||
"--background", pr_description],
|
||||
capture_output=True, text=True, check=True,
|
||||
)
|
||||
data = json.loads(proc.stdout)
|
||||
for c in data["comments"]:
|
||||
if c["start_line"] > 0:
|
||||
post_line_comment(c["path"], c["start_line"], c["content"])
|
||||
```
|
||||
|
||||
## JSON 结构
|
||||
|
||||
OCR 发出单个顶层**对象**(不是裸数组)。下面是一个带一条发现的完整 `success`
|
||||
外壳:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"summary": {
|
||||
"files_reviewed": 1,
|
||||
"comments": 1,
|
||||
"total_tokens": 12770,
|
||||
"input_tokens": 12450,
|
||||
"output_tokens": 320,
|
||||
"elapsed": "9s"
|
||||
},
|
||||
"comments": [
|
||||
{
|
||||
"path": "internal/cache/store.go",
|
||||
"content": "Concurrent map access without a lock — wrap reads and writes with `sync.RWMutex` to avoid a race on the shared cache.",
|
||||
"start_line": 42,
|
||||
"end_line": 47,
|
||||
"existing_code": "func (s *Store) Get(k string) string {\n return s.m[k]\n}",
|
||||
"suggestion_code": "func (s *Store) Get(k string) string {\n s.mu.RLock()\n defer s.mu.RUnlock()\n return s.m[k]\n}",
|
||||
"thinking": "The struct exposes `m map[string]string` without a guarding mutex, and Get/Set are called from concurrent request handlers."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 顶层字段
|
||||
|
||||
| 字段 | 类型 | 总是存在 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `status` | string | 是 | `success`、`completed_with_warnings`、`completed_with_errors`、`skipped` 之一。 |
|
||||
| `message` | string | 否 | 简短人类可读摘要。在空运行或跳过时设置,如 `"No comments generated. Looks good to me."`。 |
|
||||
| `summary` | object | 否 | 运行聚合。完成运行时存在;`skipped` 时省略。字段见下。 |
|
||||
| `comments` | array | 是 | 可能为空。每条评论 schema 见下。 |
|
||||
| `warnings` | array | 否 | 仅当一个或多个子 agent 失败或被跳过时存在。schema 见下。 |
|
||||
|
||||
### summary 结构(`summary`)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `files_reviewed` | int | 通过所有过滤并发给模型的文件数。 |
|
||||
| `comments` | int | 跨所有文件发出的评论总数(与 `comments.length` 一致)。 |
|
||||
| `total_tokens` | int | 运行中每次 LLM 调用的 prompt + completion token 之和。 |
|
||||
| `input_tokens` | int | 各次 LLM 调用的 prompt token(含缓存读 token)。 |
|
||||
| `output_tokens` | int | 各次 LLM 调用的 completion token(含缓存写 token)。 |
|
||||
| `cache_read_tokens` | int | 各次 LLM 调用的缓存读 token 总数。为零时省略(`omitempty`)。 |
|
||||
| `cache_write_tokens` | int | 各次 LLM 调用的缓存写 token 总数。为零时省略(`omitempty`)。 |
|
||||
| `elapsed` | string | 挂钟时长,取整到整秒,由 Go 的 `time.Duration.String()` 格式化(如 `"1m12s"`)。 |
|
||||
|
||||
### 每条评论字段(`comments[]`)
|
||||
|
||||
| 字段 | 类型 | 总是存在 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `path` | string | 是 | 仓库相对文件路径。 |
|
||||
| `content` | string | 是 | 评审评论,Markdown。 |
|
||||
| `start_line` | int | 是 | 受影响范围的首行。值 `< 1` 表示评论无行锚点(文件级)——应把这些合并到摘要中,而非尝试内联张贴。 |
|
||||
| `end_line` | int | 是 | 受影响范围的末行。单行评论时与 `start_line` 相等。 |
|
||||
| `existing_code` | string | 否 | 要被替换的原始代码片段。对于无 diff 的建议性评论则省略。 |
|
||||
| `suggestion_code` | string | 否 | `existing_code` 的提议替换。存在时总是与 `existing_code` 配对。 |
|
||||
| `thinking` | string | 否 | 模型推理轨迹。对分级 / 调试有用;展示给用户前可安全丢弃。 |
|
||||
|
||||
### warnings 结构(`warnings[]`)
|
||||
|
||||
一个跳过或部分文件失败的运行形如:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "completed_with_errors",
|
||||
"message": "Some files could not be reviewed due to errors.",
|
||||
"comments": [],
|
||||
"warnings": [
|
||||
{
|
||||
"file": "src/very_long_file.go",
|
||||
"message": "diff size exceeds 80% of MAX_TOKENS; skipped",
|
||||
"type": "token_threshold_exceeded"
|
||||
},
|
||||
{
|
||||
"file": "src/broken.py",
|
||||
"message": "sub-agent failed: context deadline exceeded",
|
||||
"type": "subtask_error"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `file` | string | 触发警告的文件的仓库相对路径。 |
|
||||
| `message` | string | 简短人类可读描述。 |
|
||||
| `type` | string | 用于过滤的稳定类型。当前发出:`subtask_error`(子 agent 运行失败)和 `token_threshold_exceeded`(diff 对模型来说过大)。 |
|
||||
|
||||
当 `warnings` 含至少一个 `subtask_error` 时,`status` 为
|
||||
`completed_with_errors`;否则为 `completed_with_warnings`。
|
||||
|
||||
### 无 severity / priority 字段
|
||||
|
||||
OCR **不**发出 `severity` 或 `priority` 字段。你在 [Agent Skill](../agent-skill/)
|
||||
和 [Command](../claude-code/) 文档中看到的 High/Medium/Low 分级是调用方 agent
|
||||
收到原始评论后添加的——不要尝试 `jq '.comments[].severity'`,它不存在。
|
||||
|
||||
## 空结果处理
|
||||
|
||||
**没有合格文件**的工作区通过 `status` 报告,以便调用方区分“无变更”与“无发现”:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "skipped",
|
||||
"message": "No supported files changed.",
|
||||
"comments": []
|
||||
}
|
||||
```
|
||||
|
||||
断定“全部干净”之前,始终检查 `status == "skipped"`。
|
||||
|
||||
## 另见
|
||||
|
||||
- [CI/CD](../ci/)——在子进程调用之上构建的现成 GitHub Actions 与 pre-commit
|
||||
配方。
|
||||
- [Agent Skill](../agent-skill/)——当调用方是 Anthropic SDK agent 而非普通
|
||||
脚本时。
|
||||
125
pages/src/content/docs/zh/overview.md
Normal file
125
pages/src/content/docs/zh/overview.md
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
---
|
||||
title: 概览
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
## 什么是 Open Code Review?
|
||||
|
||||
Open Code Review(简称 **OCR**,区别于光学字符识别 Optical Character
|
||||
Recognition)是一个 AI 驱动的代码评审 CLI,以
|
||||
[`@alibaba-group/open-code-review`](https://www.npmjs.com/package/@alibaba-group/open-code-review)
|
||||
NPM 包和独立的 Go 二进制形式发布。CLI 二进制名为 `ocr`。
|
||||
|
||||
只需一条命令(`ocr review`),它会:
|
||||
|
||||
1. 解析 Git diff——工作区、分支区间或单个 commit。
|
||||
2. 结合系统默认规则与用户规则对变更文件进行过滤。
|
||||
3. 为每个变更文件并行启动一个 **per-file 子 agent**。
|
||||
4. 每个子 agent 运行一个 LLM 工具调用循环;对于较大的 diff,可选地先执行
|
||||
**plan 阶段**。
|
||||
5. 模型调用 `code_comment` 记录发现,可选地调用 `file_read`、
|
||||
`code_search`、`file_find`、`file_read_diff` 收集上下文,完成后调用
|
||||
`task_done`。
|
||||
6. OCR 将每条评论解析到精确的行号,对未能精确匹配的评论运行可选的重新定位
|
||||
流程,并打印(或以 JSON 输出)最终列表。
|
||||
|
||||
## 通用 agent 的问题
|
||||
|
||||
如果你用过通用编码 agent(Claude Code 的 Skill、Cursor、Cline 等)做代码
|
||||
评审,很可能遇到过:
|
||||
|
||||
- **覆盖不全**——在较大的变更集上,agent 会悄悄偷工减料,只评审部分文件。
|
||||
- **位置漂移**——评论与它所指的代码对不上;行号和文件路径偏离目标。
|
||||
- **质量不稳定**——自然语言 Skill 难以调试,输出质量随 prompt 的微小改动
|
||||
而波动。
|
||||
|
||||
根本原因:纯语言驱动的架构缺乏对评审流程的 **硬约束**。
|
||||
|
||||
## 核心设计:确定性工程 × agent
|
||||
|
||||
OCR 的核心理念是把 **确定性工程** 与 **agent** 结合——各自做自己最擅长的事。
|
||||
|
||||
### 确定性工程——硬约束
|
||||
|
||||
对于那些 *绝不能出错* 的步骤,由工程逻辑(而非模型)保证正确性:
|
||||
|
||||
- **精确的文件选择**——一个[五重门过滤](../review-rules/#how-files-are-filtered)
|
||||
决定到底评审哪些文件,并提供显式的 `include`/`exclude` 控制。
|
||||
- **智能文件打包**——相关文件(如 `message_en.properties` 与
|
||||
`message_zh.properties`)可以合并为一个评审单元。每个包作为独立上下文交给
|
||||
子 agent 运行——分而治之,在超大变更集上依然稳定,并天然支持并发评审。
|
||||
- **细粒度规则匹配**——评审规则按文件路径匹配,首条匹配生效,让模型的注意力
|
||||
高度聚焦并消除噪声。基于模板的匹配比纯语言驱动的规则引导更稳定。
|
||||
- **外部定位与反思模块**——独立的评论定位
|
||||
([`internal/diff/relocation.go`](https://github.com/alibaba/open-code-review/blob/main/internal/diff/relocation.go))
|
||||
与重新定位流程,系统地提升位置与内容的准确性。
|
||||
|
||||
### Agent——动态决策
|
||||
|
||||
agent 的优势集中在最关键的地方:
|
||||
|
||||
- **场景化调优的 prompt**——针对代码评审场景深度调优的 prompt 模板,在降低 token
|
||||
消耗的同时提升效果(见
|
||||
[`internal/config/template/task_template.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/template/task_template.json))。
|
||||
- **场景化调优的工具集**——从大规模生产数据的工具调用 trace 分析中提炼而来
|
||||
(调用频次分布、单工具重复率、每个工具对整体调用链的影响)。最终得到一套
|
||||
专用 [六工具](../tools/) 集,比通用 agent 工具包更稳定、更可预测。
|
||||
|
||||
## 流水线如何衔接
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start["<b>ocr review --from main --to feature</b>"]
|
||||
S1["<b>1. Resolve LLM endpoint</b><br/>config / env / shell rc"]
|
||||
S2["<b>2. Load diffs from git</b><br/>workspace / commit / range"]
|
||||
S3["<b>3. Filter files</b><br/>binary → user_exclude → user_include<br/>→ ext allowlist → default path"]
|
||||
S4["<b>4. Drop diffs > 80% of MAX_TOKENS</b>"]
|
||||
S5["<b>5. Dispatch per-file sub-agents</b> (concurrent)<br/><br/>For each file:<br/> a. Plan phase (if changed lines ≥ 50)<br/> b. Main loop: LLM → tool calls → … → task_done<br/> c. code_comment results collected (async via worker pool)<br/><br/>Memory compression triggers when context<br/>exceeds 60 % (async) or 80 % (sync) of MAX_TOKENS."]
|
||||
S6["<b>6. Resolve line numbers</b><br/>from <code>existing_code</code> against diffs.<br/>Re-locate via LLM if needed."]
|
||||
S7["<b>7. Emit text or JSON output</b><br/>(and persist session to disk)"]
|
||||
|
||||
Start --> S1 --> S2 --> S3 --> S4 --> S5 --> S6 --> S7
|
||||
```
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
open-code-review/
|
||||
├── cmd/opencodereview/ # CLI 入口:分发、参数、命令
|
||||
├── internal/
|
||||
│ ├── agent/ # 每文件子 agent 循环 + 记忆压缩
|
||||
│ ├── config/
|
||||
│ │ ├── allowlist/ # 默认文件扩展名白名单与排除项
|
||||
│ │ ├── rules/ # 分层规则解析器、系统规则文档
|
||||
│ │ ├── template/ # plan / main / memory_compression prompt
|
||||
│ │ ├── testconnection/ # 内置 `ocr llm test` 任务
|
||||
│ │ └── toolsconfig/ # 发送给模型的工具定义
|
||||
│ ├── diff/ # Git diff 解析、hunk 数学、重新定位
|
||||
│ ├── gitcmd/ # Git 子进程运行器
|
||||
│ ├── llm/ # Anthropic + OpenAI 协议、重试、BPE token
|
||||
│ ├── model/ # diff / 评论 数据结构
|
||||
│ ├── pathutil/ # 路径工具
|
||||
│ ├── release/ # Release notes 生成
|
||||
│ ├── session/ # 每次评审会话的 JSONL 持久化
|
||||
│ ├── stdout/ # `--audience agent` 下可静音的 stdout writer
|
||||
│ ├── suggestdiff/ # 构建 "Apply suggestion" diff
|
||||
│ ├── telemetry/ # OpenTelemetry span、metrics、exporter
|
||||
│ ├── tool/ # 六个内置工具 + 评论收集器
|
||||
│ └── viewer/ # `ocr viewer`——历史会话的本地 Web UI
|
||||
├── pages/ # 基于 React 的营销落地页(独立)
|
||||
├── plugins/ # Claude Code 插件清单 + 命令
|
||||
├── extensions/ # 编辑器扩展(VS Code)
|
||||
├── examples/ # CI 配方(GitHub Actions、GitLab CI)
|
||||
├── skills/ # 通用 agent Skill 清单
|
||||
├── scripts/ # NPM 安装/更新助手、发布脚本
|
||||
├── npm/ # 各平台 optional dependency 包
|
||||
└── bin/ # NPM wrapper,shell 调用二进制
|
||||
```
|
||||
|
||||
## 另见
|
||||
|
||||
- [快速开始](../quickstart/)——安装并完成首次评审。
|
||||
- [架构](../architecture/)——agent 循环、plan 阶段与记忆压缩。
|
||||
- [CLI 参考](../cli-reference/)——每个参数与子命令。
|
||||
- [集成](../integrations/)——从 Claude Code 或任意 agent 调用 OCR。
|
||||
235
pages/src/content/docs/zh/quickstart.md
Normal file
235
pages/src/content/docs/zh/quickstart.md
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
---
|
||||
title: 快速开始
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
安装 OCR,连接到任意支持 Anthropic Messages API 或 OpenAI Chat Completions API
|
||||
的 LLM,然后运行你的第一次代码评审。
|
||||
|
||||
## 前置条件
|
||||
|
||||
- 一个可用的 **Git** 安装——OCR 以子进程方式驱动 Git 读取 diff。
|
||||
- 一个兼容 Anthropic 或 OpenAI 的 LLM 的 **API key**。
|
||||
- 以下之一:
|
||||
- **Node.js ≥ 18**(推荐;最低支持 Node 14——通过 NPM 安装)。
|
||||
- 或仅用 `curl` + `chmod` 把静态二进制放进 `$PATH`。
|
||||
- 或 **Go ≥ 1.25**,如果你偏好从源码构建。
|
||||
|
||||
## 第 1 步——安装 CLI
|
||||
|
||||
### 方式 A:NPM(推荐)
|
||||
|
||||
```bash
|
||||
npm install -g @alibaba-group/open-code-review
|
||||
```
|
||||
|
||||
NPM 包安装一个小的 wrapper,它在安装时(通过 postinstall hook)为你的
|
||||
操作系统 / 架构下载正确的二进制。如果运行时二进制缺失,wrapper 会报错而
|
||||
不会去下载。安装后,你得到一个全局 `ocr` 命令:
|
||||
|
||||
```bash
|
||||
ocr --version
|
||||
```
|
||||
|
||||
### 方式 B:GitHub Release 二进制
|
||||
|
||||
从 [releases 页面](https://github.com/alibaba/open-code-review/releases)
|
||||
选择对应平台的二进制,放进你的 `$PATH`:
|
||||
|
||||
```bash
|
||||
# macOS (Apple Silicon)
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-darwin-arm64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# macOS (Intel)
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-darwin-amd64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Linux x86_64
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-linux-amd64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Linux ARM64
|
||||
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-linux-arm64
|
||||
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
||||
|
||||
# Windows (AMD64)
|
||||
curl -Lo ocr.exe https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-windows-amd64.exe
|
||||
|
||||
# Windows (ARM64)
|
||||
curl -Lo ocr.exe https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-windows-arm64.exe
|
||||
```
|
||||
|
||||
### 方式 C:从源码构建
|
||||
|
||||
```bash
|
||||
git clone https://github.com/alibaba/open-code-review.git
|
||||
cd open-code-review
|
||||
make build
|
||||
sudo cp dist/opencodereview /usr/local/bin/ocr
|
||||
```
|
||||
|
||||
> 各安装方式的详情见 [安装](../installation/),包括 NPM wrapper 如何解析
|
||||
> 平台二进制。
|
||||
|
||||
## 第 2 步——配置 LLM
|
||||
|
||||
在能解析出一个完整的 LLM 端点(URL + token + model)之前,OCR 会拒绝运行
|
||||
评审。它按以下优先级顺序搜索四个来源:
|
||||
|
||||
1. `~/.opencodereview/config.json`
|
||||
2. OCR 专属环境变量(`OCR_LLM_*`)
|
||||
3. Claude Code 环境变量(`ANTHROPIC_*`)
|
||||
4. 从你的 shell rc 文件(`~/.zshrc`、`~/.bashrc`、`~/.bash_profile`、
|
||||
`~/.profile`)中解析出的 `export ANTHROPIC_*` 行
|
||||
|
||||
### 最快路径:`ocr config set`
|
||||
|
||||
```bash
|
||||
ocr config set llm.url https://api.anthropic.com/v1/messages
|
||||
ocr config set llm.auth_token sk-ant-xxxxxxxxxx
|
||||
ocr config set llm.model claude-opus-4-6
|
||||
ocr config set llm.use_anthropic true
|
||||
```
|
||||
|
||||
这些值会持久化到 `~/.opencodereview/config.json`。
|
||||
|
||||
### 替代方式:环境变量
|
||||
|
||||
优先级最高——适合不想在磁盘上留配置文件的 CI / 容器:
|
||||
|
||||
```bash
|
||||
export OCR_LLM_URL=https://api.anthropic.com/v1/messages
|
||||
export OCR_LLM_TOKEN=sk-ant-xxxxxxxxxx
|
||||
export OCR_LLM_MODEL=claude-opus-4-6
|
||||
export OCR_USE_ANTHROPIC=true # 默认 true;设为 false 走 OpenAI 协议
|
||||
```
|
||||
|
||||
### 已经在用 Claude Code?
|
||||
|
||||
OCR 会自动读取 Claude Code 使用的同一批变量,无需额外配置:
|
||||
|
||||
```bash
|
||||
export ANTHROPIC_BASE_URL=https://api.anthropic.com
|
||||
export ANTHROPIC_AUTH_TOKEN=sk-ant-xxxxxxxxxx
|
||||
export ANTHROPIC_MODEL=claude-opus-4-6
|
||||
```
|
||||
|
||||
如果 `ANTHROPIC_BASE_URL` 缺少带版本的路径,OCR 会自动追加
|
||||
`/v1/messages`。
|
||||
|
||||
### 使用 OpenAI 兼容端点?
|
||||
|
||||
把 `llm.use_anthropic` 设为 `false`(或 `OCR_USE_ANTHROPIC=false`):
|
||||
|
||||
```bash
|
||||
ocr config set llm.url https://api.openai.com/v1/chat/completions
|
||||
ocr config set llm.auth_token sk-xxxxxxxxxx
|
||||
ocr config set llm.model gpt-4o
|
||||
ocr config set llm.use_anthropic false
|
||||
```
|
||||
|
||||
> 完整的 key 参考见 [配置](../configuration/),包括用于厂商专属请求字段
|
||||
> 的 `llm.extra_body`,以及用于切换评审评论语言的 `language`。
|
||||
|
||||
## 第 3 步——测试连通性
|
||||
|
||||
```bash
|
||||
ocr llm test
|
||||
```
|
||||
|
||||
预期输出(模型名会有所不同):
|
||||
|
||||
```
|
||||
Source: OCR config file
|
||||
URL: https://api.anthropic.com/v1/messages
|
||||
Model: claude-opus-4-6
|
||||
Hello! …
|
||||
```
|
||||
|
||||
如果反而报出 `no valid LLM endpoint configured` 这类错误,请重新检查上面的
|
||||
配置 key。401 / 403 表示 token 错误或已过期。
|
||||
|
||||
## 第 4 步——运行第一次评审
|
||||
|
||||
进入任意 Git 仓库并运行:
|
||||
|
||||
```bash
|
||||
cd path/to/your-repo
|
||||
|
||||
# 工作区模式——评审 staged + unstaged + untracked 变更(默认)
|
||||
ocr review
|
||||
|
||||
# 分支区间——评审 `main..feature-branch`
|
||||
ocr review --from main --to feature-branch
|
||||
|
||||
# 单个 commit——评审该 commit 引入的 diff
|
||||
ocr review --commit abc123
|
||||
```
|
||||
|
||||
你会看到持续输出的进度信息,最后每个文件出现一条或多条评审评论。
|
||||
|
||||
> 工作区模式包含 **untracked** 文件。如果你只想评审已暂存的内容,请先用
|
||||
> `git add` 选择性暂存。
|
||||
|
||||
> 以上三种是基础用法。`ocr review` 的完整参数(并发调优、输出格式、
|
||||
> audience 模式、背景上下文等)及其他所有子命令(`config`、`rules`、
|
||||
> `llm test`、`viewer`)见 [CLI 参考](../cli-reference/)。
|
||||
|
||||
### 想先看看 *会* 评审什么?
|
||||
|
||||
```bash
|
||||
ocr review --preview # 工作区
|
||||
ocr review -c abc123 -p # commit
|
||||
```
|
||||
|
||||
`--preview` 运行每个过滤步骤但绝不调用 LLM,因此不消耗任何 token。它打印文件列表
|
||||
及每个文件的状态(`added` / `modified` / `deleted` / `renamed` / `binary`),
|
||||
对于被排除的文件还会给出原因(`binary`、`unsupported_ext`、`default_path`、
|
||||
`user_exclude`、`deleted`)。
|
||||
|
||||
### 给工具用的 JSON 输出
|
||||
|
||||
```bash
|
||||
ocr review --format json --audience agent > review.json
|
||||
```
|
||||
|
||||
- `--format json` 输出一个机器可读的评论数组,每条含 `path`、`content`、
|
||||
`start_line`、`end_line`、`existing_code`、`suggestion_code` 和可选的
|
||||
`thinking`。
|
||||
- `--audience agent` 屏蔽人性化的进度 UI,让 stdout 只剩 JSON / 最终
|
||||
摘要——正是上游 agent 或 CI 脚本所需。
|
||||
|
||||
## 第 5 步——查看结果
|
||||
|
||||
每条评论包含:
|
||||
|
||||
| 字段 | 含义 |
|
||||
|---|---|
|
||||
| `path` | 该评论所针对的文件。 |
|
||||
| `content` | 评审评论本身,使用配置的 `language`。 |
|
||||
| `start_line` / `end_line` | 文件 **新** 版本中的行范围。两者都为 `0` 表示 OCR 无法精确定位评论——问题是真实的,但需自行定位到准确位置。 |
|
||||
| `existing_code` | 评论所指的 diff 片段。内部用于行解析;在 `start_line` 为 `0` 时有用。 |
|
||||
| `suggestion_code` | 可选的修复片段。 |
|
||||
| `thinking` | 可选的模型推理。仅部分模型存在。 |
|
||||
|
||||
## 第 6 步——查看历史会话
|
||||
|
||||
每次评审都会以 JSONL 转录形式持久化到
|
||||
`~/.opencodereview/sessions/...`。在本地 Web UI 中浏览它们:
|
||||
|
||||
```bash
|
||||
ocr viewer # http://localhost:5483
|
||||
ocr viewer --addr :3000
|
||||
```
|
||||
|
||||
> 完整 UI 介绍见 [会话查看器](../viewer/)。
|
||||
|
||||
## 另见
|
||||
|
||||
- [CLI 参考](../cli-reference/)——每个子命令、参数与输出模式。
|
||||
- [评审规则](../review-rules/)——自定义评审内容。
|
||||
- [集成](../integrations/)——把 OCR 嵌入 Claude Code、Agent skill 或 CI。
|
||||
- [遥测](../telemetry/)——经 OTLP 上报 trace 与 metrics。
|
||||
- [FAQ](../faq/)——已知错误与对策。
|
||||
241
pages/src/content/docs/zh/review-rules.md
Normal file
241
pages/src/content/docs/zh/review-rules.md
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
---
|
||||
title: 评审规则
|
||||
sidebar:
|
||||
order: 7
|
||||
---
|
||||
|
||||
规则告诉 OCR 评审每个文件时**应关注什么**。它们存放在三层的 JSON 文件中,
|
||||
外加随二进制发布的一个内嵌系统默认规则。
|
||||
|
||||
## 优先级链
|
||||
|
||||
OCR 用一条**四层优先级链**解析规则。对每个文件路径,按序尝试各层;第一个匹配
|
||||
的模式生效。
|
||||
|
||||
| 优先级 | 来源 | 路径 | 说明 |
|
||||
|---|---|---|---|
|
||||
| 1(最高) | `--rule` 参数 | 用户指定 | CLI 覆盖;只要提供就总是生效。 |
|
||||
| 2 | 项目配置 | `<repoDir>/.opencodereview/rule.json` | 项目级规则——可安全提交。 |
|
||||
| 3 | 全局配置 | `~/.opencodereview/rule.json` | 用户级偏好。 |
|
||||
| 4(最低) | 系统默认 | 内嵌 `system_rules.json` | 覆盖常见语言的内置规则。 |
|
||||
|
||||
若更高优先级层的文件不存在,会被静默跳过——不是错误。因此从未添加
|
||||
`.opencodereview/rule.json` 的项目会直接落到全局 / 系统层。
|
||||
|
||||
系统层**始终**存在(随二进制发布),因此总会解析出*某个*规则。
|
||||
|
||||
## 规则文件格式(层 1–3)
|
||||
|
||||
```json
|
||||
{
|
||||
"include": ["src/**/*.{ts,tsx}", "src/**/*.go"],
|
||||
"exclude": ["**/*.test.ts", "**/generated/**"],
|
||||
"rules": [
|
||||
{
|
||||
"path": "src/api/**/*.go",
|
||||
"rule": "All exported handlers must validate request bodies before use."
|
||||
},
|
||||
{
|
||||
"path": "**/*mapper*.xml",
|
||||
"rule": "Check SQL for injection risks, parameter errors, and missing closing tags."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
三个独立字段:
|
||||
|
||||
- `include`——可选。glob 模式,用于*绕过*内置的默认排除模式(测试文件排除——见
|
||||
下文)。它不是白名单:不匹配任何 `include` 模式的文件仍会经过
|
||||
`unsupported_ext` 和 `default_path` 检查,可能仍被评审。
|
||||
- `exclude`——可选。OCR 不予评审的文件 glob 模式。过滤中优先级最高。
|
||||
- `rules`——`{path, rule}` 条目数组,按**声明顺序**求值。第一个 `path` glob
|
||||
匹配该文件的条目,决定 OCR 发给模型的 prompt。
|
||||
|
||||
### glob 能力
|
||||
|
||||
OCR 用 [`bmatcuk/doublestar/v4`](https://pkg.go.dev/github.com/bmatcuk/doublestar/v4)
|
||||
做匹配:
|
||||
|
||||
- `*`——匹配除 `/` 外的任意字符。
|
||||
- `**`——跨目录边界匹配(`src/**/*.go` 覆盖任意深度)。
|
||||
- `{a,b,c}`——花括号展开。`*.{ts,tsx,js,jsx}` 展开为四个模式并依次匹配。
|
||||
- `?`——匹配单个字符。
|
||||
- `[abc]`——字符类。
|
||||
|
||||
> 模式匹配**不区分大小写**(匹配前文件路径会被小写化)。不确定时用
|
||||
> `ocr rules check <path>` 确认。
|
||||
|
||||
## 文件如何被过滤
|
||||
|
||||
过滤是一个五重门算法,位于
|
||||
[`internal/agent/preview.go`](https://github.com/alibaba/open-code-review/blob/main/internal/agent/preview.go)。
|
||||
对每个 diff,OCR 依次问:
|
||||
|
||||
1. **`binary`**——文件是二进制吗?排除。
|
||||
2. **`user_exclude`**——路径匹配任何用户 `exclude` 模式吗?排除。
|
||||
3. **`user_include`**——若用户定义了 `include`,路径匹配吗?若是,**立即保留**
|
||||
(绕过下面的 `unsupported_ext` 和 `default_path` 门)。
|
||||
4. **`unsupported_ext`**——文件扩展名在
|
||||
[白名单](https://github.com/alibaba/open-code-review/blob/main/internal/config/allowlist/supported_file_types.json)
|
||||
里吗?不在则排除。
|
||||
5. **`default_path`**——路径匹配某个内置测试文件排除模式
|
||||
(`**/*_test.go`、`**/*.test.{js,jsx,ts,tsx}`、`**/*_spec.rb`……)吗?排除。
|
||||
|
||||
通过全部五重门的文件才发给 LLM。`deleted` 原因(不是门——它在 `Preview()` 中
|
||||
单独计算)标记新路径为 `/dev/null` 的文件;没有新内容可评审。用
|
||||
`ocr review --preview` 可在不花 token 的情况下打印此过滤结果。
|
||||
|
||||
### 默认路径排除
|
||||
|
||||
内置排除列表(见
|
||||
[`internal/config/allowlist/default_exclude_patterns.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/allowlist/default_exclude_patterns.json))
|
||||
匹配测试文件模式:
|
||||
|
||||
- `**/*_test.go`
|
||||
- `**/src/test/java/**/*.java`
|
||||
- `**/src/test/**/*.kt`
|
||||
- `**/*.test.{js,jsx,ts,tsx}`
|
||||
- `**/*.spec.{js,jsx,ts,tsx}`
|
||||
- `**/__tests__/**`
|
||||
- `**/test/**/*_test.py`
|
||||
- `**/tests/**/*_test.py`
|
||||
- `**/*_test.py`
|
||||
- `**/*_spec.rb`
|
||||
- `**/spec/**/*_spec.rb`
|
||||
- `**/*Test.java`
|
||||
- `**/*Tests.java`
|
||||
- `**/*_test.rs`
|
||||
- `**/oh_modules/**`
|
||||
- `**/*.test.ets`
|
||||
|
||||
噪声目录过滤(`vendor/`、`node_modules/`、`target/`……)发生在更早的阶段,位于
|
||||
[`internal/diff/git.go`](https://github.com/alibaba/open-code-review/blob/main/internal/diff/git.go)
|
||||
的 diff 层,先于 per-file 过滤运行。
|
||||
|
||||
要**评审**一个匹配这些测试文件模式的文件,把它加入用户 `include` 列表——那会
|
||||
覆盖 default-path 门。
|
||||
|
||||
## 每文件的规则解析
|
||||
|
||||
过滤决定某文件*将被*评审后,OCR 选择 agent 应遵循的规则文本:
|
||||
|
||||
1. 按声明顺序试 `--rule`(custom)层。
|
||||
2. 按声明顺序试 `<repo>/.opencodereview/rule.json`。
|
||||
3. 按声明顺序试 `~/.opencodereview/rule.json`。
|
||||
4. 回退到内嵌系统规则层。
|
||||
|
||||
内嵌 `system_rules.json` 自带这些模式(按序):
|
||||
|
||||
| 模式 | 规则文档 |
|
||||
|---|---|
|
||||
| `**/*.properties` | `properties.md`——i18n / 配置文件。 |
|
||||
| `**/*{mapper,dao}*.xml` | `mapper_dao_xml.md`——MyBatis 风格 mapper SQL。 |
|
||||
| `**/pom.xml` | `pom_xml.md`——Maven 依赖。 |
|
||||
| `**/build.gradle` | `build_gradle.md`——Gradle 依赖。 |
|
||||
| `**/package.json` | `package_json.md`——NPM 依赖 / 脚本。 |
|
||||
| `**/Cargo.toml` | `cargo_toml.md`——Rust manifest。 |
|
||||
| `**/*.{json,json5}` | `json.md`——通用 JSON(也匹配 `.json5`)。 |
|
||||
| `.github/workflows/**/*.{yaml,yml}` | `github_workflows.md`——GitHub Actions 工作流 YAML。 |
|
||||
| `.github/**/*.{yaml,yml}` | `github_config.md`——其他 `.github` 配置 YAML。 |
|
||||
| `**/*.{yaml,yml}` | `yaml.md` |
|
||||
| `**/*.java` | `java.md` |
|
||||
| `**/*.ets` | `arkts.md`——ArkTS / HarmonyOS。 |
|
||||
| `**/*.{ts,js,tsx,jsx}` | `ts_js_tsx_jsx.md` |
|
||||
| `**/*.{kt}` | `kotlin.md` |
|
||||
| `**/*.rs` | `rust.md` |
|
||||
| `**/*.{cpp,cc,hpp}` | `cpp.md` |
|
||||
| `**/*.c` | `c.md` |
|
||||
| *(fallback)* | `default.md` |
|
||||
|
||||
解析出的规则正文成为 plan 和 main task prompt 中 `{{system_rule}}` 占位符的内容。
|
||||
|
||||
## 查看哪条规则生效:`ocr rules check`
|
||||
|
||||
```bash
|
||||
$ ocr rules check src/main/java/com/example/UserService.java
|
||||
File: src/main/java/com/example/UserService.java
|
||||
Source: System built-in
|
||||
Pattern: **/*.java
|
||||
Rule:
|
||||
────────────────────────────────────────
|
||||
…contents of java.md…
|
||||
────────────────────────────────────────
|
||||
```
|
||||
|
||||
```bash
|
||||
$ ocr rules check --rule custom.json src/main/resources/mapper/UserMapper.xml
|
||||
File: src/main/resources/mapper/UserMapper.xml
|
||||
Source: Custom (--rule)
|
||||
Pattern: **/*mapper*.xml
|
||||
Rule:
|
||||
────────────────────────────────────────
|
||||
…contents of your custom rule…
|
||||
────────────────────────────────────────
|
||||
```
|
||||
|
||||
当某条规则未按预期生效时用它——它会显示生效的**层**与**模式**。
|
||||
|
||||
## 配方
|
||||
|
||||
### 项目级:强制编码规范
|
||||
|
||||
保存为 `<repo>/.opencodereview/rule.json` 并提交:
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"path": "src/api/**/*.go",
|
||||
"rule": "Every public handler must `defer tx.Rollback()` immediately after starting a transaction."
|
||||
},
|
||||
{
|
||||
"path": "**/*mapper*.xml",
|
||||
"rule": "Check SQL for injection risks, missing parameter binding, and unclosed XML tags."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 项目级:跳过生成代码,聚焦 src
|
||||
|
||||
```json
|
||||
{
|
||||
"include": ["src/**/*.{ts,tsx,js,jsx}"],
|
||||
"exclude": ["**/*.gen.ts", "**/generated/**"]
|
||||
}
|
||||
```
|
||||
|
||||
设置 `include` 后,`src/` 内的文件即使本会被内置默认排除模式(如测试文件)剔除
|
||||
也会被保留。`src/` 之外的文件仍走正常的 ext / default 检查——`include` 是绕过机制,
|
||||
不是白名单。
|
||||
|
||||
### 按 PR 覆盖
|
||||
|
||||
```bash
|
||||
ocr review --rule ./.review-rules-only-for-this-pr.json
|
||||
```
|
||||
|
||||
同时绕过项目层与全局层——当单个 PR 需要完全不同的评审清单(如仅安全评审)时
|
||||
很方便。
|
||||
|
||||
### 全局个人偏好
|
||||
|
||||
放到 `~/.opencodereview/rule.json`,你机器上每个仓库都会继承:
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"path": "**/*.{ts,tsx,js,jsx}",
|
||||
"rule": "Always check for unhandled promise rejections; warn on `// eslint-disable` without a reason comment."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 另见
|
||||
|
||||
- [CLI 参考](../cli-reference/)——`ocr review --rule`、`--preview` 与 `ocr rules check`。
|
||||
- [配置](../configuration/)——config 文件位置与分层解析链。
|
||||
- [架构](../architecture/)——解析出的规则如何馈入 agent prompt。
|
||||
258
pages/src/content/docs/zh/telemetry.md
Normal file
258
pages/src/content/docs/zh/telemetry.md
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
---
|
||||
title: 遥测
|
||||
sidebar:
|
||||
order: 11
|
||||
---
|
||||
|
||||
OCR 自带一流的 **OpenTelemetry** 支持。每次评审运行产出结构化的 span、
|
||||
metric 和 event。接入 collector 后,这些数据足以回答“agent 把时间花在哪了?”、
|
||||
“各模型成本如何?”、“这次运行为什么失败?”。
|
||||
|
||||
## 概览
|
||||
|
||||
遥测**默认关闭**。启用后,OCR 导出:
|
||||
|
||||
- **Span**——三个流水线级 span(`review.run`、`diff.parse`、
|
||||
`subtask.execute.<file>`)外加每个决策点事件一个短生命周期的 `event.*` span。
|
||||
- **Metric**——评审时长、评审文件数、生成评论数、LLM 请求 / token / 延迟、
|
||||
工具调用 / 延迟的聚合计数与直方图。
|
||||
- **Event**——span 内离散事件,如 `plan.skipped`、
|
||||
`token.threshold.exceeded`、`review.started`。
|
||||
|
||||
支持两种 exporter:
|
||||
|
||||
| Exporter | 何时使用 |
|
||||
|---|---|
|
||||
| `console` | 个人使用 / 调试。把 span 格式化打印到 stdout。 |
|
||||
| `otlp` | 系统集成。发送到任何 OTLP 兼容 collector(Jaeger、Tempo、OTel Collector、Datadog Agent……)。 |
|
||||
|
||||
## 启用遥测
|
||||
|
||||
与 LLM 端点一样,遥测可通过持久化 config 或环境变量配置——冲突时环境变量优先。
|
||||
|
||||
### 配置文件方式
|
||||
|
||||
```bash
|
||||
ocr config set telemetry.enabled true
|
||||
ocr config set telemetry.exporter otlp
|
||||
ocr config set telemetry.otlp_endpoint localhost:4317
|
||||
ocr config set telemetry.content_logging false
|
||||
```
|
||||
|
||||
`~/.opencodereview/config.json` 中的结果:
|
||||
|
||||
```json
|
||||
{
|
||||
"telemetry": {
|
||||
"enabled": true,
|
||||
"exporter": "otlp",
|
||||
"otlp_endpoint": "localhost:4317",
|
||||
"content_logging": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 环境变量方式
|
||||
|
||||
```bash
|
||||
export OCR_ENABLE_TELEMETRY=1
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317 # implies exporter=otlp
|
||||
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc # default. NOTE: only grpc is currently
|
||||
# implemented; http/protobuf and http/json
|
||||
# are accepted but not yet wired up.
|
||||
export OTEL_SERVICE_NAME=open-code-review-prod # optional; default: open-code-review
|
||||
export OCR_CONTENT_LOGGING=0 # reserved / currently a no-op (see Content logging)
|
||||
```
|
||||
|
||||
设置 `OTEL_EXPORTER_OTLP_ENDPOINT` 也会强制 `exporter=otlp`——适合一次性的
|
||||
`OTEL_EXPORTER_OTLP_ENDPOINT=… ocr review` 运行。
|
||||
|
||||
## 导出什么
|
||||
|
||||
### Span
|
||||
|
||||
一次评审的完整 span 树:
|
||||
|
||||
```
|
||||
review.run
|
||||
├── diff.parse
|
||||
├── event.review.started (decision-point event)
|
||||
├── subtask.execute.<file1>
|
||||
│ ├── event.plan.skipped (when changes are below threshold)
|
||||
│ ├── event.plan.failed (when plan phase errored)
|
||||
│ ├── event.token.threshold.exceeded (when prompt > 80% of max_tokens)
|
||||
│ └── event.subtask.error (when the subtask errored)
|
||||
├── subtask.execute.<file2>
|
||||
└── …
|
||||
```
|
||||
|
||||
LLM 往返和工具执行**不**作为单独 span 发出——它们只出现在 metric(见下)中。
|
||||
决策点事件作为短生命周期的 `event.<name>` span 附着到当前 context。
|
||||
|
||||
每个 span 携带有用属性:
|
||||
|
||||
| Span | 关键属性 |
|
||||
|---|---|
|
||||
| `review.run` | `error`(运行失败时设置) |
|
||||
| `diff.parse` | `files.changed`、`lines.inserted`、`lines.deleted` |
|
||||
| `subtask.execute.<file>` | `file.path`、`lines.changed`、`lines.inserted`、`lines.deleted` |
|
||||
| `event.review.started` | `file.count`、`review.count`、`repo.dir` |
|
||||
| `event.plan.skipped` | `file.path`、`lines.changed`、`threshold` |
|
||||
| `event.plan.failed` | `file.path`、`message` |
|
||||
| `event.token.threshold.exceeded` | `file.path`、`tokens`、`max_tokens` |
|
||||
| `event.subtask.error` | `file.path`、`error` |
|
||||
|
||||
### Metric
|
||||
|
||||
OCR 通过 OTel meter 记录数值 metric——计数与直方图,由 collector 在下游聚合:
|
||||
|
||||
| Metric | 类型 | 单位 | 标签 |
|
||||
|---|---|---|---|
|
||||
| `ocr.review.duration_seconds` | histogram | `s` | — |
|
||||
| `ocr.files_reviewed_total` | counter | — | — |
|
||||
| `ocr.comments_generated_total` | counter | — | — |
|
||||
| `ocr.llm.requests_total` | counter | — | `model`、`status`(`ok` / `error`) |
|
||||
| `ocr.llm.request_duration_seconds` | histogram | `s` | `model` |
|
||||
| `ocr.llm.tokens_used` | counter | — | `model`、`type`(当前总是 `total`) |
|
||||
| `ocr.tool.calls_total` | counter | — | `tool.name`、`status`(`ok` / `error`) |
|
||||
| `ocr.tool.execution_duration_seconds` | histogram | `s` | `tool.name` |
|
||||
|
||||
### Event
|
||||
|
||||
事件在决策点作为短生命周期的 `event.<name>` span 触发。完整列表:
|
||||
|
||||
| 事件 | 含义 |
|
||||
|---|---|
|
||||
| `review.started` | diff 已加载;我们知道将评审多少文件。 |
|
||||
| `no.files.changed` | diff 解析出零文件。 |
|
||||
| `plan.skipped` | 某文件低于 `PLAN_MODE_LINE_THRESHOLD`。 |
|
||||
| `plan.failed` | plan 阶段出错;main 循环无 plan 运行。 |
|
||||
| `token.threshold.exceeded` | 初始 prompt token > `MAX_TOKENS` 的 80 %;文件被跳过。 |
|
||||
| `subtask.error` | 某 per-file 子任务出错——以 `Error` span 状态发出。 |
|
||||
|
||||
借此可在用户察觉之前,及早发现评审质量退化并告警。
|
||||
|
||||
## 内容日志
|
||||
|
||||
遥测导出 LLM 流量的**形状**(计数、时长、状态),但**绝不**导出实际 prompt 或
|
||||
响应。OCR 不尝试把 LLM 消息内容附加到 span 或 event——离开进程的数据仅限于上面
|
||||
记录的 metric / event schema,别无其他。
|
||||
|
||||
`content_logging` config key(和 `OCR_CONTENT_LOGGING=1` 环境覆盖)已接入配置层,
|
||||
但目前**不**控制任何发出 prompt 内容的代码路径。请将该标志视为保留位。
|
||||
|
||||
如需检查发给 LLM 或从 LLM 返回的内容,使用[会话查看器](../viewer/)读取的本地
|
||||
JSONL 转录。它们完全存在于 `~/.opencodereview/` 下的磁盘上,绝不发往 collector。
|
||||
|
||||
## 配方
|
||||
|
||||
### 本地调试用 console exporter
|
||||
|
||||
```bash
|
||||
ocr config set telemetry.enabled true
|
||||
ocr config set telemetry.exporter console
|
||||
ocr review --commit HEAD
|
||||
```
|
||||
|
||||
span 以人类可读形式打印到 stdout。可通过管道传给 `less` 查看长运行输出。
|
||||
|
||||
### OTel Collector + Tempo + Prometheus
|
||||
|
||||
```yaml
|
||||
# otel-collector-config.yaml
|
||||
receivers:
|
||||
otlp:
|
||||
protocols: { grpc: { endpoint: 0.0.0.0:4317 } }
|
||||
|
||||
exporters:
|
||||
otlp/tempo:
|
||||
endpoint: tempo:4317
|
||||
tls: { insecure: true }
|
||||
prometheus:
|
||||
endpoint: 0.0.0.0:9464
|
||||
|
||||
service:
|
||||
pipelines:
|
||||
traces: { receivers: [otlp], exporters: [otlp/tempo] }
|
||||
metrics: { receivers: [otlp], exporters: [prometheus] }
|
||||
```
|
||||
|
||||
然后在 shell 中:
|
||||
|
||||
```bash
|
||||
export OCR_ENABLE_TELEMETRY=1
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317
|
||||
ocr review --from main --to feature/branch
|
||||
```
|
||||
|
||||
打开 Tempo → 按 `service.name=open-code-review` 搜索 → 点击任意 trace 看完整
|
||||
span 树。
|
||||
|
||||
### Datadog
|
||||
|
||||
Datadog Agent 的 OTLP receiver 默认使用 OTLP/gRPC:
|
||||
|
||||
```bash
|
||||
export OCR_ENABLE_TELEMETRY=1
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317
|
||||
export OTEL_SERVICE_NAME=open-code-review
|
||||
```
|
||||
|
||||
span 以该 service name 出现在 APM 下;LLM metric 带上述标签出现在 Metrics 下。
|
||||
|
||||
### CI 运行,结果进入仪表盘
|
||||
|
||||
在流水线步骤中注入环境变量:
|
||||
|
||||
```yaml
|
||||
- name: Code review
|
||||
env:
|
||||
OCR_LLM_URL: ${{ secrets.OCR_LLM_URL }}
|
||||
OCR_LLM_TOKEN: ${{ secrets.OCR_LLM_TOKEN }}
|
||||
OCR_LLM_MODEL: claude-opus-4-6
|
||||
OCR_ENABLE_TELEMETRY: "1"
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ vars.OTEL_COLLECTOR_URL }}
|
||||
OTEL_SERVICE_NAME: open-code-review-ci
|
||||
run: ocr review --from origin/main --to HEAD --audience agent
|
||||
```
|
||||
|
||||
`OTEL_SERVICE_NAME` 可把 CI trace 与人工开发运行的 trace 区分开。
|
||||
|
||||
## 解析优先级
|
||||
|
||||
OCR 构建最终遥测配置时:
|
||||
|
||||
1. 默认(`enabled=false`、`exporter=console`、无 endpoint)。
|
||||
2. `~/.opencodereview/config.json` 的 `telemetry.*` key。
|
||||
3. 环境变量(最高优先级,**覆盖**文件)。
|
||||
|
||||
因此你可以在 config 中保留 `telemetry.enabled=false`,按运行用
|
||||
`OCR_ENABLE_TELEMETRY=1` 开启。
|
||||
|
||||
## 采样与开销
|
||||
|
||||
OCR 导出**一切**。没有采样配置;OTel 的采样是 collector 的责任。对一次典型评审
|
||||
运行:
|
||||
|
||||
- 1 个 `review.run` span + 1 个 `diff.parse` span + 每个被评审文件 1 个
|
||||
`subtask.execute.<file>` span + 每个决策点事件 1 个短生命周期的 `event.*` span。
|
||||
- 10 文件的 PR 总共约 15–25 个 span。LLM 往返和工具调用增加 metric 计数但不创建
|
||||
额外 span。
|
||||
|
||||
导出是**批量且异步**的——遥测不阻塞评审循环。若 collector 不可达,OCR 记录警告
|
||||
并继续;评审仍会产出正常输出。
|
||||
|
||||
## 故障排查
|
||||
|
||||
| 症状 | 可能原因 |
|
||||
|---|---|
|
||||
| 什么都没导出 | `OCR_ENABLE_TELEMETRY` / `telemetry.enabled` 未设置。默认**关闭**。 |
|
||||
| OTLP 本地可用,生产失败 | OCR 当前仅实现 OTLP/gRPC——`OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`(或 `http/json`)被接受但未接入,切换也无济于事。请验证 endpoint 以及 collector 是否在监听 gRPC。 |
|
||||
| span 显示但无 metric | 一些 collector 默认只启用 traces pipeline;在配置中加 `metrics` pipeline。 |
|
||||
| span 中缺 prompt | OCR 绝不把 prompt 内容附加到遥测——见[内容日志](#content-logging)。改用[会话查看器](../viewer/)检查转录。 |
|
||||
|
||||
## 另见
|
||||
|
||||
- [配置](../configuration/)——`telemetry.*` 命名空间的完整 key 参考。
|
||||
- [架构](../architecture/)——每个 span 实际度量什么。
|
||||
- [OpenTelemetry 文档](https://opentelemetry.io/docs/)——collector 设置与 exporter。
|
||||
353
pages/src/content/docs/zh/tools.md
Normal file
353
pages/src/content/docs/zh/tools.md
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
---
|
||||
title: 工具
|
||||
sidebar:
|
||||
order: 9
|
||||
---
|
||||
|
||||
OCR 内置 **六个工具**,供 LLM 在评审过程中调用。本页记录每个工具的用途、输入
|
||||
schema 与示例输入/输出。完整的机器可读定义位于
|
||||
[`internal/config/toolsconfig/tools.json`](https://github.com/alibaba/open-code-review/blob/main/internal/config/toolsconfig/tools.json)。
|
||||
|
||||
## 各阶段工具可用性
|
||||
|
||||
每个工具声明它在 **plan 阶段**、**main task** 还是两者中都可用:
|
||||
|
||||
| 工具 | Plan | Main | 用途 |
|
||||
|---|---|---|---|
|
||||
| `task_done` | ✗ | ✓ | 表示“我完成了”——终止循环。 |
|
||||
| `code_comment` | ✗ | ✓ | 发出一条带行范围 + 建议的评审评论。 |
|
||||
| `file_read` | ✗ | ✓ | 读取变更后快照中某文件的一段。 |
|
||||
| `file_read_diff` | ✓ | ✓ | 读取另一文件的 diff 以确认跨文件关切。 |
|
||||
| `file_find` | ✓ | ✓ | 按文件名关键字定位文件。 |
|
||||
| `code_search` | ✓ | ✓ | 全仓 grep(字面量或正则)。 |
|
||||
|
||||
`task_done` 和 `code_comment` 在 plan 阶段有意**不可用**:plan 是只读的。
|
||||
|
||||
> **上下文工具是只读上下文,不是评论目标。** `main_task` prompt 明确禁止对
|
||||
> *其他*文件中的发现发表评论。`file_read`、`file_read_diff`、`file_find` 和
|
||||
> `code_search` 的存在是为了让模型更好地理解当前文件的 diff——收集该上下文时
|
||||
> 发现的任何问题按设计都会被忽略。跨文件关切只有在**当前文件 diff**中可观测
|
||||
> 时,才会作为评论出现。
|
||||
|
||||
要覆盖工具注册表,传入一个与内嵌定义同形的 JSON 文件路径 `--tools <path>`。
|
||||
借此可以禁用工具、编辑描述,或基于已有 provider 添加新工具。
|
||||
|
||||
## `task_done`
|
||||
|
||||
终止 main 循环。
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "task_done",
|
||||
"input": { "state": "DONE" }
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 必需 | 含义 |
|
||||
|---|---|---|
|
||||
| `state` | 是 | `DONE`(默认)或 `FAILED`。`FAILED` 表示“我确实无法用可用工具完成此事”——几乎从不是正确的选择。 |
|
||||
|
||||
agent 看到 `task_done` 后,停止调用 LLM 并开始处理已累积的 `code_comment`
|
||||
调用。`task_done` 立即返回(在结果记入会话日志之前),因此 `state` 值会被接受
|
||||
但**不**持久化——它也不影响退出码。
|
||||
|
||||
## `code_comment`
|
||||
|
||||
发出一条或多条评审评论。每条评论锚定到一个代码片段(`existing_code`),以便
|
||||
OCR 自动计算行号。
|
||||
|
||||
### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "code_comment",
|
||||
"input": {
|
||||
"path": "string — optional, override the file path for this comment",
|
||||
"comments": [
|
||||
{
|
||||
"content": "string — the comment in the configured language",
|
||||
"existing_code": "string — snippet from the diff to anchor on",
|
||||
"suggestion_code": "string — optional fix snippet",
|
||||
"thinking": "string — optional, the model's reasoning for this comment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`comments` 是数组,因此模型可以在一次工具调用中发出多条评论。`content` 和
|
||||
`existing_code` 必需;`suggestion_code` 可选但建议提供。`path` 是顶层可选覆盖——
|
||||
省略时,agent 会注入当前评审的文件。即便模型省略,agent 也会自动注入 `path`,因此
|
||||
模型极少需要显式设置。`thinking`(按评论)捕获模型推理,保留在评论上,但不会
|
||||
在最终评审输出中显示。
|
||||
|
||||
> **`thinking` 是运行时专属字段。** OCR 会解析并存储它,但有意**不**把它列入
|
||||
> 给模型的 `code_comment` schema(`tools.json` 中只有 `content`、
|
||||
> `existing_code` 和 `suggestion_code`)。更强模型若仍发出 `thinking` 块,也会
|
||||
> 被持久化;多数模型不会发,没问题。
|
||||
|
||||
### 锚定算法
|
||||
|
||||
OCR 用一个**动态滑动窗口**在 diff 中查找 `existing_code` 文本。匹配按序尝试:
|
||||
|
||||
1. **hunk 新侧**——一段连续的 **context + added** 行(不是仅有 deleted、也不是仅有
|
||||
unchanged),得到新文件行号。若失败,OCR 重试 **hunk 旧侧**——context +
|
||||
deleted 行——得到旧文件行号。
|
||||
2. **全新文件扫描**——若无 hunk 匹配,OCR 对整个变更后文件逐行扫描连续匹配
|
||||
(`resolveFromFileContent`)。
|
||||
3. **重新定位任务**——若文本匹配在较复杂的 diff 上仍失败,OCR 运行
|
||||
`RE_LOCATION_TASK` prompt,请模型重新锚定片段。
|
||||
|
||||
匹配**对空白不敏感**:比较前会 trim 行并去除 diff 的 `+`/`-` 标记,因此缩进
|
||||
无需精确一致。作为最后手段,评论会以 `start_line=0` 交付,告诉用户“问题是真实的,但需自行定位”。
|
||||
|
||||
### 示例
|
||||
|
||||
```json
|
||||
{
|
||||
"comments": [
|
||||
{
|
||||
"content": "`tx.Rollback()` is never deferred — early returns leak the transaction.",
|
||||
"existing_code": "tx, err := db.Begin()\nif err != nil {\n return err\n}",
|
||||
"suggestion_code": "tx, err := db.Begin()\nif err != nil {\n return err\n}\ndefer tx.Rollback()"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## `file_read`
|
||||
|
||||
读取文件**变更后**形式的一段行。
|
||||
|
||||
### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "file_read",
|
||||
"input": {
|
||||
"file_path": "src/foo.go",
|
||||
"start_line": 10,
|
||||
"end_line": 80
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 必需 | 默认 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `file_path` | 是 | — | 相对仓库根的路径。 |
|
||||
| `start_line` | 否 | `1` | 从 1 开始索引。 |
|
||||
| `end_line` | 否 | 文件末尾 | 含端点。 |
|
||||
|
||||
### 输出
|
||||
|
||||
```
|
||||
File: src/foo.go (Total lines: 220)
|
||||
IS_TRUNCATED: false
|
||||
LINE_RANGE: 10-80
|
||||
10|package foo
|
||||
11|
|
||||
12|import (
|
||||
13| "fmt"
|
||||
…
|
||||
```
|
||||
|
||||
每行内容以 1 起始行号和 `|` 分隔符为前缀,以便模型在后续 `code_comment` 调用中
|
||||
精确引用行号。
|
||||
|
||||
### 限制
|
||||
|
||||
- **每次调用最多 500 行。** 更大范围会被截断,置 `IS_TRUNCATED: true`,并追加
|
||||
`Note: Results truncated to 500 lines. Please narrow your line range.`。
|
||||
- 只读取文件的**修改后版本**。要看旧版本,用 `file_read_diff`。
|
||||
|
||||
当模型需要周围上下文(对某个只能在 diff 中看到的函数发表评论时),应从 diff
|
||||
hunk 头 `@@ -x,y +m,n @@` 计算范围——通常 `m-50` 到 `m+n+50`。
|
||||
|
||||
## `file_read_diff`
|
||||
|
||||
读取同一变更集中一个或多个*其他*文件的 diff——当评论取决于某相关文件是否被
|
||||
更新时有用。
|
||||
|
||||
### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "file_read_diff",
|
||||
"input": {
|
||||
"path_array": ["src/api/handler.go", "src/db/queries.go"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 输出
|
||||
|
||||
```
|
||||
==== FILE: src/api/handler.go ====
|
||||
--- a/src/api/handler.go
|
||||
+++ b/src/api/handler.go
|
||||
@@ -10,1 +10,2 @@
|
||||
- old line
|
||||
+ new line 1
|
||||
+ new line 2
|
||||
|
||||
==== FILE: src/db/queries.go ====
|
||||
@@ -5,1 +5,1 @@
|
||||
- query := "SELECT *"
|
||||
+ query := "SELECT id"
|
||||
```
|
||||
|
||||
若某路径不在变更集中,该条目被静默省略。若请求的路径**都不**在变更集中,工具
|
||||
返回 `Error: diff not found for the requested paths`;空的 `path_array` 返回
|
||||
`Error: no files found`。
|
||||
|
||||
## `file_find`
|
||||
|
||||
按文件名关键字(子串匹配)在仓库中查找文件。
|
||||
|
||||
### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "file_find",
|
||||
"input": {
|
||||
"query_name": "UserService",
|
||||
"case_sensitive": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 必需 | 默认 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `query_name` | 是 | — | 与每个文件的 **basename**(最后一个 `/` 之后的部分)做子串匹配,而非全路径。 |
|
||||
| `case_sensitive` | 否 | `false` | 设为 `true` 做精确大小写匹配。 |
|
||||
|
||||
候选集在工作区模式下来自 `git ls-files --cached --others --exclude-standard`,
|
||||
在区间 / commit 模式下来自 `git ls-tree -r --name-only <ref>`。无扩展名的文件
|
||||
被跳过,但 `Makefile`、`Dockerfile`、`LICENSE`、`Vagrantfile`、
|
||||
`Containerfile` 例外。
|
||||
|
||||
### 输出
|
||||
|
||||
以换行分隔的路径列表:
|
||||
|
||||
```
|
||||
src/main/java/com/example/UserService.java
|
||||
src/test/java/com/example/UserServiceTest.java
|
||||
src/main/java/com/example/internal/UserServiceImpl.java
|
||||
```
|
||||
|
||||
当没有文件匹配(或 `query_name` 为空)时,工具返回字面字符串
|
||||
`// The file was not found`。
|
||||
|
||||
### 限制
|
||||
|
||||
最多返回 **100** 条匹配;超出被静默截断。若模型需要更广搜索,应改用
|
||||
`code_search`。
|
||||
|
||||
## `code_search`
|
||||
|
||||
全仓全文搜索。由 `git grep` 驱动,因此理解 `pathspec` 语法并遵循
|
||||
`.gitignore`。
|
||||
|
||||
### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "code_search",
|
||||
"input": {
|
||||
"search_text": "TODO|FIXME",
|
||||
"file_patterns": ["*.go", ":(exclude)vendor/"],
|
||||
"case_sensitive": false,
|
||||
"use_perl_regexp": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 必需 | 默认 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `search_text` | 是 | — | 字面量字符串或 PCRE 模式(见 `use_perl_regexp`)。 |
|
||||
| `file_patterns` | 否 | 全仓 | pathspec 条目数组。用 `:(exclude)pat` 做排除。 |
|
||||
| `case_sensitive` | 否 | `false` | — |
|
||||
| `use_perl_regexp` | 否 | `false` | 为 `true` 时,`search_text` 作为正则处理。 |
|
||||
|
||||
### 输出
|
||||
|
||||
结果按文件分组。每组以 `File: <path>` 和 `Match lines: <n>` 开头,随后每条命中
|
||||
一行 `line|content`:
|
||||
|
||||
```
|
||||
File: path/to/example.java
|
||||
Match lines: 2
|
||||
433| String name = toolRequest.get().getName();
|
||||
438| logToolRequest(newPath, tool, toolRequest.get());
|
||||
|
||||
File: path/to/other.java
|
||||
Match lines: 1
|
||||
22| var req = new ToolRequest();
|
||||
```
|
||||
|
||||
无匹配时,工具返回字面字符串 `No matches found`。
|
||||
|
||||
### pathspec 速查
|
||||
|
||||
| 目标 | `file_patterns` |
|
||||
|---|---|
|
||||
| 单个文件 | `["src/main.go"]` |
|
||||
| 所有 Go 文件 | `["*.go"]` |
|
||||
| 除测试外所有 Go | `["*.go", ":(exclude)*_test.go"]` |
|
||||
| 仅一个目录 | `["src/api/"]` |
|
||||
| 多种类型,排除 vendor | `["*.go", "*.ts", ":(exclude)vendor/", ":(exclude)node_modules/"]` |
|
||||
|
||||
### 限制
|
||||
|
||||
- 通过 `git grep --max-count 100` 把每文件命中数上限设为 **100**,因此跨多文件的
|
||||
总输出可能超过 100。触及每文件上限时,输出前会加
|
||||
`Note: The results have been truncated. Only showing first 100 results.`。
|
||||
- 空 / 仅空白的 `search_text` 返回 `Error: search_text is blank`,而不是展开成
|
||||
每一行。
|
||||
- 工作区模式搜索**当前工作树**,区间 / commit 模式搜索解析出的目标 ref
|
||||
(`FileReader.Ref` 作为位置参数传给 `git grep`)。
|
||||
|
||||
## 工具执行与错误
|
||||
|
||||
工具在 agent 循环内同步执行,两个例外:
|
||||
|
||||
- `code_comment` 派发到 **CommentWorkerPool**,使循环不阻塞在行解析 + 反思上。
|
||||
- `task_done` 短路——立即返回,不调用任何 provider。
|
||||
|
||||
工具出错时(网络失败、参数格式错误、文件未找到),结果作为常规工具结果交付给模型,
|
||||
文本形如 `"Error: file not found: src/missing.go"`。模型再决定是重试、换文件,
|
||||
还是调 `task_done`。
|
||||
|
||||
若工具名不在注册表中,OCR 返回常量 `tool.NotAvailableMsg` 而不是崩溃。这使得
|
||||
(通过 `--tools`)运行时禁用工具是安全的。
|
||||
|
||||
## 自定义工具
|
||||
|
||||
两种扩展方式:
|
||||
|
||||
### 1. 禁用工具
|
||||
|
||||
复制 `tools.json`,删掉不想要的条目,然后运行:
|
||||
|
||||
```bash
|
||||
ocr review --tools ./my-tools.json
|
||||
```
|
||||
|
||||
例如,想要一个从不读额外上下文的“仅评论”评审器,只保留 `code_comment` 和
|
||||
`task_done`。
|
||||
|
||||
### 2. 重新描述工具
|
||||
|
||||
保留 `name`(provider 内部按 name 查找)但更改 `description` 以引导模型。这是
|
||||
注入项目专属指引最简单的方式——如“使用 `file_read` 时,始终读取变更附近至少
|
||||
30 行。”
|
||||
|
||||
> 添加**新**工具*名*需要在 Go 侧接入;见 `internal/tool/definitions.go` 及
|
||||
> `internal/tool/` 下的 provider。仅靠 JSON 文件无法添加新行为。
|
||||
|
||||
## 另见
|
||||
|
||||
- [架构](../architecture/)——agent 循环如何驱动工具。
|
||||
- [评审规则](../review-rules/)——告知 LLM 关注什么。
|
||||
- [会话查看器](../viewer/)——查看过去评审中到底触发了哪些工具。
|
||||
151
pages/src/content/docs/zh/viewer.md
Normal file
151
pages/src/content/docs/zh/viewer.md
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
---
|
||||
title: 会话查看器
|
||||
sidebar:
|
||||
order: 10
|
||||
---
|
||||
|
||||
`ocr viewer` 是一个小型内嵌 HTTP 服务器,以浏览器友好的 UI 渲染历史评审会话。
|
||||
无外部依赖——会话直接从 OCR 在每次评审期间写入磁盘的 JSONL 文件读取。
|
||||
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
ocr viewer # binds localhost:5483
|
||||
ocr viewer --addr :3000 # bind to all interfaces on port 3000
|
||||
ocr viewer --addr 0.0.0.0:8080 # bind on all interfaces
|
||||
```
|
||||
|
||||
默认地址是 `localhost:5483`。服务器在前台运行——`Ctrl+C` 停止。会话在每次请求时
|
||||
从 `~/.opencodereview/sessions/` 惰性扫描,因此另一个终端里运行的评审一旦其
|
||||
JSONL 文件出现就会显示。
|
||||
|
||||
> **DNS-rebinding 防护。** 查看器会对照 loopback 白名单
|
||||
> (`localhost`、`127.0.0.1`、`::1`)检查 `Host` 头。具体的绑定主机
|
||||
> (如 `--addr 192.168.1.10:5483`)会自动加入,但**通配**绑定
|
||||
> (`:3000`、`0.0.0.0`、`::`)不会——此时从 LAN IP 或主机名访问 UI 会返回
|
||||
> `forbidden host`。要让通配绑定可被访问,设置
|
||||
> `OCR_VIEWER_ALLOWED_HOSTS` 为逗号分隔的允许主机名列表
|
||||
> (如 `OCR_VIEWER_ALLOWED_HOSTS=box.local,192.168.1.10`)。
|
||||
|
||||
## 三个页面
|
||||
|
||||
查看器有三个 URL:
|
||||
|
||||
| URL | 看到内容 |
|
||||
|---|---|
|
||||
| `/` | 磁盘上有会话的所有仓库列表。 |
|
||||
| `/r/{repo}` | 单个仓库的会话列表,最新在前。 |
|
||||
| `/r/{repo}/{sessionID}` | 单个会话的完整详情。 |
|
||||
|
||||
`{repo}` 是一个路径编码字符串(分隔符 `/` 和 `\` 替换为 `-`、冒号替换为
|
||||
`_`——与磁盘目录命名相同的编码)。通常你不会手动输入它——而是点击进入。
|
||||
|
||||
### `/`——仓库列表
|
||||
|
||||
对每个至少有一条会话的仓库,显示仓库路径、总会话数和最近活动时间戳。
|
||||
|
||||
### `/r/{repo}`——单仓库会话列表
|
||||
|
||||
对每个会话:ID(一个 UUID)、分支名(OCR 能检测到时)、评审模式、模型、文件数、
|
||||
时长和开始时间戳。
|
||||
|
||||
### `/r/{repo}/{sessionID}`——会话详情
|
||||
|
||||
详情页是最有用的那个。它显示:
|
||||
|
||||
1. **头部**——diff 范围、模型、分支、总 token、运行时长。
|
||||
2. **文件分组**——每个被评审文件一个块。每个文件内,五条“任务类型”泳道:
|
||||
|
||||
| 任务类型 | 何时出现 |
|
||||
|---|---|
|
||||
| `plan_task` | 运行了 plan 阶段(文件 ≥ `PLAN_MODE_LINE_THRESHOLD`)。 |
|
||||
| `main_task` | 每个文件。主评审循环。 |
|
||||
| `review_filter_task` | 为该文件运行了评审后评论过滤流程。 |
|
||||
| `memory_compression_task` | active+compress 区超过 60 % / 80 % 预算。 |
|
||||
| `re_location_task` | 某条 `code_comment` 无法锚定,回退重新定位运行。 |
|
||||
|
||||
每条泳道是**任务卡片**的水平条带——每个 LLM 往返一张。卡片按任务类型着色,让你
|
||||
一眼看出哪些阶段主导了运行。
|
||||
|
||||
## 任务卡片里有什么
|
||||
|
||||
点击任务卡片展开。每张卡片有:
|
||||
|
||||
- 一行**头部**——请求号、模型徽章、token 徽章(`P:` prompt / `C:` completion,
|
||||
存在时还显示 `CR:` / `CW:` 缓存读写)、时长徽章,以及该轮失败时的错误徽章;
|
||||
- **Response**——原始 assistant 响应,包括任何推理 / `thinking` 块;
|
||||
- **Tool calls**——每个工具调用及其参数 + 返回结果(可折叠)。
|
||||
|
||||
发给模型的完整消息列表和作用域内工具定义**不**在卡片 UI 中渲染;如需要,可直接
|
||||
检查 JSONL 转录(每条 `llm_request` 记录的 `messages` 字段)。
|
||||
|
||||
## 使用场景
|
||||
|
||||
查看器围绕三个工作流设计:
|
||||
|
||||
### “模型为什么这么说?”
|
||||
|
||||
在终端输出中打开一条评论,在查看器中定位该文件,沿着它的 `main_task` 泳道向下查看。
|
||||
**工具调用**中包含你关心的 `code_comment` 的那张卡片,就是产出它的那一轮。卡片的
|
||||
Response 显示模型推理;要确切知道发给模型的 prompt + 上下文,在 JSONL 转录中
|
||||
打开该请求号的 `llm_request` 记录(其 `messages` 字段)。
|
||||
|
||||
### “这个文件为什么静默?”
|
||||
|
||||
一个**无评论**的文件,只有当模型*主动*调用 `task_done` 时才是成功评审。若泳道
|
||||
显示工具调用但无 `code_comment`,那是模型主动给出的干净评审。若泳道以错误卡片结束,那是
|
||||
伪装成静默的失败——应作为警告处理。
|
||||
|
||||
### “压缩保留 / 丢弃了什么?”
|
||||
|
||||
`memory_compression_task` 泳道显示每次压缩轮。其中,Response 窗格有结果摘要;
|
||||
被压缩的 compress 区渲染出的 XML 在该轮 `llm_request` 的 `messages`(JSONL 转录中)。
|
||||
排查“模型忘了早前上下文”这类反馈时有用——你能看到压缩是否丢弃了相关细节。
|
||||
|
||||
## 磁盘存储布局
|
||||
|
||||
查看器读取:
|
||||
|
||||
```
|
||||
~/.opencodereview/sessions/
|
||||
└── <path-encoded-repo-path>/
|
||||
└── <session-id>.jsonl
|
||||
```
|
||||
|
||||
JSONL 文件每行是一个事件:
|
||||
|
||||
```json
|
||||
{"type": "llm_request", "filePath": "src/foo.go", "taskType": "main_task", "request_no": 1, "messages": [{"role": "user", "content": "Review this diff…"}], "timestamp": "2026-06-02T10:15:23Z"}
|
||||
{"type": "llm_response", "filePath": "src/foo.go", "taskType": "main_task", "model": "claude-sonnet-4-6", "content": "Found 2 issues…", "duration_ms": 8421, "usage": {"prompt_tokens": 12450, "completion_tokens": 320}}
|
||||
{"type": "tool_call", "filePath": "src/foo.go", "tool_name": "file_read", "arguments": "{\"file_path\":\"src/foo.go\",\"start_line\":1,\"end_line\":50}", "result": "File: src/foo.go (Total lines: 220)\nIS_TRUNCATED: false\nLINE_RANGE: 1-50\n1|package foo…", "ok": true, "duration_ms": 14}
|
||||
```
|
||||
|
||||
行是 append-only——不完整的 JSONL 意味着会话在运行中被中断,查看器会渲染已写入的
|
||||
内容。
|
||||
|
||||
要释放磁盘空间,删除整个会话文件;查看器在下次请求时重建索引。
|
||||
|
||||
## 隐私
|
||||
|
||||
JSONL 转录包含发给 LLM 和从 LLM 收到的**一切**,包括 diff 中的任何代码。它们
|
||||
完全存在于你机器的 `~/.opencodereview/` 内。OCR 不会把它们上传到任何地方。
|
||||
|
||||
如果你的评审包含你不想长期存储的代码,可以:
|
||||
|
||||
- 定期删除会话文件,或
|
||||
- 在 CI 中把 `--audience agent --format json` 输出重定向到临时管道,并用临时
|
||||
`HOME` 运行,使 JSONL 不会被持久化。
|
||||
|
||||
OpenTelemetry exporter 是另一回事——如何让 prompt 内容不进入导出 trace 见
|
||||
[遥测](../telemetry/)。
|
||||
|
||||
## 查看器*不*适用时
|
||||
|
||||
- 程序化后处理(CI、仪表盘)用 `ocr review --format json --audience agent`。
|
||||
查看器为人渲染,不为机器。
|
||||
- 如需跨多会话 grep,直接对 JSONL 文件用 `jq`。UI 中暂无搜索框。
|
||||
|
||||
## 另见
|
||||
|
||||
- [架构](../architecture/)——那五种任务类型在底层实际做什么。
|
||||
- [工具](../tools/)——你在 `main_task` 卡片中会看到的工具调用。
|
||||
|
|
@ -258,11 +258,37 @@ export const en: TranslationKeys = {
|
|||
'docs.mcpFieldArgsDesc': 'Command-line arguments passed to the server',
|
||||
'docs.mcpFieldEnvDesc': 'Environment variables in KEY=VALUE format',
|
||||
'docs.mcpFieldToolsDesc': 'Allowed tool names; if empty, all tools from the server are available',
|
||||
'docs.mcpFieldSetupDesc': 'A shell command to run before starting the server, such as building an index (5-minute timeout)',
|
||||
'docs.mcpNote': 'If an MCP tool name conflicts with a built-in tool, it will be skipped with a warning.',
|
||||
'docs.mcpFieldSetupDesc': 'A shell command to run before starting the server, such as building an index',
|
||||
'docs.mcpNote': 'If an MCP tool name conflicts with a built-in tool, it will be skipped with a warning. The setup command has a 5-minute timeout.',
|
||||
'docs.mcpExample': 'Example: Add CodeGraph for Code Structure Analysis',
|
||||
'docs.envTitle': 'Claude Code Integration',
|
||||
'docs.envDesc': 'If you are already a Claude Code user with the following environment variables configured, Open Code Review will recognize them automatically — no extra configuration needed:',
|
||||
'docs.envNote': 'You can also use <code>ocr config</code> to override or supplement these settings.',
|
||||
'docs.copy': 'Copy',
|
||||
|
||||
// Docs Sidebar
|
||||
'docs.sidebar.gettingStarted': 'Getting Started',
|
||||
'docs.sidebar.userGuide': 'User Guide',
|
||||
'docs.sidebar.overview': 'Overview',
|
||||
'docs.sidebar.quickstart': 'QuickStart',
|
||||
'docs.sidebar.installation': 'Installation',
|
||||
'docs.sidebar.configuration': 'Configuration',
|
||||
'docs.sidebar.cliReference': 'CLI Reference',
|
||||
'docs.sidebar.reviewRules': 'Review Rules',
|
||||
'docs.sidebar.architecture': 'Architecture',
|
||||
'docs.sidebar.tools': 'Tools',
|
||||
'docs.sidebar.viewer': 'Session Viewer',
|
||||
'docs.sidebar.telemetry': 'Telemetry',
|
||||
'docs.sidebar.integrations': 'Integrations',
|
||||
'docs.sidebar.agentSkill': 'Agent Skill',
|
||||
'docs.sidebar.claudeCode': 'Command (Claude Code)',
|
||||
'docs.sidebar.subprocess': 'Direct Subprocess',
|
||||
'docs.sidebar.cicd': 'CI/CD',
|
||||
'docs.sidebar.contributing': 'Contributing',
|
||||
'docs.sidebar.faq': 'FAQ',
|
||||
'docs.search.placeholder': 'Search...',
|
||||
'docs.search.noResults': 'No results found',
|
||||
'docs.search.hint.select': 'Select',
|
||||
'docs.search.hint.open': 'Open',
|
||||
'docs.search.hint.close': 'Close',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -260,11 +260,37 @@ export const ja: TranslationKeys = {
|
|||
'docs.mcpFieldArgsDesc': 'server に渡すコマンドライン引数',
|
||||
'docs.mcpFieldEnvDesc': 'KEY=VALUE 形式の環境変数',
|
||||
'docs.mcpFieldToolsDesc': '許可するツール名。空の場合は server が提供するすべてのツールを使用できます',
|
||||
'docs.mcpFieldSetupDesc': 'server 起動前に実行する shell コマンド。例:インデックスの構築(タイムアウト 5 分)',
|
||||
'docs.mcpNote': 'MCP ツール名が組み込みツールと競合する場合、そのツールは警告付きでスキップされます。',
|
||||
'docs.mcpFieldSetupDesc': 'server 起動前に実行する shell コマンド。例:インデックスの構築',
|
||||
'docs.mcpNote': 'MCP ツール名が組み込みツールと競合する場合、そのツールは警告付きでスキップされます。setup コマンドのタイムアウトは 5 分です。',
|
||||
'docs.mcpExample': '例:CodeGraph を追加してコード構造を分析',
|
||||
'docs.envTitle': 'Claude Code 統合',
|
||||
'docs.envDesc': 'すでに Claude Code ユーザーで以下の環境変数を設定済みの場合、Open Code Review は自動的に認識します。追加設定は不要です:',
|
||||
'docs.envNote': '<code>ocr config</code> を使用してこれらの設定を上書きまたは補完することもできます。',
|
||||
'docs.copy': 'コピー',
|
||||
|
||||
// Docs Sidebar
|
||||
'docs.sidebar.gettingStarted': '入門ガイド',
|
||||
'docs.sidebar.userGuide': '利用ガイド',
|
||||
'docs.sidebar.overview': '概要',
|
||||
'docs.sidebar.quickstart': 'クイックスタート',
|
||||
'docs.sidebar.installation': 'インストール',
|
||||
'docs.sidebar.configuration': '設定',
|
||||
'docs.sidebar.cliReference': 'CLI リファレンス',
|
||||
'docs.sidebar.reviewRules': 'レビュールール',
|
||||
'docs.sidebar.architecture': 'アーキテクチャ',
|
||||
'docs.sidebar.tools': 'ツール',
|
||||
'docs.sidebar.viewer': 'セッションビューア',
|
||||
'docs.sidebar.telemetry': 'テレメトリ',
|
||||
'docs.sidebar.integrations': '統合',
|
||||
'docs.sidebar.agentSkill': 'Agent Skill',
|
||||
'docs.sidebar.claudeCode': 'Command(Claude Code)',
|
||||
'docs.sidebar.subprocess': 'Direct Subprocess',
|
||||
'docs.sidebar.cicd': 'CI/CD',
|
||||
'docs.sidebar.contributing': 'コントリビュート',
|
||||
'docs.sidebar.faq': 'FAQ',
|
||||
'docs.search.placeholder': '検索...',
|
||||
'docs.search.noResults': '結果が見つかりません',
|
||||
'docs.search.hint.select': '選択',
|
||||
'docs.search.hint.open': '開く',
|
||||
'docs.search.hint.close': '閉じる',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -260,11 +260,37 @@ export const zh: TranslationKeys = {
|
|||
'docs.mcpFieldArgsDesc': '传递给 server 的命令行参数',
|
||||
'docs.mcpFieldEnvDesc': 'KEY=VALUE 格式的环境变量',
|
||||
'docs.mcpFieldToolsDesc': '允许使用的工具名称;为空时可使用该 server 提供的全部工具',
|
||||
'docs.mcpFieldSetupDesc': '启动 server 前运行的 shell 命令,例如构建索引(超时 5 分钟)',
|
||||
'docs.mcpNote': '如果 MCP 工具名称与内置工具冲突,该工具将被跳过并显示警告。',
|
||||
'docs.mcpFieldSetupDesc': '启动 server 前运行的 shell 命令,例如构建索引',
|
||||
'docs.mcpNote': '如果 MCP 工具名称与内置工具冲突,该工具将被跳过并显示警告。setup 命令的超时时间为 5 分钟。',
|
||||
'docs.mcpExample': '示例:添加 CodeGraph 进行代码结构分析',
|
||||
'docs.envTitle': 'Claude Code 集成',
|
||||
'docs.envDesc': '如果您已经是 Claude Code 用户并配置了以下环境变量,Open Code Review 将自动识别——无需额外配置:',
|
||||
'docs.envNote': '您也可以使用 <code>ocr config</code> 覆盖或补充这些设置。',
|
||||
'docs.copy': '复制',
|
||||
|
||||
// Docs Sidebar
|
||||
'docs.sidebar.gettingStarted': '入门指南',
|
||||
'docs.sidebar.userGuide': '使用指南',
|
||||
'docs.sidebar.overview': '概览',
|
||||
'docs.sidebar.quickstart': '快速开始',
|
||||
'docs.sidebar.installation': '安装',
|
||||
'docs.sidebar.configuration': '配置',
|
||||
'docs.sidebar.cliReference': 'CLI 参考',
|
||||
'docs.sidebar.reviewRules': '评审规则',
|
||||
'docs.sidebar.architecture': '架构',
|
||||
'docs.sidebar.tools': '工具',
|
||||
'docs.sidebar.viewer': '会话查看器',
|
||||
'docs.sidebar.telemetry': '遥测',
|
||||
'docs.sidebar.integrations': '集成',
|
||||
'docs.sidebar.agentSkill': 'Agent Skill',
|
||||
'docs.sidebar.claudeCode': 'Command(Claude Code)',
|
||||
'docs.sidebar.subprocess': 'Direct Subprocess',
|
||||
'docs.sidebar.cicd': 'CI/CD',
|
||||
'docs.sidebar.contributing': '贡献',
|
||||
'docs.sidebar.faq': 'FAQ',
|
||||
'docs.search.placeholder': '搜索...',
|
||||
'docs.search.noResults': '未找到结果',
|
||||
'docs.search.hint.select': '选择',
|
||||
'docs.search.hint.open': '打开',
|
||||
'docs.search.hint.close': '关闭',
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
242
pages/src/styles/docs-markdown.css
Normal file
242
pages/src/styles/docs-markdown.css
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/* Docs Markdown dark theme styles — matching DocsPage design */
|
||||
.docs-markdown {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Segoe UI', Roboto, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.docs-markdown h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
margin: 0 0 24px 0;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.docs-markdown h2 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
margin: 40px 0 16px 0;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.docs-markdown h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
margin: 28px 0 12px 0;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.docs-markdown h4 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin: 20px 0 8px 0;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.docs-markdown p {
|
||||
margin: 0 0 12px 0;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.docs-markdown a {
|
||||
color: #2BDE5E;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.docs-markdown a:hover {
|
||||
opacity: 0.8;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
.docs-markdown code {
|
||||
font-family: 'Menlo', 'Monaco', 'Consolas', monospace;
|
||||
font-size: 13px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 4px;
|
||||
padding: 2px 6px;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
.docs-markdown pre {
|
||||
background: #000000;
|
||||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||
border-radius: 6px;
|
||||
padding: 4px 16px;
|
||||
margin: 0 0 16px 0;
|
||||
overflow-x: auto;
|
||||
display: flex;
|
||||
align-self: stretch;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.docs-markdown pre code {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font-size: 13px;
|
||||
line-height: 22px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
white-space: pre;
|
||||
display: block;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
.docs-markdown ul,
|
||||
.docs-markdown ol {
|
||||
margin: 0 0 16px 0;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.docs-markdown li {
|
||||
margin-bottom: 6px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.docs-markdown li > ul,
|
||||
.docs-markdown li > ol {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.docs-markdown table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
margin: 0 0 16px 0;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||
}
|
||||
|
||||
.docs-markdown thead th {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
text-align: left;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.16);
|
||||
}
|
||||
|
||||
.docs-markdown thead th:first-child {
|
||||
border-top-left-radius: 8px;
|
||||
}
|
||||
|
||||
.docs-markdown thead th:last-child {
|
||||
border-top-right-radius: 8px;
|
||||
}
|
||||
|
||||
.docs-markdown tbody td {
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.docs-markdown tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.docs-markdown tbody tr:last-child td:first-child {
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
|
||||
.docs-markdown tbody tr:last-child td:last-child {
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
|
||||
/* Blockquotes */
|
||||
.docs-markdown blockquote {
|
||||
margin: 0 0 16px 0;
|
||||
padding: 12px 16px;
|
||||
border-left: 2px solid #2BDE5E;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border-radius: 0 6px 6px 0;
|
||||
}
|
||||
|
||||
.docs-markdown blockquote p {
|
||||
margin: 0;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.docs-markdown blockquote p + p {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* Horizontal rule */
|
||||
.docs-markdown hr {
|
||||
border: none;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
/* Strong / bold */
|
||||
.docs-markdown strong {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Em / italic */
|
||||
.docs-markdown em {
|
||||
font-style: italic;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
/* Images */
|
||||
.docs-markdown img {
|
||||
max-width: 100%;
|
||||
border-radius: 8px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
/* Mermaid code blocks — hide raw source (will be replaced by rendered SVG) */
|
||||
.docs-markdown pre code.language-mermaid {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Rendered mermaid diagrams */
|
||||
.docs-markdown .mermaid-rendered {
|
||||
margin: 16px 0;
|
||||
padding: 24px 16px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.docs-markdown .mermaid-rendered svg {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Scrollbar styling for code blocks */
|
||||
.docs-markdown pre::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.docs-markdown pre::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.docs-markdown pre::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 3px;
|
||||
}
|
||||
10
pages/src/utils/headingId.ts
Normal file
10
pages/src/utils/headingId.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* Shared utility to generate heading IDs from text.
|
||||
* Used by both extractHeadings (DocsPage TOC) and MarkdownRenderer (heading renderer)
|
||||
* to ensure consistent anchor IDs.
|
||||
*/
|
||||
export function generateHeadingId(text: string): string {
|
||||
// Strip HTML tags first (from marked output), then strip markdown formatting chars
|
||||
const plain = text.replace(/<[^>]+>/g, '').replace(/[`*_\[\]()]/g, '').trim();
|
||||
return plain.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, '-').replace(/^-|-$/g, '');
|
||||
}
|
||||
|
|
@ -36,6 +36,10 @@ module.exports = {
|
|||
{
|
||||
test: /\.(png|jpg|jpeg|gif)$/,
|
||||
type: 'asset/resource'
|
||||
},
|
||||
{
|
||||
test: /\.md$/,
|
||||
type: 'asset/source'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue