Generate and decode Diablo IV loot-filter import/export codes from a human- and AI-authorable spec — no in-game clicking, and full round-trip (decode an existing code back into an editable spec).
The filter code is base64-wrapped protobuf. This project reads and writes it directly, so you can:
- Build a filter from a readable spec (rules → conditions) instead of the in-game editor.
- Decode any existing code — including ones shared elsewhere — into a spec you can inspect and edit.
- Use capabilities the client hides, such as translucent labels via the color's alpha channel.
Three surfaces over one engine: a web app, a scriptable CLI, and an interactive terminal builder.
Web app — nothing to install; use the hosted builder + decoder at https://zmsl.github.io/djunker/.
CLI — grab the tarball from the latest release and install it globally (it's self-contained — the engine and catalogs are bundled in):
npm install -g ./djunker-cli-<version>.tgz
djunker decode <code> # now on your PATHOr run it once without installing: npx ./djunker-cli-<version>.tgz decode <code>. To work from source
instead, see Development.
pnpm install
pnpm buildCLI — author a filter as a pipeline of small commands (a spec flows stdin → stdout), then encode. Rules are referenced by index or name; conditions by type (one of each per rule):
pnpm -s djunker draft create "Endgame" \
| pnpm -s djunker draft rule create --action recolor --color '#80ff00ff' --name "Great 2H" \
| pnpm -s djunker draft rule condition create rarity "Great 2H" --rarities Legendary,Unique \
| pnpm -s djunker draft build # → a base64 filter codeDecode any code back into a spec with pnpm -s djunker decode <code>. The command tree is
self-describing for agents: pnpm -s djunker manifest (machine-readable command tree) and
pnpm -s djunker schema (JSON Schema for the spec). (-s keeps pnpm's banner out of the pipe; an
installed package exposes the djunker binary directly.)
Web app — a themed builder + decoder:
pnpm web # Vite dev serverInteractive TUI — the same builder in your terminal:
pnpm -s djunker interactiveA Claude Code skill that turns a build, a goal, or an existing filter code into a precedence-ordered rule set and emits it through this engine. It knows the engine and the reasoning (first-match-wins ordering, the condition vocabulary, the in-game constraints), and gets the itemization — which affixes, uniques, and thresholds matter — from you or from a build guide you share. It's meta-agnostic on purpose, so it doesn't go stale with the season.
What it does:
- From a build — paste a build guide or a Mobalytics / Maxroll / Wowhead / D4Builds URL and it extracts the uniques, per-slot affix priorities, and greater-affix targets, then structures them into keeps → highlights → suppression → floor.
- From a goal — "hide the junk", "only show my build's gear", "highlight GA cooldown rolls" — it interviews for what it needs and builds to it.
- From a code — hand it an existing base64 filter code and it decodes, explains, and edits in place.
It always emits a validated JSON FilterSpec and an importable code (via the same @djunker/app
engine and djunker CLI documented above), then explains the filter back to you rule by rule.
The skill lives in skills/loot-filter-creator/ — a SKILL.md plus
references/ (the condition/action vocabulary, the authoring API, build-extraction notes, and reusable
rule archetypes). To use it with Claude Code, make it discoverable as an Agent Skill: copy it into this
repo's .claude/skills/ (project scope) or your ~/.claude/skills/ (personal scope) —
cp -r skills/loot-filter-creator .claude/skills/— then invoke it with /loot-filter-creator, or just describe what you want ("make a strict endgame
filter for my Rogue") and Claude will trigger it. The skills/ copy is the source of truth; re-copy
after editing so the two don't drift.
A pnpm monorepo: packages/data (catalogs) → packages/core (encode/decode engine + the one Zod
spec schema) → packages/app (stateless operations + the FilterEditor ViewModel) → packages/cli
(the djunker binary + Ink TUI); apps/web (Vite + React) consumes @djunker/app. All three
surfaces bind the same engine.
pnpm build— build the packages ·pnpm typecheck— type-check every projectpnpm test— engine + CLI tests ·pnpm web:test— web app ·pnpm test:all— both
Two GitHub Actions workflows in .github/workflows/:
-
Release (
release.yml) — cut a CLI release by pushing a semver tag:git tag v0.1.0 && git push origin v0.1.0The workflow typechecks and tests, stamps the tag's version into
@djunker/cli, builds, and runsscripts/pack-cli.mjsto produce a self-contained tarball — the CLI's tsup build inlines the@djunker/*workspace packages, so the tarball carries the engine and catalogs and only pulls its real npm deps (commander, ink, react, zod) on install. It's attached to a GitHub Release with auto-generated notes. Build one locally to inspect it withpnpm pack:cli. -
Deploy Pages (
deploy-pages.yml) — buildsapps/weband publishes it to GitHub Pages on every push tomain. The site is served under/djunker/(set via Vitebase). One-time setup: in the repo, go to Settings → Pages → Source → GitHub Actions to enable it.
docs/embedding.md— build your own UI on the engine: the statefulFilterEditorViewModel and the stateless operations around it, covering the full decode/new → edit → encode lifecycle (with a framework-free walkthrough and a React binding).docs/wire-format.md— the complete byte format (envelope, message layouts, condition types 0–9, enums, bitmasks, constraints), reverse-engineered from live in-game exports plus the public protobuf spec. Reference only — the engine doesn't load it; it's what the encoder/decoder is written and audited against, and proven by the round-trip fixtures inpackages/core/test/fixtures/.packages/data/src/— the reference data the engine loads: the ID ↔ name catalogs (affixes.json,uniques.jsonincl.variant: "seal"forms,item-types.json) plusenums.json(rarities, item-property tiers, visibility, condition types — the name↔wire mappings). Catalog entries have anid(fixed32hex),decimal, andname; affixes also carrycategory/stat/classeswhere known.
Names resolve through these catalogs, with a raw 0x… escape hatch for anything not catalogued.
Reverse-engineering of the format and catalogs is complete and byte-verified against real game codes
(packages/core/test/fixtures/). The engine, CLI, TUI, and web app are in active development.
MIT — see LICENSE.