Skip to content

Agent contract (llms.txt)

Co-Review is an open-source repository review application built on Eclipse Theia. A human reviews a whole repository (not just a diff) with IDE navigation, leaves threads on files, symbols, lines and ranges, and agents take part in the review as co-reviewers. This file is the integration contract for agents and tools.

Two integration paths:

  • MCP — an agent running in its own harness (Claude Code, Codex, Cursor, …) opens a review and acts as co-reviewer through MCP tools. Use this when you (the agent) did the work being reviewed.
  • ACP — the reviewer launches an ACP agent from Co-Review; Co-Review is the ACP client and sends each question as a prompt. Use this to make an agent available on demand.

Claude Code: install the Co-Review plugin (/plugin marketplace add sdsvn/co-review, /plugin install co-review@co-review); it provides these tools as plugin:co-review:co-review, the co-review / co-review-design skills, the /co-review:audit, /co-review:review, /co-review:pr and /co-review:design commands, a background co-review:co-reviewer subagent, a SessionStart hook, and a channel: with claude --dangerously-load-development-channels plugin:co-review@co-review (research preview) the reviewer’s questions arrive as channel messages (meta thread_id, event: comment | submitted); answer with reply({ threadId: thread_id }) instead of looping await_reviewer. Pi: the native package in integrations/pi, also shipped inside the app (Co-Review.app/Contents/Resources/app/integrations/pi, ~/.local/share/co-review/resources/app/integrations/pi): tools co_review_* (co_review_map = repo_map), /co-review, /co-review-audit, /co-review-change, /co-review-pr, /co-review-design. Oh My Pi (omp): the native package in integrations/omp (co-review setup omp, which installs the copy shipped inside Co-Review; co-review setup pi for Pi): the same tools and commands, plus a co-reviewer task agent. Other harnesses: docs/agent-setup.md. If Co-Review seems stuck (a window not responding, calls that keep failing), run co-review logs: it says whether Co-Review answers and prints the recent log of its windows, backend and agent bridges; show the user the relevant lines.

Endpoint: co-review mcp (stdio; node <co-review>/bin/co-review.mjs mcp [repo], repo defaults to the working directory; starts Co-Review on the first tool call if it is not running, not when the agent session starts), or Streamable HTTP at http://127.0.0.1:<port>/mcp?root=<abs repo path> (local connections only; the running instance is recorded in ~/.co-review/server.json).

Workflows (docs/workflows.md), each started by a command: the whole repository (/co-review:audit: open_review({ root })), your change (/co-review:review: open_review({ diff: "HEAD" }) or ({ diff: "main...HEAD" }), a pull-request page of the diff), someone else’s pull request (/co-review:pr <n>: a review directory with a PR.md, and open_review({ dir, diff: "<base>...<branch>", patchName })), and a design (/co-review:design: open_review({ dir }) on the design document). Every workflow has the same shape: prepare the review without showing it (open_review({ …, open: false }), which returns reviewId), do your first pass (add_findings; for a design, fix every format.warnings entry), then show it with open_review({ reviewId }) and give the reviewer the returned url. Then loop await_reviewer → investigate → reply until it returns the reviewer’s Submit, and act on it. await_reviewer is the only call to wait on.

Whole-repository review (first-pass audit, /co-review:audit): open_review({ title, open: false }) (prepares the review without showing it), then repo_map({ overview: true }) for where to start and how the code clusters, and repo_map for every file. Split the repository into 4–10 areas, read the riskiest code in each, and add_findings with the area as the first label and status: "proposed" (at most three per area; on a folder: path without line; on the repository: path: "."). Add your overview with open_review({ reviewId, overview, open: false }). Then open_review({ reviewId }) shows it, opening on the review’s overview page (your overview, every thread grouped by its first label, and the repository’s areas, as one rendered page; comments on it come back with target: "overview" and the quoted text as source); tell the reviewer the areas and counts, then answer as usual. get_review returns coverage (files viewed per area, and each area’s next unviewed file) to say what’s left.

How to answer: the reviewer is a person reading a thread. Answer the question in the first sentence, in plain language; keep it to a few sentences or a short list; explain in words rather than walking through paths and line numbers (if a pointer helps, end with one or two relative/path.ext:line links — the UI makes them clickable); read only what you need so the answer comes fast.

Tools (all ids are strings; lines are 1-based). Thread ids are accepted as short ids (t<N>) or full ids.

  • open_review({ reviewId?, root?, dir?, markdown?, patch?, diff?, patchName?, storePath?, openspec?, title?, open?, overview? }) → { reviewId, title, openThreads, proposedFindings, url?, note?, hasDoc?, patches?, dir? }. reviewId joins and shows an existing review (e.g. one prepared with open: false); the other arguments are then ignored. Otherwise either a repository (root, default: the one Co-Review was started for) or a review directory (dir) / inline markdown and patch (written to a directory under ~/.co-review/inline). diff (with root) also gives the review its code: the reviewer’s window opens on the repository (their own change) or a git worktree of the head (a pull request), so the IDE works around the diff; findings with path + line on a review directory then resolve in that code. diff is a git revision range of the repository ("HEAD" for uncommitted work, untracked files included; "main...HEAD" for a branch) that Co-Review diffs itself and opens as the patch page patchName (default "change"); with dir it is written into that directory, next to a PR.md. Makes you the review’s agent. title forces a new review. storePath is where the review state file review.json is written (default <dir>/review.json). openspec is an OpenSpec change directory rendered as cards (<dir>/openspec is detected). open (default true) shows the review: the browser, or the repository’s window in the desktop app (opened, or focused if it is already open), which returns a note instead of url. open: false prepares it without showing it (the result’s note says how to show it); open_review({ reviewId }) shows it. overview is your Markdown for the review’s front page, the overview page the reviewer reads first (what the change or repository does and why, how it works as a ```mermaid flowchart, the blast radius, where to start); pass it in the prepare step, or again with reviewId to update it. Every review opens on its overview page (with the pull request from PR.md, the files changed, and every thread), except a review directory without patches (a design or knowledge bundle), which opens on its document.
  • await_reviewer({ reviewId?, timeoutSec? = 240 }) → the reviewer’s next move, whichever it is; the one call to wait on. { status: "comment", comments[] (open threads with lastRole, needsReply), openCount, needsReply, threads: Thread[] } when threads wait for you: the reviewer’s latest message is a question (intent: "question"), in a thread you took part in (including their choice for an ask_reviewer question, as their message Chose: **…**), or — in a review directory — any reviewer comment (unresolved threads, proposed findings included). Each is delivered once per MCP session; delivered threads show “ is on it” until you reply. { status: "submitted", decision: "approve" | "request-changes" | "comment", summary, round, doc: { slug, version }, comments[], acceptedSuggestions: [{ threadId, path, startLine, before, after }], openCount, proposedCount } when the reviewer clicked Submit review (comments still waiting for you come first). A round submitted while no agent was connected is delivered to the next agent that joins. { status: "closed", reviewId, by: "reviewer", hint } when the reviewer closed the review’s window or quit Co-Review (told once, also after Co-Review restarts): stop waiting on that review and tell the user what is still open; open_review({ reviewId }) shows it again. { status: "pending" } on timeout (call again). comments[] are the open threads (accepted findings included, unaccepted proposals excluded): { id: "t2", target: "doc" | "doc:<path>" | "patch:<slug>" | "overview" | "code", kind: <anchor type>, where, line, side, severity, labels, status, body (latest message), source, origin, intent }.
  • Every thread in threads (await_reviewer, get_review) on a line, range or document text has anchorStatus: what happened to the code it is on as files changed (yours or anyone’s edits): active (still there, maybe moved), modified (changed but identified; original is the text that was commented), removed (deleted: out of scope), ambiguous (now fits several places; candidates is how many: out of scope). Out-of-scope comments are shown to the reviewer in a separate tab, not on the code.
  • reply({ threadId, body, resolve? }) → { ok }. Markdown. resolve: true resolves the thread.
  • add_findings({ findings }) → { created, ids }. Two shapes: { path, line?, endLine?, body, severity?, labels?, status? } opens a thread on repository code (without line: on the file or folder, "." for the repository; the first of labels is the area the panel’s By area view groups by; status: "proposed" makes it a finding the reviewer accepts or dismisses) (stored semantically: symbol + Tree-sitter anchor); anchored findings { id, target: "doc" | "doc:<path>" | "patch:<slug>", anchor, severity, labels, body, verdict?, proposal?: { before, after, path?, startLine? } } become proposed threads the reviewer accepts or dismisses (only accepted ones come back in the submitted batch). A proposal is a suggested edit; when the reviewer accepts it on a patch it appears in acceptedSuggestions — apply it as a new commit on the branch, do not rewrite the reviewed patch. Accepted document edits are written into the document by Co-Review (doc.version increments).
  • ask_reviewer({ question, options: string[1..6], threadId?, timeoutSec? = 900 }) → what await_reviewer returns, plus question: { threadId, status: "answered" | "pending", choice? }. Shows the options as buttons in the thread, then waits like await_reviewer: the choice arrives as the reviewer’s message in that thread; if they comment elsewhere, reply in the thread instead or submit first, that comes back first, and the choice later from await_reviewer.
  • post_review_to_github({ reviewId?, summary?, comments?: [{ threadId, body }] }) → { status: "posted", url, comments, inBody, event, notes } or { status: "declined" | "pending" }. For a pull request’s review (a review directory whose PR.md has repo + pr, or url): posts it as one GitHub review through the GitHub CLI (gh api; gh auth login is the setup). Open threads on diff lines the reviewer wrote in or accepted become line comments, each the reviewer’s point only: the body given for its thread in comments (the reviewer’s comment reworded in their voice, without the discussion with the agent), else the reviewer’s own messages; agent replies are never posted (accepted suggested edits become GitHub suggestion blocks), other comments go into the review’s body after the summary, and the reviewer’s latest verdict is the event (APPROVE, REQUEST_CHANGES, COMMENT; on your own pull request GitHub allows only COMMENT, and notes says so). Unaccepted proposed findings, resolved threads and the agent’s own threads are left out. Only when the reviewer asks: Co-Review shows them what will be posted and posts only if they click Post to GitHub. The reviewer can also post it themselves (Also post to GitHub in Submit review, or Review: Post Review to GitHub…).
  • repo_map({ root?, path?, query?, refresh?, overview? }) → plain text: every source file with the classes, functions and methods it defines (Tree-sitter), one line per file. Built on demand, cached outside the repository and refreshed for changed files. path narrows to a folder or file, query to files whose path or symbol names contain it. Use it to find where things live before searching. overview: true returns the repository overview instead (Markdown: where to start, the areas of the code and how they connect, with path:line links). When the graphify command (Graphify, https://graphify.net) is installed, Co-Review first builds or refreshes the repository’s Graphify graph (graphify update: Tree-sitter only, no LLM, seconds; written to graphify-out/, which it adds to the clone’s .git/info/exclude if it created it) and builds the overview from it (most connected definitions, communities, a Mermaid diagram of the links between them; graphify-out/GRAPH_REPORT.md has import cycles and surprising connections); otherwise from the repo map. The reviewer opens the same page with Review: Open Repository Overview.
  • get_review({ reviewId?, root? }) → the submitted batch (without blocking) plus threads: Thread[] and, for repository reviews, coverage: { total, viewed, areas: [{ path, total, viewed, next? }] }: the source files the reviewer marked as viewed (⌘⌥V; a file changed since counts as not viewed), overall and per area (folders, as deep as needed for at least three). next is the area’s first file not viewed yet.

Thread:

{
"threadId": "…", "number": 5, "status": "open | proposed | resolved", "intent": "comment | question", "severity": "high", "labels": ["orders"],
"location": { "kind": "repository | directory | file | symbol | line | range", "path": "internal/orders/service.go", "startLine": 17, "endLine": 17, "symbol": "OrderService.CreateOrder" },
"code": "\ts.repo.Save(ctx, o)",
"messages": [{ "author": "Jane", "role": "human | agent", "body": "…" }]
}

Method, examples and full specification: docs/design-docs.md.

Declare the structure with frontmatter; Co-Review then renders the design tree and checks the document against the contract, returning departures in format.warnings from open_review and in doc.format from await_reviewer’s submitted batch / get_review (also shown as a banner to the reviewer). Undeclared documents are rendered leniently (*.pseudocode.md or a ## Design section → tree; otherwise plain Markdown); nothing is ever dropped — sections outside the contract render as Markdown.

A design document is the smallest document that lets the reviewer understand, challenge and approve the change. The instructions every agent gets:

  1. Investigate the code first and reuse existing patterns. If something new is needed (a dependency, service or table), make it an explicit design step. Don’t invent requirements: ask under Open Questions.

  2. Write design/<name>/index.markdown, for a person to read:

    ---
    co-review: design
    ---
    # <the change, as a short imperative>
    ## Context why change, what happens today (a few sentences)
    ## Design required: one nested "- " list, 2 spaces per level
    L1 what (a behaviour, not a technology), L2 how, L3 what can go wrong
    ## Alternatives optional: real choices, their trade-offs, why this one
    ## Behaviour optional: runtime scenarios (failure, retry, crash, race)
    ## Open Questions optional: decisions only the reviewer can make, as "- " items

    Plain language, one decision per step. Name code once, at the top level (entry point, module, new table), then describe it in words. No line numbers, no file:line links, no pasted code (Mermaid is fine). Think through compatibility, security, failure handling and migration, but write down only what changes the design. Size it to the change: about 10-30 lines if small, 30-80 medium, 80-150 large. Cut what doesn’t help the reviewer decide.

  3. Check it before I see it: open_review({ dir: "design/<name>", open: false }) lists problems in format.warnings. Fix every one and call it again until there are none. Keep the reviewId it returns. Then show it with open_review({ reviewId }) and give me the URL (if there is one).

  4. Answer comments in their threads and revise the design; say in each thread what changed. A comment follows its text through your edits (marked changed if you reword it); if you delete what it was on, it leaves the page. An edit I accept is written into the document by Co-Review: re-read the file before editing it.

  5. Implement only after approval: keep calling await_reviewer (answering comments with reply) until it returns my Submit. Request changes → revise, reply in each thread, wait again. Approve → implement, and if the code must depart from the approved design, update the design and ask again.

Format details: sections that are present keep the order Context, Design, Alternatives, Behaviour, Diagrams, Open Questions (## Diagrams holds Mermaid; Behavior is accepted). ## Design steps are - items only (not *, + or 1.); an optional short reason goes after —; optional markers in backticks: ? open question, ⚠ risk, ✎ new name. Comments anchor to a step’s text (backticks/asterisks stripped, whitespace collapsed, first 80 chars), so add or move steps instead of rewording commented ones. Give Mermaid blocks %% id: and nodes explicit ids. Besides the structure, format.warnings reports line references, non-Mermaid code blocks, steps longer than about two lines, and code names repeated in 3+ steps.

A review directory holds what an agent wants reviewed; open it with open_review({ dir }):

  • The document, first match: index.markdown, index.md, INDEX.md, README.md, readme.md, then any *.pseudocode.md. GitHub-flavoured Markdown; ```mermaid fences render as diagrams (%% id: <name> as the first line gives a stable block id; give nodes explicit ids). A ## Design section (or a *.pseudocode.md name) renders the L1/L2/L3 design tree: one nested - list, `?` `⚠` `✎` badges, reasoning after — muted. [text](https://github.com/sdsvn/co-review/blob/main/limiter.patch) links open the patch page.
  • Markdown pages open rendered, in a code review as well as in a review directory: doc:<path> is relative to the directory, or to the repository in a code review. Comments on them are text or document anchors (or diagram and step anchors). HTML files are not rendered in Co-Review (they open as source; the reviewer opens them in the system browser): comment on them as code.
  • Other Markdown pages in the directory (e.g. the concept pages of an OKF (Open Knowledge Format) bundle: open_review({ dir: "<repo>/okf" })) open as review pages too, and their threads have target: "doc:<path relative to dir>"; use that target to anchor findings on a page. Links: /x.md resolves from the directory’s root, path/to/file.ext:42 and file.ext#L42-L50 open the code at that line (resolved next to the page, then from the directory and the folder above it, i.e. the repository for <repo>/okf). Frontmatter type, description, resource shows as a header with a link to resource.
  • *.patch / *.diff: one review page each (git diff, --cached, format-patch). PR.md / <patch>.md: key: value lines (title, pr, branch, base, commits, and repo: <owner>/<name> or url: <pull request URL> for a GitHub pull request, which lets the review be posted back to it), a blank line, then Markdown.
  • openspec/ (optional): an OpenSpec change (proposal.md, design.md, tasks.md, specs/<cap>/spec.md), shown as cards under the document.
  • <patch>.comments.json (array) or .comments.jsonl: pre-seeded findings { id, severity, labels, anchor, body, verdict?, proposal? }, ingested as proposed threads, idempotent by id, picked up live when the file changes.
  • Anchors: document document, text (exact, prefix, suffix, offset hints; the text as rendered), mermaid-block / mermaid-node / mermaid-edge (blockId, nodeId, edgeId), tree-node (nodeId = step text without backticks/asterisks, whitespace-collapsed, max 80 chars); patch patch, code-file (path), code-line (path, line, side), code-range (path, startLine, endLine, side).
  • The state is written to review.json (or storePath): { doc, threads: [{ id: "t<N>", target, anchor, status, severity, labels, origin, intent, sourceId, proposal?, messages: [{ author, role, time, body }] }], seq, review: { decision, summary, submittedAt, count } }; review.count increments on each Submit.

Server mode: co-review-server [-dir <dir> | <dir>] [-md <file>] [-patch <file>] [-addr host:port] [-store <file>] [-openspec <dir>] opens the review in Co-Review, prints review server on http://HOST:PORT …, serves /api/context, POST /api/threads/<id>/messages and POST /api/findings there (/ redirects to Co-Review), and Co-Review writes -store. co-review-server mcp = co-review mcp.

Co-Review spawns the configured command in the repository root and speaks ACP over stdio (@agentclientprotocol/sdk). Any ACP agent works; presets (offered when installed): npx -y @agentclientprotocol/claude-agent-acp, gemini --experimental-acp, opencode acp, goose acp, omp acp (Oh My Pi), npx -y pi-acp (Pi), npx -y @zed-industries/codex-acp (Codex).

  • Model: after the reviewer picks an agent, Co-Review opens a session and offers the agent’s select config options of category model and thought_level (reasoning effort). The choice is stored in review.agent.settings (config option id → value) and applied to every session with session/set_config_option. Modes are not offered (some grant permissions).

  • Client capabilities: fs.readTextFile, fs.writeTextFile (restricted to the repository). No terminal capability.

  • One ACP session per review thread (session/new with cwd = repository root, no MCP servers). The agent is started, and one session opened ahead of time, as soon as it is connected, so a question doesn’t wait for either. When the agent sends several messages in one turn, the last is the answer and earlier ones are listed as steps. The first prompt contains: the review and thread context, the commented code, a resource_link to the file, and the conversation so far. Later prompts contain only the reviewer’s new message(s).

  • session/update: agent_message_chunk (text) streams into the thread as markdown; tool_call / tool_call_update become concise activity lines (title + status).

  • session/request_permission: the options are shown as buttons in the thread; the turn waits for the reviewer. session/cancel is sent when the reviewer presses Stop.

  • The first prompt also carries the answer style above and a repository map (the repo_map outline, files near the question first, when it is ready within 2 s). The agent is started as soon as it is connected, so the first question doesn’t wait for it to launch.

  • Answer in plain language, briefly, and do not modify files unless asked.

Reviews are JSON files outside the repository: ~/.co-review/workspaces/<sha256(root uri)[0:16]>/reviews/<reviewId>.json (CO_REVIEW_HOME overrides ~/.co-review); workspace.json in the same folder records the repository path. Treat them as read-only and write through MCP (the running app caches and broadcasts state). Shape: Review { id, title, workspaceRoot, scope, participants, threads: ReviewThread[], activity, agent? }, ReviewThread { id, number, location: { kind, uri, range (0-based), symbol, anchor }, messages, status, intent, severity?, agentState? }. Types: extensions/review/src/common/review-model.ts.