Visualize Code Changes with OpenClaw Diffs Plugin
Ever had to squint at a wall of + and - signs in a terminal or chat window just to figure out what your agent actually changed? It is frustrating when you just want to see a clean, readable comparison of your code.
The diffs plugin solves this by turning messy change content into a clean, read-only diff artifact. You can get a URL to view the changes in a browser or a rendered PNG/PDF file to send directly in a chat.
Quick start
Section titled “Quick start”Getting started is simple. You just need to follow these steps:
- Enable the plugin.
- Call
diffswithmode: "view"for canvas-first flows. - Call
diffswithmode: "file"for chat file delivery flows. - Call
diffswithmode: "both"when you need both artifacts.
Enable the plugin
Section titled “Enable the plugin”To get started, add this to your configuration:
{ plugins: { entries: { diffs: { enabled: true, }, }, },}Disable built-in system guidance
Section titled “Disable built-in system guidance”If you want to keep the diffs tool enabled but disable its built-in system-prompt guidance, set plugins.entries.diffs.hooks.allowPromptInjection to false:
{ plugins: { entries: { diffs: { enabled: true, hooks: { allowPromptInjection: false, }, }, }, },}This blocks the diffs plugin’s before_prompt_build hook while keeping the plugin, tool, and companion skill available. If you want to disable both the guidance and the tool, disable the plugin instead.
Typical agent workflow
Section titled “Typical agent workflow”Here is how your agent usually handles the process:
- Your agent calls
diffsand then reads thedetailsfields. - The agent then chooses to open the
details.viewerUrlwithcanvas present, send thedetails.filePathwith amessage, or do both.
Input examples
Section titled “Input examples”You can provide the content in two ways. First, using before and after text:
{ "before": "# Hello\n\nOne", "after": "# Hello\n\nTwo", "path": "docs/example.md", "mode": "view"}Or you can use a unified patch:
{ "patch": "diff --git a/src/example.ts b/src/example.ts\n--- a/src/example.ts\n+++ b/src/example.ts\n@@ -1 +1 @@\n-const x = 1;\n+const x = 2;\n", "mode": "both"}Tool input reference
Section titled “Tool input reference”All fields are optional unless noted:
before(string): original text. Required withafterwhenpatchis omitted.after(string): updated text. Required withbeforewhenpatchis omitted.patch(string): unified diff text. Mutually exclusive withbeforeandafter.path(string): display filename for before and after mode.lang(string): language override hint for before and after mode.title(string): viewer title override.mode("view" | "file" | "both"): output mode. Defaults to plugin defaultdefaults.mode.theme("light" | "dark"): viewer theme. Defaults to plugin defaultdefaults.theme.layout("unified" | "split"): diff layout. Defaults to plugin defaultdefaults.layout.expandUnchanged(boolean): expand unchanged sections when full context is available. Per-call option only (not a plugin default key).fileFormat("png" | "pdf"): rendered file format. Defaults to plugin defaultdefaults.fileFormat.fileQuality("standard" | "hq" | "print"): quality preset for PNG or PDF rendering.fileScale(number): device scale override (1-4).fileMaxWidth(number): max render width in CSS pixels (640-2400).ttlSeconds(number): viewer artifact TTL in seconds. Default 1800, max 21600.baseUrl(string): viewer URL origin override. Must behttporhttps, no query/hash.
Validation and limits:
beforeandaftereach max 512 KiB.patchmax 2 MiB.pathmax 2048 bytes.langmax 128 bytes.titlemax 1024 bytes.- Patch complexity cap: max 128 files and 120000 total lines.
patchandbeforeoraftertogether are rejected.- Rendered file safety limits (apply to PNG and PDF):
fileQuality: "standard": max 8 MP (8,000,000 rendered pixels).fileQuality: "hq": max 14 MP (14,000,000 rendered pixels).fileQuality: "print": max 24 MP (24,000,000 rendered pixels).- PDF also has a max of 50 pages.
Output details contract
Section titled “Output details contract”The tool returns structured metadata under details.
Shared fields for modes that create a viewer:
artifactIdviewerUrlviewerPathtitleexpiresAtinputKindfileCountmode
File fields when PNG or PDF is rendered:
filePathpath(same value asfilePath, for message tool compatibility)fileBytesfileFormatfileQualityfileScalefileMaxWidth
Mode behavior summary:
mode: "view": viewer fields only.mode: "file": file fields only, no viewer artifact.mode: "both": viewer fields plus file fields. If file rendering fails, viewer still returns withfileError.
Collapsed unchanged sections
Section titled “Collapsed unchanged sections”- The viewer can show rows like
N unmodified lines. - Expand controls on those rows are conditional and not guaranteed for every input kind.
- Expand controls appear when the rendered diff has expandable context data, which is typical for before and after input.
- For many unified patch inputs, omitted context bodies are not available in the parsed patch hunks, so the row can appear without expand controls. This is expected behavior.
expandUnchangedapplies only when expandable context exists.
Plugin defaults
Section titled “Plugin defaults”You can 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", }, }, }, }, },}Supported defaults include fontFamily, fontSize, lineSpacing, layout, showLineNumbers, diffIndicators, wordWrap, background, theme, fileFormat, fileQuality, fileScale, fileMaxWidth, and mode. Explicit tool parameters override these defaults.
Security config
Section titled “Security config”You can control remote access with this setting:
{ plugins: { entries: { diffs: { enabled: true, config: { security: { allowRemoteViewer: false, }, }, }, }, },}security.allowRemoteViewer(boolean, defaultfalse)false: non-loopback requests to viewer routes are denied.true: remote viewers are allowed if tokenized path is valid.
Artifact lifecycle and storage
Section titled “Artifact lifecycle and storage”- Artifacts are stored under the temp subfolder:
$TMPDIR/openclaw-diffs. - Viewer artifact metadata contains a random artifact ID, a random token,
createdAt,expiresAt, and the storedviewer.htmlpath. - Default viewer TTL is 30 minutes when not specified.
- Maximum accepted viewer TTL is 6 hours.
- Cleanup runs after artifact creation.
- Expired artifacts are deleted.
- Fallback cleanup removes stale folders older than 24 hours when metadata is missing.
Viewer URL and network behavior
Section titled “Viewer URL and network behavior”Viewer route:
/plugins/diffs/view/\{artifactId}/\{token}
Viewer assets:
/plugins/diffs/assets/viewer.js/plugins/diffs/assets/viewer-runtime.js
URL construction behavior:
- If
baseUrlis provided, it is used after strict validation. - Without
baseUrl, viewer URL defaults to loopback127.0.0.1. - If gateway bind mode is
customandgateway.customBindHostis set, that host is used.
baseUrl rules:
- Must be
http://orhttps://. - Query and hash are rejected.
- Origin plus optional base path is allowed.
Security model
Section titled “Security model”Viewer hardening:
- Loopback-only by default.
- Tokenized viewer paths with strict ID and token validation.
- Viewer response CSP blocks outbound
connect-srcand only allows scripts and assets from self. - Remote miss throttling when remote access is enabled includes 40 failures per 60 seconds and a 60 second lockout.
File rendering hardening:
- Screenshot browser request routing is deny-by-default.
- Only local viewer assets from
http://127.0.0.1/plugins/diffs/assets/*are allowed. - External network requests are blocked.
Browser requirements for file mode
Section titled “Browser requirements for file mode”mode: "file" and mode: "both" need a Chromium-compatible browser. The system looks for the browser in this order:
browser.executablePathin OpenClaw config.- Environment variables like
OPENCLAW_BROWSER_EXECUTABLE_PATH,BROWSER_EXECUTABLE_PATH, orPLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH. - Platform command/path discovery fallback.
If you see a message saying Diff PNG/PDF rendering requires a Chromium-compatible browser, you can fix it by installing Chrome, Chromium, Edge, or Brave.
Troubleshooting
Section titled “Troubleshooting”Input validation errors:
Provide patch or both before and after text.(Include bothbeforeandafter, or providepatch).Provide either patch or before/after input, not both.(Do not mix input modes).Invalid baseUrl: ...(Usehttp(s)origin with optional path, no query/hash).\{field} exceeds maximum size (...)(Reduce payload size).- Large patch rejection (Reduce patch file count or total lines).
Viewer accessibility issues:
- Viewer URL resolves to
127.0.0.1by default. - For remote access, pass
baseUrlper tool call or usegateway.bind=custom. - Enable
security.allowRemoteVieweronly when you intend external viewer access.
Other issues:
- Unmodified-lines row has no expand button: This is expected for patch input when the patch does not carry expandable context.
- Artifact not found: The artifact might have expired due to TTL or been removed by cleanup.
Operational guidance
Section titled “Operational guidance”- Prefer
mode: "view"for local interactive reviews in canvas. - Prefer
mode: "file"for outbound chat channels that need an attachment. - Keep
allowRemoteViewerdisabled unless your deployment requires remote viewer URLs. - Set explicit short
ttlSecondsfor sensitive diffs. - Avoid sending secrets in diff input when not required.
- If your channel compresses images aggressively, prefer PDF output (
fileFormat: "pdf").
The diff rendering engine is powered by Diffs.
Related docs
Section titled “Related docs”OpenClaw Expert
Still stuck?
If this page didn't answer your case, ask OpenClaw Expert for step-by-step guidance.