θ thlibo · v0.10.1

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.10.1 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 (17 pages, 326 KB) via pdf-to-md → cordon-filter · python + local model
~38,250 tok ~12,173 tok −68.2%
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 log never fits a context window, so it's the size thlibo saves you from sending. 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, and more. Compiled Go with tight regex rules, run in-process. They collapse diff hunks, strip dep-tree chatter, keep the failing tests, redact secrets from HTTP archives. Nothing a model could do better, faster, or more reliably — and no Python needed.

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. No opt-in-to-ship-anonymised-usage-data. 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
  • telemetrynone
  • 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