lgs-search について
lgs-searchは、Magic: The Gatheringのカードリストを地域のゲームショップやオンラインカタログから調達するコマンドラインスキルです。実際の検索・カート結果に基づいてカードを割り当て、オンラインカートの商品と送料の合計を比較します。
主な機能
- 01デッキJSONまたは行ごとのリストに対応
- 02地域の2店舗を並行検索
- 03価格とカート条件に基づく割り当て
- 04オンラインの商品代と送料を比較
- 05未検出カードの報告と条件緩和検索
- 06カート作成とチェックアウト画面の表示
活用シーン
- 01デッキリストの複数店舗での購入計画
- 02カードの版や状態の条件指定
- 03在庫切れカードの再検索
- 04購入前の各店舗カート確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dan-blanchard/ mtg-skills --skill lgs-searchインストール後、Claude Code で/lgs-searchを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CONTEXT.mdpyproject.tomluv.lock詳細
LGS Search
2 つの地域のゲームショップ(The Gathering Place、Atomic Empire)と 2 つの大手オンラインカタログ(TCGPlayer、Mana Pool)から MTG カードリストを調達し、合計コストが最小になるように割り当てます。ユーザーが購入手続きをするカートは最大 3 つです。TGP、AE、そして不足分の購入先として TCGPlayer と Mana Pool のうち安い方です。
鉄則
CLI が返さなかった価格を報告してはいけません。
概要に記載する価格はすべて、Playwright の実際の検索結果またはカート結果から取得する必要があります。Scryfall の USD 価格は、LGS とオンラインショップのどちらで購入するかを確認する際のプロキシとして内部で使用されますが、レポートには表示されません。在庫切れリストと見つからなかったカードのリストには、学習データによる推測ではなく、実際のページで確認した結果を反映する必要があります。
オーケストレーターの実行
オーケストレーターは通常の長時間実行型 CLI で、Phase 5 / Phase 7 では画面表示ありの Chromium ウィンドウを開きます。Bash サブプロセスから安全に起動できます。ユーザーが OS 上で画面表示ありのウィンドウを閉じると、処理が次に進みます。サブプロセスは、端末での操作なしにウィンドウのクローズイベントを検知します。反射的に ! プレフィックス付きでの実行をユーザーに求めないでください。これは stdin から読み取る処理がある場合にのみ必要です。--yes で最初の確認を省略し、ログイン情報がすでに保存されていて、カートが空であれば、input() のプロンプトは表示されません。
エージェントに推奨される呼び出し方:
uv run --directory <skill-install-dir> lgs-search \
--input <list> --output-dir <dir> --yes
進行状況を報告する必要がある場合は、run_in_background=True を指定して、出力ファイルを順次確認してください。stdin のプロンプトが表示されると予想される具体的な理由がある場合(未ログインのショップでログインのフォールバックが実行される、または --clear-existing-carts を指定せずにカートの混入確認が表示される場合)に限り、代替案として ! lgs-search ... を提案してください。
セットアップ(初回実行時)
uv sync --directory <skill-install-dir>
uv run --directory <skill-install-dir> playwright install chromium
# One-time login per store (saves to ~/.cache/mtg-skills/lgs-profiles/<store>/):
lgs-search login --store tgp
lgs-search login --store atomic_empire
lgs-search login --store tcgplayer
lgs-search login --store manapool
保存したプロファイルには、そのショップで有効なセッション Cookie が保持されます。セッションが期限切れになるまでは、パスワードと同じように機能します。プロファイルディレクトリのアクセス権は所有者のみに制限されています(0700)。買い物の後にセッションを破棄するには、lgs-search logout(すべてのショップ)または lgs-search logout --store <store> を実行してください。
ログインを明示的に行わない場合、オーケストレーターは「not logged in」を最初に検知した際に、画面表示ありのウィンドウを開きます。やや手間は増えますが、機能上は同じです。
ワークフロー
Phase 1 — 入力の解決
解析済みデッキの JSON またはプレーンテキストのリスト(1 行につき 1 枚、Nx の接頭辞は任意)を受け付けます。メインボードとサイドボードは合算されます。基本土地(Plains、Island、Swamp、Mountain、Forest、Wastes)はデフォルトで除外され、概要に表示されます。除外を解除するには --include-basics を指定してください。Snow-Covered Plains などの冠雪基本土地は除外されません。
Phase 2 — LGS の一括検索
両方の LGS に対し、スレッドプールを使って店舗ごとの並列数上限を守りながら、すべてのカードを並行して検索します。各カードについて、ユーザーの設定(状態、フォイル、優先セット)に合致する出品のうち、最も安いものを結果として保持します。検索に失敗した場合は None として処理され、その店舗では該当カードが「在庫なし」と見なされます。
フェーズ 3 — 割り当て
各カードについて:
- どちらの LGS にも在庫がない場合、またはオンライン価格の目安が大幅に安い場合 (デフォルト:20%超または$2超)、カードはオンライン枠に振り分けられます。
- それ以外の場合:最安値の LGS が優先されます。両方の LGS の価格差が$1以内または 10%以内の場合は、店舗を回る回数を最小限にするため、現在のカート合計が 高いほうの LGS にカードをまとめます。
- 数量が1より多い場合:1店舗で必要数すべてを確保できなければ、店舗間で分けます。 残りはオンラインに振り分けます。
オンライン価格の目安には、各カードの全ての印刷版における、非フォイルかつ非デジタルの最安 USD 価格
を使用します(MTGJSON カードデータから取得し、共有カードプールを介するため、
表面の名前「Brazen Borrower」で指定された両面カードも価格が取得されます)。デフォルトの印刷版の
USD 価格ではありません。デフォルトの印刷版の価格では、安価な
再録を見落とすことがよくあります(例:Beast Within のデフォルト版には usd がありませんが、安価な再録版は$0.50です。Kessig Wolf Run のデフォルト版は約$15ですが、
最安の再録版は$0.30です)。バルクデータがない場合、目安価格は0.0となり、オンラインへの振り分け判定は
行われません。そのため、在庫のある LGS の掲載価格がすべて優先されます。MP/TCG の価格が5~30倍安い場合でも同様です。
オーケストレーターは、download-mtgjson が書き込む MTGJSON カードデータを
($MTG_SKILLS_CACHE_DIR または
~/.cache/mtg-skills/ の下で)自動検出します。これは他のすべてのツールが使用するものと同じ検索方法です。
データが見つからない場合は、オンラインへの振り分けが行われない問題について大きく警告し、
download-mtgjson を案内します。特定のパスが必要な場合を除き、--bulk-data は指定しないでください。
フェーズ 4 — オンラインでの最適化
オンラインに振り分けられたリストが TCGPlayer Mass Entry と Mana
Pool の /add-deck に送信されます。各サイトの最適化機能が実行され(販売者を集約し、
送料を計算)、商品代金+送料の合計が低いカートが選択されます。税金はチェックアウト時に計算されます。
両マーケットプレイスの一括送信エンドポイントは、既存のカートを置き換えるのではなく、追加します。そのため、最適化機能の合計にはカート全体(既存の商品+新たに送信した商品)が反映されます。アダプターは
bulk_submit_and_optimize の冒頭で get_existing_cart による事前確認を実行し、カートが空でない場合は
CartNotEmptyError を発生させます。オーケストレーターはこれを
optimize_marketplace の店舗ごとのループ内で捕捉し、「[store] カートにはすでに N 件の商品があります」という明確な
エラーを表示して、引き続き利用可能なマーケットプレイスで処理を続けます。
このエラーが表示された場合、再実行する前にユーザーが汚染されたカートを空にする必要があります。
MP 自体の UI にある「Clear cart」ボタンは動作しません(確認済み:クリックすると Sentry エラーが発生し、メタデータ同期の RPC は何も行いません)。そのためユーザーは、画面表示モードのハンドオフウィンドウで MP のカートを手動で空にします。
TGP の clear_cart はプログラムから動作し、--clear-existing-carts を指定すると呼び出されます。
フェーズ 5 — 確認ゲートとカートの構築
割り当て全体を Markdown で表示し、デフォルトで N を選択するプロンプトで続行するか確認します。プロンプトを省略するには --yes を指定します。カートに触れず、割り当ての生成後に停止するには --dry-run
を指定します。
カートに触れる前に、対象となるすべての店舗のカートを確認します。
カートが1つでも空でない場合は、汚染された店舗を明示するメッセージとともに処理を中止します。追加の前に自動で空にするには
--clear-existing-carts を指定します。
フェーズ 6 — 入手不能カードと制約を緩和した再試行
フェーズ 5 の後、どの店舗からも入手できないカードを一覧表示します。制約を緩和して再試行するか、ユーザーに確認します(MP/HP を許可、フォイルを許可、優先セットのバイアスを解除)。この再試行を1回自動で行うには、--retry-relaxed を指定します。
フェーズ 7 — ハンドオフ
空でないカートがある店舗ごとに、その店舗の永続プロファイルを使用し、カート URL を開いた画面表示の Chromium ウィンドウを起動します。ユーザーが内容を確認し、手動でチェックアウトします。省略するには --no-handoff を指定します。
--collection を指定すると、紙版の所有ルール(ADR-0058)に基づき、所有済みのカードを差し引きます。基本土地は所有済みとして扱われるため、リストから除外されます。ただし --include-basics を指定すると、コレクションに含まれる分を購入するため、基本土地がリストに残ります。
フラグのリファレンス
lgs-search \
--input <deck.json | cards.txt> \
[--collection <collection.json>]
[--bulk-data <default-cards.json>]
[--condition {nm,lp,mp,hp,any}] # default: lp (worst condition allowed)
[--allow-foil]
[--prefer-set <SET>]
[--lgs-online-threshold-pct 20]
[--lgs-online-threshold-usd 2.00]
[--consolidate-threshold-pct 10]
[--consolidate-threshold-usd 1.00]
[--no-handoff]
[--dry-run]
[--retry-relaxed]
[--clear-existing-carts]
[--include-basics]
[--yes]
[--search-timeout-seconds 20]
[--cart-timeout-seconds 30]
[--max-retries 3]
[--output-dir <dir>]
障害発生時の動作
- サイトのセレクターが変更された場合。 アダプターは、失敗したセレクターと見つからなかった URL を含む
StoreSelectorErrorを発生させます。オーケストレーターはエラーを表示し、そのストアの一括処理を中止します。カードを黙ってスキップすることはありません。 - ログインの有効期限が切れた場合。
is_logged_inによってストアごとに検出されます。遅延フォールバックにより、実行中にブラウザーを表示したログインウィンドウが開きます。ログイン後にウィンドウを閉じると、実行が再開されます。 - マーケットプレイスのカートに既存の商品がある場合。 マーケットプレイスのカートにすでに商品があると、
bulk_submit_and_optimizeはCartNotEmptyErrorを発生させます(Phase 4 では、ユーザーが事前に追加していた商品がオプティマイザーの比較に混在してしまいます)。オーケストレーターはそのマーケットプレイスをスキップし、ほかのマーケットプレイスで続行します。再実行する前に、ユーザーが手動でカートを空にします。 - TCGPlayer の CAPTCHA。 TCG のボット対策は Playwright の
persistent_contextを検知し、ログインをブロックします(確認済み)。ストアごとの失敗を許容するoptimize_marketplaceは、ログにメッセージを記録して TCG をスキップし、デフォルトで MP が最適な選択肢になります。TCG にブラウザーを表示してログインしようとしても時間の無駄です。CAPTCHA は解除できません。 - MP の「カートを空にする」が機能しない場合。 MP 独自の SvelteKit ハンドラーでは、「カートを空にする」ボタンを押すとエラーが発生し(Sentry で報告済み)、カートが変更されません。
--clear-existing-cartsは MP の場合、手動での確認プロンプトに切り替わります。ユーザーはブラウザーを表示したカートのウィンドウでカートを空にし、Enter キーを押します。 - カートの明細行数の上限。 一部のストアでは、1 つのカートに追加できる明細行数に上限があります。上限に達すると、アダプターは一部のみを入力し、残りがあることを報告します。残りの内容はサイドカーに記録され、概要にも表示されます。
- HTTP 429/レート制限。
Retry-Afterを尊重します。指定がない場合のデフォルトは 30s です。同じストアで 429 が 3 回連続すると、警告を表示してそのストアをスキップします。 - Phase 5 でカートへの追加に失敗した場合。 致命的なエラーです。根本原因を修正し、最初から再実行してください。途中から再開する方法はありません。
<output-dir>/lgs-cart-allocation.jsonのサイドカーは、発生した内容を記録する書き込み専用の監査記録であり、チェックポイントではありません。
deck-wizard/cube-wizard への引き継ぎ
このスキルがデッキを変更することはありません。見つからなかったカードは Markdown の概要に表示され、代わりのカードを使うかどうかは、呼び出し元のスキルまたはユーザーが判断します。類似カードを自動的に「入れ替える」方法はありません。