Files
zeekay 22cd2c5694 State a version once
A version written into a manifest is written into every manifest that wants it,
which is how eslint came to exist in a hundred and four forms across this estate
and how a package came to resolve twice at run time. The catalog moves the number
out of the manifest: a manifest names the package, the workspace names the
version.

Four are held below their latest because the newer release cannot be used, and
the reason is recorded rather than the version alone: @noble/hashes 2 renamed
every subpath the web3 stack imports, @splinetool/runtime 2 depends on a package
that was never published, @dynamic-labs 5.4.1 the same, and react-resizable-panels
3 points its types condition at the bundle.
2026-08-29 18:13:28 -07:00
..
2026-08-29 18:13:28 -07:00

@hanzo/bot-diffs

Read-only diff viewer plugin for Hanzo Bot agents.

It gives agents one tool, diffs, that can:

  • render a gateway-hosted diff viewer for canvas use
  • render the same diff to a file (PNG or PDF)
  • accept either arbitrary before and after text or a unified patch

What Agents Get

The tool can return:

  • details.viewerUrl: a gateway URL that can be opened in the canvas
  • details.filePath: a local rendered artifact path when file rendering is requested
  • details.fileFormat: the rendered file format (png or pdf)

When the plugin is enabled, it also ships a companion skill from skills/ and prepends stable tool-usage guidance into system-prompt space via before_prompt_build. The hook uses prependSystemContext, so the guidance stays out of user-prompt space while still being available every turn.

This means an agent can:

  • call diffs with mode=view, then pass details.viewerUrl to canvas present
  • call diffs with mode=file, then send the file through the normal message tool using path or filePath
  • call diffs with mode=both when it wants both outputs

Tool Inputs

Before and after:

{
  "before": "# Hello\n\nOne",
  "after": "# Hello\n\nTwo",
  "path": "docs/example.md",
  "mode": "view"
}

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"
}

Useful options:

  • mode: view, file, or both
  • layout: unified or split
  • theme: light or dark (default: dark)
  • fileFormat: png or pdf (default: png)
  • fileQuality: standard, hq, or print
  • fileScale: device scale override (1-4)
  • fileMaxWidth: max width override in CSS pixels (640-2400)
  • expandUnchanged: expand unchanged sections (per-call option only, not a plugin default key)
  • path: display name for before and after input
  • title: explicit viewer title
  • ttlSeconds: artifact lifetime
  • baseUrl: override the gateway base URL used in the returned viewer link (origin or origin+base path only; no query/hash)

Input safety limits:

  • before and after: max 512 KiB each
  • patch: max 2 MiB
  • patch rendering cap: max 128 files / 120,000 lines

Plugin Defaults

Set plugin-wide defaults in ~/.hanzoai/bot.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",
          },
        },
      },
    },
  },
}

Explicit tool parameters still win over these defaults.

Security options:

  • security.allowRemoteViewer (default false): allows non-loopback access to /plugins/diffs/view/... token URLs

Example Agent Prompts

Open in canvas:

Use the `diffs` tool in `view` mode for this before and after content, then open the returned viewer URL in the canvas.

Path: docs/example.md

Before:
# Hello

This is version one.

After:
# Hello

This is version two.

Render a file (PNG or PDF):

Use the `diffs` tool in `file` mode for this before and after input. After it returns `details.filePath`, use the `message` tool with `path` or `filePath` to send me the rendered diff file.

Path: README.md

Before:
Hanzo Bot supports plugins.

After:
Hanzo Bot supports plugins and hosted diff views.

Do both:

Use the `diffs` tool in `both` mode for this diff. Open the viewer in the canvas and then send the rendered file by passing `details.filePath` to the `message` tool.

Path: src/demo.ts

Before:
const status = "old";

After:
const status = "new";

Patch input:

Use the `diffs` tool with this unified patch in `view` mode. After it returns the viewer URL, present it in the canvas.

diff --git a/src/example.ts b/src/example.ts
--- a/src/example.ts
+++ b/src/example.ts
@@ -1,3 +1,3 @@
 export function add(a: number, b: number) {
-  return a + b;
+  return a + b + 1;
 }

Notes

  • The viewer is hosted locally through the gateway under /plugins/diffs/....
  • Artifacts are ephemeral and stored in the plugin temp subfolder ($TMPDIR/hanzo-bot-diffs).
  • Default viewer URLs use loopback (127.0.0.1) unless you set baseUrl (or use gateway.bind=custom + gateway.customBindHost).
  • Remote viewer misses are throttled to reduce token-guess abuse.
  • PNG or PDF rendering requires a Chromium-compatible browser. Set browser.executablePath if auto-detection is not enough.
  • If your delivery channel compresses images heavily (for example Telegram or WhatsApp), prefer fileFormat: "pdf" to preserve readability.
  • N unmodified lines rows may not always include expand controls for patch input, because many patch hunks do not carry full expandable context data.
  • Diff rendering is powered by Diffs.