From hs-cli
Use when the user wants anything involving Hearthstone — decode/analyze/compare decks, look up cards by name or id, search cards with filters, or query game metadata (sets, classes, card types, rarities). Triggers on deck-code strings (base64 starting with "AAEC", "AAEB", "AAEA"), Hearthstone in any language or community nickname (HS, Blizzard Hearthstone; 하스스톤, 하스, 돌겜, 하섭; ハースストーン, ハース, ハスス; 炉石传说, 炉石, 炉石酒馆战棋; 爐石戰記, 爐石, 爐戰; Hearth, Pedra do Lar; Хартстоун, Хартса), mode keywords in any language (Standard / Wild / Twist / Arena / Battlegrounds / BG; 정규 / 야생 / 전장 / 투기장 / 선술집난투 / 용병단; 标准 / 狂野 / 酒馆战棋 / 竞技场), mana-curve/dust/win-condition/meta/nerf discussion, single-card lookups, name searches in any language, and class/format/expansion queries.
How this skill is triggered — by the user, by Claude, or both
Slash command
/hs-cli:hearthstone-deckThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use the `hs` CLI to decode deck codes, look up and search cards, and query game metadata for any agent task that involves Hearthstone.
Use the hs CLI to decode deck codes, look up and search cards, and query game metadata for any agent task that involves Hearthstone.
This skill covers the full surface of the hs CLI:
hs deck <code> — decode and summarize a deck codehs card <dbfId|cardId|name> — single-card lookuphs card --search <q> [--class CLASS] [--cost N] — name + filter searchhs meta sets|classes|types|rarities — game metadataThis skill applies whenever the user references Hearthstone in any language, including community nicknames. Common names to recognize:
| Locale | Official | Common short / slang |
|---|---|---|
| English (global) | Hearthstone | HS, Hearth |
| 한국어 (KR) | 하스스톤 | 하스, 돌겜, 하섭 (server context), HS |
| 日本語 (JP) | ハースストーン | ハース, ハスス, HS |
| 中文 (简体, CN) | 炉石传说 | 炉石, LSCS (pinyin abbr.), HS |
| 中文 (繁體, TW/HK) | 爐石戰記 | 爐石, 爐戰, HS |
| Português (BR) | Hearthstone | HS, Hearth, Pedra do Lar (colloquial) |
| Español (ES/LATAM) | Hearthstone | HS, Hearth |
| Français (FR) | Hearthstone | HS |
| Deutsch (DE) | Hearthstone | HS |
| Русский (RU) | Hearthstone / Хартстоун | Хартса, HS |
| Tiếng Việt (VN) | Hearthstone | HS |
| ภาษาไทย (TH) | Hearthstone | HS |
| Mode | English | 한국어 | 日本語 | 中文(简) |
|---|---|---|---|---|
| Constructed Standard | Standard | 정규 | スタンダード | 标准 |
| Constructed Wild | Wild | 야생 | ワイルド | 狂野 |
| Constructed Twist | Twist | 트위스트 | ツイスト | 时变 |
| Arena | Arena | 투기장 | 闘技場 | 竞技场 |
| Battlegrounds | Battlegrounds / BG | 전장 | バトルグラウンド / BG | 酒馆战棋 |
| Tavern Brawl | Tavern Brawl | 선술집 난투 | 酒場の喧嘩 | 乱斗 |
| Mercenaries | Mercenaries | 용병단 | 傭兵団 | 佣兵战纪 |
| Duels (retired 2026) | Duels | 결투 | デュエル | 决斗 |
| Ranked ladder | Ranked / Constructed | 등급전, 일반전 | ランク戦 | 排名赛 |
Treat any of the above as a trigger.
These are not modes but appear frequently in community discussion across languages — recognize them as Hearthstone context:
The CLI defaults to enUS data. When the user passes -l ko or has HS_CLI_LOCALE=ko set, output will contain Korean (Hangul) class and card names. Use the table to translate output for the user's prompt language and to recognize community slang as a trigger.
Cell format: Official (short / slang). Empty cell = no widely-used localized form, English is used.
| Class code | English (slang) | 한국어 (축약) | 日本語 (略) | 中文 (简, 简称) | 中文 (繁, 簡稱) | Español | Français | Deutsch (Kürzel) | Русский |
|---|---|---|---|---|---|---|---|---|---|
| DEATHKNIGHT | Death Knight (DK) | 죽음의 기사 (죽기, DK) | デスナイト (DK) | 死亡骑士 (死骑, DK) | 死亡騎士 (DK) | Caballero de la Muerte (DK) | Chevalier de la mort (CdM) | Todesritter (TR) | Рыцарь Смерти (РС) |
| DEMONHUNTER | Demon Hunter (DH) | 악마 사냥꾼 (악사, DH) | デーモンハンター (DH) | 恶魔猎手 (恶魔, DH) | 惡魔獵人 (DH) | Cazador de demonios (DH) | Chasseur de démons (CdD) | Dämonenjäger (DJ) | Охотник на демонов (ОД) |
| DRUID | Druid | 드루이드 (드루) | ドルイド (ドル) | 德鲁伊 (德) | 德魯伊 (德) | Druida | Druide | Druide | Друид |
| HUNTER | Hunter (Hunt) | 사냥꾼 (사냥, 헌터) | ハンター (ハン) | 猎人 (猎) | 獵人 (獵) | Cazador | Chasseur | Jäger | Охотник |
| MAGE | Mage | 마법사 (법사, 마법) | メイジ | 法师 (法) | 法師 (法) | Mago | Mage | Magier | Маг |
| PALADIN | Paladin (Pally / Pal) | 성기사 (성기, 팔라, 팔라딘) | パラディン (パラ) | 圣骑士 (圣骑, 骑士) | 聖騎士 (聖騎, 騎士) | Paladín | Paladin | Paladin | Паладин (Палыч) |
| PRIEST | Priest | 사제 (프리스트) | プリースト (プリ) | 牧师 (牧) | 牧師 (牧) | Sacerdote | Prêtre | Priester | Жрец |
| ROGUE | Rogue | 도적 (도둑, 로그) | ローグ | 潜行者 (盗贼, 贼) | 盜賊 (賊) | Pícaro | Voleur | Schurke | Разбойник (Роуг) |
| SHAMAN | Shaman (Shammy) | 주술사 (주술, 슈먼) | シャーマン (シャマ) | 萨满 (萨) | 薩滿 (薩) | Chamán | Chaman | Schamane | Шаман |
| WARLOCK | Warlock (Lock / Locks) | 흑마법사 (흑마, 흑법) | ウォーロック (ロック) | 术士 (术) | 術士 (術) | Brujo | Démoniste | Hexenmeister (Hexer) | Чернокнижник (ЧК) |
| WARRIOR | Warrior (Warr) | 전사 (워리) | ウォリアー (ウォリ) | 战士 (战) | 戰士 (戰) | Guerrero | Guerrier | Krieger | Воин |
| NEUTRAL | Neutral | 중립 | 中立 | 中立 | 中立 | Neutral | Neutre | Neutral | Нейтрал |
Two additional codes appear in CLI filter validation but rarely in community talk:
WHIZBANG — Maestro Whizbang dynamic-deck hero. Most languages just write "Whizbang" / 위즈뱅 / ウィズバン / 维兹班.DREAM — internal "Dream" class for boss-token cards (Ysera-only historically). Not used in community deck talk.When the user uses any cell in the table (e.g. "용암 흑법덱 보여줘", "Lock deck", "术士 OTK"), normalize to the class code in the first column and pass it as --class.
The hs binary must be on PATH. Install via any of:
brew install say8425/tap/hs-cli # macOS / Linux, no runtime needed
npm install -g @say8425/hs-cli # any OS with Node 22+
# or download a binary from https://github.com/say8425/hs-cli/releases/latest
For local development of the CLI itself, clone the repo and run bun install && bun run build && bun link (requires Bun 1.3+).
hs --version # verify
Card data is auto-fetched from the HearthstoneJSON CDN on first use (cached 24h at ~/.hs-cli/). No API key required.
If hs is not on PATH, instruct the user to install via one of the channels above before running any commands.
Default locale is enUS. Use -l ko (or HS_CLI_LOCALE=ko) for Korean card names.
Every subcommand accepts:
-f, --format <table|json> — output format. Default table (agent-friendly compressed). Switch to json only to extract specific fields.-l, --locale <code> — card data locale. Default enUS. Accepts short forms: ko, ko-KR, koKR all resolve to koKR. Supported: enUS enGB frFR deDE koKR esES esMX ruRU zhTW zhCN itIT ptBR plPL jaJP thTH.--help — show usage for the subcommand.Top-level: hs --version, hs --help.
hs deck <code>Decode a deck code into a card list. Reports class, format, dust cost, mana curve, and the 30 cards.
<code>: required, base64 deck code (typically starts with AAEC / AAEB / AAEA).0 if the code decodes cleanly, 1 if invalid or corrupted.hs deck AAECAQcAA0VjgAEAAA== # table (default, enUS)
hs deck AAECAQcAA0VjgAEAAA== -l koKR # Korean card names
hs deck AAECAQcAA0VjgAEAAA== -f json # raw JSON
hs card <id|name>Single-card lookup. The positional accepts a dbfId (numeric), a cardId (string), or a name. Resolution order:
findCardByDbfId.findCardById (exact cardId match like EX1_572).findCardById returns nothing → fallback to searchCards(query) and pick the first match.--search must be provided. Otherwise the CLI exits with Provide a card ID, dbfId, or use --search.hs card 64034 # by dbfId (enUS, default)
hs card EX1_572 # by cardId
hs card "Lich King" # by name (enUS data)
hs card "리치 왕" -l ko # Korean name (koKR data)
hs card EX1_572 -f json | jq .cost # extract a single field
hs card --search <query>Substring search with optional filters. Both --search and -s (short alias) work.
--search, -s <string> — substring match against card name (English and Korean fields).--class <CLASS> — one of DEATHKNIGHT, DEMONHUNTER, DRUID, HUNTER, MAGE, PALADIN, PRIEST, ROGUE, SHAMAN, WARLOCK, WARRIOR, NEUTRAL, WHIZBANG, DREAM. Case-insensitive (CLI uppercases internally).--cost <N> — mana cost. Accepts a string; CLI parses it as an integer.An empty --search "" is rejected by the CLI (treated as missing). Use --search " " (single space) as a wildcard substring to "browse all X-cost Y-class cards".
hs card --search "Zilliax" # enUS (default)
hs card --search "질리악스" -l ko # Korean search
hs card -s "fire" --class MAGE --cost 3
hs card --search " " --class PRIEST --cost 3 # browse all 3-cost priest cards
hs meta <type>Game metadata. Useful for filtering, validation, or class-name translation.
<type>: required. One of sets, classes, types, rarities. Any other value exits with Invalid type: ... Must be one of: sets, classes, types, rarities.hs meta classes # 14 class codes
hs meta sets # every released set
hs meta types # MINION, SPELL, WEAPON, HERO, ...
hs meta rarities # FREE, COMMON, RARE, EPIC, LEGENDARY
Cache the output in working memory for the session — these rarely change.
table. Switch to -f json only when you need specific fields.hs deck first, then drill into individual cards with hs card <id> only if needed.hs card --search rather than guessing IDs.hs card <id>.Unknown (dbfId). This is a HearthstoneJSON data gap, not a CLI bug.hs meta repeatedly within a session — cache the output.Multi-step workflows that combine commands or pipe to jq. Single-command usage lives in Command reference.
These recipes target the questions Hearthstone players actually ask in communities (Reddit /r/hearthstone & /r/CompetitiveHS, 인벤 하스 갤러리, NGA 炉石区, 5ch ハース). Each one is tagged with the colloquial question it answers.
hs deck <code> -f json
{
"heroClass": "WARRIOR", # class code (KO/EN map in table form)
"heroDbfId": 7,
"format": "standard"|"wild",
"cards": [ { "card": { ... }, "count": 1|2 }, ... ]
}
hs card <id> -f json | jq keys
# artist, attack, cardClass, collectible, cost, dbfId, flavor, health,
# id, mechanics[], name, race, races[], rarity, referencedTags[], set,
# spellSchool, text, type
# Notes:
# - mechanics / referencedTags can be null
# - race is single ("DRAGON"), races is the multi-tribe array ("ALL" etc.)
# - text and name are in the active locale (enUS by default; use -l ko for koKR)
# - spellSchool is set only on cards with type == "SPELL"
# (values: FIRE, FROST, NATURE, ARCANE, HOLY, SHADOW, FEL, ...)
hs meta <type> -f json
{ "type": "sets"|"classes"|"types"|"rarities", "values": [ "...", ... ] }
The deck JSON does NOT include total dust or mana curve — only the table format renders those. Compute them with jq when needed (see "Total dust" recipe).
hs meta returns raw enum codes (English). The CLI does not embed a Standard / Wild rotation tag, dust cost, or localized labels — these come from the table in the Class names / Game modes / Common community shorthand sections above, or from official Blizzard rotation pages.
hs deck "$A" -f json > /tmp/a.json
hs deck "$B" -f json > /tmp/b.json
diff <(jq -r '.cards[] | "\(.count) \(.card.name)"' /tmp/a.json | sort) \
<(jq -r '.cards[] | "\(.count) \(.card.name)"' /tmp/b.json | sort)
The deck JSON has rarity but no per-card dust value. Map rarity → craft cost in jq:
hs deck "$CODE" -f json | jq '
def dust:
if . == "LEGENDARY" then 1600
elif . == "EPIC" then 400
elif . == "RARE" then 100
else 40 end;
[ .cards[] | .count * (.card.rarity | dust) ] | add
'
hs deck "$CODE" -f json | jq '
[.cards[] | {cost: .card.cost, count}]
| group_by(.cost)
| map({cost: .[0].cost, total: (map(.count) | add)})
'
hs deck "$CODE" -f json | jq '
[.cards[] | {r: .card.rarity, n: .count}]
| group_by(.r)
| map({rarity: .[0].r, count: (map(.n) | add)})
'
hs deck "$CODE" -f json | jq '
[.cards[] | {t: .card.type, n: .count}]
| group_by(.t)
| map({type: .[0].t, count: (map(.n) | add)})
'
hs deck "$CODE" -f json | jq '
[.cards[] | {n: .count, neutral: (.card.cardClass == "NEUTRAL")}]
| group_by(.neutral)
| map({neutral: .[0].neutral, count: (map(.n) | add)})
'
TRIBE=DRAGON # MURLOC, PIRATE, BEAST, DEMON, MECH, ELEMENTAL, UNDEAD, NAGA, QUILBOAR, ALL
hs deck "$CODE" -f json | jq --arg t "$TRIBE" '
[.cards[] | select(.card.race == $t) | {name: .card.name, n: .count}]
'
hs deck "$CODE" -f json | jq '
[.cards[] | {set: .card.set, n: .count}]
| group_by(.set)
| map({set: .[0].set, count: (map(.n) | add)})
| sort_by(-.count)
'
for ID in EX1_572 OG_141; do
hs card "$ID" -f json | jq '{name, cost, attack, health, type, rarity, text}'
done
The CLI doesn't classify archetype directly. Use --search " " plus --class and --cost to enumerate the candidate pool, then build your own curve:
for COST in 1 2 3; do
hs card --search " " --class ROGUE --cost "$COST" -f json | jq '.[].name'
done
hs deck "$CODE" -f json | jq '
.cards[] | select(.card.rarity == "LEGENDARY") | {name: .card.name, count}
'
--search " " returns all candidates at that filter; pipe through jq for tribe:
hs card --search " " --class MAGE --cost 3 -f json | jq '
.[] | select(.race == "DRAGON") | {name, attack, health, text}
'
When HearthstoneJSON doesn't have a dbfId, the card object shows name: "Unknown (<id>)". Surface them so the user understands the data gap:
hs deck "$CODE" -f json | jq '.cards[] | select(.card.type == "UNKNOWN") | .card'
if ! hs deck "$CODE" > /dev/null 2>&1; then
echo "Invalid deck code"
exit 1
fi
hs deck "$CODE" # table summary
# Look up by Korean name → get the dbfId → re-fetch in English
hs card --search "리치 왕" -l ko -f json | jq '.[0] | {name, id, dbfId}'
# Then look up the same card in English using the dbfId (locale-independent)
hs card <dbfId> # enUS (default)
# Or look up in any target locale directly
hs card <dbfId> -l jaJP # Japanese name
--class code ("법사 카드 뽑아줘", "show me Lock cards", "术士牌")The user types a class in their language or slang. Validate with hs meta classes before passing to --class, and fall back to the multilingual class table in this skill for translation.
USER_CLASS="법사" # could be Lock, 法师, Mage, パラ, etc.
# Step 1: translate via the class table in this SKILL (above) -> e.g. "법사" -> MAGE
CLASS_CODE="MAGE"
# Step 2: verify it is a valid CLI filter value
if hs meta classes -f json | jq -e --arg c "$CLASS_CODE" '.values | index($c)' > /dev/null; then
hs card --search " " --class "$CLASS_CODE"
else
echo "Invalid class code"; hs meta classes
fi
Mechanics live on each card object as an array. Aggregate them across the deck:
hs deck "$CODE" -f json | jq '
[.cards[] | (.card.mechanics // []) | .[] as $m | {mech: $m, count: $count // 1}]
'
# Compact version: how many copies of each mechanic appear
hs deck "$CODE" -f json | jq '
[ .cards[] as $c | ($c.card.mechanics // []) | .[] | {mech: ., n: $c.count} ]
| group_by(.mech)
| map({mech: .[0].mech, total: (map(.n) | add)})
| sort_by(-.total)
'
Common mechanics in card data: BATTLECRY, DEATHRATTLE, RUSH, CHARGE, TAUNT, STEALTH, DIVINE_SHIELD, LIFESTEAL, WINDFURY, POISONOUS, SECRET, OVERLOAD, COMBO, INSPIRE, SPELL_POWER. Korean community labels: 전투의 함성 / 죽음의 메아리 / 속공 / 돌진 / 도발 / 은신 / 천상의 보호막 / 생명력 흡수.
hs meta raritieshs meta rarities enumerates the rarity tier codes; dust values come from community knowledge (no CLI source for them). Useful when displaying rarity → craft cost together:
hs meta rarities -f json | jq -r '
.values[] |
(if . == "LEGENDARY" then [., 1600]
elif . == "EPIC" then [., 400]
elif . == "RARE" then [., 100]
else [., 40] end)
| "\(.[0]): \(.[1]) dust"
'
hs meta types includes Battlegrounds-internal, Mercenaries, and editor-mode codes that never appear in a constructed deck. Filter to the codes a deck-analysis agent actually cares about:
hs meta types -f json | jq -r '
.values[] | select(test("BATTLEGROUND|LETTUCE|MOVE_MINION|GAME_MODE|PET") | not)
'
# Yields: ENCHANTMENT, HERO, HERO_POWER, LOCATION, MINION, SPELL, WEAPON.
# For a "what's in this deck?" answer, only MINION / SPELL / WEAPON / LOCATION matter.
The CLI does NOT tag sets with Standard / Wild membership or release date. Workarounds:
# All set codes (raw enum, alphabetical):
hs meta sets
# Set of a specific card (the card object carries it):
hs card EX1_572 -f json | jq .set # -> "EXPERT1"
# Per-deck set distribution (see "Set distribution & rotation risk" recipe above).
For "is this set in Standard right now?", point the user to the official Blizzard rotation page — that data is not in the CLI.
The CLI doesn't classify archetypes. Approximate by sampling cards and checking signals:
hs deck "$CODE" -f json | jq '
{
avg_cost: ([.cards[] | .card.cost * .count] | add) / 30,
pirate_count: ([.cards[] | select(.card.race == "PIRATE") | .count] | add // 0),
dragon_count: ([.cards[] | select(.card.race == "DRAGON") | .count] | add // 0),
has_taunt: ([.cards[] | select((.card.mechanics // []) | index("TAUNT"))] | length > 0),
has_secret: ([.cards[] | select((.card.mechanics // []) | index("SECRET"))] | length > 0),
big_cards: [.cards[] | select(.card.cost >= 7) | {name: .card.name, cost: .card.cost, n: .count}]
}
'
# Interpretation rules of thumb:
# avg_cost < 2.7 -> Aggro
# 2.7 <= avg < 3.5 -> Midrange / Tempo
# avg_cost >= 3.5 -> Control
# pirate_count >= 6 or dragon_count >= 6 -> tribe-themed
Card text is in the active locale. The example below uses -l ko to grep for the Korean phrase "카드를 뽑" (covers "카드를 뽑습니다", "카드를 뽑으세요", etc.). For enUS, grep for "Draw" instead.
hs deck "$CODE" -l ko -f json | jq '
[ .cards[] | select(.card.text and (.card.text | test("카드를 뽑"))) | {name: .card.name, n: .count} ]
| {sources: ., total: (map(.n) | add // 0)}
'
spellSchool is set on spell cards only.
hs deck "$CODE" -f json | jq '
[ .cards[] | select(.card.type == "SPELL") | {school: (.card.spellSchool // "NONE"), n: .count} ]
| group_by(.school)
| map({school: .[0].school, count: (map(.n) | add)})
| sort_by(-.count)
'
Step 1: figure out the user's class and current tribe count from the deck. Step 2: enumerate tribe candidates inside the same class.
HERO_CLASS=$(hs deck "$CODE" -f json | jq -r .heroClass)
TRIBE=DRAGON
# class-locked + neutral candidates that share the tribe:
hs card --search " " --class "$HERO_CLASS" -f json | jq --arg t "$TRIBE" '
.[] | select(.race == $t or (.races // []) | index($t)) | {name, cost, attack, health, dbfId}
'
hs card --search " " --class NEUTRAL -f json | jq --arg t "$TRIBE" '
.[] | select(.race == $t or (.races // []) | index($t)) | {name, cost, attack, health, dbfId}
'
Useful for evaluating whether a custom card / class identity card is over- or under-statted versus the 2/cost vanilla benchmark.
hs card --search " " --class NEUTRAL -f json | jq '
[ .[] | select(.type == "MINION" and .collectible) ]
| group_by(.cost)
| map({
cost: .[0].cost,
sample: length,
avg_attack: (map(.attack) | add / length),
avg_health: (map(.health) | add / length)
})
'
Heuristic: high-cost legendaries with damage / kill text. Combine cost threshold + rarity + Korean text greps.
hs deck "$CODE" -f json | jq '
.cards[] | select(.card.rarity == "LEGENDARY" and .card.cost >= 5)
| {name: .card.name, cost: .card.cost, type: .card.type, n: .count, text: .card.text}
'
Discover effects are tagged in referencedTags (not always in mechanics). Korean text key: 발견.
hs card --search "발견" -l ko -f json | jq '
.[] | select(.collectible and (.cardClass == "PRIEST")) # tweak class
| {name, cost, type, text}
'
hs deck "$CODE" -f json | jq --arg k "TAUNT" '
.cards[] | select((.card.mechanics // []) | index($k))
| {name: .card.name, cost: .card.cost, n: .count}
'
# Replace TAUNT with RUSH, DIVINE_SHIELD, LIFESTEAL, DEATHRATTLE, SECRET, etc.
races array is set when a card belongs to more than one tribe (e.g. "Murloc + Beast" or "ALL").
hs deck "$CODE" -f json | jq '
.cards[] | select((.card.races // []) | length > 1)
| {name: .card.name, races: .card.races, n: .count}
'
hs card --search and localeSearch matches against card name fields in the active locale. The default locale is enUS, so English queries work out of the box. For other languages, pass -l <code> or set HS_CLI_LOCALE.
# English search (default enUS)
hs card --search "Zilliax"
# Korean search — requires koKR data
hs card --search "질리악스" -l ko
# Japanese search — requires jaJP data
hs card --search "ジリアックス" -l jaJP
# If you only know the cardId or dbfId, locale doesn't matter
hs card EX1_567 # Doomhammer — works in any locale
hs card 1657 # by dbfId — locale-independent
When the user's prompt is in English, use the default enUS (no flag needed). When the prompt is in Korean, add -l ko (or advise export HS_CLI_LOCALE=ko). cardId and dbfId lookups are locale-independent and always work.
Unknown (dbfId).npx claudepluginhub say8425/hs-cli --plugin hs-cliSearches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.