Skip to content

Releases: aurokin/warcraft_cli

v0.6.0

Choose a tag to compare

@aurokin aurokin released this 04 Oct 07:39
dd77084

[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, provenance and data, plus error on failure. warcraft schema exposes 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-strict and --profile agent|human work on every binary and wrapper passthrough. Compact output preserves URLs, talent strings, export codes, SimC profiles and command/input values, and lists every cut in provenance.compacted_paths. Missing projected paths appear in fields_missing; strict projection fails missing_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 for pipx and uvx installation.
  • blizzard pvp-season, pvp-leaderboard and pvp-character read season metadata, title cutoffs, ranked players and character ratings/records. Leaderboards support --season and --limit up 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 collections returns mounts, pets, toys, heirlooms and transmogs with counts, name filters and limits. auctions <realm> and retail commodities summarize listings and units per item id with minimum and unit-weighted median prices in copper. --item-id selects exact ids with not_listed; otherwise --limit selects the most-listed items. Bid-only listings count toward units/listings but have no price. Market snapshots, leaderboards and PvP cutoffs report Blizzard's Last-Modified and 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-era and classic-anniversary, alongside retail and progression classic; doctor reports verification for all four. character also takes REGION REALM NAME, and --section reads PvP summaries/brackets, professions or mount/pet/toy collections as raw character_section data.
  • raiderio leaderboard raids --raid returns guild rankings with region/realm filters, paging and returned/requested counts. raiderio raids lists raid/encounter slugs; affixes returns this week's affixes; dungeons lists season dungeon pools; cutoffs returns regional top 0.1%, 1%, 10%, 25% and 40% player-rating cutoffs with UTC updated_at.
  • Raider.IO profiles add last_crawled_at; character profiles include every recent run and each dungeon's best run, with counts and played spec, spec_slug and role. Raid rankings disclose hidden pulls/percentages in privacy. Guild roster previews carry rank, active role and realm identity, sort guild master first, and support --roster-limit with roster_truncated; member counts mean members Raider.IO tracks.
  • icy-veins index-refresh crawls 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-snapshot replaces 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_export references for SimC. Converted rows carry class/spec build_identity, source.converted_from and 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-calc decodes 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 report decoded: false with 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-page add infobox/map facts, quest start/end and ordered chains, and NPC/object locations; entity reports spawn counts while entity-page keeps coordinates. Listview links retain tab ids and upstream drop/vendor listview_data. Linked previews expose fetch_more_truncated; Wiki navigation adds more_available and fetch_more_command.
  • Wowhead news and blue-tracker rows include UTC posted_at and timestamp-parse counts. Raider.IO run rows include logged_run_id, its own logged-run id rather than a Warcraft Logs code. Warcraft Logs guild/character rows add class_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; run returns player/target result headers. sim reports other actors, ranked profilesets and stat weights; SimC and Raidbots profilesets include mean_error, mean_stddev and 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.cache with 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-hour BLIZZARD_SNAPSHOT_CACHE_TTL_SECONDS for 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: {}, parsed query, and details under error.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 use auth_failed, not_found, rate_limited or upstream_error; connection errors use network_error. Lorrgs treats inaccessible private reports as not_found.
  • Search/resolve fields: every provider uses kind: "search_results"/"resolve_match", results/candidates, returned-row count, complete total_matches and truncated. Coming-soon/not-supported totals are null. Rows have provider, kind, id, name, url and follow_up: {command, surface}. Migrate recommended_command/recommended_surface and alternatives to command/surface and alternative_commands; article rows drop entity_type/type_name and repeated metadata slug/title. Raider.IO discovery replaces profile_url with url; profile/run payloads retain profile_url. Lorrgs/WCL resolve replace resolution with resolve_match; Lorrgs replaces results with candidates and drops repeated data.provider.
  • Search stubs use empty lists, count: 0, total_matches: null and truncated: false; resolve stubs include confidence: "none", resolved, match and next_command as 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 top match and a fallback search when one can help; zero-match results and Warcraft Logs' explicit-only discovery have no fallback.
  • Wrapper discovery: --brief replaces the old subcommand --compact; global --compact must precede the subcommand. The wrapper keeps provider rows and order, merges by query intent/title fit with a per-provider half-page cap, and exposes wrapper_ranking and merge_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...
Read more

v0.5.0

Choose a tag to compare

@aurokin aurokin released this 25 Jun 01:39

[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, or fresh). The wrapper now treats warcraftlogs as expansion_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 key fresh selects --site fresh. ptr, beta, and classic-ptr remain unsupported for Warcraft Logs and are rejected instead of coerced. Live coverage verifies token exchange and GraphQL schema access for all three site profiles when WARCRAFTLOGS_LIVE_TESTS=1 and credentials are configured.

Fixed

  • Warcraft Logs no longer depends on simc-cli at runtime for talent-tree transport validation. The shared validate_talent_tree_transport helper now lives in warcraft_core, with simc_cli.talent_transport kept as a compatibility re-export, and the provider-to-provider import-linter exception was removed (AUR-501).

v0.4.0 - Lorrgs cooldown evidence

Choose a tag to compare

@aurokin aurokin released this 23 Jun 15:38

[0.4.0] - 2026-06-23

Added

  • Lorrgs provider: new no-auth lorrgs command for the public Lorrgs API (https://api2.lorrgs.io/api/*) after a user requested support for lorrgs.io. Ships doctor; 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 commands spec-ranking <spec-slug> <boss-slug> and spec-ranking-info <spec-slug> <boss-slug> for top-parse cooldown timelines; comp-ranking <boss-slug> with repeatable --role/--spec filters and kill-time bounds; search / resolve for 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-only user-report / user-report-fights reads. Browser-driving the public Lorrgs page showed the UI preserves type=damage-done on /user_reports/<id>/fights, so user-report-fights now preserves type from report URLs and accepts --type. Each command emits {ok, provider, command, kind, query, provenance, data} on success, with provenance carrying the exact source_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) for not_found, invalid_query, rate_limited, http_error, network_error, and invalid_response. Routed through the wrapper as warcraft lorrgs ... with search/resolve ready, expansion_mode=fixed, and supported_expansions=["retail"]. The CLI intentionally does not expose Lorrgs queued load/dirty endpoints. Live public API coverage is gated by LORRGS_LIVE_TESTS=1 pytest -q -m live tests/test_lorrgs_live.py, including the user-provided Warcraft Logs URL https://www.warcraftlogs.com/reports/bG3xDYPqKjLm8XaR?fight=22&type=damage-done. See docs/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-parse spec-ranking samples, and Warcraft Logs report-events --data-type casts for 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 curseforge command for the public CurseForge addon API, decided GO in AUR-395. First slice ships curseforge doctor (install state, api_key auth posture for CURSEFORGE_API_KEY discovered via .env.local → ~/.config/warcraft/providers/curseforge.env → environment, and capability metadata — doctor/addon ready, search/resolve coming_soon) and curseforge addon <slug-or-id> — resolves the addon (numeric → mod id, else a gameId=1 slug search) and returns data.metadata (raw CurseForge mod record), data.latest_files (the mod's latestFiles), and data.changelog (the newest file's changelog, best-effort: top-level null only when the addon has no files; otherwise an object with the file's body — null when that file exposes no notes — plus source_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 carries game_id/mod_id/slug/resolved_by/source_urls — and {ok:false, ..., error:{code, message}} with a nonzero exit (never a traceback) for missing_api_key, addon_not_found, http_error, network_error, and invalid_response. Routed through the wrapper as warcraft curseforge … with expansion_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 carries provenance.verified: false; run CURSEFORGE_LIVE_TESTS=1 pytest -q -m live tests/test_curseforge_live.py with a real CURSEFORGE_API_KEY to confirm them. See docs/curseforge/README.md. Deferred: dependency graph / game-version compatibility / file-download workflows. search/resolve are not implemented yet but exist as structured coming_soon stubs (a JSON envelope with coming_soon: true and a suggested_command, not a Click "No such command" error), matching the surface doctor advertises.
  • Additive identity normalization across provider surfaces (AUR-458): raw fields preserved; each surface gains a sibling identity block from the shared warcraft_core.identity contract. raiderio — class_spec_identity on ranking roster rows, resolve character-profile candidates, character search results (class-only normalized), and guild roster_preview rows (high confidence only when both class+spec resolve, else none). raidbots — class_spec_identity on inspect-report actor summaries from the sim report's class/specialization (high when both present, unknown when both missing). wowprogress — class_spec_identity on the character payload (status normalized, confidence: none; spec sourced from the SimDPS table when present), and encounter_identity on guild encounter kill rows (name-only → normalized, encounter_id: null). method + icy-veins — ability_identity on Wowhead spell linked-entity rows (status canonical, spell_id set); other entity types unchanged. docs/foundation/IDENTITY_CONTRACT.md coverage 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 under blizzard-api-client-credentials, keyed by sha256(region, id, secret), reused until ~60s before expiry) and three typed read commands — blizzard realm <slug> (dynamic), blizzard item <id> (static), and blizzard character <realm> <name> (profile, retail only). Region/namespace routing is honored: --region (default BLIZZARD_REGION, else us; us/eu/kr/tw/cn with aliases like na), and --game-version retail|classic (or the --classic shorthand; passing --classic alongside a contradictory explicit --game-version is rejected with unsupported_game_version rather than silently routing to classic) selects the namespace-class infix; --locale passes through. Each command emits {ok, provider, command, kind, query, provenance, data} on success — provenance carries region/namespace/namespace_class/game_version/source_url — and {ok:false, ..., error:{code, message}} with a nonzero exit (never a traceback) for missing_client_credentials, unsupported_region, unsupported_game_version, classic_profile_unsupported, http_error, network_error, and invalid_response. blizzard doctor flips game_data/profile to ready and surfaces the region/routing block. search/resolve remain coming_soon but exist as structured stubs (a JSON envelope with coming_soon: true and a suggested_command, not a Click "No such command" error), matching the surface doctor advertises. The endpoint hosts, OAuth token URL, and namespace strings follow documented Blizzard API conventions but are pending one-time live confirmation — doctor carries this marker under region.verification and every command payload carries provenance.verified: false; run BLIZZARD_LIVE_TESTS=1 pytest -q -m live tests/test_blizzard_api_live.py with real credentials to confirm them (CN endpoints and classic namespace strings are least certain). See docs/blizzard-api/README.md and docs/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). New wowprogress history-trajectory <region> <realm> <name> (kind: history_trajectory) emits per-tier rows ordered oldest→newest with tier-over-tier delta_vs_previous (world/region/realm rank change with an explicit improved flag + item-level change), each row carrying its page_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 doctor reports the new guild_snapshot/history_trajectory capabilities.
  • Raider.IO season-aware leaderboard + explicit resolved_season (AUR-391): new raiderio leaderboard mythic-plus command — a thin, season-scoped, single-page view over the existing sampled-run primitive emitting kind: mythic_plus_leaderboard, a query block with the explicit resolved_season, run rows, sampled freshness (sampled_at + cache_ttl_seconds), and citations.leaderboard_urls. Every Mythic+ analytics command (mythic-plus-runs, sample/distribution/threshold families) now additionally surfaces resolved_season in its query block alongside the existing season key, 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 literal current. raiderio doctor reports the new `mythic_p...
Read more

v0.3.0 — private reports + WCL resilience

Choose a tag to compare

@aurokin aurokin released this 08 May 16:26

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

Choose a tag to compare

@aurokin aurokin released this 06 May 03:33
d56827f

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

#7

🤖 Reviewed by Codex across 5 iterations until clean.