Install
$ agentstack add skill-jtsternberg-claude-plugins-diff-view ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
diff-view
Generates a single self-contained HTML file that renders a rich code diff — dark GitHub-ish theme, line-numbered, syntax-highlighted, with word-level intra-line highlighting on lines that changed. No external dependencies, no network: the diff is computed by a small vanilla-JS pass embedded in the file, which runs on load. The file opens in the user's $EDITOR/browser and can be screenshotted full-page for dropping straight into a GitHub PR description or Slack.
This is a collab-tools skill: like its companions temp-draft and promote-draft, it routes work that doesn't belong in chat (a big visual diff) into a real artifact under /tmp/collab-tools/.
Two modes, auto-detected by how many sources you pass:
- 2-way (2 sources) — side-by-side. Line-level LCS alignment; adjacent delete/insert runs pair into "changed" rows.
- 3-way (3 sources) — three columns. A 3D LCS finds the lines common to all three (the shared "spine"); lines unique to a column stack in per-column gaps. Ideal for before / after / extracted-shared refactors.
When to use this
Use it when:
- The user invokes
/collab-tools:diff-view, or asks to "diff these", "compare side by side", "show this as an HTML diff", "screenshot the diff to share", etc. - Comparing two versions of a file, two near-identical functions, or three variants where a unified
git diffwould be noisy or hard to read. - The user wants something shareable (a screenshot for a PR/Slack), not just a terminal read.
Don't use it for:
- Applying patches, staging, or anything that mutates code.
- A quick terminal read of a small change — plain
git diffis fine.
How it works
A bundled Node script (scripts/gen-diff.js, zero dependencies — Node built-ins only, no npm install) templates the HTML: it injects the sources, title, column labels, and syntax keyword set, picks the 2-way vs 3-way layout, writes the .html, and optionally drives a headless browser to capture a PNG.
Key properties (carried over from the proven prototype, hardened for arbitrary input):
- Whitespace-insensitive line matching — indentation/spacing runs are collapsed for matching, so re-indentation never registers as a change; the original lines are still displayed verbatim.
- Blank lines are neutral — numbered, no highlight, never counted as a change.
- Word-level intra-line marks — when the present cells on a row are similar enough (Jaccard ≥ 0.34, i.e. "same line, changed"), only the tokens not common to the row are emphasized; genuinely different lines keep a solid tint instead of lighting up every token (avoids noise). Marks are color-coded per column.
- Light syntax highlighting — a single-pass tokenizer (strings / words / numbers /
$vars/ keywords / fn-calls). The keyword set is pluggable via--langor--keywords(defaults to PHP, matching the prototype;js,ts,python,go,ruby,rust,sql,genericare built in, and the language is inferred from the file extension when--langis omitted). - Robust embedding — sources are embedded as ASCII-safe JS string literals (`
,,&, and Unicode — round-trips losslessly. (The prototype used+textContent`, which silently breaks on any of those; this skill fixes that.)
Procedure
1. Gather the sources
You need exactly 2 or 3 sources. Each can come from:
- A file — pass the path directly.
- Pasted code — write it to a temp file first (
Writeto/tmp/collab-tools/-.), then pass that path. Keeps chat lean and gives the script a clean file. - A git ref — use
--git "REF:PATH"(e.g.--git "HEAD~1:src/Foo.php"), which resolves viagit showin the current repo. Repeatable; great for "this file before vs after".
2. Run the generator
# Codex: this path resolves under Claude Code; substitute the directory containing this SKILL.md.
SKILL_DIR="${CLAUDE_SKILL_DIR}"
node "$SKILL_DIR/scripts/gen-diff.js" [sourceC] \
--title "Short descriptive title" \
--label "Column A label" --label "Column B label" [--label "Column C label"] \
--lang php \
--screenshot
Useful options (run with -h for the full list):
| Option | Purpose | |---|---| | --title "..." | Page title / H1. Defaults to a label-derived title. | | --subtitle "..." | Muted one-line description under the title. | | --label "..." | Column header label (repeatable, in column order). Defaults to the file basename. | | --sublabel "..." | Small text under a column label (repeatable). | | --lang | Keyword set: php (default), js, ts, python, go, ruby, rust, sql, generic. Inferred from extension if omitted. | | --keywords "a,b,c" | Explicit keyword list, overrides --lang. | | --note "..." | Footer note (plain text). | | --git "REF:PATH" | Add a source from a git ref (repeatable; takes precedence over positional file args). | | --out PATH | Output HTML path. Defaults to /tmp/collab-tools/-.html. | | --screenshot | Also render a full-page PNG (best-effort). | | --screenshot-out PATH | PNG path. Defaults to the HTML path with .png. | | --width N | Screenshot width in px (default 1400). |
The script prints HTML: and (with --screenshot) PNG: .
3. Open it for the user
Open the generated HTML the same way temp-draft does — non-blocking, in their editor:
eval "${EDITOR:-vi}" '/tmp/collab-tools/your-diff.html' &
disown 2>/dev/null || true
If the user wants to view rendered rather than edit source, mention they can open the .html in a browser (open on macOS), or just hand them the PNG.
4. Report briefly
One or two lines: where the HTML landed, and the PNG path if one was produced. Don't paste the HTML or the diff body into chat — the artifact is the deliverable. If the user asked to share it, the PNG is the drag-and-drop target for a PR/Slack.
Screenshots — what to expect
Capture is best-effort and degrades gracefully; the .html is always produced regardless.
Detection order:
- Playwright (
playwrighton PATH) — preferred. Gives an exact full-page capture at any height, no guessing. - Headless Chrome / Chromium / Edge / Brave — fallback. Uses a fixed window whose height is estimated from the line count, so a very tall or very short page may show trailing whitespace or, rarely, clip. Still fine for most diffs.
- Neither found — the script prints a clear message ("HTML written; install Playwright or Chrome to enable screenshots") and exits success. Surface that to the user; don't treat it as a failure.
If a screenshot looks clipped/padded and only Chrome was available, suggest installing Playwright (npx playwright install chromium) for exact full-page output, or just open the HTML in a browser.
Examples
Two near-identical functions (PHP), screenshot for a PR:
# Codex: this path resolves under Claude Code; substitute the directory containing this SKILL.md.
SKILL_DIR="${CLAUDE_SKILL_DIR}"
node "$SKILL_DIR/scripts/gen-diff.js" /tmp/collab-tools/tag.php /tmp/collab-tools/untag.php \
--title "TagSubscribersByFilter vs UnTagSubscribersByFilter" \
--label "Tag (orig)" --label "UnTag (orig)" --lang php --screenshot
Before / after / extracted-shared (3-way) with sublabels:
# Codex: this path resolves under Claude Code; substitute the directory containing this SKILL.md.
SKILL_DIR="${CLAUDE_SKILL_DIR}"
node "$SKILL_DIR/scripts/gen-diff.js" a.php b.php shared.php \
--title "Tag / UnTag / applyTagOperationByFilter" \
--label "Tag()" --sublabel "original — INSERT path" \
--label "UnTag()" --sublabel "original — DELETE path" \
--label "applyTagOperationByFilter()" --sublabel "new — shared" \
--lang php --screenshot
One file across two git revisions:
# Codex: this path resolves under Claude Code; substitute the directory containing this SKILL.md.
SKILL_DIR="${CLAUDE_SKILL_DIR}"
node "$SKILL_DIR/scripts/gen-diff.js" \
--git "HEAD~1:src/Subscribers.php" --git "HEAD:src/Subscribers.php" \
--title "Subscribers.php — last commit" --label "before" --label "after" --screenshot
Notes
- The diff engine is client-side JS embedded in the output — open the HTML anywhere (no server, no network) and it renders. The Node script only templates and (optionally) screenshots.
- Output is intentionally pure ASCII; non-ASCII source characters are preserved via
\uXXXXescapes and render correctly in the browser. - This skill never mutates the sources or the repo. It reads inputs and writes an artifact under
/tmp/collab-tools/(or--out).
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: jtsternberg
- Source: jtsternberg/claude-plugins
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.