Compare two versions of a EULA or privacy policy and see what actually changed — a clause-level redline instead of a noisy line diff, so reflow, renumbering, and moved sections don't drown out the substance.
eula-diff is a small, deterministic Go tool: it extracts, normalizes, segments
into clauses, aligns the two versions, and emits a structured redline (text or
JSON). It ships with a companion Claude skill that reads the JSON and
adds the semantic layer — classifying each change and calling out who it favors.
Two revisions of the same agreement rarely line up textually. A paragraph
rewraps and every line reads as changed; inserting one section renumbers all the
rest; an arbitration clause moves from §12 to §3. A line diff screams at noise
and buries the one edit that matters. eula-diff aligns clauses, not lines,
so cosmetic churn falls away and real changes stand out.
go install github.com/husobee/eula-diff/cmd/eula-diff@latest
Or build from a checkout:
go build -o eula-diff ./cmd/eula-diff
eula-diff [--json] <old> <new>
eula-diff --text <file>
<old>,<new>— the two versions as.txt,.md, or.htmlfiles (convert PDFs to text first, e.g. withpdftotext).--json— emit the structured diff instead of the human summary.--text— print the cleaned, normalized text of a single file (the input the segmenter sees). Use it to re-analyze raw text when confidence is low.--version— print the version.
Clause splitting is heuristic and does not cope with every layout, so each run
reports a confidence level (high / medium / low). When it isn't high
— typically because a document collapsed into a single clause — the clause-level
diff is unreliable and should be treated as such; the human summary prints a
warning, and the companion skill falls back to --text and re-reads the prose.
Human summary:
$ eula-diff testdata/privacy_v1.txt testdata/privacy_v2.txt
testdata/privacy_v1.txt -> testdata/privacy_v2.txt
7 -> 7 clauses | +1 added -1 removed ~3 modified >1 moved =2 unchanged
[MODIFIED] 1 Information We Collect
[MODIFIED] 3 Data Retention
[MOVED] 4 Your Rights
[MODIFIED] 5 Third Parties
[ADDED] 6 Security
[REMOVED] 3 Cookies
Structured diff (--json) reports each change with a word-level redline:
old file ─┐
├─► extract ─► normalize ─► segment ─► align ─► redline ─► report (text | JSON)
new file ─┘
- extract —
.txt/.mdas-is,.htmlreduced to visible text. - normalize — unify glyphs and whitespace, de-hyphenate line breaks, strip page furniture.
- segment — split into clauses on numbered / labeled / all-caps headings.
- align — match clauses across versions by content similarity; classify each as added, removed, modified, moved, or unchanged.
- redline — word-level LCS diff for modified clauses.
- report — assemble the JSON contract (or a terse terminal summary).
skill/ is a Claude skill that runs eula-diff --json and turns the
structured diff into a plain-language review: each change tagged by category,
direction (favors user / vendor / neutral), and severity, with the red flags
(added arbitration, indefinite retention, enabled data sale, removed opt-outs…)
surfaced first. See skill/assets/example-report.md
for a worked example. To use it locally, symlink or copy skill/ to
~/.claude/skills/eula-diff/ and put eula-diff on your PATH.
The tool is deterministic and testable; the semantic judgment lives in the skill. It is a drafting aid, not legal advice.
go test ./... # run the suite
go test ./internal/cli -update # regenerate golden fixtures
MIT — see LICENSE.
{ "status": "modified", "v1": { "number": "3", "heading": "Data Retention" }, "v2": { "number": "3", "heading": "Data Retention" }, "similarity": 0.68, "redline": [ { "op": "eq", "text": "We retain your personal data for " }, { "op": "del", "text": "12 months after account closure." }, { "op": "ins", "text": "as long as your account is active." } ] }