Repository navigation
Releases: aurokin/warcraft_cli
Release list
v0.6.0
[0.6.0] - 2026-10-04
This release standardizes every binary's JSON, errors and global output flags, expands provider
reads and guide discovery, and corrects build and sampled-analysis results.
Breaking migration: read payloads from data, use --brief after wrapper search/resolve
and global output flags before the subcommand, and update renamed fields and removed commands
listed below. Regenerate existing SimC build harnesses and rerun their comparisons: earlier
harnesses could replace the supplied build with the spec's default talents.
Added
- Every binary emits one envelope with
ok,provider,command,kind,schema_version: "1",query,provenanceanddata, pluserroron failure.warcraft schemaexposes the shared schema. Commands, arguments and options have help text and generated references, including option bounds and metavars. - Global
--compact,--compact-max-chars,--fields,--fields-strictand--profile agent|humanwork on every binary and wrapper passthrough. Compact output preserves URLs, talent strings, export codes, SimC profiles and command/input values, and lists every cut inprovenance.compacted_paths. Missing projected paths appear infields_missing; strict projection failsmissing_fields. Failure envelopes stay whole. - Provider tiers appear in
warcraft doctor: core Wowhead, Warcraft Logs and SimC; supported Method, Icy Veins, Raider.IO, Warcraft Wiki and Lorrgs; experimental Raidbots, Blizzard and CurseForge. Lorrgs moves from experimental to supported. - Release-tag CI runs
make check, builds the root wheel and attaches it to the GitHub release forpipxanduvxinstallation. blizzard pvp-season,pvp-leaderboardandpvp-characterread season metadata, title cutoffs, ranked players and character ratings/records. Leaderboards support--seasonand--limitup to 5000; PvP supports retail, progression Classic and Anniversary; Classic character reads probe 2v2, 3v3, 5v5 and rbg when the summary omits them. Classic Era has no PvP seasons.blizzard collectionsreturns mounts, pets, toys, heirlooms and transmogs with counts, name filters and limits.auctions <realm>and retailcommoditiessummarize listings and units per item id with minimum and unit-weighted median prices in copper.--item-idselects exact ids withnot_listed; otherwise--limitselects the most-listed items. Bid-only listings count toward units/listings but have no price. Market snapshots, leaderboards and PvP cutoffs report Blizzard'sLast-Modifiedand its age at read time. Collections and commodity prices are retail-only in practice; Classic auctions omit trade goods.- Blizzard realm, item and character reads support
--game-version classic-eraandclassic-anniversary, alongside retail and progressionclassic; doctor reports verification for all four.characteralso takesREGION REALM NAME, and--sectionreads PvP summaries/brackets, professions or mount/pet/toy collections as rawcharacter_sectiondata. raiderio leaderboard raids --raidreturns guild rankings with region/realm filters, paging and returned/requested counts.raiderio raidslists raid/encounter slugs;affixesreturns this week's affixes;dungeonslists season dungeon pools;cutoffsreturns regional top 0.1%, 1%, 10%, 25% and 40% player-rating cutoffs with UTCupdated_at.- Raider.IO profiles add
last_crawled_at; character profiles include every recent run and each dungeon's best run, with counts and playedspec,spec_slugandrole. Raid rankings disclose hidden pulls/percentages inprivacy. Guild roster previews carry rank, active role and realm identity, sort guild master first, and support--roster-limitwithroster_truncated; member counts mean members Raider.IO tracks. icy-veins index-refreshcrawls pages missing from its stale sitemap into a durable local index. It preserves past seasons, resumes a frontier, prioritizes unseen pages, obeys--max-requests(default 250) and at least one-second pacing and stops on blocking responses or repeated failures. A bundled index snapshot makes current boss, dungeon and delve pages discoverable before a crawl; doctor reports the index in use.make icy-veins-snapshotreplaces the bundled snapshot only after a complete crawl.- Icy Veins discovery combines sitemap, current site menu and site index. Rows report source, sitemap modification time, menu title, indexed headline/publication date and parent; provenance names each source and warns about stale, missing or unreadable data. Searchable content includes raid encounters, dungeons, delves, professions, tier lists, hubs, transmog and PvP subpages.
- Icy Veins converts calculator builds on PvE and PvP guides into
wow_talent_exportreferences for SimC. Converted rows carry class/specbuild_identity,source.converted_fromand tree-data URL. Published equivalent imports add no duplicate on that page; labels merge shared builds. Calculator URLs remain when PvP selections matter or conversion fails, with conversion status and failure reason. wowhead talent-calcdecodes Classic Era/SoD, Classic PTR, TBC, Wrath, Cataclysm and WoW Forever builds into tree points and ranked talents, and MoP Classic builds into tier choices. Unsupported/unreadable builds reportdecoded: falsewith a reason. Trimmed calculator data is cached for 30 days; glyph, rune and click-order segments remain raw. SimC continues to decode retail builds only.- Wowhead item sets are searchable/resolvable entities.
entity/entity-pageadd infobox/mapfacts, quest start/end and ordered chains, and NPC/object locations;entityreports spawn counts whileentity-pagekeeps coordinates. Listview links retain tab ids and upstream drop/vendorlistview_data. Linked previews exposefetch_more_truncated; Wiki navigation addsmore_availableandfetch_more_command. - Wowhead news and blue-tracker rows include UTC
posted_atand timestamp-parse counts. Raider.IO run rows includelogged_run_id, its own logged-run id rather than a Warcraft Logs code. Warcraft Logs guild/character rows addclass_name; sampled fights add key level/time and difficulty/key-level counts with mixed-cohort notes. Event pages describe next-page retrieval. - SimC doctor reports ripgrep/binary dependencies, disabled commands and checkout/binary commit mismatch. Priority/opener/describe results report totals and truncation;
runreturns player/target result headers.simreports other actors, ranked profilesets and stat weights; SimC and Raidbots profilesets includemean_error,mean_stddevand iterations. Simulation disclosures flag default gear and default talents. - Guide comparisons add subject identities/agreement, bundles without builds and source
content_updated_at, separate from export time. Icy Veins/Method reads expose requested/served redirects; orchestration persists them on exported and reused rows. - Successful cached reads include
provenance.cachewith backend, lookups, hits, hit/all-hit flags, hit age/TTL and backend errors. Wrapper totals preserve each provider's own block; ages are unknown whenever any hit came from Redis. Lorrgs, Blizzard and CurseForge gain configurable caches; Blizzard adds the one-hourBLIZZARD_SNAPSHOT_CACHE_TTL_SECONDSfor collections and market/PvP snapshots, reducing large bodies before caching. Doctors report cache configuration, availability and failures. Blizzard/CurseForge doctor status becomes ready with credentials and degraded without them, with typed capabilities marked as requiring client credentials/API keys; Warcraft Logs status reflects public access, and Lorrgs/Raidbots report ready.
Changed
- Envelope and errors: top-level payload copies are removed. Failures have
kind: "error",data: {}, parsedquery, and details undererror.details; full nested command paths are preserved. Uncaught exceptions become JSON errors on stderr. Exit codes are 2 usage, 3 auth, 4 not found, 5 network/timeout/rate limit/upstream, and 1 other failures. HTTP errors useauth_failed,not_found,rate_limitedorupstream_error; connection errors usenetwork_error. Lorrgs treats inaccessible private reports asnot_found. - Search/resolve fields: every provider uses
kind: "search_results"/"resolve_match",results/candidates, returned-rowcount, completetotal_matchesandtruncated. Coming-soon/not-supported totals are null. Rows haveprovider,kind,id,name,urlandfollow_up: {command, surface}. Migraterecommended_command/recommended_surfaceandalternativestocommand/surfaceandalternative_commands; article rows dropentity_type/type_nameand repeated metadata slug/title. Raider.IO discovery replacesprofile_urlwithurl; profile/run payloads retainprofile_url. Lorrgs/WCL resolve replaceresolutionwithresolve_match; Lorrgs replacesresultswithcandidatesand drops repeateddata.provider. - Search stubs use empty lists,
count: 0,total_matches: nullandtruncated: false; resolve stubs includeconfidence: "none",resolved,matchandnext_commandas applicable. Warcraft Logs report rows carry their site's URL. Lorrgs ranking rows carry Lorrgs URLs; a report URL row preserves that URL, while metadata rows have no URL. Unresolved provider results retain their topmatchand a fallback search when one can help; zero-match results and Warcraft Logs' explicit-only discovery have no fallback. - Wrapper discovery:
--briefreplaces the old subcommand--compact; global--compactmust precede the subcommand. The wrapper keeps provider rows and order, merges by query intent/title fit with a per-provider half-page cap, and exposeswrapper_rankingandmerge_policy, including provider totals and returned merged count. Weak partial/snippet-only matches are not rescaled into full matches. Provider-score ties use provider name, and stale g...
v0.5.0
[0.5.0] - 2026-06-25
Added
- Warcraft Logs site profiles (AUR-497):
warcraftlogs --site retail|classic|fresh ...now routes OAuth authorize/token endpoints and public/user GraphQL endpoints through the selected Warcraft Logs host (www,classic, orfresh). The wrapper now treatswarcraftlogsasexpansion_mode=profiled:warcraft --expansion retail warcraftlogs ...selects--site retail, classic-family keys (classic,tbc,wotlk,cata,mop-classic) select--site classic, and the new wrapper expansion keyfreshselects--site fresh.ptr,beta, andclassic-ptrremain unsupported for Warcraft Logs and are rejected instead of coerced. Live coverage verifies token exchange and GraphQL schema access for all three site profiles whenWARCRAFTLOGS_LIVE_TESTS=1and credentials are configured.
Fixed
- Warcraft Logs no longer depends on
simc-cliat runtime for talent-tree transport validation. The sharedvalidate_talent_tree_transporthelper now lives inwarcraft_core, withsimc_cli.talent_transportkept as a compatibility re-export, and the provider-to-provider import-linter exception was removed (AUR-501).
v0.4.0 - Lorrgs cooldown evidence
[0.4.0] - 2026-06-23
Added
- Lorrgs provider: new no-auth
lorrgscommand for the public Lorrgs API (https://api2.lorrgs.io/api/*) after a user requested support forlorrgs.io. Shipsdoctor; static metadata commands (roles,classes,specs,spec,spec-spells,zones,zone,zone-bosses,bosses,boss,boss-spells,spell,trinkets); season metadata commands (current-season,season <slug>); ranking commandsspec-ranking <spec-slug> <boss-slug>andspec-ranking-info <spec-slug> <boss-slug>for top-parse cooldown timelines;comp-ranking <boss-slug>with repeatable--role/--specfilters and kill-time bounds;search/resolvefor Lorrgs ranking URLs, Warcraft Logs report URLs, bare mixed-alphanumeric report codes, and free text spec/boss queries;report-overview <report-id-or-url>for report metadata without queueing fight/player timeline work; and cached-onlyuser-report/user-report-fightsreads. Browser-driving the public Lorrgs page showed the UI preservestype=damage-doneon/user_reports/<id>/fights, souser-report-fightsnow preservestypefrom report URLs and accepts--type. Each command emits{ok, provider, command, kind, query, provenance, data}on success, with provenance carrying the exactsource_url, API host, site URL, and upstream source posture (warcraftlogs, Wowhead tooltips), and{ok:false, ..., error:{code, message}}with a nonzero exit (never a traceback) fornot_found,invalid_query,rate_limited,http_error,network_error, andinvalid_response. Routed through the wrapper aswarcraft lorrgs ...withsearch/resolveready,expansion_mode=fixed, andsupported_expansions=["retail"]. The CLI intentionally does not expose Lorrgs queued load/dirty endpoints. Live public API coverage is gated byLORRGS_LIVE_TESTS=1 pytest -q -m live tests/test_lorrgs_live.py, including the user-provided Warcraft Logs URLhttps://www.warcraftlogs.com/reports/bG3xDYPqKjLm8XaR?fight=22&type=damage-done. Seedocs/lorrgs/README.md. - Wrapper cooldown evidence packet: new
warcraft cooldown-packet <warcraftlogs-report-url-or-code> --actor-id <source-id> --phase <n>composes cached Lorrgs report fight data (phase markers, boss casts, boss/spec slugs, player source ids), Lorrgs spell metadata and optional top-parsespec-rankingsamples, and Warcraft Logsreport-events --data-type castsfor exact player cast timestamps. The packet emits selected phase windows, player cooldown casts in the phase, boss casts, tracked spell metadata, top-parse comparison samples, source commands/provenance, and caveats. It is intentionally evidence-only, not a synthesized cooldown recommendation. - CurseForge provider scaffold (AUR-499): new
curseforgecommand for the public CurseForge addon API, decided GO in AUR-395. First slice shipscurseforge doctor(install state,api_keyauth posture forCURSEFORGE_API_KEYdiscovered via.env.local→~/.config/warcraft/providers/curseforge.env→ environment, and capability metadata —doctor/addonready,search/resolvecoming_soon) andcurseforge addon <slug-or-id>— resolves the addon (numeric → mod id, else agameId=1slug search) and returnsdata.metadata(raw CurseForge mod record),data.latest_files(the mod'slatestFiles), anddata.changelog(the newest file's changelog, best-effort: top-levelnullonly when the addon has no files; otherwise an object with the file'sbody—nullwhen that file exposes no notes — plussource_url, or a{file_id, error}marker on a failed fetch, never failing the lookup). Each command emits{ok, provider, command, kind, query, provenance, data}on success — provenance carriesgame_id/mod_id/slug/resolved_by/source_urls— and{ok:false, ..., error:{code, message}}with a nonzero exit (never a traceback) formissing_api_key,addon_not_found,http_error,network_error, andinvalid_response. Routed through the wrapper aswarcraft curseforge …withexpansion_mode=none(addon game-version compatibility lives inside file records, not the wrapper's expansion axis). The host, endpoints, and response shapes follow the documented public CurseForge Core API but are pending one-time live confirmation — every command payload carriesprovenance.verified: false; runCURSEFORGE_LIVE_TESTS=1 pytest -q -m live tests/test_curseforge_live.pywith a realCURSEFORGE_API_KEYto confirm them. Seedocs/curseforge/README.md. Deferred: dependency graph / game-version compatibility / file-download workflows.search/resolveare not implemented yet but exist as structuredcoming_soonstubs (a JSON envelope withcoming_soon: trueand asuggested_command, not a Click "No such command" error), matching the surfacedoctoradvertises. - Additive identity normalization across provider surfaces (AUR-458): raw fields preserved; each surface gains a sibling identity block from the shared
warcraft_core.identitycontract. raiderio —class_spec_identityon ranking roster rows, resolve character-profile candidates, character search results (class-onlynormalized), and guildroster_previewrows (highconfidence only when both class+spec resolve, elsenone). raidbots —class_spec_identityoninspect-reportactor summaries from the sim report'sclass/specialization(highwhen both present,unknownwhen both missing). wowprogress —class_spec_identityon thecharacterpayload (statusnormalized,confidence: none; spec sourced from the SimDPS table when present), andencounter_identityon guild encounter kill rows (name-only →normalized,encounter_id: null). method + icy-veins —ability_identityon Wowheadspelllinked-entity rows (statuscanonical,spell_idset); other entity types unchanged.docs/foundation/IDENTITY_CONTRACT.mdcoverage matrix + notes updated. - Blizzard API Game Data + Profile reads + live OAuth (AUR-455): completes the AUR-390 scaffold with a client-credentials OAuth token flow (ported from
warcraftlogs: token fetched once and cached in shared state underblizzard-api-client-credentials, keyed bysha256(region, id, secret), reused until ~60s before expiry) and three typed read commands —blizzard realm <slug>(dynamic),blizzard item <id>(static), andblizzard character <realm> <name>(profile, retail only). Region/namespace routing is honored:--region(defaultBLIZZARD_REGION, elseus;us/eu/kr/tw/cnwith aliases likena), and--game-version retail|classic(or the--classicshorthand; passing--classicalongside a contradictory explicit--game-versionis rejected withunsupported_game_versionrather than silently routing to classic) selects the namespace-class infix;--localepasses through. Each command emits{ok, provider, command, kind, query, provenance, data}on success — provenance carriesregion/namespace/namespace_class/game_version/source_url— and{ok:false, ..., error:{code, message}}with a nonzero exit (never a traceback) formissing_client_credentials,unsupported_region,unsupported_game_version,classic_profile_unsupported,http_error,network_error, andinvalid_response.blizzard doctorflipsgame_data/profiletoreadyand surfaces the region/routing block.search/resolveremaincoming_soonbut exist as structured stubs (a JSON envelope withcoming_soon: trueand asuggested_command, not a Click "No such command" error), matching the surfacedoctoradvertises. The endpoint hosts, OAuth token URL, and namespace strings follow documented Blizzard API conventions but are pending one-time live confirmation —doctorcarries this marker underregion.verificationand every command payload carriesprovenance.verified: false; runBLIZZARD_LIVE_TESTS=1 pytest -q -m live tests/test_blizzard_api_live.pywith real credentials to confirm them (CN endpoints and classic namespace strings are least certain). Seedocs/blizzard-api/README.mdanddocs/architecture/AUTH_ARCHITECTURE.md(phase 3). - WowProgress guild-snapshot + history-trajectory (AUR-392): new
wowprogress guild-snapshot <region> <realm> <name>composes current progress, world/region/realm ranks, item level, and encounter summary with a per-tier rank series into one provider-local normalized payload (kind: guild_snapshot) plus citations and freshness — built from a single guild-history traversal (no extra guild-page fetch in the command layer). Newwowprogress history-trajectory <region> <realm> <name>(kind: history_trajectory) emits per-tier rows ordered oldest→newest with tier-over-tierdelta_vs_previous(world/region/realm rank change with an explicitimprovedflag + item-level change), each row carrying itspage_url, plus caveats that each row is a final-for-tier snapshot (not live) and that consecutive tiers are different raids/difficulties (descriptive movement, not a normalized metric).wowprogress doctorreports the newguild_snapshot/history_trajectorycapabilities. - Raider.IO season-aware leaderboard + explicit
resolved_season(AUR-391): newraiderio leaderboard mythic-pluscommand — a thin, season-scoped, single-page view over the existing sampled-run primitive emittingkind: mythic_plus_leaderboard, aqueryblock with the explicitresolved_season, run rows, sampledfreshness(sampled_at+cache_ttl_seconds), andcitations.leaderboard_urls. Every Mythic+ analytics command (mythic-plus-runs,sample/distribution/thresholdfamilies) now additionally surfacesresolved_seasonin itsqueryblock alongside the existingseasonkey, and--season current(or empty) now resolves to the Raider.IO current default season (the request omits the season param and the effective slug is recovered from the API response) rather than forwarding the literalcurrent.raiderio doctorreports the new `mythic_p...
v0.3.0 — private reports + WCL resilience
Highlights
Private and guild-stealth reports actually work now
v0.2.0 routed authenticated traffic to /api/v2/user, but the typed encounter analytics still parsed the legacy report.table.entries shape. Live WCL responses use report.table.data.entries (and data.auras for buffs), so report-encounter-damage-source-summary, report-encounter-damage-target-summary, report-encounter-damage-breakdown, and report-encounter-aura-summary were silently returning zero rows on real reports despite passing the fixtures. The parser now drills into the data wrapper and the aura/damage commands return populated rows end-to-end against private reports the user has access to.
view-private-reports is now first-class alongside view-user-profile. auth login, auth pkce-login, auth status, auth token, and doctor decode the access token's JWT body to surface scopes.has_view_private_reports, and the scope_warning message walks you through what to re-auth for. WCL's token response omits the granted-scope list, so reading the JWT is the only reliable way to know what the user actually has — the CLI now uses it.
Tolerant partial-error handling for flaky WCL responses
WCL occasionally returns { "data": ..., "errors": [...] } partials — a non-nullable nested field errors out, the resolver nulls it, and the rest of the payload still resolves. Previously the client raised graphql_error and discarded the otherwise-good payload. Now the client tolerates partial errors with a recursive useful-data check: if any leaf value (or a non-empty list) is populated at any depth, the response flows through with the error list captured as last_warnings. If every leaf is null, the original GraphQL error is raised so users see the real failure rather than a misleading not_found.
Every WCL command surfaces those warnings as notes and graphql_warnings in its JSON output via a centralized _emit(client=client) path, so partial-failure context is no longer hidden from agents that script against the CLI.
Null GraphQL variables are dropped at the wire
WCL's GraphQL endpoint rejects null variable values with Internal server error instead of treating them as omitted. character-rankings ... --spec-name <slug> and similar flag-driven calls were 500'ing whenever the user left an optional flag unset and another was provided. The client now prunes None from the request body before posting, matching WCL's documented behavior of falling back to defaults for omitted variables. Cache keys use the pruned variable set so cache identity stays stable across null/omitted callers.
GuildAttendance no longer trips on Zone.frozen
Zone.frozen was the upstream field nulling out for real guilds (e.g. gn / us-malganis) and tripping the partial-error path. The query no longer selects it; pagination, rosters, and zone identity continue to flow.
report-events hint when a slice resolves to null
When a narrowed slice still returns events.data: null, the CLI now appends a hint that WCL needs --data-type (e.g. casts, damage-done, healing) for non-null event slices. Previously it surfaced a bare null with no indication of the missing flag.
Upgrading
pip install -U -e '.[dev]'
warcraftlogs auth login --scope view-user-profile --scope view-private-reports --redirect-uri http://localhost:8787/callback
Existing tokens issued without view-private-reports continue to work for public queries — auth status will report the missing scope so you know to re-auth before reaching for private data.
Full changelog
- #8 — Unblock private/guild-stealth report access end-to-end
- #9 — Tolerate WCL partial GraphQL errors and prune null variables
🤖 Reviewed by Codex across 4 iterations until clean.
v0.2.0 — WCL user-endpoint routing
Highlights
WCL: route through /api/v2/user when authenticated
Every report and guild GraphQL query previously went to /api/v2/client with the client-credentials token, even after a user completed an OAuth login. Private and guild-stealth reports therefore returned "permission denied" no matter how many times the user re-authenticated.
_graphql now delegates to _graphql_user whenever a non-expired user token is saved, so private reports become accessible as soon as the user has the right OAuth scope. Cache payloads are namespaced by endpoint family so client- and user-API responses can never collide on disk.
view-user-profile scope is now first-class
WCL requires the view-user-profile scope to establish a user identity; without it, userData.currentUser resolves to null and ACL checks fall through to anonymous. auth login, auth pkce-login, auth token, auth status, and doctor now expose scopes.granted, scopes.requested, scopes.has_view_user_profile, and a scope_warning when the scope is missing.
Authenticate with:
warcraftlogs auth login --scope view-user-profile --redirect-uri http://localhost:8787/callback
Public-API probes stay pinned to the client endpoint
rate_limit() and probe_live_public_api() use a new force_client=True opt-out so client-credential health checks always exercise /api/v2/client, even when a user token is saved. Without this, broken client credentials could be masked by a working user token.
Resilient auth state loading
load_provider_auth_state now returns None on OSError / JSONDecodeError, matching the contract provider_auth_status already followed. Corrupt or hand-edited state files no longer crash the CLI.
auth token no longer cries wolf
The scope_warning now only fires when there's actually a saved user token; fresh setup, logged-out, and corrupt-state cases report scope_warning: null.
Upgrading
pip install -U -e '.[dev]'
warcraftlogs auth login --scope view-user-profile --redirect-uri http://localhost:8787/callback
Existing tokens issued without view-user-profile will continue to work for public queries — auth status will report the missing scope so you know to re-auth before reaching for private data.
Full changelog
🤖 Reviewed by Codex across 5 iterations until clean.