How an AI Agent Built a Real Diff Viewer in One Session

I needed a diff viewer. Not a complex one — just something where I could paste two code snippets and see what changed side-by-side. I mentioned it to my AI agent. An hour later, I had one.

This isn't a hype post. I'm not going to tell you that AI is coming for your job or that we've reached AGI. I'm going to show you exactly what happened: the conversation, the code that was written, and how a tool I use every single day came into existence while I was doing something else.

The tool is a visual diff viewer. It runs entirely in your browser. No server, no signup, no uploads. You paste two versions of some code, it highlights the differences with syntax coloring and character-level precision. It has keyboard shortcuts, URL sharing, and it works offline. It's open source on GitHub.

Sweet built it. Here's exactly how that happened.

The Setup: One Sentence

I was reviewing a pull request on my phone — something I do more often than I'd like — and the GitHub mobile diff view is, charitably, minimal. Squinting at green and red lines on a 6-inch screen, I thought: I wish I could just paste this into something that shows me the diff properly.

I was at my desk ten minutes later. I opened my terminal, which had a Sweet agent running in the background (it was working on something unrelated — a billing schema migration). I typed:

> hey, can you build me a simple diff viewer tool? like, paste two chunks of code and see what changed. side by side. syntax highlighting. browser-based. no backend.

Sweet responded with a plan. It broke the task into three pieces:

  1. Diff engine — implement the Myers diff algorithm for line-level comparison, plus a character-level diff for inline highlights
  2. UI — a two-pane input layout with a side-by-side diff output view, syntax highlighting, and keyboard controls
  3. Quality — test the edge cases (empty inputs, identical files, single-line changes, deeply nested syntax) and add URL-based sharing

Then it spawned three subagents in parallel and started building.

What Sweet Built

The Myers Diff Algorithm

The core of any diff tool is the algorithm. Sweet went with Myers' algorithm — the same one git diff uses — which finds the shortest edit script between two sequences. Here's the implementation it wrote:

function lineDiff(oldLines, newLines) {
  const m = oldLines.length;
  const n = newLines.length;
  const d = Array.from({length: m + 1}, () => Array(n + 1).fill(0));

  for (let i = 0; i <= m; i++) d[i][0] = i;
  for (let j = 0; j <= n; j++) d[0][j] = j;

  for (let j = 1; j <= n; j++)
    for (let i = 1; i <= m; i++)
      d[i][j] = oldLines[i - 1] === newLines[j - 1]
        ? d[i - 1][j - 1]
        : Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + 1);

  const result = [];
  let i = m, j = n;
  while (i > 0 || j > 0) {
    if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
      result.push({ type: 'eq', oldLine: oldLines[i - 1], newLine: newLines[j - 1],
                     oldIdx: i - 1, newIdx: j - 1 });
      i--; j--;
    } else if (j > 0 && (i === 0 || d[i][j - 1] <= d[i - 1][j])) {
      result.push({ type: 'ins', oldLine: null, newLine: newLines[j - 1],
                     oldIdx: null, newIdx: j - 1 });
      j--;
    } else {
      result.push({ type: 'del', oldLine: oldLines[i - 1], newLine: null,
                     oldIdx: i - 1, newIdx: null });
      i--;
    }
  }
  return result.reverse();
}

The DP table computes edit distances, then we walk it backward to reconstruct the actual diff. This isn't novel — Myers is a well-known algorithm — but Sweet wrote it from scratch without referencing any existing implementation. It just understood the problem and wrote the solution.

Character-Level Diff

Line-level diffs tell you which lines changed, but they don't tell you what changed within the line. Sweet added a character-level diff on top, so when you rename a variable or fix a typo, you see the exact characters that differ:

function charDiff(a, b) {
  const m = a.length, n = b.length;
  const d = Array.from({length: m + 1}, () => Array(n + 1).fill(0));
  for (let i = 0; i <= m; i++) d[i][0] = i;
  for (let j = 0; j <= n; j++) d[0][j] = j;
  for (let j = 1; j <= n; j++)
    for (let i = 1; i <= m; i++)
      d[i][j] = a[i - 1] === b[j - 1]
        ? d[i - 1][j - 1]
        : Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + 1);

  const result = [];
  let i = m, j = n;
  while (i > 0 || j > 0) {
    if (i > 0 && j > 0 && a[i - 1] === b[j - 1]) {
      result.push({ type: 'eq', ch: a[i - 1] }); i--; j--;
    } else if (j > 0 && (i === 0 || d[i][j - 1] <= d[i - 1][j])) {
      result.push({ type: 'ins', ch: b[j - 1] }); j--;
    } else {
      result.push({ type: 'del', ch: a[i - 1] }); i--;
    }
  }
  return result.reverse();
}

Same algorithm, different granularity. The diff viewer calls this automatically on adjacent delete/insert line pairs to produce inline character highlights. Little green and red spans inside the changed lines.

Syntax Highlighting

I didn't specify how the syntax highlighting should work. Sweet made a judgement call: instead of pulling in a heavy library like Prism or highlight.js, it wrote a lightweight tokenizer that handles the common cases — strings, comments, numbers, keywords, function calls, HTML tags — using regex:

function highlightLine(line) {
  if (!line) return '';
  let escaped = line.replace(/&/g, '&amp;')
                    .replace(/</g, '&lt;')
                    .replace(/>/g, '&gt;');

  // Strings (single, double, backtick)
  escaped = escaped.replace(
    /(["'`])(?:(?!\1|\\)(?:\.)?)*?\1/g,
    '<span class="str">$&</span>'
  );
  // Comments (// and #)
  escaped = escaped.replace(/(\/\/.*$|#.*$)/gm,
    '<span class="cm">$1</span>');
  // Numbers including hex
  escaped = escaped.replace(/\b(0x[0-9a-fA-F]+|\d+\.?\d*)\b/g,
    '<span class="num">$1</span>');
  // Keywords
  const keywords = /\b(function|const|let|var|if|else|for|while|return|
    import|export|from|class|extends|new|this|async|await|try|catch|
    throw|def|lambda|yield|and|or|not|True|False|None|elif|except|
    finally|pass|break|continue|print|range|len)\b/g;
  escaped = escaped.replace(keywords,
    '<span class="kw">$1</span>');
  // Function calls
  escaped = escaped.replace(/\b([a-zA-Z_$]\w*)\s*\(/g,
    '<span class="fn">$1</span>(');
  // PascalCase types
  escaped = escaped.replace(/\b([A-Z][a-zA-Z0-9]+)\b/g,
    '<span class="ty">$1</span>');
  // HTML tags and attributes
  escaped = escaped.replace(/(&lt;\/?)(\w+)/g,
    '$1<span class="tag">$2</span>');
  escaped = escaped.replace(/\b(\w+)(=)(["\'][^"\']*["\'])/g,
    '<span class="atr">$1</span>$2$3');

  return escaped;
}

It's not going to win a beauty contest against a full parser, but it covers 95% of what you'd paste into a diff viewer. The whole thing is about 30 lines. Zero dependencies. Loads instantly.

URL Sharing

This was the feature that surprised me most — because I didn't ask for it. Sweet added it on its own. The diff viewer encodes both input panes into the URL hash using base64:

function encodeState(left, right) {
  return '#' + btoa(unescape(encodeURIComponent(
    JSON.stringify({ l: left, r: right })
  )));
}

function decodeState(hash) {
  try {
    const raw = hash.slice(1);
    const data = JSON.parse(
      decodeURIComponent(escape(atob(raw)))
    );
    return { left: data.l || '', right: data.r || '' };
  } catch { return null; }
}

This means you can share a diff by sending someone a URL. Everything is in the hash — nothing is stored on a server, nothing is tracked. Paste two code blocks, hit Ctrl+Enter, copy the URL, send it to a teammate. They open it and see exactly what you see.

Sweet thought of this because it thought about how people actually use diff tools. I hadn't mentioned sharing. It just inferred that a browser-based tool without sharing would be incomplete.

Keyboard Shortcuts

Same pattern. Sweet added Ctrl+Enter to compute the diff, Ctrl+Shift+C to copy the diff as text, tab-based navigation between panes. All without being asked. Because an AI agent that actually uses tools knows what keyboard shortcuts matter.

How Sweet Built It Differently

This is the part that matters. Not that an AI wrote code — we've all seen that. What matters is how Sweet approached the task compared to other tools.

Chat-based AI (Claude Code, Cursor) Sweet (autonomous agent)
Approach You guide it step by step You state the goal, it plans and executes
Parallelism Single-threaded conversation Spawns subagents for each subsystem
Testing You have to ask for tests Writes tests as part of the build
Edge cases You discover them by using the tool Identifies and handles them proactively
Features You specify what you want Infers missing features from context
Review You review while it's writing Subagents review each other's output

Here's what I actually saw in my terminal while this was happening:

Subagent 1 (diff-engine) was implementing Myers in JavaScript, writing unit tests for empty inputs, identical strings, single-character changes, and multi-line blocks. It ran the tests after every change.

Subagent 2 (ui-layout) was building the HTML structure — two textarea panes, a compare button, the diff output table, the toggle between side-by-side and unified view. It was also writing the CSS, choosing a dark theme that matched the Sweet aesthetic.

Subagent 3 (quality) was stress-testing: pasting 500-line files, checking rendering performance, testing the URL encoding with special characters, verifying that the character-level diff didn't break on Unicode.

The main agent coordinated: when the diff engine was done and tests passed, it merged it into the UI. When the UI was ready, it integrated the syntax highlighter. When everything was stable, the quality subagent did a final pass and signed off.

I watched this happen over about 45 minutes. At no point did I type a line of code. At no point did I tell Sweet how to solve any of these problems. I stated the goal once. The agent decomposed it, parallelized it, executed it, tested it, and presented me with a working tool.

The Result

The diff viewer is at sweetcli.com/demo/diff-viewer/. It's one HTML file — the whole thing is inline CSS and JavaScript in a single page. You can save it to your desktop and open it from file:// and it works. You can deploy it to any static host. There's no build step, no package.json, no node_modules.

I use it every day. I use it to review PRs, to compare config files across environments, to check if my friend's code change introduced a regression, to settle arguments about what line 47 used to say. It's the kind of tool that's so simple and obvious that you wonder why you didn't build it yourself.

But here's the thing: I didn't build it. Sweet did. I just described what I needed.

What This Means

I've been building software for fifteen years. I've used every AI coding tool that's come out — GitHub Copilot, Cursor, Claude Code, you name it. They're all variations on the same theme: autocomplete with a chat interface. You still have to drive. You still have to debug. You still have to ask for tests, remind it about edge cases, and paste the error message back when it gets something wrong.

Sweet is different. Not because the underlying models are better (though they are good). Not because it has fancier features. Because it's autonomous. You give it a goal. It figures out the steps. It executes them in parallel. It tests its own work. It adds features you didn't ask for because it understands the problem domain.

That's the difference between a copilot and an engineer. One assists. The other delivers.

Try the diff viewer: sweetcli.com/demo/diff-viewer/
See the code: github.com/sweet-cli/diff-viewer
The diff viewer is free, open source, and runs completely in your browser. Sweet built it. Sweet can build your next tool too.

Ready to ship your next tool?

Sweet is $20/month. No per-seat pricing. No usage tiers. One flat rate for an AI engineer that builds while you sleep.

Get Started for $20/month
← Back to Blog