θ thlibo · v0.11.6

a utility · ancient greek · mit license

θ θλίβω  — to press, crush, compress.

Your AI assistant doesn't need to read
the whole git diff.

thlibo sits between your AI coding agent — Claude Code, Codex, Cursor, GitHub Copilot CLI, or VS Code Copilot — and the shell. It rewrites git diff, npm install, cargo test and other verbose commands so the model sees the summary, not the noise. Deterministic native-Go filters handle the usual suspects; a local Gemma 4 model compresses everything else. Nothing leaves your machine.

macos / linux
$curl -fsSL https://raw.githubusercontent.com/3rg0n/thlibo/main/scripts/install.sh | bash
windows · powershell
PS>irm https://raw.githubusercontent.com/3rg0n/thlibo/main/scripts/install.ps1 | iex

v 0.11.6 binary ~7 MB model 5.1 GB (opt-in) hf tokens none

§ i

What you'd send vs. what thlibo sends.

git diff HEAD~5 via git-filter · native Go
~17,205 tok ~232 tok −98.6%
npm list (200 deps) via npm-filter · native Go
~2,307 tok ~13 tok −99.4%
cargo test (20 failures) via cargo-filter · native Go
~3,307 tok ~359 tok −89.2%
.har capture (30 requests) via har-filter · native Go · redacts secrets
~2,601 tok ~115 tok −95.6%
saved web page (.mhtml, 7 MB) via mhtml-filter · native Go · drops embedded assets
~1.76 M tok ~33,147 tok −98.1%
PDF (7 pages, 160 KB, born-digital) via pdf-filter · native Go · no Python
~142,701 tok ~1,814 tok −98.7%
scanned PDF (2 pages, image-only) via pdf-to-md → vision OCR · inferd Gemma · vs Claude's PNG reads
~3,180 tok ~499 tok −84%
traefik access log (7 d, 543 MB) via cordon-filter · embeddings + k-NN
~135.8 M tok ~42,315 tok −99.97%
application log (7 d, 551 MB, 633k lines) via cordon-filter · embeddings + k-NN
~137.8 M tok ~63,565 tok −99.95%
Methodology + reproduction notes in the README. raw-token figure estimated from byte count; the input never fits a context window, so it's the size thlibo saves you from sending. For a PDF that raw count is the container's bytes, not extractable prose — the honest comparison is against the pages you'd otherwise read as images (see ). baseline is Claude's own fallback for an image-only PDF: install PDF libs, render each page to PNG, read the pages as multimodal images (~1,590 tok/page).
§ i·b

Same runs, actual output.

application log — 122 ndjson lines, one error repeating
via ndjson-filter · collapses identical records, keeps the distinct ones · 18,925 → 333 bytes
before~4,731 tok
{"level":"error","ts":"2026-07-08T17:01:03Z","msg":"connection refused","host":"db-primary.internal:5432","attempt":1,"request_id":"req-7001"}
{"level":"error","ts":"2026-07-08T17:02:03Z","msg":"connection refused","host":"db-primary.internal:5432","attempt":2,"request_id":"req-7002"}
{"level":"error","ts":"2026-07-08T17:03:03Z","msg":"connection refused","host":"db-primary.internal:5432","attempt":3,"request_id":"req-7003"}
…  117 more identical "connection refused" lines  …
{"level":"warn","ts":"2026-07-08T17:40:00Z","msg":"pool exhausted","size":20}
{"level":"info","ts":"2026-07-08T17:59:00Z","msg":"reconnected","host":"db-primary.internal:5432"}
after~83 tok · −98.2%
{"level":"error","ts":"2026-07-08T17:01:03Z","msg":"connection refused","host":"db-primary.internal:5432","attempt":1,"request_id":"req-7001","_count":120}
{"level":"warn","ts":"2026-07-08T17:40:00Z","msg":"pool exhausted","size":20}
{"level":"info","ts":"2026-07-08T17:59:00Z","msg":"reconnected","host":"db-primary.internal:5432"}
.har capture — 30 requests, secrets in headers/URLs/bodies
via har-filter · drops static assets, keeps API calls, redacts every secret · 10,405 → 461 bytes
before~2,601 tok
{ "log": { "version": "1.2", "creator": {…},
  "entries": [
    { "request": { "method":"GET",
        "url":"…/assets/chunk.1.js" },   ← 26 static assets
      "response": { "status":200, "content":
        { "size":48120, "mimeType":"application/javascript" }}},

    { "request": { "method":"POST",
        "url":"…/v2/auth/login?client_id=PUBLICAPP",
        "headers":[{"name":"Authorization",
          "value":"Bearer sk-live-9a8b7c6d5e4f3g2h1i0j"}],
        "postData":{"text":
          "{\"password\":\"tr1n1ty!42\",\"username\":\"neo\"}"}},
      "response":{"content":{"text":
        "{\"token\":\"eyJhbGciOiJIUzI1NiJ9.eyJ…\"}"}}}, …
after~115 tok · −95.6%
HAR 1.2: 2 requests (26 static dropped)
POST 200 …/v2/auth/login?client_id=PUBLICAPP  (json 180B 312ms)
  Authorization: <redacted>
  → {"password":"<redacted>","username":"neo"}
  ← {"expires_in":3600,"token":"<redacted>","user_id":1007}
GET 200 …/v2/profile?access_token=<redacted>&scid=<redacted>  (json 140B 88ms)
  ← {"email":"neo@example.com","name":"Neo Anderson"}
scanned PDF — 2 image-only pages, no extractable text
via pdf-to-md → vision OCR · each page rendered & read by inferd's Gemma vision model → text · 60 KB → ~2 KB
without thlibo~3,180 tok
# Claude can't read the image-only PDF as text, so it
# bootstraps its own pipeline, then reads the pages as images:
$ pip install pypdf pdfplumber pypdfium2   ← install noise
$ python render.py  → page-1.png (1191×1684)
$ python render.py  → page-2.png (1191×1684)
[image] page-1.png   ~1,590 tok
[image] page-2.png   ~1,590 tok
# multi-step, multimodal, and the model re-reads pixels
# on every follow-up question.
with thlibo~499 tok · −84%
## Page 1
# Peninsula Property Surveying Ltd
16. The client agrees to pay the full survey fee, which
    is £250, within 7 days of the invoice date. If payment
    is not received within 7 days, a £50 late fee is added…
Signed: ____________  Date: ___ / ___ / ___
## Page 2
Phil Routledge MRICS FISVA MRPSA MAE
Chartered Building Surveyor · RICS Registered Valuer
[RICs Logo]   [Signature]
saved web page — .mhtml archive, one article + 87 embedded assets
via mhtml-filter · extracts the article HTML → Markdown, drops the base64 images/CSS/scripts (~93% of the file) · 7 MB → ~130 KB
before~1.76 M tok
From: <Saved by Blink>
Content-Type: multipart/related; boundary="----MultipartB…"
------MultipartBoundary…
Content-Type: text/html
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE html><html lang=3D"en">… the article …
------MultipartBoundary…
Content-Type: image/webp        ← 67 jpeg + 11 webp + svg,
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAAA…  (megabytes of base64) …
------MultipartBoundary…  (6 CSS parts too)
after~33,147 tok · −98.1%
# Inside Hyper-V Sockets: A Deep Dive…
## Introduction
Microsoft introduced its Hyper-V virtualization technology
quite some time ago… Hyper-V Sockets connect to VMs from the
host using the VMBus as transport instead of TCP/IP.
- Root partition — Windows Server with the Hyper-V role.
- Guest OS — a Generation 2 virtual machine.
[image ref] · [More on VMBus](https://…/vmbus)
# prose, headings, lists, links, code — no asset bytes.
§ ii

How it works, in three moves.

i.

A hook, not a proxy.

thlibo installs a Claude Code PreToolUse hook. Before the Bash tool runs, the hook asks thlibo rewrite whether to wrap the command. If yes, it swaps git status for thlibo exec -- git status.

The same idea reaches four more agents. Codex and GitHub Copilot CLI use the symmetric PostToolUse path — the compressed result replaces the tool output. Cursor and VS Code Copilot rewrite the command on the way in, like Claude Code. One engine underneath.

ii.

Native filters, where a regex will do.

git-filter, npm-filter, cargo-filter, har-filter, pdf-filter, and more. Compiled Go, run in-process. They collapse diff hunks, strip dep-tree chatter, keep the failing tests, redact secrets from HTTP archives, and read PDFs without shelling out. Nothing a model could do better, faster, or more reliably — and Python is off the compression path entirely: it's needed only to rasterize a scanned PDF for OCR.

Add your own in ~/.thlibo/processors/<name>/ — a processor.yaml descriptor and a script. It picks up on next invocation.

iii.

A local model, for the rest.

When no deterministic filter matches, thlibo asks a local Gemma 4 E4B — quantised, ~5 GB on disk — which processor (if any) to route to. Prompt processors summarise stack traces, error logs, and arbitrary output. The same model reads images: a scanned PDF with no extractable text is OCR'd page-by-page through Gemma 4's vision path, so a photographed contract comes back as real Markdown instead of a "no text" placeholder.

Inference runs in inferd, a separate sidecar daemon thlibo installs alongside itself. One warm model per host. Unix socket or named pipe, ACL'd to your user.

If any step fails — daemon down, model missing, script crashes — the original bytes pass through unchanged. The model sees what it would have seen without thlibo. The compressor never breaks the client.

§ iii

It's not sending your diffs anywhere.

thlibo runs as your user. The daemon binds a Unix domain socket (or Windows named pipe) with an ACL that grants only your SID access. Nothing listens on the network.

No telemetry by default. Optional OpenTelemetry emission is off unless you set THLIBO_ENABLE_TELEMETRY, and even then it ships only sizes, counts, and durations to a collector you run — never your code, prompts, commands, or file paths. No HuggingFace token required — the model pull is a plain anonymous HTTPS GET against a public Apache-licensed repack, SHA-256 pinned at build time.

If your repo has a clause against uploading source, thlibo does not violate it. If your model is air-gapped, it works offline.

  • transportunix socket / named pipe · never network
  • unix acl0660 · group inferd-users (or user-only)
  • windows aclpipe DACL · current user SID only
  • telemetryoff by default · opt-in OTel, content-free
  • modelunsloth/gemma-4-E4B-it · Apache-2.0
  • installper-user · no sudo, no admin
# IPC endpoints, per platform (inferd sidecar)
linux   $XDG_RUNTIME_DIR/inferd/inferd.sock
darwin  $TMPDIR/inferd/inferd.sock
windows \\.\pipe\inferd

# permissions
infer 0660 group inferd-users (or user-only)
admin 0600 owner only

# single-instance lock
$XDG_RUNTIME_DIR/inferd/inferd.lock

# model, on disk (shared per-platform store)
linux   ~/.local/share/models/gemma-4-e4b-ud-q4-k-xl.gguf
darwin  ~/Library/Application Support/models/...
windows %LOCALAPPDATA%\models\...
5,126,304,928 bytes · sha256 pinned