docs/tools/diffs.md
diffs is an optional bundled plugin tool that turns before/after text or a unified patch into a read-only diff artifact. It also prepends short agent guidance into the system prompt and ships a companion skill for fuller instructions.
Input: before + after text, or a unified patch (mutually exclusive).
Output: a gateway viewer URL for canvas presentation, a rendered PNG/PDF file path for message delivery, or both.
To keep the tool but drop the prepended system-prompt guidance, set plugins.entries.diffs.hooks.allowPromptInjection to false:
{
plugins: {
entries: {
diffs: {
enabled: true,
hooks: {
allowPromptInjection: false,
},
},
},
},
}
This blocks the plugin's before_prompt_build hook while keeping the tool and skill available. To disable both guidance and the tool, disable the plugin instead.
All fields are optional unless noted.
<ParamField path="before" type="string"> Original text. Required with `after` when `patch` is omitted. </ParamField> <ParamField path="after" type="string"> Updated text. Required with `before` when `patch` is omitted. </ParamField> <ParamField path="patch" type="string"> Unified diff text. Mutually exclusive with `before` and `after`. </ParamField> <ParamField path="path" type="string"> Display filename for before/after mode. </ParamField> <ParamField path="lang" type="string"> Language override hint for before/after mode. Unknown values and languages outside the default viewer set fall back to plain text unless the Diff Viewer Language Pack plugin is installed. </ParamField> <ParamField path="title" type="string"> Viewer title override. </ParamField> <ParamField path="mode" type='"view" | "file" | "both"'> Output mode. Defaults to plugin default `defaults.mode` (`both`). Deprecated alias: `"image"` behaves identically to `"file"`. </ParamField> <ParamField path="theme" type='"light" | "dark"'> Viewer theme. Defaults to plugin default `defaults.theme`. </ParamField> <ParamField path="layout" type='"unified" | "split"'> Diff layout. Defaults to plugin default `defaults.layout`. </ParamField> <ParamField path="expandUnchanged" type="boolean"> Expand unchanged sections when full context is available. Per-call option only (not a plugin default key). </ParamField> <ParamField path="fileFormat" type='"png" | "pdf"'> Rendered file format. Defaults to plugin default `defaults.fileFormat`. </ParamField> <ParamField path="fileQuality" type='"standard" | "hq" | "print"'> Quality preset for PNG/PDF rendering. </ParamField> <ParamField path="fileScale" type="number"> Device scale override (`1`-`4`). </ParamField> <ParamField path="fileMaxWidth" type="number"> Max render width in CSS pixels (`640`-`2400`). </ParamField> <ParamField path="ttlSeconds" type="number" default="1800"> Artifact TTL in seconds for viewer and standalone file outputs. Max `21600`. </ParamField> <ParamField path="baseUrl" type="string"> Viewer URL origin override. Overrides plugin `viewerBaseUrl`. Must be `http` or `https`, no query/hash. </ParamField> <AccordionGroup> <Accordion title="Legacy input aliases"> Still accepted for backward compatibility:- `format` -> `fileFormat`
- `imageFormat` -> `fileFormat`
- `imageQuality` -> `fileQuality`
- `imageScale` -> `fileScale`
- `imageMaxWidth` -> `fileMaxWidth`
Built-in languages:
javascript, typescript, tsx, jsx, json, markdown, yaml, css, html, sh, python, go, rust, java, c, cpp, csharp, php, sql, docker, ruby, swift, kotlin, r, dart, lua, powershell, xml, and toml.
Common aliases (js, ts, bash, md, yml, c++, dockerfile, rb, kt, ps1, etc.) normalize to those languages.
Install the Diff Viewer Language Pack plugin for more languages (Astro, Vue, Svelte, MDX, GraphQL, Terraform/HCL, Nix, Clojure, Elixir, Haskell, OCaml, Scala, Zig, Solidity, Verilog/VHDL, Fortran, MATLAB, LaTeX, Mermaid, Sass/Less/SCSS, Nginx, Apache, CSV, dotenv, INI, diff, and more):
openclaw plugins install clawhub:@openclaw/diffs-language-pack
Without the pack, unsupported languages still render as readable plain text. See Diffs Language Pack plugin and Shiki languages for the upstream catalog.
All successful results include changed: identical before/after input returns false without creating an artifact; rendered results return true.
| Mode | Returns |
|---|---|
"view" | Viewer fields only. |
"file" | File fields only, no viewer artifact. |
"both" | Viewer fields plus file fields. If file rendering fails, viewer still returns with fileError/imageError. |
The viewer shows rows like N unmodified lines. Expand controls only appear when the rendered diff has expandable context data (typical for before/after input). Many unified patches omit context bodies in their hunks, so the row can appear without an expand control -- expected, not a bug. expandUnchanged only applies when expandable context exists.
Patches that touch more than one file start with a changed-files summary card: total +N / -N counts, per-file counts, added/deleted/renamed badges, and anchor links that jump to each file. Rendered PNG/PDF files keep the per-file header counts but drop the interactive view toggles, since those are dead controls in a static file.
Set plugin-wide defaults in ~/.openclaw/openclaw.json:
{
plugins: {
entries: {
diffs: {
enabled: true,
config: {
defaults: {
fontFamily: "Fira Code",
fontSize: 15,
lineSpacing: 1.6,
layout: "unified",
showLineNumbers: true,
diffIndicators: "bars",
wordWrap: true,
background: true,
theme: "dark",
fileFormat: "png",
fileQuality: "standard",
fileScale: 2,
fileMaxWidth: 960,
mode: "both",
ttlSeconds: 21600,
},
},
},
},
},
}
Supported defaults keys: fontFamily, fontSize, lineSpacing, layout, showLineNumbers, diffIndicators, wordWrap, background, theme, fileFormat, fileQuality, fileScale, fileMaxWidth, mode, ttlSeconds. Explicit tool call parameters override these.
{
plugins: {
entries: {
diffs: {
enabled: true,
config: {
viewerBaseUrl: "https://gateway.example.com/openclaw",
},
},
},
},
}
{
plugins: {
entries: {
diffs: {
enabled: true,
config: {
security: {
allowRemoteViewer: false,
},
},
},
},
},
}
$TMPDIR/openclaw-diffs.createdAt/expiresAt, and the stored viewer.html path.Viewer route: /plugins/diffs/view/{artifactId}/{token}
Viewer assets:
/plugins/diffs/assets/viewer.js/plugins/diffs/assets/viewer-runtime.js/plugins/diffs-language-pack/assets/viewer.js (only when the diff uses a language pack language)The viewer document resolves these assets relative to the viewer URL, so an optional baseUrl path prefix carries through to asset requests too.
URL resolution order: tool-call baseUrl (after strict validation) -> plugin viewerBaseUrl -> loopback 127.0.0.1 default. If gateway bind mode is custom and gateway.customBindHost is set, that host is used instead of loopback.
baseUrl rules: must be http:// or https://; query and hash are rejected; origin plus optional base path is allowed.
mode: "file" and mode: "both" need a Chromium-compatible browser.
Resolution order:
<Steps> <Step title="Config"> `browser.executablePath` in OpenClaw config. </Step> <Step title="Environment variables"> - `OPENCLAW_BROWSER_EXECUTABLE_PATH` - `BROWSER_EXECUTABLE_PATH` - `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` </Step> <Step title="Platform fallback"> Common install paths and `PATH` lookups for Chrome, Chromium, Edge, and Brave. </Step> </Steps>Common failure text: Diff PNG/PDF rendering requires a Chromium-compatible browser.... Fix by installing Chrome, Chromium, Edge, or Brave, or setting one of the executable path options above.
mode: "view" for local interactive reviews in canvas.mode: "file" for outbound chat channels that need an attachment.allowRemoteViewer disabled unless your deployment requires remote viewer URLs.ttlSeconds for sensitive diffs.fileFormat: "pdf").