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.
MCP (agent in a harness)
Section titled “MCP (agent in a harness)”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? }.reviewIdjoins and shows an existing review (e.g. one prepared withopen: 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) / inlinemarkdownandpatch(written to a directory under~/.co-review/inline).diff(withroot) 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 withpath+lineon a review directory then resolve in that code.diffis 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 pagepatchName(default"change"); withdirit is written into that directory, next to aPR.md. Makes you the review’s agent.titleforces a new review.storePathis where the review state filereview.jsonis written (default<dir>/review.json).openspecis an OpenSpec change directory rendered as cards (<dir>/openspecis 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 anoteinstead ofurl.open: falseprepares it without showing it (the result’snotesays how to show it);open_review({ reviewId })shows it.overviewis 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 withreviewIdto update it. Every review opens on its overview page (with the pull request fromPR.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 anask_reviewerquestion, as their messageChose: **…**), 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 hasanchorStatus: 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;originalis the text that was commented),removed(deleted: out of scope),ambiguous(now fits several places;candidatesis 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: trueresolves the thread.add_findings({ findings })→{ created, ids }. Two shapes:{ path, line?, endLine?, body, severity?, labels?, status? }opens a thread on repository code (withoutline: on the file or folder,"."for the repository; the first oflabelsis 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). Aproposalis a suggested edit; when the reviewer accepts it on a patch it appears inacceptedSuggestions— 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.versionincrements).ask_reviewer({ question, options: string[1..6], threadId?, timeoutSec? = 900 })→ whatawait_reviewerreturns, plusquestion: { threadId, status: "answered" | "pending", choice? }. Shows the options as buttons in the thread, then waits likeawait_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 fromawait_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 whosePR.mdhasrepo+pr, orurl): posts it as one GitHub review through the GitHub CLI (gh api;gh auth loginis the setup). Open threads on diff lines the reviewer wrote in or accepted become line comments, each the reviewer’s point only: thebodygiven for its thread incomments(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 GitHubsuggestionblocks), 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 onlyCOMMENT, andnotessays 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.pathnarrows to a folder or file,queryto files whose path or symbol names contain it. Use it to find where things live before searching.overview: truereturns the repository overview instead (Markdown: where to start, the areas of the code and how they connect, withpath:linelinks). When thegraphifycommand (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 tographify-out/, which it adds to the clone’s.git/info/excludeif 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.mdhas 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) plusthreads: 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).nextis 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": "…" }]}Design documents
Section titled “Design documents”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:
-
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.
-
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 levelL1 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 "- " itemsPlain 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.
-
Check it before I see it:
open_review({ dir: "design/<name>", open: false })lists problems informat.warnings. Fix every one and call it again until there are none. Keep thereviewIdit returns. Then show it withopen_review({ reviewId })and give me the URL (if there is one). -
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.
-
Implement only after approval: keep calling
await_reviewer(answering comments withreply) 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.
Review directories
Section titled “Review directories”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;```mermaidfences render as diagrams (%% id: <name>as the first line gives a stable block id; give nodes explicit ids). A## Designsection (or a*.pseudocode.mdname) 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 aretextordocumentanchors (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 havetarget: "doc:<path relative to dir>"; use that target to anchor findings on a page. Links:/x.mdresolves from the directory’s root,path/to/file.ext:42andfile.ext#L42-L50open 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). Frontmattertype,description,resourceshows as a header with a link toresource. *.patch/*.diff: one review page each (git diff,--cached,format-patch).PR.md/<patch>.md:key: valuelines (title,pr,branch,base,commits, andrepo: <owner>/<name>orurl: <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 byid, 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); patchpatch,code-file(path),code-line(path,line,side),code-range(path,startLine,endLine,side). - The state is written to
review.json(orstorePath):{ 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.countincrements 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.
ACP (agent launched by Co-Review)
Section titled “ACP (agent launched by Co-Review)”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
selectconfig options of categorymodelandthought_level(reasoning effort). The choice is stored inreview.agent.settings(config option id → value) and applied to every session withsession/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/newwithcwd= 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, aresource_linkto 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_updatebecome concise activity lines (title + status). -
session/request_permission: the options are shown as buttons in the thread; the turn waits for the reviewer.session/cancelis sent when the reviewer presses Stop. -
The first prompt also carries the answer style above and a repository map (the
repo_mapoutline, 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.
Review data
Section titled “Review data”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.
- README: overview and quick start
- How agents review with you: ACP vs MCP, setup for harnesses
- Running and packaging: build, run, package
- Architecture: how the extension is put together