Skip to content

Composable diff analysis and automation for GitHub and the CLI

The devil is in the diff.

Measure changes. Match rules. Act on the result.

Git gives you a patch and a pair of raw counters. diffdevil turns the details inside that diff into facts you can see in GitHub, query locally, evaluate in workflows, and hand to agents.

The CLI, library and Actions are released. The browser extension is coming to the Chrome Web Store. The GitHub App is in private preview; public access is coming.

src/Foo.cs · one edit blockreplacement-lines-v1
@@ -41,2 +41,3 @@
− var limit = ReadLimit(config);
− return Clamp(value, 0, limit);
+ var limit = ReadLimit(options);
+ Guard.NonNegative(limit);
+ return Clamp(value, minimum, limit);

Replacement-aware · what a size policy counts

3changed= exact

~ 2 modified · + 1 added-only · − 0 deleted-only

Raw counters · what the patch shows

+3−2

raw churn 5 · still reported, never renamed

Calling this "5 changed lines" would misrepresent the edit. Two lines were replaced, one was added.

01Run diffdevil yourself

CLI · TypeScript · GitHub Actions

One workflow file gives a repository size labels, no comments, and an inspectable summary. The same engine answers local queries, script conditions, the TypeScript API and agents.

.github/workflows/size.ymlwrites labels · pull-requests: write
name: Pull-request size
on:
  pull_request_target:
    types: [opened, reopened, synchronize, edited]
permissions:
  pull-requests: write
jobs:
  size:
    runs-on: ubuntu-latest
    steps:
      - uses: Wolfsblvt/diffdevil@v1

02Bring Changed into GitHub

Browser extension

See replacement-aware Changed, per-file decomposition, personal or repository size bands, and inspectable reports directly on pull requests. No workflow, App installation, or repository write access required.

Changed178+45 −18 ~115size/M
Coming to the Chrome Web StoreExplore the browser extension →

03Let it run for you

GitHub App

Install the GitHub App once for automatic analysis, native checks, labels and managed configuration, then read pull-request flow, size and history across your repositories. No workflow file, token assembly, or runner upkeep.

#measure

Why changed lines exist

Raw churn counts additions and deletions separately. Replacement-aware changed lines classify adjacent replacements once, while keeping added-only, deleted-only and modified as separate facts. Your size policy reads one number; the rest stays visible so nobody has to trust it blind.

docs/examples/reports/exact.json · policy full.yml3 files · 2 included · 1 excluded

Policy-focus metric · review

178changed

= exactlines.changed

band m → size/M

Decomposition

  • + added-only45
  • − deleted-only18
  • ~ modified115

Raw · contrast

  • + additions160
  • − deletions133
  • churn293

▣ package-lock.json excluded by **/package-lock.json — observed churn would be 1,193.

Try this lesson in the playground →

#extension

Changed, where you already review

diffdevil for GitHub adds replacement-aware totals, per-file decomposition, size bands and inspectable reports directly to pull requests, without requiring a repository workflow or write access.

The diffdevil analysis report open on a GitHub pull request: 178 changed, exact, its decomposition, raw additions and deletions, files and coverage, the personal size policy selecting M, and the desired size/M label with readback not observed.
The report opens in place: the number, its evidence, raw churn, coverage, the policy in effect and the label it would choose. Actual extension UI on an authored pull-request fixture, captured for the Store listing.
  • See the useful numberAggregate and per-file Changed, with added-only, deleted-only, and modified positions kept distinct.
  • Apply your own policySee virtual size bands even when the repository has no workflow, labels, App installation, or diffdevil configuration.
  • Open the evidenceInspect the complete report, policy origin, exclusions, and evidence quality without leaving the pull request.
Coming to the Chrome Web StoreExplore diffdevil for GitHub →Local analysis by default. Raw patches are not persisted.

#app · diffdevil GitHub App

diffdevil,
with a memory.

Every pull request in every repository you choose, remembered: what merged, how big it was, how long it took, and how this period compares with the last. Native checks and size labels run on every pull request with the same engine and .diffdevil.yml as the Action, and nothing to maintain.

Public access is comingHow the App worksSelf-host it →

Free, Pro and Business plans. The Community App is open source (AGPL-3.0-only) and self-hostable.

  • History that remembersEvery merged pull request, its size and its lifecycle, period over period. Numbers and paths, never source.
  • All your repositories at onceOverview, pull-request flow, time to merge and change volume across everything you can read.
  • Checks and labels on every PROne native check summary per head and size labels in declared groups, read back after every write.
  • Install onceNo workflow file, no personal token, no runner minutes or upgrades. It stays current.
  • Defaults that scaleA preset per account or organisation; a repository’s .diffdevil.yml still wins where it speaks.
  • Every setting explainedSee the effective policy and where each value came from. Export it any time.
The diffdevil App Overview across seven repositories: 20 merged pull requests in 30 days against 19 before, opened, open, time to merge, median Changed and raw churn, then the change-volume chart split into modified, added-only and deleted-only lines.
Overview: every repository you can read, merged, opened and open work, time to merge and replacement-aware change volume over the period. Actual App UI on its sample accounts; every figure is fixture data.

#query

Query what changed

Analyze once. Read the result as prose, a value, a condition, a path list, or data. False, unknown and error are different exit codes, so a shell can tell them apart.

docs/examples/reports/exact.json · policy full.ymlshell
$ diffdevil query --expr metrics.review --format value
178
$ diffdevil query --files --select path --format lines
src/Foo.cs
tests/Foo.Tests.cs
$ diffdevil check --metric changed --gt 100 && echo over
over
$ diffdevil check --metric changed --gt 1000; echo $?
1   # false, not an error
$ diffdevil analyze --format json | jq -c .totals.lines.changed
{"status":"exact","value":178}
$ diffdevil query --files --metric changed --gt 100 --select path
src/Foo.cs
$ diffdevil analyze --format agent
diffdevil.agent-report/1 schema=1.0
semantics language=diffdevil-expr/1 numbers=diffdevil-number/1 replacement_lines=replacement-lines-v1 paths=diffdevil-glob/1 presets=[] limits="diffdevil-limits/1"
source kind=git comparison=direct id="fixture-exact-comparison" base="fixture-base" head="fixture-head"
identity report="report-e0a41e5620b9af3dc270f7e1067413fc0de876c53665565916c6d4aef9d29a55" policy="policy-3254961b05ecd7c46d4f2dc17ec26d8afba67d241b462ebc1ca59116e307d4cf"
evidence status=exact file_set=complete
lines changed=178 modified=115 added_only=45 deleted_only=18
raw churn=293 added=160 deleted=133
files total=3 excluded=1 included=2 added=0 deleted=0 modified=2 renamed=0 copied=0 binary=0 unmeasurable=0
scope id="production" file_set=complete files=1 changed=173 raw_churn=286
scope id="tests" file_set=complete files=1 changed=5 raw_churn=7
metrics "churn"=293 "destructive"=133 "productionReview"=173 "review"=178 "weighted"=524
bands "size"=resolved("m",lower=100,upper=500)
…

same report → human · scalar · lines · NUL · JSON · JSONL · env · Markdown · agentstdout is the result; diagnostics go to stderr

#policy

Policy → plan → apply

Facts are not effects. Planning is not applying. A token being present never turns reading into writing. You declare the policy; diffdevil evaluates it, shows the intended effects, and mutates only when told to.

§ Configured · .diffdevil.ymlyou own this
bands:
  size:
    value: metrics.review
    ranges:
    - { id: xs, lt: 20 }
    - { id: s, lt: 100 }
    - { id: m, lt: 500 }
    - { id: l, lt: 1000 }
    - { id: xl, otherwise: true }
rules:
  size:
    band: size
    effects:
      labels:
        group: size
        byBand: { xs: size/XS, s: size/S,
                  m: size/M, l: size/L,
                  xl: size/XL }
→ Proposed · diffdevil planinspectable, no writes
{ "kind": "diffdevil.plan",
  "stage": "desired",
  "rules": { "size": {
    "band": { "id": "m",
              "lower": 100, "upper": 500 },
    "disposition": "matched" } },
  "operations": [
    { "kind": "label.remove",
      "name": "review/destructive" },
    { "kind": "label.select",
      "group": "size",
      "selected": "size/M",
      "members": ["size/XS", "size/S",
        "size/M", "size/L",
        "size/XL", "size/Unknown"] }
  ] }
⟲ Applied + readback · diffdevil applyexplicit
Read current labels on PR #123.
Group size: size/S present, size/M absent.
  − remove size/S
  + add size/M
Unrelated labels untouched (3).
Applied 2 label changes.
Readback confirms size/M.

What an explicit apply reports. Readback is observed provider state, never assumed.

#agents

Give agents facts, not patches to eyeball

A coding agent that reads a diff re-derives arithmetic and invents precision. diffdevil hands it deterministic facts with evidence standing, in a projection built for a context window. Same semantics as the report, nothing added.

What a person readsdiffdevil analyze
diffdevil analysis
git · fixture-base..fixture-head · direct · = exact

Changed    178 lines
  +45 added only · -18 deleted only · ~115 modified

Raw        +160 additions · -133 deletions · 293 churn
Files      3 total · 2 included · 1 excluded

Policy
  Band       size → m
  Metrics    churn 293 · destructive 133 · productionReview 173 · … 2 more
What an agent consumesdiffdevil analyze --format agent
diffdevil.agent-report/1 schema=1.0
semantics language=diffdevil-expr/1 numbers=diffdevil-number/1 replacement_lines=replacement-lines-v1 paths=diffdevil-glob/1 presets=[] limits="diffdevil-limits/1"
source kind=git comparison=direct id="fixture-exact-comparison" base="fixture-base" head="fixture-head"
identity report="report-e0a41e5620b9af3dc270f7e1067413fc0de876c53665565916c6d4aef9d29a55" policy="policy-3254961b05ecd7c46d4f2dc17ec26d8afba67d241b462ebc1ca59116e307d4cf"
evidence status=exact file_set=complete
lines changed=178 modified=115 added_only=45 deleted_only=18
raw churn=293 added=160 deleted=133
files total=3 excluded=1 included=2 added=0 deleted=0 modified=2 renamed=0 copied=0 binary=0 unmeasurable=0
scope id="production" file_set=complete files=1 changed=173 raw_churn=286
scope id="tests" file_set=complete files=1 changed=5 raw_churn=7
metrics "churn"=293 "destructive"=133 "productionReview"=173 "review"=178 "weighted"=524
bands "size"=resolved("m",lower=100,upper=500)

= ≈ ? ∅ survive into this form. No judgment, no prose, no renamed churn.

Teach your agent diffdevil once

Agent Skill · v1.0.0

A versioned Skill folder, including its core, references and notice, installed without rewriting it for your harness. It teaches the agent to analyze, query, check, plan, and apply under your explicit grant; to read = ≈ ? ∅; and to keep facts, policy, proposed and applied effects apart. It can check whether its installed version is current.

/setup/skill.mdRead the Skill core →

Or let an agent set diffdevil up

one sentence + a URL
Let an agent set up

Adapts the one-file workflow to existing automation, explains its permissions and verifies the selected event and effects.

/setup/actions.mdAny harness · no vendor

#evidence

Evidence you can trust

Every value states how it was established. A convenient number is not worth inventing certainty, so exact, bounded, unknown and unmeasurable stay different words in every output.

= exact178

Every included edit block was observed.

≈ bounded60–70

A proven interval. When the whole interval lies inside one band, the band is still proven.

? unknown≥ 5

File set incomplete; only a lower bound. No band is invented; the rule holds, or selects only its declared unknown label.

∅ unmeasurableundefined

Lines are undefined for binary content. Shown as undefined, never as 0.

A line count does not establish importance, risk, complexity or quality. Your policy owns that judgment; diffdevil evaluates it and shows its work.

Support the work.

Help fund development, maintenance, and the ideas behind Wolfsblvt Works.

Star diffdevil on GitHub