Plugins
Extend ocra with VCS adapters, runtimes, reviewers, rules, tools and listeners.
ocra's own adapters, runtimes, reviewers and session log are built-in plugins; yours use the same contract.
Writing a plugin
A plugin is a module that exports an object as default (or plugin):
// tools/ocra-team-rules.mjs
export default {
name: "team-rules",
configure(ctx) {
ctx.registerRules([
{
path: "services/**",
rule: `Owned by ${ctx.settings.team}: check idempotency keys on every write.`,
},
]);
},
};Register it in .ocra/config.json:
{
"plugins": ["./tools/ocra-team-rules.mjs"],
"pluginSettings": { "team-rules": { "team": "payments" } }
}Plugins resolve from the repository's dependencies (package names) or from paths relative to the repository root (which may point outside it). They run code, so they load only in local reviews, never for pull or merge requests or with --no-repo-config.
Plugins from your ocra Cloud account
When you are signed in, your ocra Cloud settings may name plugins by npm package name, with pluginSettings. The account only names them: a plugin loads on a machine only after you allow it there with ocra plugins allow.
ocra plugins allow ocra-plugin-team-rules@1.4.0 # shows the package and its publisher, then installs it
ocra plugins list
ocra plugins deny ocra-plugin-team-rulesallow takes an exact version, never a range or tag. It shows the registry's name, version, publisher, maintainers and integrity, installs with install scripts off (details in ocra plugins), and records name, version and integrity in plugins.json (readable only by you). npm runs in that directory, so your project's npm configuration does not apply; a package without a listed integrity is refused. The integrity covers the package tarball, not its dependencies, which npm resolves at install time. Allowing a plugin is a supply-chain decision, like adding a dependency.
A review loads a plugin your account names only:
- if this machine allowed it, from that directory alone, never from the reviewed checkout's
node_modules, and only while the installed package still has the version and integrity recorded when you allowed it; - where a repository's plugins could load: local reviews, never
--pror--mr, never with--no-repo-config.
An account plugin this machine has not allowed is skipped with one warning giving ocra plugins allow <name>@<version>. One the repository already lists loads as the repository's; one whose plugin name is taken is skipped; one that fails to load is skipped with a warning, never failing the review.
The account's pluginSettings are keyed by npm package name (@acme/ocra-team-rules, not the team-rules plugin it exports) and reach only the account's loaded plugins, under their plugin name. The configuration's pluginSettings win, and a repository plugin's settings come from the repository alone.
Lifecycle
| Hook | Runs | On failure |
|---|---|---|
bootstrap(ctx) | All plugins concurrently | Warning; the review continues |
configure(ctx) | In load order | The review stops, naming the plugin |
postConfigure(ctx) | Concurrently, after all registrations | The review stops |
Use bootstrap for work that may fail harmlessly, such as fetching remote settings.
What a plugin can register
| Method | Contributes |
|---|---|
registerVcs(name, factory) | A code host adapter |
registerRuntime(name, factory) | An agent runtime |
registerReviewer(definition) | A reviewer with its own prompt and model tier |
registerRules(rules) | Path-scoped review rules |
registerTool(tool) | A tool reviewers can call, with a Zod input schema |
onEvent(listener) | A listener for every review event (telemetry, notifications). A listener that throws is disabled with one warning; the review continues |
Plugins receive only their own settings (ctx.settings), validated by an optional settingsSchema. Registering a name twice, using a reserved tool name, or registering after configure fails with an error naming the plugin.
Writing a runtime
- Factory input: the model chains, the environment, the declared providers,
sampling(temperature,seed) when configured, andagentModels: every agent's own chain by agent id, for a runtime that prepares providers before the first call. Such an agent also sends its chain asmodelson its task specs and completion requests; a runtime that ignores both uses the tier's chain. - Sampling: the runtime's
samplingproperty says what it sends, withnotAppliedfor what it cannot pass on; without it, the runtime is reported as applying none. - Effort: a task spec carries its reviewer's
effort; a completion request names itsagent(the plan call's reviewer, orverifier,judge,helper) andeffort; both are absent when none is configured. A runtime that sends effort implementsappliedTo(agent): whether every call of that agent sent it, inunsupportedthe models that do not take the level (the run warns once per agent), and innotAppliedthe sampling settings left out. Without it, the runtime is reported as sending no effort, with one warning.effortCapability(model)from@open-cr-agent/coregives ocra's table of a model's parameter style and levels. - Failover:
ChainRunnerfrom@open-cr-agent/corecan serverunTaskandcomplete, as in both built-in runtimes. It picks each call's chain (the agent's, else its tier's), refuses a tier without a model withCONFIG_INVALID, tracks each model's health for the run (short rate limits waited out, out-of-quota models dropped, a circuit breaker) and moves to the next model when an attempt fails. You give itModelAttempts:taskandcompleterun one attempt on one model and return anAttemptOutcome(findings, steps, tool calls, text, usage, and anerrorsaying whether another model may succeed and, with aQuotaError, how long to wait; an attempt the abort signal cut short returns one, since an outcome without anerrorcounts as finished); optionalrefusesays before any request why a chain cannot be served, optionalreadystarts what attempts need. Create one runner per runtime instance, so health spans the run. - Helpers in
@open-cr-agent/core, so your runtime reviews as the built-in ones do:reviewTools, the read-only review tools to offer the agent next toRuntimeOptions.tools, andREVIEW_TOOLS, their names (an attempt's findings are what the agent passes toreport_finding;task_doneends it);MAX_AGENT_STEPS, the cap on model requests per review attempt;RESUME_MESSAGE, sent once to an agent that stops with steps left, no answer and notask_done;parseModel(model), which splitsprovider/modeland throwsCONFIG_INVALIDfor anything else;parseQuotaError(message, status?), which reads a provider's rate-limit or quota error into aQuotaError;withoutSecrets(text, secrets), to redact keys from a provider's error before it is quoted;emptyUsage()andaddUsage();EFFORT_LEVELSandthinkingBudget(capability, level, outputLimit?), the thinking budget ocra derives for a level; andproxiedFetch(env), afetchthat honorsHTTP_PROXY,HTTPS_PROXYandNO_PROXYfrom the given environment.
Security
A plugin runs code. Load plugins only from repositories you trust, and allow a plugin your account names only after checking who publishes it; to review someone else's branch, run ocra review --no-repo-config, which ignores the repository's config and plugins. In CI, read configuration from the protected base branch, never from the pull request under review.